Examples of FuzzyAHP package application (ver. 0.9.0)

Jan Caha

2017-03-09 12:57:26

Introduction

The package consists several S4 objects that are used in set of functions. For each object various validation conditions are checked in order to ensure that objects are valid and that their usage in functions will provide valid result. Each problem that can occur is described as best as possible in the error message which should help with solving the issue.

AHP (non fuzzy)

Pairwise Comparison Matrix Creation

The pairwise comparison matrix can either be loaded from a file or inputted manually. Since the version 0.6.9 the values can be represented as either character or double. However, the character representation seem as slightly better idea because it allows printing more comprehensible output. The matrix needs to be squared reciprocal matrix with maximal value smaller or equal to 9.

The matrix in text file can have the following form.

;"crit1";"crit2";"crit3"
"crit1";"1";"9";"5" 
"crit2";"1/9";"1";"1/3"
"crit3";"1/5";"3";"1"

Since version 0.9.0 it is also possible to use only upper or lower triangle of the pairwise comparison matrix (including the main diagonal). So the definition above can also look like this:

;"crit1";"crit2";"crit3"
"crit1";"1";"9";"5" 
"crit2";"";"1";"1/3"
"crit3";"";"";"1"

In case of numerical matrix the values representing undefined comparisons is either 0 or NA.

The loading is then done as:

matrixFile =  "comparison_matrix.csv"
comparisonMatrix = read.csv(matrixFile, sep = ";",
                   stringsAsFactors = FALSE, header = TRUE, row.names = 1, strip.white = TRUE)
comparisonMatrix = as.matrix(comparisonMatrix)

If the matrix is inputted manually then it can be done by following commands:

comparisonMatrixValues = c(1,9,5,
                           NA,1,1/3,
                           NA,NA,1)
comparisonMatrix = matrix(comparisonMatrixValues, nrow = 3, ncol = 3, byrow = TRUE)

In the case that the values are represented as double. If the values are represented as character then the code looks like this:

comparisonMatrixValues = c("1","9","5",
                           "","1","1/3",
                           "","","1")
comparisonMatrix = matrix(comparisonMatrixValues, nrow = 3, ncol = 3, byrow = TRUE)

Either way the comparison matrix is created using command pairwiseComparisonMatrix. After printing we can see that the S4 object contains two representation of the matrix, one as characters and other one as numeric as well as slot for variables names, which in this case is filled automatically.

comparisonMatrix = pairwiseComparisonMatrix(comparisonMatrix)
show(comparisonMatrix)
## An object of class "PairwiseComparisonMatrix"
## Slot "valuesChar":
##      [,1]  [,2] [,3] 
## [1,] "1"   "9"  "5"  
## [2,] "1/9" "1"  "1/3"
## [3,] "1/5" "3"  "1"  
## 
## Slot "values":
##           [,1] [,2]      [,3]
## [1,] 1.0000000    9 5.0000000
## [2,] 0.1111111    1 0.3333333
## [3,] 0.2000000    3 1.0000000
## 
## Slot "variableNames":
## [1] "C_1" "C_2" "C_3"

The textual representation of the pairwise comparison matrix can be obtained as:

textMatrix = textRepresentation(comparisonMatrix, whole = FALSE)
print(textMatrix)
##     C_1 C_2 C_3
## C_1   1   9   5
## C_2       1 1/3
## C_3           1

The whole parameter specifies if the full matrix is printed or only the upper triangle including main diagonal. Setting the parameter to TRUE is equal to using:

print(comparisonMatrix)
##     C_1 C_2 C_3
## C_1   1   9   5
## C_2 1/9   1 1/3
## C_3 1/5   3   1

Testing consistency of Pairwise Comparison Matrix

There are three consistency checks implemented in FuzzyAHP package. The first is consistency ratio as defined by T. Saaty (1980). The functions output short message summarizing the calculation and provides value of the consistency ratio. The consistency ration can be calculated for matrices with size up to \(15 \times 15\) according to T. L. Saaty and Tran (2007).

