AHP(Analytic Hierarchy Process) 분석

설문조사를 통해 요소별 가중치를 도출하는 AHP분석을 Python을 이용해서 수행하는 방법에 대해서 알아보겠습니다. AHP 설문결과를 계산하는 방법에는 몇 가지가 있는데, 이 글에서는 이 블로그의 계산 방법을 참고하도록 하겠습니다.

본 글은 다음의 순서대로 진행됩니다.

  1. 첫번째로, 설문결과 전체를 대상으로 각 요소의 가중치(A1, A2, A3 간 가중치와 B1, B2, B3, B4, B5 간 가중치)를 구합니다.
  2. 두번째로, 설문 응답 중 일관성이 0.1 이하인 응답을 대상으로 각 요소의 가중치를 구합니다.
  3. 세번째로, 응답자의 특성(직급 및 부서)에 따른 각 요소의 가중치를 구합니다.
  4. 네번째로, AHP결과(요소별 가중치와 일관성)를 DataFrame에 저장합니다.

필요한 모듈 불러오기

import pandas as pd
import numpy as np
from scipy.stats.mstats import gmean

설문결과 불러오기

이번에 활용할 설문은 요소 (A1, A2, A3) 사이의 가중치와 (B1, B2, B3, B4, B5) 사이의 가중치를 구하기 위해 실시한 설문입니다. 설문에는 설문자의 직급과 부서가 포함되어 있습니다.

dir00 = '/content/drive/My Drive/(03)personal/(04)Blog/(01)게시글_Python/'
xlsx = pd.ExcelFile(dir00 + '(01)AHP설문결과.xlsx')
df01 = xlsx.parse('raw', header = 0).iloc[:, :]
#불러온 설문결과가 어떻게 생겼는지 확인해보겠습니다.
print(df01.head())

1. AHP분석(전체 응답 대상)

요소별 가중치 도출

우선은 (A1, A2, A3)의 가중치를 도출하겠습니다. 전체 응답을 대상으로 AHP분석을 수행하기 위해서 각 컬럼의 기하평균을 계산하겠습니다.

a = gmean(df04['A12'])
b = gmean(df04['A13'])
c = gmean(df04['A23'])
for i, j in zip([a, b, c], ['a', 'b', 'c']):
print('기하평균({}) : {}'.format(j, i))

print 결과는 아래와 같습니다.

기하평균(a) : 0.6176919155401562
기하평균(b) : 0.442493076235233
기하평균(c) : 0.8096662696677938

앞에서 구한 기하평균으로 쌍대비교행렬을 생성합니다. 쌍대비교행렬은 numpy를 활용하겠습니다.

np01 = np.array([[1, a, b], [1/a, 1, c], [1/b, 1/c, 1]])
print(np01)

print 결과는 아래와 같습니다.

[[1. 0.61769192 0.44249308]
 [1.61893004 1. 0.80966627]
 [2.25992237 1.23507677 1. ]]

쌍대비교행렬을 구했으면, 각 열의 합이 1이 되도록 열의 값을 정규화합니다. 정규화는 행렬의 값을 열의 합으로 나눠줍니다. 정규화된 행렬의 행별 평균을 구하면 각 요소(A1, A2, A3)별 가중치가 도출됩니다.

col_sums = np01.sum(axis=0) # 열의 합
print('열의 합 : {}'.format(col_sums))
np02 = np01 / col_sums # 정규화(np01의 값을 동일한 열에 있는 col_sumns의 값으로 나눠줍니다.)
print('정규화 : {}'.format(np02))
row_avg = np02.mean(axis=1) # 속성별 중요도, 가중치(mean(axis=1)은 행의 평균을 계산합니다.)
print('가중치 : {}'.format(row_avg))
 
for i, j in zip(['A1', 'A2', 'A3'], row_avg):
print('최종 중요도({}) : {}'.format(i, j))

print 결과는 아래와 같습니다.

열의 합 : [4.87885241 2.85276868 2.25215935]
정규화 : [[0.20496623 0.21652366 0.19647503]
 [0.33182599 0.35053666 0.35950665]
 [0.46320777 0.43293968 0.44401832]]
