[Pandas 기초] 피벗 테이블(pivot_table)과 멀티인덱스(MultiIndex)

업데이트:

개요

jpg

이번 포스팅에서는 엑셀에서 사용하는 피벗테이블과 같은 기능을 처리하는 방법과 부차적으로 알아야할 멀티인덱스에 대해 알아보자.
기본적인 개념과 사용법만 알고 있다면, 복잡한 데이터 처리를 거치지 않고 간단하게 원하는 데이터를 만들어 주는 경우가 많다.

예제로 타이타닉 데이터를 이용한다.

import pandas as pd
import seaborn as sns

df = sns.load_dataset('titanic')[['age','sex','class','fare','survived']]
df.head()
age sex class fare survived
0 22.0 male Third 7.2500 0
1 38.0 female First 71.2833 1
2 26.0 female Third 7.9250 1
3 35.0 female First 53.1000 1
4 35.0 male Third 8.0500 0


1. 피벗 테이블 함수 : pivot_table()

pivot_table() 함수의 기본 구성요소는 다음과 같다.

  • 행 인덱스
  • 열 인덱스
  • 데이터 값
  • 데이터 집계함수

각 구성요소에 적용할 데이터프레임의 열을 각각 함수의 인자로 전달한다.
4가지 구성요소를 적절히 입력하고 피벗테이블을 생성해보자.

pdf1 = pd.pivot_table(df,                # 피벗할 데이터프레임
                     index = 'class',    # 행 위치에 들어갈 열
                     columns = 'sex',    # 열 위치에 들어갈 열
                     values = 'age',     # 데이터로 사용할 열
                     aggfunc = 'mean')   # 데이터 집계함수
pdf1
sex female male
class
Third 21.750000 26.507589
First 34.611765 41.281386
Second 28.722973 30.740707

행에는 class열의 3가지 그룹, 열에는 sex열의 2가지그룹, 값에는 age열을 평균값(mean)으로 집계한 값이 들어간 것을 확인할 수 있다.

이번에는 구조를 조금 바꾸고, 집계함수를 2개 넣어보자

pdf2 = pd.pivot_table(df,                # 피벗할 데이터프레임
                     index = 'class',    # 행 위치에 들어갈 열
                     columns = 'sex',    # 열 위치에 들어갈 열
                     values = 'survived',     # 데이터로 사용할 열
                     aggfunc = ['mean', 'sum'])   # 데이터 집계함수
pdf2
mean sum
sex female male female male
class
Third 0.500000 0.135447 72 47
First 0.968085 0.368852 91 45
Second 0.921053 0.157407 70 17

3등석(Third)에 탄 여자(female)들의 생존여부(survived)는 평균 50%정도,
1등석(First)에 탄 남자(male)들의 생존자는 모두 45명이다.

이번에는 더많은 열을 인자로 입력해보자.

pdf3 = pd.pivot_table(df,
                     index = ['class','sex'],
                     columns = 'survived',
                     values = ['age','fare'],
                     aggfunc = ['mean','max'])
pdf3
mean max
age fare age fare
survived 0 1 0 1 0 1 0 1
class sex
Third female 23.818182 19.329787 19.773093 12.464526 48.0 63.0 69.55 31.3875
male 27.255814 22.274211 12.204469 15.579696 74.0 45.0 69.55 56.4958
First female 25.666667 34.939024 110.604167 105.978159 50.0 63.0 151.55 512.3292
male 44.581967 36.248000 62.894910 74.637320 71.0 80.0 263.00 512.3292
Second female 36.000000 28.080882 18.250000 22.288989 57.0 55.0 26.00 65.0000
male 33.369048 16.022000 19.488965 21.095100 70.0 62.0 73.50 39.0000

age와 fare열을 값(values)으로 지정한 경우 보기 쉽게 두 열로 명시해준다.
3등석(Third)에 탄 여자(female)중 생존(survived)한 사람들의 최고(max) 연령은 63세이고,
2등석(Second)에 탄 남자(male)중 사망(survived)한 사람들이 지불한 평균(mean) 요금(fare)은 19.488965이다.

이처럼 그룹핑을 사용하지 않고도 편리하게 활용할 수 있는 장점이 있다.


2. 멀티 인덱스

앞의 그룹연산 포스팅에서 다중 열을 기준으로 그룹객체를 생성하면 멀티인덱스 인덱스를 구성하는 것을 봤었다.