consistencyRatio(comparisonMatrix)
## Consistency ratio is: 0.0279459296138796. The pairwise comparison matrix is consistent for calculations.
## [1] 0.02794593
CR = consistencyRatio(comparisonMatrix, print.report = FALSE)
print(CR)
## [1] 0.02794593

Another check is a weak consistency that checks if for \(a_{ij}>1\) and \(a_{jk}>1\) applies that \(a_{ik}>=\max(a_{ij},a_{jk})\) for every \(i,j,k = 1,2,\dots,n\), where \(n\) is a size of matrix \(a\) (Stoklasa, Jandová, and Talašová 2013). The functions returns TRUE if the matrix passes the test or FALSE if it fails and print short message (warning message summarizing the issues, if they exist, is printed as well).

weakConsistency = weakConsistency(comparisonMatrix)
## The comparison matrix is weakly consistent.

Strict consistency is much stronger prerequisite that is generally true only for absolutely consistent evaluator. It might be problematic to fulfill this condition for more complex matrices. However, it seems reasonable to try this verification just to study the outcome. The strict consistency checks that \(a_{ik} = a_{ij} \times a_{jk}\) for every \(i,j,k = 1,2,\dots,n\), where \(n\) is a size of matrix \(a\) (Basile and D’Apuzzo 1997). Again TRUE or FALSE value is returned along with short message.

strictConsistency = strictConsistency(comparisonMatrix)
## Comparison matrix isn't strictly consistent. These indeces violate the condition: 
## [1,3] != ([1,2]*[2,3]) -- 5 != 3
## [1,2] != ([1,3]*[3,2]) -- 9 != 15
## [2,3] != ([2,1]*[1,3]) -- 0.333333333333333 != 0.555555555555556
## [2,1] != ([2,3]*[3,1]) -- 0.111111111111111 != 0.0666666666666667
## [3,2] != ([3,1]*[1,2]) -- 3 != 1.8
## [3,1] != ([3,2]*[2,1]) -- 0.2 != 0.333333333333333

Calculations with Pairwise Comparison Matrix

If the matrix is consistent for calculation according to users conditions subsequent calculations can be made. First step is obtaining weights of criteria. The weights are calculated as geometric mean of each row of the pairwise comparison matrix.

weights = calculateWeights(comparisonMatrix)
print(weights)
##  w_C_1  w_C_2  w_C_3 
## 0.7514 0.0704 0.1782

To calculate the AHP some data are needed. As mentioned in the description the FuzzyAHP package is prepared for data that uses scale (categorical) ranking of alternatives (the higher the value the better), although classic AHP can be calculated as well. In order to calculate AHP we need matrix of data that has the same number of columns as there are weights.

Data that have even one element equal to NA are to taken into account at all, the result is automatically determined as NA.

values = c(4,5,3,
1,3,9,
8,6,4,
3,2,7,
6,7,5,
4,5,3,
NA,9,9,
NA,NA,NA)
values = matrix(values, nrow = length(values)/length(weights@weights), ncol = length(weights@weights), byrow = TRUE)

Now we can calculate the result according to the weights. The values with higher resulting value are better solutions of the AHP problem.

result = calculateAHP(weights, values)
print(result)
##        result
## [1,] 3.892240
## [2,] 2.566257
## [3,] 7.146454
## [4,] 3.642293
## [5,] 5.892240
## [6,] 3.892240
## [7,]       NA
## [8,]       NA

If we wan to rank solutions from the best to the worst, we can do that.

rank = compareResults(result)
print(rank)
##      [,1]
## [1,]    3
## [2,]    6
## [3,]    1
## [4,]    5
## [5,]    2
## [6,]    3
## [7,]   NA
## [8,]   NA

It is also possible to put the results together to produce better outcome.