가중치 : [0.20598831 0.34728977 0.44672192]
최종 중요도(A1) : 0.20598830771905127
최종 중요도(A2) : 0.3472897679844087
최종 중요도(A3) : 0.44672192429654006

일관성 지수 도출

요소별 가중치를 도출했습니다. 이번에는 얼마나 일관성이 있는지 알아보겠습니다. 앞서 소개해드린 블로그에서 일관성을 도출하기 위해 전문적인 설명을 이어가고 있습니다. 이를 요약하면, 다음과 같습니다.

  1. 쌍대비교행렬과 가중치 행렬을 곱한다.
  2. 곱한 행렬의 각 값을 가중치로 나눈다.
  3. 각 값의 평균에서 요소의 개수(여기서는 3)을 빼고 요소의 개수보다 1작은 수(여기서는 2)로 나눈다.

수식으로 표현하면 다음과 같습니다.

print('쌍대비교행렬 : \\n{}'.format(np01))
print('가중치 행렬 : \\n{}'.format(row_avg[:, np.newaxis]))
np03 = np01.dot(row_avg[:, np.newaxis]) # 1번
print('1번 결과 : \\n{}'.format(np03))
np04 = np03/row_avg[:, np.newaxis] # 2번
print('2번 결과 : \\n{}'.format(np04))
consistency = (np04.mean(axis=0)-3)/2 # 3번, 일관성 지수
print('3번 결과(일관성 지수) : {}'.format(consistency))

print 결과는 아래와 같습니다.

쌍대비교행렬 :
[[1. 0.61769192 0.44249308]
 [1.61893004 1. 0.80966627]
 [2.25992237 1.23507677 1. ]]
가중치 행렬 :
[[0.20598831]
 [0.34728977]
 [0.44672192]]
1번 결과 :
[[0.61817775]
 [1.0424661 ]
 [1.34116903]]
2번 결과 :
[[3.00103319]
 [3.00171844]
 [3.00224583]]
3번 결과(일관성 지수) : [0.00083291]

일관성 지수가 0.00083291로 도출되었습니다. 일관성 지수 0.1 이하로 위 설문 결과는 일관성이 있다고 할 수 있습니다.

정리 : 코드 정리하기

지금까지 도출한 요소별 가중치와 일관성 지수를 함수를 활용하여 간략하게 정리하면 다음과 같습니다.

요소가 3개인 경우

요소가 3개인 경우 AHP분석을 위한 함수를 정의해줍니다.

def AHP_3(a, b, c): # 요소가 3개인 경우 AHP분석 함수
    np01 = np.array([[1, a, b], [1/a, 1, c], [1/b, 1/c, 1]])
    col_sums = np01.sum(axis=0)
    np02 = np01 / col_sums[np.newaxis, :]
    row_avg = np02.mean(axis=1) # 최종중요도(가중치)
    np03 = np01.dot(row_avg[:, np.newaxis])/row_avg[:, np.newaxis]
    consistency = (np03.mean(axis=0)-3)/2 # 일관성 지수
    return row_avg, consistency

요소가 3개인 경우의 AHP 함수를 불러와서 계산합니다.

a = gmean(df04['A12'])
b = gmean(df04['A13'])
c = gmean(df04['A23'])
for i, j in zip([a, b, c], ['a', 'b', 'c']):
    print('기하평균({}) : {}'.format(j, i))
 
row_avg, consistency = AHP_3(a, b, c)
 
for i, j in zip(['A1', 'A2', 'A3'], row_avg):
    print('최종 중요도({}) : {}'.format(i, j))
print('일관성 지수 : {}'.format(consistency[0]))

print 결과는 아래와 같습니다.

기하평균(a) : 0.6176919155401562
기하평균(b) : 0.442493076235233
기하평균(c) : 0.8096662696677938
최종 중요도(A1) : 0.20598830771905127
최종 중요도(A2) : 0.3472897679844087
최종 중요도(A3) : 0.44672192429654006
일관성 지수 : 0.0008329100723674099

요소가 5개인 경우

요소가 5개인 경우 AHP분석을 위한 함수를 정의해줍니다.

