1 Introduction of the ahpsurvey package

The ahpsurvey package provides a workflow to quantify and visualise inconsistent pairwise comparisons that aids researchers in improving the AHP design and adopting appropriate analytically methods for the AHP.

Install the package directly from CRAN (if you have not already installed the package).

# install.packages("ahpsurvey")

And load the ahpsurvey library.

library(ahpsurvey)

1.1 The e-Palette survey dataset

A Saaty scale is composed of 9 items on each end (17 options per pairwise comparison) where decision-makers are asked to indicate how much attribute/ characteristic A is more preferred to B (or vice versa), and how much it is preferred in a 9-point scale. Respondents are asked to make pairwise comparisons for a range of attributes, and indicate their priorities for each of them. Afterwards, we load the data needed, cleaned_data.csv, which consists of cleaned data of 17 individuals based on the raw data collected from students via the distributed questionnaire (designed on the QuestionPro website at https://questionpro.com/t/AQ41hZtckV):

ID FD_TC FD_SR FD_HC FD_LL FD_CF TC_SR TC_HC TC_LL TC_CF SR_HC SR_LL SR_CF HC_LL HC_CF LL_CF
1 4 -9 3 5 -3 -9 -4 1 -3 9 9 9 5 4 -3
2 1 -9 8 3 -3 -9 5 3 -3 9 9 9 -9 -9 -5
3 9 -5 9 9 9 -9 9 9 9 9 9 9 -3 -2 -4
4 -8 -9 -6 -7 -8 -9 -4 2 8 9 8 8 -8 -7 8
5 -7 -6 -6 -6 7 4 3 -4 9 4 5 9 2 9 9
6 -5 -7 -3 -6 -8 -6 -2 -5 -7 6 5 3 -7 -4 -4
7 5 -5 9 3 -3 -5 4 -3 -2 6 3 3 -9 -9 -2
8 -6 -9 7 -2 -4 -8 8 6 -3 9 8 5 -5 -9 -4
9 -2 -8 8 -5 1 -2 6 1 1 8 1 5 -9 -8 4
10 -7 -7 7 -6 -7 -4 8 -7 -3 8 -6 6 -8 -6 -7
11 -4 -7 4 5 5 -5 8 7 7 9 9 9 -2 2 1
12 -7 -7 -5 -7 9 -7 -7 -7 9 8 6 9 -7 9 9
13 -5 -7 5 -4 7 -5 6 -5 7 7 5 7 -7 5 9
14 2 -3 5 4 1 -5 1 -2 -2 5 4 6 -5 -5 1
15 -9 2 -9 -9 -7 9 4 -3 7 -9 -9 -6 -8 -5 7
16 9 1 9 1 9 -7 9 -7 9 9 4 9 -9 -9 9
17 -8 -4 -5 -6 -3 2 4 1 2 4 4 5 -5 -3 3

In the data:

  • FD: Food delivery

  • TC: Tourism, cultural exchange

  • SR: Shower room

  • HC: Health care

  • LL: Luggage / Locker Storage

  • CF: Cafe

atts <- c("FD", "TC", "SR", "HC", "LL", "CF")
dict <- c("FD" = "Food delivery",
          "TC" = "Tourism, cultural exchange",
          "SR" = "Shower rooms",
          "HC" = "Health care",
          "LL" = "Luggage / Locker Storage",
          "CF" = "Cafe")

The simulated dataset consists of fifteen pairwise comparisons of 6 attributes, which are “Food delivery”, “Tourism, cultural exchange”, “Shower room”, “Health care”, “Luggage / Locker Storage” and “Cafe.” An individual compares the attributes in a pairwise attribute comparison; if “Food delivery” is more important than “Tourism, cultural exchange” by 2 units on the Saaty scale, the dataset will code it as -2. This is essential to bear in mind as we move on.

2 Before dealing with inconsistency

2.1 Creating pairwise comparison matrices

Based on the Saaty scale, a pairwise comparison matrix of \(N\) attributes for the \(k^{th}\) individual is obtained:

\[ \mathbf{S_k} =\begin{pmatrix} a_{1,1} & a_{1,2} & \cdots & a_{1,N} \\ a_{2,1} & a_{2,2} & \cdots & a_{2,N} \\ \vdots & \vdots & a_{i,j} & \vdots \\ a_{N,1} & a_{N,2} & \cdots & a_{N,N} \end{pmatrix} \]

Where \(a_{i,j}\) represents the pairwise comparison between the attribute \(i\) and \(j\). If \(i\) is more important than \(j\) for 6 units, \(a_{i,j} = 6\) and \(a_{j,i} = \frac{1}{6}\), i.e. the reciprocal. Data must be reformatted into this pairwise comparison matrix format to proceed.

The reformatting of the survey data (with one row per individual) into such a matrix necessary for further analysis is cumbersome for researchers. Furthermore, as researchers conducting the AHP as an integrated part of a survey, we typically receive data in the above format: the pairwise comparisons are coded in positive and negative numbers as opposed to reciprocals. In the case where the decision-maker chose 6, the sensible codebook maker would code it as -6, which denotes that Food delivery is more important than Tourism, cultural exchange in 6 units for that decision-maker. For ahp.mat to work, the value in A_B variable have to be the importance A has over B in positive values. In this case, the values should be converted from negative to positive, and the negative values would be converted to its reciprocal in the pairwise matrix. When data is coded in the above way, set negconvert = TRUE. If the data is already coded in the reciprocal (as opposed to negatives), set reciprocal = FALSE.

Some caveats prior to entering the data into the ahp.mat function. ahp.mat does not recognise the names of the original dataframe, and figures out which attribute corresponds to which entirely based on the order of the columns. For example, when the attributes are A, B, C and D, the dataframe should be ordered in A_B, A_C, A_D, B_C, B_D, C_D, and the attributes listed as c(A,B,C,D), in that order.

The ahp.mat function takes four arguments:

  • df: the dataframe

  • atts: a list of attributes in the correct order

  • negconvert: whether to convert all positive values to negative (logical, defaults to FALSE)

  • reciprocal: whether to convert negative values (after negconvert) to its reciprocals (defaults to TRUE).

The ahp.mat function creates a list of pairwise comparison matrices for all decision-makers. As seen below, the pairwise matrices resembles the original Saaty criteria weights, which is a good sanity check.

The following only shows the first decision-maker’s pairwise comparison matrix:

AHP_data_mat <- ahp.mat(df = AHP_data, atts = atts, negconvert = TRUE)
AHP_data_mat %>% head(1) %>% 
  kable()
FD TC SR HC LL CF
FD 1.000 0.250 9 0.333 0.200 3.000
TC 4.000 1.000 9 4.000 1.000 3.000
SR 0.111 0.111 1 0.111 0.111 0.111
HC 3.000 0.250 9 1.000 0.200 0.250
LL 5.000 1.000 9 5.000 1.000 3.000
CF 0.333 0.333 9 4.000 0.333 1.000

2.2 Individual preference weights

The ahp.indpref function computes the individual priorities of the decision-makers, and returns a data.frame containing the preference weights of the decision-makers. The three arguments are as follows:

  • ahpmat: The list of matrices created by ahp.mat.

  • atts: a list of attributes in the correct order.

  • method: It normalises the matrices so that all the columns add up to 1, and then computes the averages of the row as the preference weights of each attribute. Four modes of finding the averages are available:

    • arithmetic: the arithmetic mean
    • geometric: the geometric mean
    • rootmean: the square root of the sum of the squared value
    • eigen: the individual preference weights are computed using the Dominant Eigenvalue method described in (Saaty 2003)

Here are the Individual priorities using arithmetic mean method.

arithm_ind <- ahp.indpref(AHP_data_mat, atts, method = "arithmetic")
round(arithm_ind, 3) %>% 
  rownames_to_column('ID') %>% 
  kable()
ID FD TC SR HC LL CF
1 0.123 0.292 0.021 0.111 0.316 0.137
2 0.102 0.110 0.021 0.520 0.183 0.064
3 0.040 0.097 0.021 0.361 0.282 0.199
4 0.419 0.118 0.020 0.193 0.072 0.178
5 0.224 0.058 0.053 0.075 0.076 0.514
6 0.428 0.218 0.030 0.189 0.088 0.047
7 0.094 0.186 0.037 0.519 0.099 0.064
8 0.187 0.083 0.022 0.496 0.160 0.052
9 0.165 0.075 0.039 0.552 0.047 0.122
10 0.232 0.113 0.061 0.454 0.072 0.069
11 0.094 0.048 0.022 0.275 0.249 0.312
12 0.200 0.144 0.022 0.096 0.048 0.489
13 0.136 0.087 0.027 0.238 0.051 0.461
14 0.089 0.232 0.039 0.371 0.146 0.123
15 0.317 0.037 0.398 0.124 0.023 0.101
16 0.032 0.153 0.027 0.482 0.048 0.259
17 0.433 0.050 0.060 0.226 0.081 0.150

2.3 Aggregated preference weights

The ahp.aggpref function computes the aggregated priorities of all decision-makers using the specified methods. The following arguments are given:

  • method: Same as ahp.indpref. It normalises the matrices so that all the columns add up to 1, and then computes the averages of the row as the preference weights of each attribute. Four modes of finding the averages are available:
    • arithmetic: the arithmetic mean
    • geometric: the geometric mean
    • rootmean: the square root of the sum of the squared value
    • eigen: the individual preference weights are computed using the Dominant Eigenvalues method.
  • aggmethod: how to aggregate the individual priorities.
    • arithmetic, geometric and rootmean (same principle as method)
    • tmean trimmed mean
    • tgmean trimmed geometric mean
    • sd returns the standard deviation from the arithmetic mean.
arithm_agg <- ahp.aggpref(AHP_data_mat, atts, method = "arithmetic", aggmethod = "arithmetic")
round(arithm_agg, 3) %>% t() %>% kable()
FD TC SR HC LL CF
0.195 0.124 0.054 0.311 0.12 0.197

3 Measuring and checking consistency

The consistency indices and consistency ratio of a given choice is defined by the following equation:

\[CR = \bigg(\frac{\lambda_{max}-n}{n-1}\bigg)\bigg(\frac{1}{RI}\bigg)\]

Where \(\lambda_{max}\) is the maximum eigenvalue of the pairwise comparison vector and \(n\) is the number of attributes. The \(RI\) when 6 attributes are present is 1.258. See the documentation for ahp.ri to generate your own RI based on a specific number of dimensions and random seed.

Saaty showed that when the \(CR\) is higher than 0.1, the choice is deemed to be inconsistent. The ahpsurvey package allows researchers to quantify the inconsistency among the decision-makers and make decisions in their analysis, either to drop inconsistent observations or look for ways to adjust for inconsistency.

The ahp.cr function returns a vector of \(CR\) that can be merged to other dataframes as a measure of the individuals’ consistency.

(cr1 <- ahp.cr(AHP_data_mat, atts) %>% 
   data.frame('CR1'=.) %>%
   rownames_to_column('ID')) %>%
  t() %>%
  kable()
ID 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
CR1 0.1937 0.1672 0.2617 0.6225 0.3441 0.1397 0.1321 0.1665 0.0817 0.5708 0.0906 0.3617 0.2163 0.0812 0.2100 0.2639 0.1269
table(cr1$CR1 <= 0.1)
## 
## FALSE  TRUE 
##    14     3

You may also specify your own random index generated with ahp.ri to be used with ahp.cr, as follows:

## Generate a random index with 1000 simulations, 6 dimensions and seed 30000 for reproducibility (seed = 42 by default).
(RI <- ahp.ri(nsims = 1000, dim = length(atts), seed = 30000))
## [1] 1.26
## Use this RI to calculate the consistency ratio instead of the default one.
(cr2 <- ahp.cr(AHP_data_mat, atts, RI)%>% 
  data.frame('CR2'=.) %>% 
  rownames_to_column('ID')) %>%
  t() %>%
  kable()
ID 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
CR2 0.1924 0.1661 0.2600 0.6183 0.3418 0.1388 0.1312 0.1654 0.0812 0.5670 0.0900 0.3593 0.2149 0.0807 0.2086 0.2622 0.1260
table(cr2$CR2 <= 0.1)
## 
## FALSE  TRUE 
##    14     3

4 Dealing with inconsistent data

4.1 Finding inconsistent pairwise comparisons by maximum

A better way would be to extract the pairwise comparison with the maximum inconsistency error, and returning a list of the most inconsistent pairwise comparisons for each decision-maker. This process is automated in the ahp.pwerror function, which returns a dataframe of the top three most inconsistent pairwise comparison made by each decision-maker.

AHP_data %>%
  ahp.mat(atts) %>%
  ahp.pwerror(atts) %>%
  head() %>% 
  kable()
top1 top2 top3
FD_CF HC_CF FD_HC
HC_LL TC_HC HC_CF
TC_HC TC_CF HC_LL
HC_LL FD_TC FD_LL
TC_SR LL_CF TC_CF
HC_LL FD_LL FD_TC

A better way to visualise the pairwise comparisons is a bar chart:

AHP_data_mat %>%
  ahp.pwerror(atts) %>% 
  gather(top1, top2, top3, key = "max", value = "pair") %>%
  table() %>%
  as.data.frame() %>%
  ggplot(aes(x = pair, y = Freq, fill = max)) + 
  geom_bar(stat = 'identity') +
  scale_y_continuous("Frequency", breaks = seq(0, 180, 20)) +
  scale_fill_discrete(breaks = c("top1", "top2", "top3"), labels = c("1", "2", "3")) +
  scale_x_discrete("Pair") +
  guides(fill = guide_legend(title="Rank")) +
  theme(axis.text.x = element_text(angle = 20, hjust = 1),
        panel.background = element_rect(fill = NA),
        panel.grid.major.y = element_line(colour = "grey80"),
        panel.grid.major.x = element_blank(),
        panel.ontop = FALSE)
\label{fig:figs}Figure 4. Pairwise comparison and its frequency
 as the most, second-most, and third most inconsistent pairwise comparisons

Figure 4. Pairwise comparison and its frequency as the most, second-most, and third most inconsistent pairwise comparisons

4.2 Transforming inconsistent matrices

The function ahp.harker takes five optional arguments:

  • round is logical and tells ahp.harker whether to convert the newly replaced values to integers and its reciprocals, and can be set to TRUE if desired.

  • iterations denotes how many pairwise comparisons should be changed. For example, if iterations = 3, ahp.harker changes the first, second, and third most inconsistent pairwise comparisons using that method. Researchers should think carefully how many pairwise comparisons should be replaced, as every time a pairwise comparison is replaced, some information is inevitably lost. Note that the maximum number of iterations is capped at \(iterations \leq \frac{1}{2}n(n-1)\) with \(n\) being the number of attributes.

  • stopcr: The stopping Consistency Ratio. It complements iterations by giving iterations a criteria to stop when a matrix is sufficiently consistent. ahp.harker will continue looping and replacing more elements of the pairwise comparison matrices until the consistency ratio of the new matrix is lower than stopcr, or the maximum number of iterations is reached, and will stop and move onto the next individual. When stopcr is set, the number of replaced elements will differ amongst each decision-maker. Thus, it is advised that the analyst set printiter = TRUE to see how many iterations has the pairwise matrix of that individual has been modified by the algorithm.

  • limit: In many cases, the algorithm will intend to replace a value with a number higher than 9 or lower than \(\frac{1}{9}\). limit caps the maximum and minimum value of the replacement to 9 and \(\frac{1}{9}\) respectively.

  • printiter is a logical argument of whether the number of iterations taken for each pairwise matrix is reported or not. Generally it is not needed if stopcr is not specified. When stopcr is specified, this is a good way of identifying how many pairwise comparisons are actually replaced by the algorithm for each decision maker. The printout above shows "Ind 1 Iterations: 1", which shows that although I specified iterations = 10, individual 1 (Ind 1) was only iterated one time before it reached the target consistency ratio, 0.1. Only one element was replaced.

The following code will demonstrate how ahp.harker improved the consistency of the decision-makers in our collected data.

n_iterations <- length(atts)*(length(atts)-1)/2
crmat <- matrix(NA, nrow = nrow(AHP_data), ncol = n_iterations + 1)
colnames(crmat) <- 0:n_iterations

crmat[,1] <- AHP_data %>%
    ahp.mat(atts, negconvert = TRUE) %>%
    ahp.cr(atts)

for (it in 1:n_iterations){
  crmat[,it+1] <- AHP_data %>%
    ahp.mat(atts, negconvert = TRUE) %>%
    ahp.harker(atts, iterations = it, stopcr = 0.1,
               limit = T, round = T, printiter = F) %>%
    ahp.cr(atts)
}

data.frame(table(crmat[,1] <= 0.1), 
           table(crmat[,3] <= 0.1),
           table(crmat[,5] <= 0.1),
           table(crmat[,7] <= 0.1),
           table(crmat[,9] <= 0.1),
           table(crmat[,11] <= 0.1),
           table(crmat[,13] <= 0.1),
           table(crmat[,15] <= 0.1)
) %>% 
  select(Var1, Freq, Freq.1, Freq.2, Freq.3, Freq.4, Freq.5, Freq.6, Freq.7) %>%
  rename("Consistent?" = "Var1",
         "No iteration" = "Freq",
         "2 iterations" = "Freq.1",
         "4 iterations" = "Freq.2",
         "6 iterations" = "Freq.3",
         "8 iterations" = "Freq.4",
         "10 iterations" = "Freq.5",
         "12 iterations" = "Freq.6",
         "14 iterations" = "Freq.7") %>% 
  kable()
Consistent? No iteration 2 iterations 4 iterations 6 iterations 8 iterations 10 iterations 12 iterations 14 iterations
FALSE 14 11 8 7 6 4 5 3
TRUE 3 6 9 10 11 13 12 14

While using Harker’s method cannot completely lower the CR of all decision-makers to desired levels, it allows researchers to keep a lot more observations; whereas we would have to truncate 14 samples, now we only have to censor 3 samples with 14 iteration.

crmat %>% 
  as.data.frame() %>%
  gather(key = "iter", value = "cr") %>%
  mutate(iter = as.integer(iter)) %>%
  ggplot(aes(x = iter, y = cr, group = iter)) +
  geom_hline(yintercept = 0.1, color = "red", linetype = "dashed")+
  geom_jitter(alpha = 0.2, width = 0.3, height = 0, color = "turquoise4") +
  geom_boxplot(fill = "transparent", color = "#808080", outlier.shape = NA) + 
  scale_x_continuous("Iterations", breaks = 0:n_iterations) +
  scale_y_continuous("Consistency Ratio") +
  theme_minimal()
\label{fig:figs}Figure 5. Consistency Ratios under different number of iterations with the maximum deviation method

Figure 5. Consistency Ratios under different number of iterations with the maximum deviation method

it <- 14
thres <- 0.1
cr.df1 <- data.frame(cr = AHP_data %>%
  ahp.mat(atts, negconvert = TRUE) %>%
  ahp.harker(atts, iterations = it, stopcr = 0.1, limit = T, round = T, printiter = F) %>%
  ahp.cr(atts))

cr.df2 <- cr.df1 %>%
  mutate(rowid = seq_len(nrow(AHP_data)), cr.dum = as.factor(ifelse(. <= thres, 1, 0))) %>%
  select(cr.dum, rowid)

AHP_data_mat_harker <- AHP_data %>%
  ahp.mat(atts = atts, negconvert = TRUE) %>% 
  ahp.harker(atts, iterations = it, stopcr = 0.1, limit = T, round = T, printiter = F)

4.3 Individual priorities

Here are the individual priorities after dealing with inconsistent data

arithm_ind_harker <- ahp.indpref(AHP_data_mat_harker, atts, method = "arithmetic")
arithm_ind_harker %>% rownames_to_column('ID') %>% kable()
ID FD TC SR HC LL CF
1 0.085 0.246 0.021 0.112 0.339 0.197
2 0.087 0.118 0.022 0.452 0.244 0.077
3 0.027 0.082 0.028 0.288 0.288 0.288
4 0.297 0.191 0.033 0.123 0.081 0.275
5 0.108 0.033 0.065 0.109 0.187 0.497
6 0.354 0.306 0.032 0.146 0.113 0.049
7 0.072 0.184 0.032 0.545 0.099 0.069
8 0.148 0.061 0.024 0.520 0.185 0.062
9 0.165 0.075 0.039 0.552 0.047 0.122
10 0.158 0.077 0.040 0.537 0.042 0.145
11 0.094 0.048 0.022 0.275 0.249 0.312
12 0.119 0.107 0.033 0.095 0.053 0.595
13 0.093 0.052 0.025 0.201 0.105 0.523
14 0.089 0.232 0.039 0.371 0.146 0.123
15 0.309 0.039 0.380 0.129 0.024 0.120
16 0.030 0.121 0.031 0.404 0.068 0.348
17 0.347 0.055 0.062 0.300 0.083 0.153

4.4 Aggregated priorities

Here are the aggregated priorities after dealing with inconsistent data

arithm_agg_harker <- ahp.aggpref(AHP_data_mat_harker, atts, method = "arithmetic", aggmethod = "arithmetic")
arithm_agg_harker %>% t() %>% kable()
FD TC SR HC LL CF
0.152 0.119 0.055 0.303 0.138 0.233

5 AHP on steroids: a canned routine

5.1 Before excluding observations having inconsistency

As of version 0.3.0, you can quickly execute most of the functions described in this vignette with a canned routine. Here I demonstrate how the AHP_data dataset is processed using the AHP to output the aggregated and individual priorities of individuals, using the arithmetic mean method.

Here are the individual priorities:

canned <- ahp(df = as.data.frame(AHP_data),
              atts = atts,
              negconvert = TRUE, 
              reciprocal = TRUE,
              method = 'arithmetic',
              aggmethod = "arithmetic",
              agg = TRUE)

canned$indpref %>% rownames_to_column('ID') %>% kable()
ID FD TC SR HC LL CF CR top1 top2 top3
1 0.123 0.292 0.021 0.111 0.316 0.137 0.194 FD_CF HC_CF FD_HC
2 0.102 0.110 0.021 0.520 0.183 0.064 0.167 HC_LL TC_HC HC_CF
3 0.040 0.097 0.021 0.361 0.282 0.199 0.262 TC_HC TC_CF FD_HC
4 0.419 0.118 0.020 0.193 0.072 0.178 0.622 HC_LL FD_TC LL_CF
5 0.224 0.058 0.053 0.075 0.076 0.514 0.344 TC_SR LL_CF TC_CF
6 0.428 0.218 0.030 0.189 0.088 0.047 0.140 HC_LL FD_LL FD_TC
7 0.094 0.186 0.037 0.519 0.099 0.064 0.132 HC_LL HC_CF TC_HC
8 0.187 0.083 0.022 0.496 0.160 0.052 0.167 TC_HC FD_TC HC_LL
9 0.165 0.075 0.039 0.552 0.047 0.122 0.082 HC_LL SR_LL TC_HC
10 0.232 0.113 0.061 0.454 0.072 0.069 0.571 SR_LL HC_LL TC_HC
11 0.094 0.048 0.022 0.275 0.249 0.312 0.091 TC_HC TC_CF FD_TC
12 0.200 0.144 0.022 0.096 0.048 0.489 0.362 LL_CF HC_LL TC_CF
13 0.136 0.087 0.027 0.238 0.051 0.461 0.216 LL_CF HC_LL TC_HC
14 0.089 0.232 0.039 0.371 0.146 0.123 0.081 HC_LL HC_CF FD_HC
15 0.317 0.037 0.398 0.124 0.023 0.101 0.210 SR_LL TC_SR SR_HC
16 0.032 0.153 0.027 0.482 0.048 0.259 0.264 HC_LL TC_HC LL_CF
17 0.433 0.050 0.060 0.226 0.081 0.150 0.127 TC_SR FD_TC FD_LL

You can also see clearly the individual priorities calculated using the arithmetic mean method, the consistency ratio of each individual, and which three pairwise comparisons are the most inconsistent.

Here are the aggregated priorities:

canned$aggpref %>% t() %>% kable()
FD TC SR HC LL CF
AggPref 0.195 0.124 0.054 0.311 0.120 0.197
SD.AggPref 0.132 0.072 0.090 0.168 0.089 0.157

The most preference is Health care.

canned$aggpref %>% 
        as.data.frame() %>% 
        rownames_to_column() %>% 
        ggplot(aes(x = rowname, y = AggPref, fill = rowname)) +
        geom_col() +
        gghighlight(rowname == rownames(canned$aggpref)[which.max(canned$aggpref)]) +
        labs(x = 'Factor of e-Palette', y = 'Aggregated Preference') +
        theme(legend.position = "none")
\label{fig:figs}Figure 6. The most aggregate prefered factor of e-Palette before excluding observations having inconsistency

Figure 6. The most aggregate prefered factor of e-Palette before excluding observations having inconsistency

5.2 After excluding observations having inconsistency

You can also quickly censor inconsistent observations and estimate the aggregated preference weight of the population, and its standard deviation. The unique command here in this canned routine is censorcr, where you may specify the consistency ratio censoring threshold, which allows you to remove observations a higher consistency ratio than you specified. Usually, researchers censor observations with a CR higher than 0.1. But sometimes it is different depending on your discipline. Note that specifying qt only affects aggpref calculation, and does not censor the observations in $indpref.

Here are the number of observations that were excluded because of the inconsistency in their responses.

canned1 <- ahp(df = as.data.frame(AHP_data),
              atts = atts,
              negconvert = TRUE, 
              reciprocal = TRUE,
              method = 'arithmetic',
              aggmethod = "arithmetic",
              qt = 0.2,
              censorcr = 0.1,
              agg = TRUE)
## [1] "Number of observations censored = 14"

Here are the individual priorities:

canned1$indpref %>% rownames_to_column('ID') %>% kable()
ID FD TC SR HC LL CF CR top1 top2 top3
1 0.165 0.075 0.039 0.552 0.047 0.122 0.082 SR_HC FD_HC FD_SR
2 0.094 0.048 0.022 0.275 0.249 0.312 0.091 FD_LL TC_LL HC_CF
3 0.089 0.232 0.039 0.371 0.146 0.123 0.081 FD_LL TC_HC FD_TC

Here are the aggregated priorities:

canned1$aggpref %>% t() %>% kable() 
FD TC SR HC LL CF
AggPref 0.116 0.118 0.033 0.399 0.147 0.186
SD.AggPref 0.042 0.099 0.010 0.141 0.101 0.110

The most preference is also Health care.

canned1$aggpref %>% 
        as.data.frame() %>% 
        rownames_to_column() %>% 
        ggplot(aes(x = rowname, y = AggPref, fill = rowname)) +
        geom_col() +
        gghighlight(rowname == rownames(canned$aggpref)[which.max(canned$aggpref)]) +
        labs(x = 'Factor of e-Palette', y = 'Aggregated Preference') +
        theme(legend.position = "none")
\label{fig:figs}Figure 7. The most aggregate prefered factor of e-Palette after excluding observations having inconsistency

Figure 7. The most aggregate prefered factor of e-Palette after excluding observations having inconsistency

REFERENCES

Saaty, Thomas L. 2003. “Decision-Making with the AHP: Why Is the Principal Eigenvector Necessary.” European Journal of Operational Research 145 (1): 85–91. https://doi.org/10.1016/s0377-2217(02)00227-8.