Introduction

What is AHP?

AHP is a method allowing individuals or groups to make complex decisions. The core concept of AHP is that alternatives are always compared pairwise (and not, say, by giving a score, or sorting alternatives). AHP is used in many fields, from finance to criminal investigation.

For a general introduction to AHP, you might be interested in this.

Another good introduction is here

For a more formal introduction to the subject, see: Saaty, T. L. (2001). Decision Making for Leaders: The Analytic Hierarchy Process for Decisions in a Complex World, New Edition 2001 (3 Revised). Pittsburgh, PA: RWS Publications.

About this Package

This package is built on top of the data.tree package, and uses R6 reference classes. To learn more about these, you might be interested in the Introduction:

library(R6)
vignette('Introduction', package = 'R6')

Even though this is not required, the data.tree Introduction vignette might also be helpful:

library(data.tree)
vignette(package = 'data.tree')

What is in this Document?

This vignette follows a well-known AHP example that you can also find e.g. on Wikipedia. In this example, the Jones family wants to buy a new car and uses AHP to decide systematically which car they should buy. Even though a few details are different from the original article, it is recommended to consider this vignette as an extension of that article, and not as a self sufficient document.

Creating the AHP criteria tree

When deciding which car to buy, the family considers the following criteria: cost, safety, style, and capacity. Additionally, some of the criteria have sub-criteria.

The Jones’ have a shortlist of 6 cars.

We can draw the hierarchy like this:

Ahp Tree

The ahp file format

Using the ahp package, the AHP hierarchy tree can be defined in an ahp file. An ahp file is a text file in yaml format, following a few conventions. For example:

## #########################
## # Alternatives Section
## #
## 
## Alternatives: &alternatives
## # Here, we list all the alternatives, together with their attributes. 
## # We can use these attributes later in the file when defining 
## # preferenceFunctions. The attributes can be quantitative or 
## # qualitative.
##   Accord Sedan:
##     price: 20360
##     mpg: 31
##     passengers: 5
##     cargo: 14
##     curb weight: 3289
##     safety class: Midsize Car
##     crash rating: 4 in Side Impact Front
##     residual value: 0.52
##   Accord Hybrid:
##     price: 31090
##     mpg: 35
##     passengers: 5
##     cargo: 14
##     curb weight: 3501
##     safety class: Midsize Car
##     crash rating: 4 in Side Impact Front
##     residual value: 0.46
##   Pilot:
##     price: 27595
##     mpg: 22
##     passengers: 8
##     cargo: 87.6
##     curb weight: 4264
##     safety class: Midsize SUV
##     crash rating: 4 in Rollover
##     residual value: 0.4
##   CR-V:
##     price: 20700
##     mpg: 27
##     passengers: 5
##     cargo: 72.9
##     curb weight: 3389
##     safety class: Small SUV
##     crash rating: 4 in Rollover
##     residual value: 0.55
##   Element:
##     price: 18980
##     mpg: 25
##     passengers: 4
##     cargo: 74.6
##     curb weight: 3433
##     safety class: Small SUV
##     crash rating: 3 in Rollover
##     residual value: 0.48
##   Odyssey:
##     price: 25645
##     mpg: 26
##     passengers: 8
##     cargo: 147.4
##     curb weight: 4385
##     safety class: Minivan
##     crash rating: All 5 Stars
##     residual value: 0.48
## 
## #
## # End of Alternatives Section
## #####################################
## 
## #####################################
## # Goal Section
## #
## 
## 
## Goal:
## # The goal spans a tree of criteria and the alternatives
##   name: Buy Car
##   preferences:
##     # preferences are defined pairwise
##     # 1 means: A is equal to B
##     # 9 means: A is highly preferrable to B
##     # 1/9 means: B is highly preferrable to A
##     - [Cost, Safety, 3]
##     - [Cost, Style, 7]
##     - [Cost, Capacity, 3]
##     - [Safety, Style, 9]
##     - [Safety, Capacity, 1]
##     - [Style, Capacity, 1/7]
##   children: 
##     Cost:
##       preferences:
##         - [Purchase Price, Fuel Cost, 2]
##         - [Purchase Price, Maintenance Cost, 5]
##         - [Purchase Price, Resale Value, 3]
##         - [Fuel Cost, Maintenance Cost, 2]
##         - [Fuel Cost, Resale Value, 2]
##         - [Maintenance Cost, Resale Value, 1/2]
##       children:
##         Purchase Price:
##           preferences: 
##             - [Accord Sedan, Accord Hybrid, 9]
##             - [Accord Sedan, Pilot, 9]
##             - [Accord Sedan, CR-V, 1]
##             - [Accord Sedan, Element, 1/2]
##             - [Accord Sedan, Odyssey, 5]
##             - [Accord Hybrid, Pilot, 1]
##             - [Accord Hybrid, CR-V, 1/9]
##             - [Accord Hybrid, Element, 1/9]
##             - [Accord Hybrid, Odyssey, 1/7]
##             - [Pilot, CR-V, 1/9]
##             - [Pilot, Element, 1/9]
##             - [Pilot, Odyssey, 1/7]
##             - [CR-V, Element, 1/2]
##             - [CR-V, Odyssey, 5]
##             - [Element, Odyssey, 6]
##           children: *alternatives
##           # We don't need to retype the alternatives here. Instead
##           # we can simply make a reference to the alternatives anchor
##           # defined in the alternatives section of the file.
##         Fuel Cost:
##           # Alternatively to the pairwise preferences, you 
##           # can define a preference function. This function
##           # is in R syntax, and needs to have two arguments.
##           # The Calculate method will pass all combinations
##           # of alternatives to this function, and the function
##           # is expected to return the pairwise preference, i.e.
##           # a number between 1/9 and 9.
##           preferenceFunction: 
##             function(a1, a2) min(9, max(1/9, a1$mpg/a2$mpg))
##           children: *alternatives
##         Maintenance Cost: 
##           preferences: 
##             - [Accord Sedan, Accord Hybrid, 1.5]
##             - [Accord Sedan, Pilot, 4]
##             - [Accord Sedan, CR-V, 4]
##             - [Accord Sedan, Element, 4]
##             - [Accord Sedan, Odyssey, 5]
##             - [Accord Hybrid, Pilot, 4]
##             - [Accord Hybrid, CR-V, 4]
##             - [Accord Hybrid, Element, 4]
##             - [Accord Hybrid, Odyssey, 5]
##             - [Pilot, CR-V, 1]
##             - [Pilot, Element, 1.2]
##             - [Pilot, Odyssey, 1]
##             - [CR-V, Element, 1]
##             - [CR-V, Odyssey, 3]
##             - [Element, Odyssey, 2]
##           children: *alternatives
##         Resale Value:
##           preferenceFunction: >
##             GetResalePreference <- function(a1, a2) {
##               if (a1$`residual value` < a2$`residual value`) return (1/GetResalePreference(a2, a1))
##               ratio <- a1$`residual value` / a2$`residual value`
##               if (ratio < 1.05) return (1)
##               if (ratio < 1.1) return (2)
##               if (ratio < 1.15) return (3)
##               if (ratio < 1.2) return (4)
##               if (ratio < 1.25) return (5)
##               return (5)
##             }
##           children: *alternatives 
##     Safety: 
##       preferences:
##         - [Accord Sedan, Accord Hybrid, 1]
##         - [Accord Sedan, Pilot, 5]
##         - [Accord Sedan, CR-V, 7]
##         - [Accord Sedan, Element, 9]
##         - [Accord Sedan, Odyssey, 1/3]
##         - [Accord Hybrid, Pilot, 5]
##         - [Accord Hybrid, CR-V, 7]
##         - [Accord Hybrid, Element, 9]
##         - [Accord Hybrid, Odyssey, 1/3]
##         - [Pilot, CR-V, 2]
##         - [Pilot, Element, 9]
##         - [Pilot, Odyssey, 1/8]
##         - [CR-V, Element, 2]
##         - [CR-V, Odyssey, 1/8]
##         - [Element, Odyssey, 1/9]
##       children: *alternatives
##     Style: 
##       preferences: 
##             - [Accord Sedan, Accord Hybrid, 1]
##             - [Accord Sedan, Pilot, 7]
##             - [Accord Sedan, CR-V, 5]
##             - [Accord Sedan, Element, 9]
##             - [Accord Sedan, Odyssey, 6]
##             - [Accord Hybrid, Pilot, 7]
##             - [Accord Hybrid, CR-V, 5]
##             - [Accord Hybrid, Element, 9]
##             - [Accord Hybrid, Odyssey, 6]
##             - [Pilot, CR-V, 1/6]
##             - [Pilot, Element, 3]
##             - [Pilot, Odyssey, 1/3]
##             - [CR-V, Element, 7]
##             - [CR-V, Odyssey, 5]
##             - [Element, Odyssey, 1/5]
##       children: *alternatives
##     Capacity:
##       preferences:
##         - [Cargo Capacity, Passenger Capacity, 1/5]
##       children:
##         Cargo Capacity: 
##           preferenceFunction: >
##             CargoPreference <- function(a1, a2) {
##               if (a1$cargo < a2$cargo) return (1/CargoPreference(a2, a1))
##               ratio <- a1$cargo / a2$cargo
##               if (ratio < 3) return (1)
##               if (ratio < 8) return (2)
##               return (3)
##             }
##           children: *alternatives
##         Passenger Capacity: 
##           preferences:
##             - [Accord Sedan, Accord Hybrid, 1]
##             - [Accord Sedan, Pilot, 1/2]
##             - [Accord Sedan, CR-V, 1]
##             - [Accord Sedan, Element, 3]
##             - [Accord Sedan, Odyssey, 1/2]
##             - [Accord Hybrid, Pilot, 1/2]
##             - [Accord Hybrid, CR-V, 1]
##             - [Accord Hybrid, Element, 3]
##             - [Accord Hybrid, Odyssey, 1/2]
##             - [Pilot, CR-V, 2]
##             - [Pilot, Element, 6]
##             - [Pilot, Odyssey, 1]
##             - [CR-V, Element, 3]
##             - [CR-V, Odyssey, 1/2]
##             - [Element, Odyssey, 1/6]
##           children: *alternatives
## 
## #
## # End of Goal Section
## #####################################