이번 포스팅에서 이 멀티인덱스를 다루는 방법에 대해 간단하게만 알아보도록 하자.

타이타닉 데이터를 로드해서, class, age열에 대해 그룹객체를 생성한 후, 평균(mean)으로 집계해보자.

import pandas as pd
import seaborn as sns

df = sns.load_dataset('titanic')[['age','sex','class','fare','survived']]
grouped = df.groupby(['class','sex'])
gdf = grouped.mean()
gdf
age fare survived
class sex
First female 34.611765 106.125798 0.968085
male 41.281386 67.226127 0.368852
Second female 28.722973 21.970121 0.921053
male 30.740707 19.741782 0.157407
Third female 21.750000 16.118810 0.500000
male 26.507589 12.661633 0.135447

두 그룹에 대해 멀티 행인덱스를 생성한 것을 확인할 수 있다.

2-1. 멀티 인덱스의 인덱싱(indexing)

멀티 인덱스도 그냥 데이터프레임을 인덱싱하는 방법과 동일하다.

앞에서 만든 그룹객체는 class열로 그룹한 후, sex열로 다시 그룹했다는 것을 알아두자.(순서)
이 순서에 따라 인덱싱도 같은 방법으로 적용하면 된다.

class열의 First 행만 인덱싱

gdf.loc['First']
age fare survived
sex
female 34.611765 106.125798 0.968085
male 41.281386 67.226127 0.368852

처음부터 class그룹, sex그룹에 대해 바로 인덱싱 하려면, 튜플 형태로 인덱싱하면 된다.

그룹객체를 생성하면 튜플형태로 저장되는 사실을 앞의 그룹연산 포스팅에서 배운바 있다.

gdf.loc[('First','female')]
[Output]
age          34.611765
fare        106.125798
survived      0.968085
Name: (First, female), dtype: float64

2-2. 멀티인덱서 : .xs

sex그룹의 male값을 갖는 행을 추출, 즉 등급(class)별 male에 대한 자료를 인덱싱하려면 다음과 같이 수행하면 된다.

gdf.xs('male', level='sex')
age fare survived
class
First 41.281386 67.226127 0.368852
Second 30.740707 19.741782 0.157407
Third 26.507589 12.661633 0.135447

쉽게 말해 판다스의 기본 데이터프레임 인덱싱 함수 lociloc를 이용하려면 큰 그룹부터 순차적으로 인덱싱을 해야하는데, 멀티인덱서 .xs를 이용하면 그룹 범주와 상관없이 수준(level)만 명시해주면 인덱싱이 가능하다.

위에서 sex는 두번째 그룹범주이므로 ‘sex’또는 1로 입력해도 무방하다.

2-3. 멀티인덱스 해제

멀티 행인덱스를 풀고 싶다면

print(gdf.reset_index())
[Output]
    class     sex        age        fare  survived
0   First  female  34.611765  106.125798  0.968085
1   First    male  41.281386   67.226127  0.368852
2  Second  female  28.722973   21.970121  0.921053
3  Second    male  30.740707   19.741782  0.157407
4   Third  female  21.750000   16.118810  0.500000
5   Third    male  26.507589   12.661633  0.135447

컬럼이 멀티인덱스인 경우,

gdf2 = df.groupby('class').agg(['mean','max'])[['age','fare']]
print(gdf2)
[Output]
              age             fare          
             mean   max       mean       max
class                                       
First   38.233441  80.0  84.154687  512.3292
Second  29.877630  70.0  20.662183   73.5000
Third   25.140620  74.0  13.675550   69.5500

이렇게 생긴 멀티 열인덱스가 어떤 목적을 위해 보기가 싫다면,

gdf2.columns = ['age_mean', 'age_max', 'fare_mean','fare_max']
print(gdf2.head())
[Output]
         age_mean  age_max  fare_mean  fare_max
class                                          
First   38.233441     80.0  84.154687  512.3292
Second  29.877630     70.0  20.662183   73.5000
Third   25.140620     74.0  13.675550   69.5500

컬럼명을 새로 지정해주면 멀티인덱스가 해제된다.

2-4. 피벗테이블의 멀티인덱싱 추가 응용

1번 목차의 마지막에 생성했던 피벗테이블을 다시 생성해보자.

pdf3 = pd.pivot_table(df,
                     index = ['class','sex'],
                     columns = 'survived',
                     values = ['age','fare'],
                     aggfunc = ['mean','max'])
