🔥알림🔥
① 테디노트 유튜브 -
구경하러 가기!
② LangChain 한국어 튜토리얼
바로가기 👀
③ 랭체인 노트 무료 전자책(wikidocs)
바로가기 🙌
④ RAG 비법노트 LangChain 강의오픈
바로가기 🙌
⑤ 서울대 PyTorch 딥러닝 강의
바로가기 🙌
#07-Pandas(판다스) Groupby와 Pivot table
이번 에피소드에서는 Pandas DataFrame의 groupby()
, pivot_table()
을 활용한 데이터 분석 방법에 대하여 다뤄 보도록 하겠습니다.
groupby()
는 데이터를 피봇팅하여 통계량을 볼 수 있도록 도와주는 메서드이면서, 데이터를 특정 조건에 맞게 전처리해 줄 때로 용이합니다.
pivot_table()
은 데이터를 특정 조건에 따라 행(row)과 열(column)을 기준으로 데이터를 펼쳐서 그에 대한 통계량을 볼 때 활용합니다.
모듈 import
from IPython.display import Image
import numpy as np
import pandas as pd
import seaborn as sns
데이터셋 로드
df = sns.load_dataset('titanic')
df.head()
survived | pclass | sex | age | sibsp | parch | fare | embarked | class | who | adult_male | deck | embark_town | alive | alone | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 3 | male | 22.0 | 1 | 0 | 7.2500 | S | Third | man | True | NaN | Southampton | no | False |
1 | 1 | 1 | female | 38.0 | 1 | 0 | 71.2833 | C | First | woman | False | C | Cherbourg | yes | False |
2 | 1 | 3 | female | 26.0 | 0 | 0 | 7.9250 | S | Third | woman | False | NaN | Southampton | yes | True |
3 | 1 | 1 | female | 35.0 | 1 | 0 | 53.1000 | S | First | woman | False | C | Southampton | yes | False |
4 | 0 | 3 | male | 35.0 | 0 | 0 | 8.0500 | S | Third | man | True | NaN | Southampton | no | True |
컬럼(columns) 설명
- survivied: 생존여부 (1: 생존, 0: 사망)
- pclass: 좌석 등급 (1등급, 2등급, 3등급)
- sex: 성별
- age: 나이
- sibsp: 형제 + 배우자 수
- parch: 부모 + 자녀 수
- fare: 좌석 요금
- embarked: 탑승 항구 (S, C, Q)
- class: pclass와 동일
- who: 성별과 동일
- adult_male: 성인 남자 여부
- deck: 데크 번호 (알파벳 + 숫자 혼용)
- embark_town: 탑승 항구 이름
- alive: 생존여부 (yes, no)
- alone: 혼자 탑승 여부
apply() - 함수를 적용
apply()
는 데이터 전처리시 굉장히 많이 활용하는 기능입니다.
좀 더 복잡한 logic을 컬럼 혹은 DataFrame에 적용하고자 할 때 사용합니다.
df.head()
survived | pclass | sex | age | sibsp | parch | fare | embarked | class | who | adult_male | deck | embark_town | alive | alone | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 3 | male | 22.0 | 1 | 0 | 7.2500 | S | Third | man | True | NaN | Southampton | no | False |
1 | 1 | 1 | female | 38.0 | 1 | 0 | 71.2833 | C | First | woman | False | C | Cherbourg | yes | False |
2 | 1 | 3 | female | 26.0 | 0 | 0 | 7.9250 | S | Third | woman | False | NaN | Southampton | yes | True |
3 | 1 | 1 | female | 35.0 | 1 | 0 | 53.1000 | S | First | woman | False | C | Southampton | yes | False |
4 | 0 | 3 | male | 35.0 | 0 | 0 | 8.0500 | S | Third | man | True | NaN | Southampton | no | True |
who 컬럼에 대하여 man은 남자, woman은 여자, child는 아이로 변경하고자 한다면 apply를 활용하여 해결할 수 있습니다.
df['who'].value_counts()
man 537 woman 271 child 83 Name: who, dtype: int64
함수(function) 정의
def transform_who(x):
if x == 'man':
return '남자'
elif x == 'woman':
return '여자'
else:
return '아이'
df['who'].apply(transform_who)
0 남자 1 여자 2 여자 3 여자 4 남자 .. 886 남자 887 여자 888 여자 889 남자 890 남자 Name: who, Length: 891, dtype: object
분포를 확인하면 다음과 같습니다.
df['who'].apply(transform_who).value_counts()
남자 537 여자 271 아이 83 Name: who, dtype: int64
def transform_who(x):
return x['fare'] / x['age']
df.apply(transform_who, axis=1)
0 0.329545 1 1.875876 2 0.304808 3 1.517143 4 0.230000 ... 886 0.481481 887 1.578947 888 NaN 889 1.153846 890 0.242188 Length: 891, dtype: float64
apply() - lambda 함수
간단한 logic은 함수를 굳이 정의하지 않고, lambda 함수로 쉽게 해결할 수 있습니다.
df['survived'].value_counts()
0 549 1 342 Name: survived, dtype: int64
0: 사망, 1: 생존 으로 변경하도록 하겠습니다.
df.head()
survived | pclass | sex | age | sibsp | parch | fare | embarked | class | who | adult_male | deck | embark_town | alive | alone | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 3 | male | 22.0 | 1 | 0 | 7.2500 | S | Third | man | True | NaN | Southampton | no | False |
1 | 1 | 1 | female | 38.0 | 1 | 0 | 71.2833 | C | First | woman | False | C | Cherbourg | yes | False |
2 | 1 | 3 | female | 26.0 | 0 | 0 | 7.9250 | S | Third | woman | False | NaN | Southampton | yes | True |
3 | 1 | 1 | female | 35.0 | 1 | 0 | 53.1000 | S | First | woman | False | C | Southampton | yes | False |
4 | 0 | 3 | male | 35.0 | 0 | 0 | 8.0500 | S | Third | man | True | NaN | Southampton | no | True |
df['survived'].apply(lambda x: '생존' if x == 1 else '사망')
0 사망 1 생존 2 생존 3 생존 4 사망 .. 886 사망 887 생존 888 사망 889 생존 890 사망 Name: survived, Length: 891, dtype: object
df['survived'].apply(lambda x: '생존' if x == 1 else '사망').value_counts()
사망 549 생존 342 Name: survived, dtype: int64
groupby() - 그룹
데이터를 특정 기준으로 그룹핑할 때 활용합니다. 엑셀의 피봇테이블과 유사합니다.
df.head()
survived | pclass | sex | age | sibsp | parch | fare | embarked | class | who | adult_male | deck | embark_town | alive | alone | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 3 | male | 22.0 | 1 | 0 | 7.2500 | S | Third | man | True | NaN | Southampton | no | False |
1 | 1 | 1 | female | 38.0 | 1 | 0 | 71.2833 | C | First | woman | False | C | Cherbourg | yes | False |
2 | 1 | 3 | female | 26.0 | 0 | 0 | 7.9250 | S | Third | woman | False | NaN | Southampton | yes | True |
3 | 1 | 1 | female | 35.0 | 1 | 0 | 53.1000 | S | First | woman | False | C | Southampton | yes | False |
4 | 0 | 3 | male | 35.0 | 0 | 0 | 8.0500 | S | Third | man | True | NaN | Southampton | no | True |
타이타닉 호의 생존자와 사망자를 성별 기준으로 그룹핑하여 평균을 살펴보겠습니다.
df.groupby('sex').mean()
survived | pclass | age | sibsp | parch | fare | adult_male | alone | |
---|---|---|---|---|---|---|---|---|
sex | ||||||||
female | 0.742038 | 2.159236 | 27.915709 | 0.694268 | 0.649682 | 44.479818 | 0.000000 | 0.401274 |
male | 0.188908 | 2.389948 | 30.726645 | 0.429809 | 0.235702 | 25.523893 | 0.930676 | 0.712305 |
groupby()
를 사용할 때는 반드시 aggregate 하는 통계함수와 일반적으로 같이 적용합니다.
2개 이상의 컬럼으로 그룹
2개 이상의 컬럼으로 그룹핑할 때도 list로 묶어서 지정하면 됩니다.
# 성별, 좌석등급 별 통계
df.groupby(['sex', 'pclass']).mean()
survived | age | sibsp | parch | fare | adult_male | alone | ||
---|---|---|---|---|---|---|---|---|
sex | pclass | |||||||
female | 1 | 0.968085 | 34.611765 | 0.553191 | 0.457447 | 106.125798 | 0.000000 | 0.361702 |
2 | 0.921053 | 28.722973 | 0.486842 | 0.605263 | 21.970121 | 0.000000 | 0.421053 | |
3 | 0.500000 | 21.750000 | 0.895833 | 0.798611 | 16.118810 | 0.000000 | 0.416667 | |
male | 1 | 0.368852 | 41.281386 | 0.311475 | 0.278689 | 67.226127 | 0.975410 | 0.614754 |
2 | 0.157407 | 30.740707 | 0.342593 | 0.222222 | 19.741782 | 0.916667 | 0.666667 | |
3 | 0.135447 | 26.507589 | 0.498559 | 0.224784 | 12.661633 | 0.919308 | 0.760807 |
1개의 특정 컬럼에 대한 결과 도출
우리의 주요 관심사는 survived
컬럼입니다. 만약 survived
컬럼에 대한 결과만 도출하고 싶다면 컬럼을 맨 끝에 지정합니다.
# 성별, 좌석등급 별 통계
df.groupby(['sex', 'pclass'])['survived'].mean()
sex pclass female 1 0.968085 2 0.921053 3 0.500000 male 1 0.368852 2 0.157407 3 0.135447 Name: survived, dtype: float64
예쁘게 출력하려면 pd.DataFrame()
으로 감싸주거나, survived
컬럼을 []로 한 번 더 감싸주면 됩니다.
# 성별, 좌석등급 별 통계
df.groupby(['sex', 'pclass'])['survived'].mean()
sex pclass female 1 0.968085 2 0.921053 3 0.500000 male 1 0.368852 2 0.157407 3 0.135447 Name: survived, dtype: float64
# DataFrame으로 출력
pd.DataFrame(df.groupby(['sex', 'pclass'])['survived'].mean())
survived | ||
---|---|---|
sex | pclass | |
female | 1 | 0.968085 |
2 | 0.921053 | |
3 | 0.500000 | |
male | 1 | 0.368852 |
2 | 0.157407 | |
3 | 0.135447 |
# DataFrame으로 출력
df.groupby(['sex', 'pclass'])[['survived']].mean()
survived | ||
---|---|---|
sex | pclass | |
female | 1 | 0.968085 |
2 | 0.921053 | |
3 | 0.500000 | |
male | 1 | 0.368852 |
2 | 0.157407 | |
3 | 0.135447 |
reset_index(): 인덱스 초기화
reset_index()
: 그룹핑된 데이터프레임의 index를 초기화하여 새로운 데이터프레임을 생성합니다.
# index 초기화
df.groupby(['sex', 'pclass'])['survived'].mean().reset_index()
sex | pclass | survived | |
---|---|---|---|
0 | female | 1 | 0.968085 |
1 | female | 2 | 0.921053 |
2 | female | 3 | 0.500000 |
3 | male | 1 | 0.368852 |
4 | male | 2 | 0.157407 |
5 | male | 3 | 0.135447 |
다중 컬럼에 대한 결과 도출
끝에 단일 컬럼이 아닌 여러 개의 컬럼을 지정합니다.
# 성별, 좌석등급 별 통계
df.groupby(['sex', 'pclass'])[['survived', 'age']].mean()
survived | age | ||
---|---|---|---|
sex | pclass | ||
female | 1 | 0.968085 | 34.611765 |
2 | 0.921053 | 28.722973 | |
3 | 0.500000 | 21.750000 | |
male | 1 | 0.368852 | 41.281386 |
2 | 0.157407 | 30.740707 | |
3 | 0.135447 | 26.507589 |
다중 통계 함수 적용
여러 가지의 통계 값을 적용할 때는 agg()
를 사용합니다.
# 성별, 좌석등급 별 통계
df.groupby(['sex', 'pclass'])[['survived', 'age']].agg(['mean', 'sum'])
survived | age | ||||
---|---|---|---|---|---|
mean | sum | mean | sum | ||
sex | pclass | ||||
female | 1 | 0.968085 | 91 | 34.611765 | 2942.00 |
2 | 0.921053 | 70 | 28.722973 | 2125.50 | |
3 | 0.500000 | 72 | 21.750000 | 2218.50 | |
male | 1 | 0.368852 | 45 | 41.281386 | 4169.42 |
2 | 0.157407 | 17 | 30.740707 | 3043.33 | |
3 | 0.135447 | 47 | 26.507589 | 6706.42 |
numpy 의 통계 함수도 적용 가능합니다. (결과는 동일합니다)
# 성별, 좌석등급 별 통계
df.groupby(['sex', 'pclass'])[['survived', 'age']].agg([np.mean, np.sum])
survived | age | ||||
---|---|---|---|---|---|
mean | sum | mean | sum | ||
sex | pclass | ||||
female | 1 | 0.968085 | 91 | 34.611765 | 2942.00 |
2 | 0.921053 | 70 | 28.722973 | 2125.50 | |
3 | 0.500000 | 72 | 21.750000 | 2218.50 | |
male | 1 | 0.368852 | 45 | 41.281386 | 4169.42 |
2 | 0.157407 | 17 | 30.740707 | 3043.33 | |
3 | 0.135447 | 47 | 26.507589 | 6706.42 |
pivot_table()
피벗테이블은 엑셀의 피벗과 동작이 유사하며, groupby()
와도 동작이 유사합니다.
기본 동작 원리는 index
, columns
, values
를 지정하여 피벗합니다.
1개 그룹에 대한 단일 컬럼 결과
# index에 그룹을 표기
df.pivot_table(index='who', values='survived')
survived | |
---|---|
who | |
child | 0.590361 |
man | 0.163873 |
woman | 0.756458 |
# columns에 그룹을 표기
df.pivot_table(columns='who', values='survived')
who | child | man | woman |
---|---|---|---|
survived | 0.590361 | 0.163873 | 0.756458 |
다중 그룹에 대한 단일 컬럼 결과
df.pivot_table(index=['who', 'pclass'], values='survived')
survived | ||
---|---|---|
who | pclass | |
child | 1 | 0.833333 |
2 | 1.000000 | |
3 | 0.431034 | |
man | 1 | 0.352941 |
2 | 0.080808 | |
3 | 0.119122 | |
woman | 1 | 0.978022 |
2 | 0.909091 | |
3 | 0.491228 |
index에 컬럼을 중첩하지 않고 행과 열로 펼친 결과
df.pivot_table(index='who', columns='pclass', values='survived')
pclass | 1 | 2 | 3 |
---|---|---|---|
who | |||
child | 0.833333 | 1.000000 | 0.431034 |
man | 0.352941 | 0.080808 | 0.119122 |
woman | 0.978022 | 0.909091 | 0.491228 |
다중 통계함수 적용
df.pivot_table(index='who', columns='pclass', values='survived', aggfunc=['sum', 'mean'])
sum | mean | |||||
---|---|---|---|---|---|---|
pclass | 1 | 2 | 3 | 1 | 2 | 3 |
who | ||||||
child | 5 | 19 | 25 | 0.833333 | 1.000000 | 0.431034 |
man | 42 | 8 | 38 | 0.352941 | 0.080808 | 0.119122 |
woman | 89 | 60 | 56 | 0.978022 | 0.909091 | 0.491228 |
댓글남기기