result = cbind(values, result, rank)
colnames(result) = c("crit1", "crit2", "crit3", "result_value", "ranking")
print(result)
##      crit1 crit2 crit3 result_value ranking
## [1,]     4     5     3     3.892240       3
## [2,]     1     3     9     2.566257       6
## [3,]     8     6     4     7.146454       1
## [4,]     3     2     7     3.642293       5
## [5,]     6     7     5     5.892240       2
## [6,]     4     5     3     3.892240       3
## [7,]    NA     9     9           NA      NA
## [8,]    NA    NA    NA           NA      NA

Obviously, since AHP is a hierarchical process the result from one calculation can be merged with outcomes from another calculation (ie. using cbind command) and used as input to another AHP calculation.

Fuzzy AHP

Fuzzy Pairwise Comparison Matrix Creation

The fuzzy pairwise comparison matrix is an extended version of pairwise comparison matrix constructed with respect to fuzzy scale. Details about fuzzy AHP are provided in several articles by Laarhoven and Pedrycz (1983) and Chang (1996).

comparisonMatrixValues = c("1","9","5",
                       "1/9","1","1/3",
                       "1/5","3","1")
comparisonMatrix = matrix(comparisonMatrixValues, nrow = 3, ncol = 3, byrow = TRUE)
comparisonMatrix = pairwiseComparisonMatrix(comparisonMatrix)

Due to the fact, that checking of fuzzy comparison matrix consistency is relatively complicated, it is simpler to check the non fuzzy matrix and assume that if non fuzzy matrix is consisted than so is the fuzzy matrix. The checks are described above.

The comparison matrix is fuzzified with respect to the fuzzy scale. Default fuzzy scale ranging from 1 to 9 with width of fuzzy number equal to 2 is provided. However, user can specify his own fuzzy scale. For the details please see documentation.

fuzzyComparisonMatrix = fuzzyPairwiseComparisonMatrix(comparisonMatrix)
print(fuzzyComparisonMatrix)
##               C_1     C_2           C_3
## C_1       (1;1;1) (8;9;9)       (4;5;6)
## C_2 (1/9;1/9;1/8) (1;1;1) (1/4;1/3;1/2)
## C_3 (1/6;1/5;1/4) (2;3;4)       (1;1;1)

Calculations with Fuzzy Pairwise Comparison Matrix

With the fuzzy comparison matrix the result of AHP process can be calculated in the same way as with classic comparison matrix. The weights of fuzzy comparison matrix are calculated by approach described by Krejčí, Pavlačka, and Talašová (2016).

result = calculateAHP(fuzzyComparisonMatrix, values)

The resulting values have issue of representing uncertain value. Such values are hard to present to user directly. Even though it is possible. The user can extract singe fuzzy numbers from the set.

fuzzyNumer = getFuzzyNumber(result, as.integer(2))
print(fuzzyNumer)
##      minimal    modal  maximal
## [1,] 2.17584 2.566257 3.055858

It might be reasonable for user to either defuzzify the values into single output or rank then using fuzzy ranking methods. Defuzzification can be done using several approaches, here Yager’s index is used as described by Tesfamariam and Sadiq (2006). The results of defuzzification can be ranked as classic results of AHP.

defuzzified = defuzziffy(result, "Yager")
print(defuzzified)
##          [,1]
## [1,] 3.897250
## [2,] 2.599318
## [3,] 7.135438
## [4,] 3.654089
## [5,] 5.899425
## [6,] 3.897250
## [7,]       NA
## [8,]       NA
rank = (nrow(values)+1) - sum(is.na(defuzzified)) - rank(defuzzified, na.last = "keep", ties.method= "max")
print(rank)
## [1]  3  6  1  5  2  3 NA NA

Ranking of fuzzy numbers can be done using various approaches. One of them is Chen’s method described by Tesfamariam and Sadiq (2006).

