Analytic Hierarchy Process (AHP) with Python??

Analytic Hierarchy Process (AHP) with Python??

The Analytic Hierarchy Process

This article presents the implementation of a simple Analytic Hierarchy process with unlimited criteria and participants (no sub-criteria or alternatives available).

No alt text provided for this image

The Analytic Hierarchy Process (AHP) is a method for organizing and analyzing complex decisions, using math and psychology. It was developed by Thomas L. Saaty in the 1970s and has been refined since then. It contains three parts: the ultimate goal or problem you're trying to solve, all of the possible solutions, called alternatives, and the criteria you will judge the alternatives on. AHP provides a rational framework for a needed decision by quantifying its criteria and alternative options, and for relating those elements to the overall goal. You can also check a very useful Excel Implementation from BPMSG?and Klaus Goepel.

The implementation in this article is simpler and aims to prioritize or add weights to the different criteria.


Pre-process

  1. Select all the criteria you want to be compared (Fill: C = [C1, C2, C3, ..., Ci])
  2. Add the criteria to the rows and columns of a square table.

No alt text provided for this image

3. Assign a score to each pair of the upper triangle in the table (Fill w = [w1, w2, w3, ..., wi])

No alt text provided for this image

  • If vertical is more important than horizontal, add the score between 0 and 1. (Example: If C2 is of strong importance compared to C1: w1 = 1/5)
  • If vertical is more important than horizontal, add the score between 1 and 9. (Example: If C1 is of strong importance compared to C2: w1 = 5)
  • If criteria are of equal importance add 1. (Example: If C1 is equal importance compared to C2: w1 = 1)

4. Fill the table A

5. Assign the scores into an array in a specific order: w = [w1, w2, w3, ..., wi].

Keep the order you can see at the table above. If more or fewer criteria, follow the same logic.


Python Scripting

Import Libraries

import pandas as p
import seaborn as sns
from scipy.spatial.distance import squareform
import numpy as np
import matplotlib.pyplot as pltd        

Functions

Make Matrix

def make_matrix(arr):
  """ 
  Goal:
  Transform the input to appropriate format

  Input:
  arr: the array with the weight with respect to the right order (w = w1, w2, w3, w4...)
  
  Output:
  A: Transformed the w array to get the right format of the A matrix
  """
  X = (squareform(arr)).astype(float)
  row, col = np.diag_indices(X.shape[0])
  X[row,col] = np.ones(X.shape[0])
  for i in range(0, len(row)):
      for j in range(0, len(col)):
          if j < i:
              X[i, j] = (1 / X[i, j])
  A = np.asarray(X)
  return A        

