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 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 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 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 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)
A *plotalt1* chart of `Car1` and `Car2`.

plotalt2: A scatterplot of multiple alternatives with respect to two attributes:

>>> plotalt2(car, "PRICE", "TECH.CHAR.")
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)
`CAR` alternatives plotted on parallel axes.

plotalt_radar: Plots multiple alternatives with respect to multiple attributes, using a “radar” chart:

>>> plotalt_radar(car)
`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