ranked = compareFuzzyNumbers(result, "Chen")
print(ranked)
##           [,1]
## [1,] 0.3241398
## [2,] 0.1067964
## [3,] 0.8682673
## [4,] 0.2849724
## [5,] 0.6625851
## [6,] 0.3241398
## [7,]        NA
## [8,]        NA

Another possibility is the utilization of possibility theory as described by Dubois and Prade (1983). These indices work very well if there is a relatively bigger set of very good solutions. Unlike defuzzification or other methods these indices can rank them very well. However, if there is only one dominant best solution the result might not be very descriptive.

For large datasets the calculation might take a while. Because of that a graphical progress-bar can be printed if it is turn on by progress-bar parameter.

ranked = compareFuzzyNumbers(result, "possibilityTheory")
# ranked = compareFuzzyNumbers(result, "possibilityTheory", progressBar = TRUE)
print(ranked)
##      possiblity necessity
## [1,]          0         0
## [2,]          0         0
## [3,]          1         1
## [4,]          0         0
## [5,]          0         0
## [6,]          0         0
## [7,]         NA        NA
## [8,]         NA        NA

Other methods for determination of fuzzy weights

Since version 0.8.0 three methods for determination of fuzzy weights were added to the package. These are implemented in function:

calculateWeights_old_methods(fuzzyComparisonMatrix, type)

and should only be used for comparison to the new approach proposed by Krejčí, Pavlačka, and Talašová (2016). All three methods do not provide optimal outputs and significantly overestimate the amount of uncertainty of fuzzy weights in fuzzy AHP. The implemented methods are from publications Chang (1996), Tesfamariam and Sadiq (2006) and Wang, Luo, and Hua (2008).

All three mentioned methods are often used to calculate so called weighting (or priority) vector of crisp weights based on comparisons of fuzzy weights (Chang (1996)). However, Wang, Luo, and Hua (2008) showed that the priority vector can assign irrational weights at specific situations and thus is not suitable for practical calculations. For the purpose of comparison the method for determination of fuzzy vector is implemented as:

calculate_weighting_vector(fuzzyWeights).

Construction of pairwise comparison matrix from several evaluations

Firstly, we prepare three pairwise comparison matrices that represent evaluations of the same problem by three experts.

pmatrix1 = matrix(c(1,3,5,1/3,1,2,1/5,1/2,1), nrow = 3, byrow = TRUE)
pmatrix1 = pairwiseComparisonMatrix(pmatrix1)
pmatrix2 = matrix(c(1,2,7,1/2,1,4,1/7,1/4,1), nrow = 3, byrow = TRUE)
pmatrix2 = pairwiseComparisonMatrix(pmatrix2)
pmatrix3 = matrix(c(1,1,4,1/1,1,2,1/4,1/2,1), nrow = 3, byrow = TRUE)
pmatrix3 = pairwiseComparisonMatrix(pmatrix3)

Now we can create pairwise comparsion matrix that unifies the evaluations into single matrix using geometric (or arithmetic) mean. Here the fraction representation will not look so good anymoore so the decimal representation maybe better.

unified_matrix = buildPairwiseComparisonMatrix(list(pmatrix1, pmatrix2, pmatrix3), agg = "geometric")
print(unified_matrix)
##                 C_1             C_2            C_3
## C_1               1 8797722/4841573   283910/54677
## C_2 4056460/7371077               1 2204819/874983
## C_3    23315/121063  836731/2108430              1
print(unified_matrix@values)
##           [,1]      [,2]     [,3]
## [1,] 1.0000000 1.8171206 5.192494
## [2,] 0.5503212 1.0000000 2.519842
## [3,] 0.1925857 0.3968503 1.000000

It is also possible to create fuzzy pairwise comparison matrix using mininal, geometric mean and maximal value from the list of pairwise comparison matrices.