Let’s look at the various elements of this file.

Alternatives

The first section is a list of alternatives. Note that they have properties. For example:

Alternatives: &alternatives
              Accord Sedan:
                price: 20360
                mpg: 31
                passengers: 5
                cargo: 14
                curb weight: 3289
                safety class: Midsize Car
                crash rating: 4 in Side Impact Front
                residual value: 0.52
              Accord Hybrid:
                price: 31090
                mpg: 35
                passengers: 5
                ...

You are entirely free regarding the type and format of information you store in your alternatives. Some models can do without any properties at all. However, often you would like to define your preferences as a function of properties. Later, you will see how you can define functions that refer to these properties to derive the pairwise preferences of alternatives with respect to a specific criteria.

Goal

The second section of the ahp file is the Goal. Here, you define your criteria tree, your pairwise preferences, and - if applicable - your preference functions.

Criteria

Criteria are grouped hierarchically. Each criteria has at least the following sections:

  1. a name of the criteria (e.g. Cost)
  2. the preferences, either
    • pairwise preferences, or
    • a preferenceFunction
  3. its sub-criteria (e.g. Maintenance Cost) or alternatives, as we’ll see in the next section

An example of a criteria which has explicit pairwise preference is the

Cost:
  preferences:
    - [Purchase Price, Fuel Cost, 2]
    - [Purchase Price, Maintenance Cost, 5]
    - [Purchase Price, Resale Value, 3]
    - [Fuel Cost, Maintenance Cost, 2]
    - [Fuel Cost, Resale Value, 2]
    - [Maintenance Cost, Resale Value, 1/2]
  children:
    Purchase Price:
      preferences: 
      ...

Here, we say, for instance, that Purchase Price is slightly more important to Fuel Cost. Purchase Price is, however, much more important than maintenance cost.

Alternatives

To keep the ahp file short, you can reference to the alternatives defined above. In order for this to work, your alternatives need to be defined as an anchor, using the & sign:

Alternatives: &alternatives
              Accord Sedan:
                price: 20360
                ...

Note that this is a standard YAML feature.

You can then later refer to the anchor using the * reference feature:

Style: 
  preferences: 
    - [Accord Sedan, Accord Hybrid, 1]
    - [Accord Sedan, Pilot, 7]
    - [Accord Sedan, CR-V, 5]
    - [Accord Sedan, Element, 9]
    - [Accord Sedan, Odyssey, 6]
    - [Accord Hybrid, Pilot, 7]
    - [Accord Hybrid, CR-V, 5]
    - [Accord Hybrid, Element, 9]
    - [Accord Hybrid, Odyssey, 6]
    - [Pilot, CR-V, 1/6]
    - [Pilot, Element, 3]
    - [Pilot, Odyssey, 1/3]
    - [CR-V, Element, 7]
    - [CR-V, Odyssey, 5]
    - [Element, Odyssey, 1/5]
  children: *alternatives

Preference Function

Instead of defining the preferences explicitly in a pairwise manner, you can define a preference function. For example, you can define

Fuel Cost:
  preferenceFunction: 
    function(a1, a2) min(9, max(1/9, a1$mpg/a2$mpg))
  children: *alternatives
  ...