AHP - One participant

 def AHP_1_Participant(arr)
  """ 
  Goal:
  Apply the AHP only on one participant to calculate the Consistency ratio, the weights, their standard deviation and the rgmm
  
  Input:
  arr: the array with the weight with respect to the right order (w = [w1, w2, w3, w4, ..., wi])
  
  Output:
  A: Transformed the w array to get the right format of the A matrix (using the make_matrix function)
  p: A dataframe with the normalized weights of the criterions and their st. deviation and the RGMM and their standard deviation
  cr: The consistency ratio of the participant
  rggm: The RGMM values of the participant

  Plot:
  1. The A matrix with the answers to cross validate it
  2. The Consistency Ratio Matrix to detect the "suspicious" weights
  3. The consistency ration of each participant and the normalized one
  """

  alpha = 0.1
  A = make_matrix(arr)
  n = len(A)
  x_ticks = ['C{}'.format(i) for i in range(1, n+1)]
  sums = np.array(pd.DataFrame(A).sum())
  ln_rgmm = np.log(A)
  rgmm_sum = np.array(np.exp(pd.DataFrame(ln_rgmm).sum(axis = 1) / n))
  rgmm_sum_2 = rgmm_sum.sum()
  rggm = rgmm_sum / rgmm_sum_2
  errors = np.zeros(np.shape(A))
  
  size = np.shape(errors)[1]
  for i in range(0, size):
      for j in range(0, size):
          errors[i, j] = np.log(A[i, j] * rggm[j] / rggm[i]) ** 2
  
  errors_sum = np.sum(errors, 0)
  error_calc = np.sqrt(errors_sum / (size - 1))
  rggm_cosh = rggm * np.cosh(error_calc)
  rggm_cosh_sum = np.sum(rggm_cosh)
  rggm_final = rggm_cosh / rggm_cosh_sum
  rggm_matmul = np.matmul(sums, rggm)

  plus_minus = rggm * np.sinh(error_calc)/rggm_cosh_sum
  cr0 = (rggm_matmul - n)/((2.7699*n-4.3513)-n)
  eig_val = np.linalg.eig(A)[0].max()
  eig_vec = np.linalg.eig(A)[1][:,0]
  p = np.round(np.real(eig_vec/eig_vec.sum()), 3)
  cr = np.round(np.real((eig_val - n)/((2.7699 * n - 4.3513) - n)), 3)
  evt = np.real(A * size / eig_val)

  for i in range(0, size):
      for j in range(0, size):
          evt[i, j] = evt[i, j]* rggm_final[j]

  pi_pi = np.zeros(np.shape(A))
  for i in range(0, size):
      for j in range(0, size):
          pi_pi[i, j] = rggm[j] / rggm[i]

  pi_pi_A = pi_pi * A
  pi_pi_A2 = np.zeros(np.shape(A))
  for i in range(0, size):
      for j in range(0, size):
          if pi_pi_A[i, j] > 1/9 and pi_pi_A[i, j] < 9:
              if pi_pi_A[i, j] > 1:
                  pi_pi_A2[i, j] = A[i, j] * pi_pi[i, j]
              else:
                  pi_pi_A2[i, j] = 1 / (A[i, j] * pi_pi[i, j])
          else:
              pi_pi_A2[i, j] = 0
  Consistency_ratio = list(pi_pi_A2[np.triu_indices(n, k = 1)])
  std = np.array(pd.DataFrame(evt).std(1))
  plt.title('A')
  g1 = sns.heatmap(pd.DataFrame(np.tril(A)), annot=True, cmap = "viridis", cbar=False)
  g1.set_xticklabels(x_ticks)
  g1.set_yticklabels(x_ticks)
  plt.show()
  plt.title('Consistency Ratio Matrix')
  g2 = sns.heatmap(pd.DataFrame(np.tril(pi_pi_A2)), annot=True, cmap = "viridis", cbar=False)
  g2.set_yticklabels(x_ticks)
  g2.set_xticklabels(x_ticks)
  plt.show()
  p = pd.DataFrame(p, columns = ['Weights'])
  p.index = p.index + 1
  p.index = 'Crit-' + p.index.astype(str)
  p['Weights'] = p['Weights'].astype(float).map("{:.2%}".format)
  p['Weights +/-'] = std
  p['Weights +/-'] = p['Weights +/-'].astype(float).map("{:.2%}".format)
  p['RGMM'] = rggm_final
  p['RGMM'] = p['RGMM'].astype(float).map("{:.2%}".format)
  p['+/-'] = plus_minus
  p['+/-'] = p['+/-'].astype(float).map("{:.2%}".format)
  print(p)
  print(' ')
  print('Consistency Ratio: {:.2%} & Consistency Ratio of Weighted: {:.2%}'.format(cr0, cr))
  return A, p, cr, rggm:        

AHP - Consolidated