pdf3
mean max
age fare age fare
survived 0 1 0 1 0 1 0 1
class sex
Third female 23.818182 19.329787 19.773093 12.464526 48.0 63.0 69.55 31.3875
male 27.255814 22.274211 12.204469 15.579696 74.0 45.0 69.55 56.4958
First female 25.666667 34.939024 110.604167 105.978159 50.0 63.0 151.55 512.3292
male 44.581967 36.248000 62.894910 74.637320 71.0 80.0 263.00 512.3292
Second female 36.000000 28.080882 18.250000 22.288989 57.0 55.0 26.00 65.0000
male 33.369048 16.022000 19.488965 21.095100 70.0 62.0 73.50 39.0000

이 피벗테이블이 인덱싱이 굉장히 많아서 연습하기 좋을 것 같아서 말이다.

대범주 class열의 First그룹 인덱싱

pdf3.xs('First')
mean max
age fare age fare
survived 0 1 0 1 0 1 0 1
sex
female 25.666667 34.939024 110.604167 105.978159 50.0 63.0 151.55 512.3292
male 44.581967 36.248000 62.894910 74.637320 71.0 80.0 263.00 512.3292

두번째 행인덱스 범주 male을 멀티인덱서 xs로 인덱싱(level='sex'옵션 필수)

pdf3.xs('male', level='sex')
mean max
age fare age fare
survived 0 1 0 1 0 1 0 1
class
Third 27.255814 22.274211 12.204469 15.579696 74.0 45.0 69.55 56.4958
First 44.581967 36.248000 62.894910 74.637320 71.0 80.0 263.00 512.3292
Second 33.369048 16.022000 19.488965 21.095100 70.0 62.0 73.50 39.0000

여기서 레벨(level)에 사용된 0은 컬렴명이 아니라 행인덱스의 레벨을 의미한다.
0은 ‘class’, 1은 ‘sex’를 의미하며, ‘sex’대신 1을 입력해도 같은 결과이다.

pdf3.xs(('Second','male'),level=[0,'sex'])
mean max
age fare age fare
survived 0 1 0 1 0 1 0 1
class sex
Second male 33.369048 16.022 19.488965 21.0951 70.0 62.0 73.5 39.0

여기서 멀티인덱서 .xs에 level을 추가하냐 안하냐의 차이는 범주의 크기에 따른 것도 있지만, 반환되는 객체의 형태에도 차이가 있다.

동일한 결과를 주는 다음 두 형태를 보자.

pdf3.xs(('First','female'))
[Output]
            survived
mean  age   0            25.666667
            1            34.939024
      fare  0           110.604167
            1           105.978159
max   age   0            50.000000
            1            63.000000
      fare  0           151.550000
            1           512.329200
Name: (First, female), dtype: float64
pdf3.xs(('First','female'), level=['class','sex'])
mean max
age fare age fare
survived 0 1 0 1 0 1 0 1
class sex
First female 25.666667 34.939024 110.604167 105.978159 50.0 63.0 151.55 512.3292

첫번째의 경우는 사실 튜플형태로 직접 입력했으므로 .xs가 아닌 .loc함수로도 인덱싱이 가능하다.
어쨋거나 level을 지정해주면 데이터프레임 객체를 반환한다는 점 알아두자.

이번엔 열의 멀티인덱스를 인덱싱해보자. 행인덱스 인덱싱방법과 동일하며 axis=1옵션만 추가해주면 된다.

mean열만 인덱싱

pdf3.xs('mean', axis=1)
age fare
survived 0 1 0 1
class sex
Third female 23.818182 19.329787 19.773093 12.464526
male 27.255814 22.274211 12.204469 15.579696
First female 25.666667 34.939024 110.604167 105.978159
male 44.581967 36.248000 62.894910 74.637320
Second female 36.000000 28.080882 18.250000 22.288989
male 33.369048 16.022000 19.488965 21.095100

mean열의 age열 인덱싱

pdf3.xs(('mean','age'), axis=1)
survived 0 1
class sex
Third female 23.818182 19.329787
male 27.255814 22.274211
First female 25.666667 34.939024
male 44.581967 36.248000
Second female 36.000000 28.080882
male 33.369048 16.022000


Reference

도서 [파이썬 머신러닝 판다스 데이터 분석]을 공부하며 작성하였습니다.

댓글남기기