Note that, in this case, we have a field preferenceFunction instead of preferences. The function itself is a standard R function, and the arguments a1 and a2 are pairwise combinations of the criteria, i.e. in this case, the alternatives.

Here, we simply say that the AHP preference is the ratio of the mpg (i.e. miles per gallon) a car uses. And since we store properties mpg on the alternatives, we can reference them directly here. We want the ratio to be between 1/9 and 9, as these are the typical AHP acceptable preferences.

You can even define a named preference function that you can use for recursion:

Resale Value:
  preferenceFunction: >
    GetResalePreference <- function(a1, a2) {
      if (a1$`residual value` < a2$`residual value`) return (1/GetResalePreference(a2, a1))
      ratio <- a1$`residual value` / a2$`residual value`
      if (ratio < 1.05) return (1)
      if (ratio < 1.1) return (2)
      if (ratio < 1.15) return (3)
      if (ratio < 1.2) return (4)
      if (ratio < 1.25) return (5)
      return (5)
    }
  children: *alternatives 

Loading the ahp file

Loading the file is done by calling LoadFile:

library(ahp)
ahpFile <- system.file("extdata", "car.ahp", package="ahp")
carAhp <- LoadFile(ahpFile)

The ahp tree structure

carAhp, the return value of LoadFile, is a data.tree structure. Its structure is the same as the one of the file. If you are interested in solving AHP problems programmatically, all you need to do is to create a tree with that structure, using the data.tree functions.

library(data.tree)
print(carAhp, filterFun = isNotLeaf)
##                     levelName
## 1  Buy Car                   
## 2   ¦--Cost                  
## 3   ¦   ¦--Purchase Price    
## 4   ¦   ¦--Fuel Cost         
## 5   ¦   ¦--Maintenance Cost  
## 6   ¦   °--Resale Value      
## 7   ¦--Safety                
## 8   ¦--Style                 
## 9   °--Capacity              
## 10      ¦--Cargo Capacity    
## 11      °--Passenger Capacity

Calculating the AHP problem

Calculate(carAhp)
print(carAhp, "weight")
##                     levelName     weight
## 1  Buy Car                    1.00000000
## 2   ¦--Cost                   0.51007502
## 3   ¦   ¦--Purchase Price     0.48805379
## 4   ¦   ¦   ¦--Accord Sedan   0.24617542
## 5   ¦   ¦   ¦--Accord Hybrid  0.02455426
## 6   ¦   ¦   ¦--Pilot          0.02455426
## 7   ¦   ¦   ¦--CR-V           0.24617542
## 8   ¦   ¦   ¦--Element        0.36575063
## 9   ¦   ¦   °--Odyssey        0.09279001
## 10  ¦   ¦--Fuel Cost          0.25153610
## 11  ¦   ¦   ¦--Accord Sedan   0.18674699
## 12  ¦   ¦   ¦--Accord Hybrid  0.21084337
## 13  ¦   ¦   ¦--Pilot          0.13253012
## 14  ¦   ¦   ¦--CR-V           0.16265060
## 15  ¦   ¦   ¦--Element        0.15060241
## 16  ¦   ¦   °--Odyssey        0.15662651
## 17  ¦   ¦--Maintenance Cost   0.09986256
## 18  ¦   ¦   ¦--Accord Sedan   0.35819852
## 19  ¦   ¦   ¦--Accord Hybrid  0.31325042
## 20  ¦   ¦   ¦--Pilot          0.08369953
## 21  ¦   ¦   ¦--CR-V           0.09953251
## 22  ¦   ¦   ¦--Element        0.08791282
## 23  ¦   ¦   °--Odyssey        0.05740619
## 24  ¦   °--Resale Value       0.16054755
## 25  ¦       ¦--Accord Sedan   0.23408904
## 26  ¦       ¦--Accord Hybrid  0.11101029
## 27  ¦       ¦--Pilot          0.03808108
## 28  ¦       ¦--CR-V           0.36023288
## 29  ¦       ¦--Element        0.12829335
## 30  ¦       °--Odyssey        0.12829335
## 31  ¦--Safety                 0.23435219
## 32  ¦   ¦--Accord Sedan       0.21624385
## 33  ¦   ¦--Accord Hybrid      0.21624385
## 34  ¦   ¦--Pilot              0.07515991
## 35  ¦   ¦--CR-V               0.03603025
## 36  ¦   ¦--Element            0.02229468
## 37  ¦   °--Odyssey            0.43402745
## 38  ¦--Style                  0.04051875
## 39  ¦   ¦--Accord Sedan       0.35785923
## 40  ¦   ¦--Accord Hybrid      0.35785923
## 41  ¦   ¦--Pilot              0.03886101
## 42  ¦   ¦--CR-V               0.15508522
## 43  ¦   ¦--Element            0.02275249
## 44  ¦   °--Odyssey            0.06758283
## 45  °--Capacity               0.21505404
## 46      ¦--Cargo Capacity     0.16666667
## 47      ¦   ¦--Accord Sedan   0.09193018
## 48      ¦   ¦--Accord Hybrid  0.09193018
## 49      ¦   ¦--Pilot          0.19640904
## 50      ¦   ¦--CR-V           0.19640904
## 51      ¦   ¦--Element        0.19640904
## 52      ¦   °--Odyssey        0.22691251
## 53      °--Passenger Capacity 0.83333333
## 54          ¦--Accord Sedan   0.13636364
## 55          ¦--Accord Hybrid  0.13636364
## 56          ¦--Pilot          0.27272727
## 57          ¦--CR-V           0.13636364
## 58          ¦--Element        0.04545455
## 59          °--Odyssey        0.27272727