def AHP_Consolidated(A, rggm, w = 1)
  """ 
  Goal:
  Apply the AHP to multiple participants to calculate the Consistency ratio, the weights, their standard deviation and the consolidated A
  
  Input:
  A: a list containing the different A matrices (A = [A1, A2, A3, A4, ..., Ai]) (came from the AHP_1_Participant function)
  rggm: a list containing the different rgmm matrices (rggm = [rgmm1, rgmm2, rgmm3, rgmm, ..., rgmmi]) (came from the AHP_1_Participant function)
  w: the weight of the different stakeholders - Not yet implemented

  Output:
  cons_exp: The consolidated A matrix of the multiple participants
  p: A dataframe with the normalized weights of the criterions and their st. deviation
  cr: The consolidated consistency ratio

  Plot:
  1. The A matrix with the consolidated answers to cross validate it
  2. The consolidated Consistency ratio and the consunsus value

  """
  n = len(A)
  logs = []
  for i in A:
      logs.append(np.array(np.log(i)))
  cons = np.zeros(np.shape(logs[0]))
  table_rggm = pd.DataFrame(rggm)
  table_rggm_ln = -table_rggm*np.log(table_rggm)
  alphas = table_rggm_ln.sum(1)
  alpha = np.exp(np.sum(alphas)/n)
  Da = np.exp(alpha)
  gammas0 = table_rggm.sum(0)/n
  gammas = -gammas0*np.log(gammas0)
  gamma = np.exp(np.sum(gammas))
  beta = gamma/alpha
  for i in logs:
      cons += i
  cons = cons/n
  cons_exp = (np.exp(cons))
  size = np.shape(cons_exp)[1]
  x_ticks = ['C{}'.format(i) for i in range(1, size+1)]
  ahp_cor1 = np.exp((-9/(size+8)*np.log(9/(size+8))-(size-1)*(1/(size+8)*np.log(1/(size+8)))))
  ahp_cor2 = np.exp((size-n)*(-1/(size+8)*np.log(1/(size+8)))+n*(-(n+8)/(size+8)/n*np.log((n+8)/(size+8)/n)))
  ahp_cor3 = size / ahp_cor1
  it0 = (cons_exp.sum(1)/10)
  it = np.matmul(cons_exp, it0)
  scale0 = it0 / np.max(it0)
  scale = it / np.max(it)
  for i in range(20):
      it = np.matmul(cons_exp, scale)
      scale = it / np.max(it)
  
  norm = np.zeros(len(scale))
  for i in range(len(scale)):
      norm[i] = scale[i] / sum(scale)
  p = pd.DataFrame(norm, columns = ['Cons Weights'])
  sum_cols = cons_exp.sum(0)
  lamda = (sum(sum_cols*norm))
  evt = np.real(size / lamda * cons_exp)
  for i in range(0, size):
      for j in range(0, size):
          evt[i, j] = evt[i, j]* norm[j]
  std = np.array(pd.DataFrame(evt).std(1))
  cr = (lamda - len(sum_cols)) / ((2.7699*len(sum_cols)-4.3513)-len(sum_cols))
  consensus = (1/beta-1/ahp_cor3)/(1-1/ahp_cor3)
  g = sns.heatmap(pd.DataFrame(np.tril(cons_exp)), annot=True, cmap = "viridis", cbar=False)
  g.set_xticklabels(x_ticks)
  g.set_yticklabels(x_ticks)
  plt.show()
  p.index = p.index + 1
  p.index = 'Crit-' + p.index.astype(str)
  p['Cons Weights'] = p['Cons Weights'].astype(float).map("{:.2%}".format)
  p['Weights +/-'] = std
  p['Weights +/-'] = p['Weights +/-'].astype(float).map("{:.2%}".format)
  print(p)
  print(' ')
  print('Consistency Ratio of Consolidated: {:.2%} \nConsensus: {:.2%}'.format(cr, consensus))
  return cons_exp, p, cr:        

Example

A11, weights, cr, rgmm11 = AHP_1_Participant([3, 1, 1/2]        
No alt text provided for this image
A12, weights, cr, rgmm12 = AHP_1_Participant([6, 3, 8]        
No alt text provided for this image
A13, weights, cr, rgmm13 = AHP_1_Participant([1/8, 1/3, 1/4])        
No alt text provided for this image
cons_A, p, cr = AHP_Consolidated([A11, A12, A13], [rgmm11, rgmm12, rgmm13]))))        
No alt text provided for this image


You can download the Python Notebook from my Github!


References

  1. Klaus D. Goepel, (2013). Implementing the Analytic Hierarchy Process as a Standard Method for Multi-Criteria Decision Making In Corporate Enterprises – A New AHP Excel Template with Multiple Inputs,?Proceedings of the International Symposium on the Analytic Hierarchy Process, Kuala Lumpur 2013. DOI:?https://doi.org/10.13033/isahp.y2013.047
  2. https://www.passagetechnology.com/what-is-the-analytic-hierarchy-process


Bengisu Yavuz

Industrial Engineering Student at Dokuz Eylul University

10 个月

I couldn't run the code with my own data, can you help me?

回复
Michael U Odiaka

Network Administrator

1 年

Please I need help The Analytic Hierarchy Process base on cost-effectiveness .

回复
Sanjukta Mitra

Sr Data Scientist at Boeing

1 年

Thanks, Good Article

回复
Luis Alberto Bernales Ghacham

Tasador en TASING SAC | Banco de Chile | Cortes de Apelaciones de Chile

2 年

Hi! i would like your opinion using my AHP software, joining at https://ahp.luisbernales.com/login just have to register and you can use it at once

回复
DJIBO BOUBE Bachirou

GIS for Energy planning|| Energy Modeling|| Off-grid system|| Rural development || PhD in Green hydrogen

2 年

Great Job, Thanks Dimos T. Touloumidis

回复

要查看或添加评论,请登录

Dimos T. Touloumidis的更多文章

社区洞察

其他会员也浏览了