def AHP_5(a, b, c, d, e, f, g, h, i, j): # 요소가 5개인 경우 AHP분석 함수
    np01 = np.array([[1, a, b, c, d], [1/a, 1, e, f, g], [1/b, 1/e, 1, h, i], [1/c, 1/f, 1/h, 1, j], [1/d, 1/g, 1/i, 1/j, 1]])
    col_sums = np01.sum(axis=0)
    np02 = np01 / col_sums[np.newaxis, :]
    row_avg = np02.mean(axis=1) # 최종중요도(가중치)
    np03 = np01.dot(row_avg[:, np.newaxis])/row_avg[:, np.newaxis]
    consistency = (np03.mean(axis=0)-5)/4 # 일관성 지수
    return row_avg, consistency

요소가 5개인 경우의 AHP 함수를 불러와서 계산합니다.

a = gmean(df04['B12'])
b = gmean(df04['B13'])
c = gmean(df04['B14'])
d = gmean(df04['B15'])
e = gmean(df04['B23'])
f = gmean(df04['B24'])
g = gmean(df04['B25'])
h = gmean(df04['B34'])
i = gmean(df04['B35'])
j = gmean(df04['B45'])
for x, y in zip([a, b, c, d, e, f, g, h, i, j], ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']):
    print('기하평균({}) : {}'.format(y, x))
 
row_avg, consistency = AHP_5(a, b, c, d, e, f, g, h, i, j)
 
for i, j in zip(['B1', 'B2', 'B3', 'B4', 'B5'], row_avg):
    print('최종 중요도({}) : {}'.format(i, j))
print('일관성 지수 : {}'.format(consistency[0]))

print 결과는 아래와 같습니다.

기하평균(a) : 0.3050103357642936
기하평균(b) : 0.5858258671322156
기하평균(c) : 0.3108620646504318
기하평균(d) : 0.4171698328212431
기하평균(e) : 2.0591593773509027
기하평균(f) : 0.9063962168379981
기하평균(g) : 1.2283755877584674
기하평균(h) : 0.4058726608293928
기하평균(i) : 0.4850255305447254
기하평균(j) : 1.0016532216342333
최종 중요도(B1) : 0.08539220381140773
최종 중요도(B2) : 0.2703814268781585
최종 중요도(B3) : 0.12599413173595256
최종 중요도(B4) : 0.27898892874149267
최종 중요도(B5) : 0.23924330883298847
일관성 지수 : [0.00507996]

2. AHP분석(일관성 지수 0.1이하 응답 대상)

일관성 지수가 0.1 이하인 응답만 추려서 AHP분석을 수행하기 위해서는 개별 응답의 일관성 지수를 도출해야 합니다.

응답별 일관성 지수 도출

dma01 = df04[['A12', 'A13', 'A23']]
dma02 = df04[['직급', '부서']]
 
for index, row in dma01.iterrows():
a = row['A12']
b = row['A13']
c = row['A23']
row_avg, consistency = AHP_3(a, b, c)
dma01.loc[index, 'consis'] = consistency
 
print(dma01.head())
dma03 = dma02.join(dma01)

필터링 대상으로 AHP분석

a = gmean(dma04['A12'])
b = gmean(dma04['A13'])
c = gmean(dma04['A23'])
for i, j in zip([a, b, c], ['a', 'b', 'c']):
print('기하평균({}) : {}'.format(j, i))
 
row_avg, consistency = AHP_3(a, b, c)
 
for i, j in zip(['A1', 'A2', 'A3'], row_avg):
print('최종 중요도({}) : {}'.format(i, j))
print('일관성 지수 : {}'.format(consistency[0]))

print 결과는 아래와 같습니다.

기하평균(a) : 0.8108976435991946
기하평균(b) : 0.3888148312992916
기하평균(c) : 0.547408690351228
최종 중요도(A1) : 0.2106126851902216
최종 중요도(A2) : 0.27142099571874784
최종 중요도(A3) : 0.5179663190910305
일관성 지수 : 0.0009752983448947372

3. AHP분석(응답자 특성에 따른 분류)

응답자 특성은 직급과 부서가 있었습니다. 직급과 부서에 따라 가중치와 일관성 지수가 어떻게 나오는지 확인하겠습니다. 간단하게 앞서 수행했던 작업을 직급과 그룹별로 수행하면 됩니다.

그룹별 AHP분석(직급)

직급 = list(set(df04['직급'].tolist()))
for cl in 직급:
    a = gmean(df04['A12'])
    b = gmean(df04['A13'])
    c = gmean(df04['A23'])
    for i, j in zip([a, b, c], ['a', 'b', 'c']):
        print('직급 {}의 기하평균({}) : {}'.format(cl, j, i))
 
    row_avg, consistency = AHP_3(a, b, c)
 
    for i, j in zip(['A1', 'A2', 'A3'], row_avg):
        print('직급 {}의 최종 중요도({}) : {}'.format(cl, i, j))
    print('직급 {}의 일관성 지수 : {}'.format(cl, consistency))

print 결과는 아래와 같습니다.

직급 5급의 기하평균(a) : 0.6176919155401562
직급 5급의 기하평균(b) : 0.442493076235233
직급 5급의 기하평균(c) : 0.8096662696677938
직급 5급의 최종 중요도(A1) : 0.20598830771905127
직급 5급의 최종 중요도(A2) : 0.3472897679844087
직급 5급의 최종 중요도(A3) : 0.44672192429654006
직급 5급의 일관성 지수 : [0.00083291]
직급 4급의 기하평균(a) : 0.6176919155401562
직급 4급의 기하평균(b) : 0.442493076235233
직급 4급의 기하평균(c) : 0.8096662696677938
직급 4급의 최종 중요도(A1) : 0.20598830771905127
직급 4급의 최종 중요도(A2) : 0.3472897679844087
직급 4급의 최종 중요도(A3) : 0.44672192429654006
직급 4급의 일관성 지수 : [0.00083291]
직급 3급의 기하평균(a) : 0.6176919155401562
직급 3급의 기하평균(b) : 0.442493076235233
직급 3급의 기하평균(c) : 0.8096662696677938
직급 3급의 최종 중요도(A1) : 0.20598830771905127
직급 3급의 최종 중요도(A2) : 0.3472897679844087
직급 3급의 최종 중요도(A3) : 0.44672192429654006
직급 3급의 일관성 지수 : [0.00083291]
직급 2급의 기하평균(a) : 0.6176919155401562
직급 2급의 기하평균(b) : 0.442493076235233
직급 2급의 기하평균(c) : 0.8096662696677938
직급 2급의 최종 중요도(A1) : 0.20598830771905127
직급 2급의 최종 중요도(A2) : 0.3472897679844087
직급 2급의 최종 중요도(A3) : 0.44672192429654006
직급 2급의 일관성 지수 : [0.00083291]

그룹별 AHP분석(부서)

부서 = list(set(df04['부서'].tolist()))
for cl in 부서:
a = gmean(df04['A12'])
b = gmean(df04['A13'])
c = gmean(df04['A23'])
for i, j in zip([a, b, c], ['a', 'b', 'c']):
print('부서 {}의 기하평균({}) : {}'.format(cl, j, i))
 
row_avg, consistency = AHP_3(a, b, c)
 
for i, j in zip(['A1', 'A2', 'A3'], row_avg):
    print('부서 {}의 최종 중요도({}) : {}'.format(cl, i, j))
print('부서 {}의 일관성 지수 : {}'.format(cl, consistency))

print 결과는 아래와 같습니다.

부서 DDD부서의 기하평균(a) : 0.6176919155401562
부서 DDD부서의 기하평균(b) : 0.442493076235233
부서 DDD부서의 기하평균(c) : 0.8096662696677938
부서 DDD부서의 최종 중요도(A1) : 0.20598830771905127
부서 DDD부서의 최종 중요도(A2) : 0.3472897679844087
부서 DDD부서의 최종 중요도(A3) : 0.44672192429654006
부서 DDD부서의 일관성 지수 : [0.00083291]
부서 BBB부서의 기하평균(a) : 0.6176919155401562
부서 BBB부서의 기하평균(b) : 0.442493076235233
부서 BBB부서의 기하평균(c) : 0.8096662696677938
부서 BBB부서의 최종 중요도(A1) : 0.20598830771905127
부서 BBB부서의 최종 중요도(A2) : 0.3472897679844087
부서 BBB부서의 최종 중요도(A3) : 0.44672192429654006
부서 BBB부서의 일관성 지수 : [0.00083291]
부서 CCC부서의 기하평균(a) : 0.6176919155401562
부서 CCC부서의 기하평균(b) : 0.442493076235233
부서 CCC부서의 기하평균(c) : 0.8096662696677938
부서 CCC부서의 최종 중요도(A1) : 0.20598830771905127
부서 CCC부서의 최종 중요도(A2) : 0.3472897679844087
부서 CCC부서의 최종 중요도(A3) : 0.44672192429654006
부서 CCC부서의 일관성 지수 : [0.00083291]
부서 AAA부서의 기하평균(a) : 0.6176919155401562
부서 AAA부서의 기하평균(b) : 0.442493076235233
부서 AAA부서의 기하평균(c) : 0.8096662696677938
부서 AAA부서의 최종 중요도(A1) : 0.20598830771905127
부서 AAA부서의 최종 중요도(A2) : 0.3472897679844087
부서 AAA부서의 최종 중요도(A3) : 0.44672192429654006
부서 AAA부서의 일관성 지수 : [0.00083291]

4. AHP분석결과 DF로 저장하기

AHP분석을 하긴 했는데, 결과를 ’print’로만 확인하면 보기에도 어렵고 나중에 어딘가에 옮길 때에도 귀찮습니다. 그래서 AHP결과를 Dict에 저장해서 DataFrame으로 변환하려고 합니다. 그룹별 AHP분석결과를 바탕으로 AHP결과를 DataFrame에 저장하는 코드는 다음과 같습니다. 먼저 그룹별 AHP분석결과를 저장할 딕셔너리를 생성합니다.

Part1 = {'직급': dict(), '부서': dict()}

직급별 AHP분석결과를 앞서 생성한 Part1 딕셔너리에 저장하는 코드를 추가한 코드입니다.

직급 = list(set(df04['직급'].tolist()))
for cl in 직급:
a = gmean(df04['A12'])
b = gmean(df04['A13'])
c = gmean(df04['A23'])
for i, j in zip([a, b, c], ['a', 'b', 'c']):
print('직급 {}의 기하평균({}) : {}'.format(cl, j, i))
 
row_avg, consistency = AHP_3(a, b, c)
 
Part1['직급'][cl] = {}
for i, j in zip(['A1', 'A2', 'A3'], row_avg):
    print('직급 {}의 최종 중요도({}) : {}'.format(cl, i, j))
    Part1['직급'][cl][i] = j
print('직급 {}의 일관성 지수 : {}'.format(cl, consistency))
Part1['직급'][cl]['일관성'] = consistency[0]

부서별 AHP분석결과를 앞서 생성한 Part1 딕셔너리에 저장하는 코드를 추가한 코드입니다.

부서 = list(set(df04['부서'].tolist()))
for cl in 부서:
a = gmean(df04['A12'])
b = gmean(df04['A13'])
c = gmean(df04['A23'])
for i, j in zip([a, b, c], ['a', 'b', 'c']):
print('부서 {}의 기하평균({}) : {}'.format(cl, j, i))
 
row_avg, consistency = AHP_3(a, b, c)
 
Part1['부서'][cl] = {}
for i, j in zip(['A1', 'A2', 'A3'], row_avg):
    print('부서 {}의 최종 중요도({}) : {}'.format(cl, i, j))
    Part1['부서'][cl][i] = j
print('부서 {}의 일관성 지수 : {}'.format(cl, consistency))
Part1['부서'][cl]['일관성'] = consistency[0]

딕셔너리를 데이터프레임의로 변환합니다.

part1_df = pd.DataFrame.from_dict({(i, j): Part1[i][j] for i in Part1.keys() for j in Part1[i].keys()}).T
print(part1_df)

이상으로 파이썬을 활용하여 AHP 설문을 분석하는 방법을 알아보았습니다.