Running the analysis

GetDataFrame(carAhp)
##                               Weight Odyssey Accord Sedan  CR-V Accord Hybrid Element Pilot Consistency
## 1  Buy Car                    100.0%   21.8%        21.6% 16.3%         15.1%   14.7% 10.6%        7.4%
## 2   ¦--Cost                    51.0%    5.7%        12.3% 11.7%          5.8%   12.5%  3.0%        1.5%
## 3   ¦   ¦--Purchase Price      24.9%    2.3%         6.1%  6.1%          0.6%    9.1%  0.6%        6.8%
## 4   ¦   ¦--Fuel Cost           12.8%    2.0%         2.4%  2.1%          2.7%    1.9%  1.7%        0.0%
## 5   ¦   ¦--Maintenance Cost     5.1%    0.3%         1.8%  0.5%          1.6%    0.4%  0.4%        2.3%
## 6   ¦   °--Resale Value         8.2%    1.1%         1.9%  2.9%          0.9%    1.1%  0.3%        3.2%
## 7   ¦--Safety                  23.4%   10.2%         5.1%  0.8%          5.1%    0.5%  1.8%        8.1%
## 8   ¦--Style                    4.1%    0.3%         1.5%  0.6%          1.5%    0.1%  0.2%       10.2%
## 9   °--Capacity                21.5%    5.7%         2.8%  3.1%          2.8%    1.5%  5.6%        0.0%
## 10      ¦--Cargo Capacity       3.6%    0.8%         0.3%  0.7%          0.3%    0.7%  0.7%        0.4%
## 11      °--Passenger Capacity  17.9%    4.9%         2.4%  2.4%          2.4%    0.8%  4.9%        0.0%

Note that, thanks to the formattable package, the columns are numerics, even though they are formatted as percentages.

Another way to display the AHP analysis is with the ShowTable function, which displays an html table with color codings to help interpret the numbers:

ShowTable(carAhp)
Weight Odyssey Accord Sedan CR-V Accord Hybrid Element Pilot Consistency
Buy Car 100.0% 21.8% 21.6% 16.3% 15.1% 14.7% 10.6% 7.4%
Cost 51.0% 5.7% 12.3% 11.7% 5.8% 12.5% 3.0% 1.5%
Purchase Price 24.9% 2.3% 6.1% 6.1% 0.6% 9.1% 0.6% 6.8%
Fuel Cost 12.8% 2.0% 2.4% 2.1% 2.7% 1.9% 1.7% 0.0%
Maintenance Cost 5.1% 0.3% 1.8% 0.5% 1.6% 0.4% 0.4% 2.3%
Resale Value 8.2% 1.1% 1.9% 2.9% 0.9% 1.1% 0.3% 3.2%
Safety 23.4% 10.2% 5.1% 0.8% 5.1% 0.5% 1.8% 8.1%
Style 4.1% 0.3% 1.5% 0.6% 1.5% 0.1% 0.2% 10.2%
Capacity 21.5% 5.7% 2.8% 3.1% 2.8% 1.5% 5.6% 0.0%
Cargo Capacity 3.6% 0.8% 0.3% 0.7% 0.3% 0.7% 0.7% 0.4%
Passenger Capacity 17.9% 4.9% 2.4% 2.4% 2.4% 0.8% 4.9% 0.0%