unified_fuzzy_matrix = buildFuzzyPairwiseComparisonMatrix(list(pmatrix1, pmatrix2, pmatrix3))
print(unified_fuzzy_matrix)
##                         C_1                      C_2                  C_3
## C_1                 (1;1;1)    (1;8797722/4841573;3)   (4;283910/54677;7)
## C_2 (1/3;4056460/7371077;1)                  (1;1;1) (2;2204819/874983;4)
## C_3  (1/7;23315/121063;1/4) (1/4;836731/2108430;1/2)              (1;1;1)

If the matrices are fuzzified first, it is still possible to use them to construct fuzzy pairwise comparison matrix. The minimal, modal and maximal values are calculated as geometric means of the relevant values.

fpmatrix1 = fuzzyPairwiseComparisonMatrix(pmatrix1, getFuzzyScale("full"))
fpmatrix2 = fuzzyPairwiseComparisonMatrix(pmatrix2, getFuzzyScale("full"))
fpmatrix3 = fuzzyPairwiseComparisonMatrix(pmatrix3, getFuzzyScale("full"))

unified_fuzzy_matrix = buildFuzzyPairwiseComparisonMatrix(list(fpmatrix1, fpmatrix2, fpmatrix3))
print(unified_fuzzy_matrix)
##                                          C_1                                  C_2
## C_1                                  (1;1;1)    (1;8797722/4841573;597449/207124)
## C_2         (50623/146022;4056460/7371077;1)                              (1;1;1)
## C_3 (6374/39611;23315/121063;286556/1192121) (1661/5908;836731/2108430;4303/6206)
##                                            C_3
## C_1 (1293367/310893;283910/54677;423348/68123)
## C_2     (13361/9264;2204819/874983;15067/4236)
## C_3                                    (1;1;1)

Author’s contribution

Jan Caha - creator, programmer

Aneta Drážná - tester (up to version 0.6.5)

References

Basile, L., and L. D’Apuzzo. 1997. “Ranking and weak consistency in the A.H.P. context.” Rivista Di Matematica Per Le Scienze Economiche E Sociali 20 (1): 99–109.

Chang, Da-Yong. 1996. “Applications of the extent analysis method on fuzzy AHP.” European Journal of Operational Research 95 (3): 649–55. doi:10.1016/0377-2217(95)00300-2.

Dubois, Didier, and Henri Prade. 1983. “Ranking Fuzzy Numbers in the Setting of Possibility Theory.” Information Sciences 30 (3): 183–224.

Krejčí, Jana, Ondřej Pavlačka, and Jana Talašová. 2016. “A fuzzy extension of Analytic Hierarchy Process based on the constrained fuzzy arithmetic.” Fuzzy Optimization and Decision Making. doi:10.1007/s10700-016-9241-0.

Laarhoven, P.J.M. van, and W. Pedrycz. 1983. “A fuzzy extension of Saaty’s priority theory.” Fuzzy Sets and Systems 11 (1): Pages 229–41.

Saaty, T.L. 1980. The Analytical Hierarchy Process. New York: McGraw Hill.

Saaty, Thomas L., and Liem T. Tran. 2007. “On the invalidity of fuzzifying numerical judgments in the Analytic Hierarchy Process.” Mathematical and Computer Modelling 46 (7-8): 962–75. doi:10.1016/j.mcm.2007.03.022.

Stoklasa, Jan, Věra Jandová, and Jana Talašová. 2013. “Weak consistency in Saaty’s AHP - evaluating creative work outcomes of Czech Art Colleges.” Neural Network World 23 (1): 61–77. doi:10.14311/NNW.2013.23.005.

Tesfamariam, Solomon, and Rehan Sadiq. 2006. “Risk-based environmental decision-making using fuzzy analytic hierarchy process (F-AHP).” Stochastic Environmental Research and Risk Assessment 21 (1): 35–50. doi:10.1007/s00477-006-0042-9.

Wang, Ying Ming, Ying Luo, and Zhongsheng Hua. 2008. “On the extent analysis method for fuzzy AHP and its applications.” European Journal of Operational Research 186 (2): 735–47. doi:10.1016/j.ejor.2007.01.050.