Examples ======== A typical DEXiPy workflow ------------------------- This example uses a simple DEXi model for evaluating cars, which is distributed together with the DEXi software (including DEXiPy) and is used throughout DEX literature to illustrate the methodological approach (`https://en.wikipedia.org/wiki/Decision_EXpert `_). First, this model is loaded and printed as follows: >>> import dexipy.dexi as dxi >>> car = dxi.read_dexi("data/car.dxi") >>> print(car) DEXi Model: CAR_MODEL Description: Car demo index id structure scale funct 0 CAR_MODEL CAR_MODEL 1 CAR +- CAR unacc; acc; good; exc (+) 12 3x4 2 PRICE |- PRICE high; medium; low (+) 9 3x3 3 BUY.PRICE | |- BUY.PRICE high; medium; low (+) 4 MAINT.PRICE | +- MAINT.PRICE high; medium; low (+) 5 TECH.CHAR. +- TECH.CHAR. bad; acc; good; exc (+) 9 3x3 6 COMFORT |- COMFORT small; medium; high (+) 36 3x4x3 7 #PERS | |- #PERS to_2; 3-4; more (+) 8 #DOORS | |- #DOORS 2; 3; 4; more (+) 9 LUGGAGE | +- LUGGAGE small; medium; big (+) 10 SAFETY +- SAFETY small; medium; high (+) Rows in the printout correspond to individual attributes. The columns display: * `index`: Indices of attributes. * `id`: Unique attribute IDs, generated by DEXiPy from original DEXi names, in order to assure unambiguous referencing of attributes. * `structure`: The hierarchical structure of attributes, named as in the original DEXi model. * `scale`: Value scales associated with each attribute. The symbol ``(+)`` indicates that the corresponding scale is ordered preferentially in an increasing order. * `funct`: Information about the size (number of rules) and dimensions of the corresponding decision tables. Looking at the structure of attributes, please notice that the attribute at index 0 is virtual and does not appear in the original DEXi model. In DEXiPy it allows using models that have multiple root attributes; these models appear as subtrees of the virtual root. The "real" root of `CAR_MODEL` is actually `CAR` at index 1. It depends on two lower-level attributes, `PRICE` and `TECH.CHAR.` These are decomposed further. Overall, the model consists of: 1. six input (*basic*) attributes: `BUY.PRICE`, `MAINT.PRICE`, `#PERS`, `#DOORS`, `LUGGAGE` and `SAFETY`, and 2. four output (*aggregate*) attributes: `CAR`, `PRICE`, `TECH.CHAR.` and `COMFORT`. Among the latter, `CAR` is the most important and represents the overall evaluation of cars. The next step usually consists of defining a decision alternative or a list of alternatives (i.e., cars in this case). The Car model already comes with a list of two cars, accessible using :py:attr:`dexipy.dexi.DexiModel.alternatives`. Each alternative is represented as a dictionary: >>> car.alternatives[0] {'name': 'Car1', 'CAR': 3, 'PRICE': 2, 'BUY.PRICE': 1, 'MAINT.PRICE': 2, 'TECH.CHAR.': 3, 'COMFORT': 2, '#PERS': 2, '#DOORS': 2, 'LUGGAGE': 2, 'SAFETY': 2} Alternatives can be printed in a tabular form: >>> print(car.alt_table()) alternative Car1 Car2 CAR 3 2 PRICE 2 1 BUY.PRICE 1 1 MAINT.PRICE 2 1 TECH.CHAR. 3 2 COMFORT 2 2 #PERS 2 2 #DOORS 2 2 LUGGAGE 2 2 SAFETY 2 1 In this printout, attribute values are shown using the internal DEXiPy representation, i.e., using ordinal value numbers. A more readable output can be obtained by :py:meth:`dexipy.dexi.DexiModel.alt_text`: >>> print(car.alt_text()) alternative Car1 Car2 CAR exc good PRICE low medium BUY.PRICE medium medium MAINT.PRICE low medium TECH.CHAR. exc good COMFORT high high #PERS more more #DOORS 4 4 LUGGAGE big big SAFETY high medium This data can be edited using common Python list and dictionary functions. Additionally, DEXiPy provides the method :py:meth:`dexipy.dexi.DexiModel.alternative` for defining a single decision alternative, for example: >>> alt = car.alternative("MyCar1", values = {'BUY.PRICE': "low", 'MAINT.PRICE': 2, '#PERS': "more", '#DOORS': "4", 'LUGGAGE': 2, 'SAFETY': "medium"}) >>> print(car.alt_table(alt)) alternative MyCar1 CAR None PRICE None BUY.PRICE low MAINT.PRICE 2 TECH.CHAR. None COMFORT None #PERS more #DOORS 4 LUGGAGE 2 SAFETY medium Finally, alternatives can be evaluated using :py:func:`dexipy.dexi.DexiModel.evaluate`: >>> eval_alt = car.evaluate(alt) >>> print(car.alt_text(eval_alt)) alternative MyCar1 CAR exc PRICE low BUY.PRICE low MAINT.PRICE low TECH.CHAR. good COMFORT high #PERS more #DOORS 4 LUGGAGE big SAFETY medium Examples of using value sets and distributions ---------------------------------------------- For example, let us consider a car for which we have no evidence about its possible maintenance costs. For the value of `MAINT.PRICE`, we may use ``"*"`` to denote the full range of the corresponding attribute values (in this case equivalent to ``{0, 1, 2}`` or ``('high', 'medium', 'low')``. Notice how the evaluation method considers all the possible values of `MAINT.PRICE` and propagates them upwards the model structure. >>> alt = car.alternative("MyCar1a", values = {'BUY.PRICE': "low", 'MAINT.PRICE': "*", '#PERS': "more", '#DOORS': "4", 'LUGGAGE': 2, 'SAFETY': "medium"}) >>> eval_alt = car.evaluate(alt) >>> print(car.alt_text(eval_alt)) alternative MyCar1a CAR ('unacc', 'exc') PRICE ('high', 'low') BUY.PRICE low MAINT.PRICE ('high', 'medium', 'low') TECH.CHAR. good COMFORT high #PERS more #DOORS 4 LUGGAGE big SAFETY medium The above result is not really useful, as the car turns out to be ``('unacc', 'exc')``, that is, either ``"unacc"`` or ``"exc"``, depending on maintenance costs. Thus, let us try using value distribution for `MAINT.PRICE`, telling DEXiPy that high maintenance costs are somewhat unexpected (with probability *p* = 0.1) and that medium costs (*p* = 0.6) are more likely than low (*p* = 0.3). The evaluation method ``"prob"`` gives the following results: >>> alt = car.alternative("MyCar1b", values = {'BUY.PRICE': "low", 'MAINT.PRICE': {"low": 0.3, "medium": 0.6, "high": 0.1}, '#PERS': "more", '#DOORS': "4", 'LUGGAGE': 2, 'SAFETY': "medium"}) >>> eval_alt = car.evaluate(alt, method = "prob") >>> print(car.alt_text(eval_alt, decimals = 2)) alternative MyCar1b CAR {'unacc': 0.1, 'exc': 0.9} PRICE {'high': 0.1, 'low': 0.9} BUY.PRICE low MAINT.PRICE {'high': 0.1, 'medium': 0.6, 'low': 0.3} TECH.CHAR. good COMFORT high #PERS more #DOORS 4 LUGGAGE big SAFETY medium In this case, the final evaluation of `CAR` is ``{'unacc': 0.1, 'exc': 0.9}``, that is, it is much more likely that ``MyCar1b`` is ``"exc"`` than ``"unacc"``. Analysis of alternatives ------------------------ In DEXiPy, the evaluation of alternatives can be enhanced by analysis. There are three main analysis methods implemented in DEXiPy: `selective explanation`, `plus/minus analysis and `comparison of alternatives`. The following examples illustrate these analyses using the two alternatives, `Car1` and `Car2`, included in the `Car.dxi` model. **Selective explanation** prints out particularly bad and particularly good evaluations: >>> car.selective_explanation() Alternative Car1 Weak points None Strong points attribute Car1 +-CAR exc |-PRICE low | +-MAINT.PRICE low +-TECH.CHAR. exc |-COMFORT high | |-#PERS more | +-LUGGAGE big +-SAFETY high Alternative Car2 Weak points None Strong points attribute Car2 |-COMFORT high | |-#PERS more | +-LUGGAGE big **Plus/minus analysis** investigates the effects of changing one attribute value at a time. For example, considering alternative `Car2`: >>> car.plus_minus(car.alternatives[1]) Attribute -2 -1 Car2 +1 +2 CAR good | |-BUY.PRICE [ unacc medium exc ] | +-MAINT.PRICE [ unacc medium exc ] | |-#PERS unacc more ] | |-#DOORS unacc 4 ] | +-LUGGAGE unacc big ] +-SAFETY [ unacc medium exc ] Here, the column `Car2` shows the current situation, where the overall value of `Car2` is "good". The columns labelled from -2 to +2 display *overall evaluations* (i.e., values of the `CAR` attribute) in the cases when the values of corresponding attributes change for the indicated number of qualitative steps. For instance, if `BUY.PRICE` takes a one step lower value (-1, from "medium" to "high"; the latter is not shown explicitly), the overall `CAR` evaluation would have become "unacc". Similarly, if `SAFETY` improves by +1, i.e., from "medium" to "high", the overall evaluation improves to "exc". The backets ``[`` and ``]`` indicate positions that exceed scale limits. **Compare alternatives** compares an alternative with other alternatives. For instance, comparison of `Car1` with `Car2`: >>> car.compare_alternatives(car.alternatives[0], car.alternatives[1]) Attribute Car1 Car2 +-CAR exc > good |-PRICE low > medium | |-BUY.PRICE medium = | +-MAINT.PRICE low > medium +-TECH.CHAR. exc > good |-COMFORT high = | |-#PERS more = | |-#DOORS 4 = | +-LUGGAGE big = +-SAFETY high > medium Charts ------ DEXiPy provides several methods for drawing alternatives' data and evaluation results: **plotalt1**: Plots multiple alternatives with respect to a single attribute: >>> plotalt1(car) .. image:: images/plotalt1.png :width: 450 :alt: A *plotalt1* chart of `Car1` and `Car2`. **plotalt2**: A scatterplot of multiple alternatives with respect to two attributes: >>> plotalt2(car, "PRICE", "TECH.CHAR.") .. image:: images/plotalt2.png :width: 450 :alt: A *plotalt2* chart of two `CAR` alternatives with `PRICE` and `TECH.CHAR.` on the axes. **plotalt_parallel**: Plots multiple alternatives with respect to multiple attributes, using parallel axes: >>> plotalt_parallel(car) .. image:: images/plotalt_parallel.png :alt: `CAR` alternatives plotted on parallel axes. **plotalt_radar**: Plots multiple alternatives with respect to multiple attributes, using a "radar" chart: >>> plotalt_radar(car) .. image:: images/plotalt_radar.png :width: 450 :alt: `CAR` alternatives plotted on a "radar" chart. Writing alternatives -------------------- Any alternatives, either read to or created in DEXiPy Python environment, can be written to an external tab-delimited or csv-type file, which can be subsequently imported to the DEXi or DEXiWin software. When the `filename` argument is "", as in the following examples, file contents is written to the console: >>> import csv >>> # use csv format >>> write_alternatives(car, car.alternatives, "", quotechar = '"', quoting = csv.QUOTE_ALL) "name","Car1","Car2" "CAR","4","3" ". PRICE","3","2" ". . BUY.PRICE","2","2" ". . MAINT.PRICE","3","2" ". TECH.CHAR.","4","3" ". . COMFORT","3","3" ". . . #PERS","3","3" ". . . #DOORS","3","3" ". . . LUGGAGE","3","3" ". . SAFETY","3","2" >>> # use tab-delimited format >>> write_alternatives(car, car.alternatives, "", delimiter = "\t") name Car1 Car2 CAR 4 3 . PRICE 3 2 . . BUY.PRICE 2 2 . . MAINT.PRICE 3 2 . TECH.CHAR. 4 3 . . COMFORT 3 3 . . . #PERS 3 3 . . . #DOORS 3 3 . . . LUGGAGE 3 3 . . SAFETY 3 2 Further Jupyter notebook examples --------------------------------- .. toctree:: :maxdepth: 1 notebooks/Car notebooks/Data