Python pandas 데이터 재형성, 연산, 집계하는 법
데이터 분석 4단계

지난 시간에는 Python pandas 데이터 병합, 정제, 변형하는 법을 살펴보았습니다. 이번 시간에는 DataFrame을 재형성하고, DataFrame 간 연산하기, 그리고 데이터를 집계하는 법을 알아보겠습니다.
준비하기
실습을 위해서 먼저 데이터를 준비하겠습니다. 여기서는 Seaborn 라이브러리의 내장 데이터셋 중 타이타닉호(Titanic) 데이터셋을 사용하겠습니다.
pythonimport pandas as pdfrom seaborn import load_datasetdf = load_dataset('titanic')df.head(5)

먼저 DataFrame을 재형성하는 법부터 살펴보겠습니다.
재형성하기(Reshaping)
DataFrame을 재형성한다는 것은 DataFrame의 구조를 물리적으로 재배치하는 것을 의미합니다. 데이터셋의 열과 행을 변경해서 데이터를 이해하고 분석하기 쉬운 형태로 다시 만드는 것입니다. pandas에서 DataFrame을 재형성하는 방법에는 크게 3가지가 있습니다.
- 교차표(cross-tabulation) 만들기: crosstab, pivot_table
- 긴 형식의 데이터셋을 넓은 형식의 데이터셋으로 바꾸기: pivot
- 넓은 형식의 데이터셋을 긴 형식의 데이터셋으로 바꾸기
1) 간단하게 교차표 만들기: crosstab
교차표는 2개 이상의 범주형 데이터의 빈도를 나타낸 표입니다. 교차표를 이용하면 2개 이상의 범주형 변수 간의 관계를 효과적으로 파악할 수 있습니다. pandas에서 교차 빈도표를 만드는 가장 기본적인 방법은 crosstab
함수를 이용하는 것입니다.
사용 전 주의점
crosstab
함수를 사용하기 전에 결측치를 어떻게 처리할 것인지 결정해야 합니다. crosstab
함수는 기본적으로 누락된 행이나 열을 제외하고 결과 테이블에 포함하지 않습니다(기본값: dropna=True). 만일, 누락된 값을 교차 빈도표에 포함하고 싶다면 매개변수 설정을 dropna=False
로 변경해야 합니다. 여기서는 기본 설정대로 사용하겠습니다.
기본
crosstab
함수의 기본적인 사용법은 아래와 같습니다.
pythonpd.crosstab(df['sex'], df['alive'])# 결과alive no yessexfemale 81 233male 468 109
매개변수를 명확히 표기하면 다음과 같습니다.
pythonpd.crosstab(index=df['sex'], columns=df['alive'], values=None)
옵션
crosstab
함수에는 다양한 매개변수 옵션이 있습니다.
비율 계산
- normalize='columns': 각 열의 총합이 1이 되도록 정규화한 비율 계산
- normalize='index': 각 행의 총합이 1이 되도록 정규화한 비율 계산
- normalize='all' 또는 normalize=True: 모든 데이터의 비율 계산
- margins=True: 총계를 마지막 행 또는 열에
All
열에 추가- margins_name: 총계열('All') 이름 새로 지정
다음은 타이타닉호의 성별 생존자수를 만드는 코드입니다.
pythoncrosstb = pd.crosstab(df['sex'], df['alive'],normalize='index', margins=True).round(3) * 100crosstb# 결과alive no yessexfemale 25.8 74.2male 81.1 18.9All 61.6 38.4
결과를 다중 막대그래프로 보면 다음과 같습니다.
pythoncrosstb.plot.bar(rot=0)

수치형 변수 추가
범주형 변수에 수치형 변수를 추가하고 싶다면, aggfunc
매개변수에 집계함수를 지정하면 됩니다.
pythonpd.crosstab(df['sex'], df['alive'], df['age'], aggfunc='median')# 결과alive no yessexfemale 24.5 28.0male 29.0 28.0
계층 세분화
계층을 세분화하고 싶다면 대괄호를 이용해서 색인 단계를 높이면 됩니다.
pythonpd.crosstab([df['sex'], df['class']],[df['alive'], df['sibsp']],df['age'],aggfunc='median')

여러 개의 집계함수 사용
여러 개의 집계함수를 적용하고 싶다면 aggfunc
매개변수에 집계함수의 리스트를 추가하면 됩니다.
pythonpd.crosstab(df['sex'], df['alive'], df['sibsp'],aggfunc=['median', 'mean'])

지금까지 crosstab
함수의 사용법을 알아보았습니다. crosstab
함수는 데이터셋의 크기가 작거나, 각 범주가 차지하는 비율을 계산하고 싶거나, 범주형 데이터에 대한 교차표를 간편하게 만들고 싶을 때 유용합니다. 데이터셋의 크기가 크거나, 결측치를 처리하는 옵션을 설정하거나, 수치형 데이터를 추가해서 데이터를 다양하게 연산하고 싶을 경우에는 pivot_table
을 사용하는 것이 좋습니다.
pivot_table
은 먼저 인덱스와 컬럼을 그룹화하고 집계함수를 적용해서 데이터를 변환합니다. 그래서 교차표를 생성할 때 전체 데이터셋을 다시 처리하는 crosstab
함수보다 큰 데이터셋을 처리하는 속도가 빠릅니다. 그럼 pivot_table
함수의 사용법을 살펴보겠습니다.
2) 세밀하게 교차표 만들기: pivot_table
사용 전 주의점
pivot_table
함수 역시 crosstab
함수와 마찬가지로 결측치는 제외하고 연산됩니다(기본값: dropna=True). pivot_table
에서는 crosstab
함수와 달리 fill_value
매개변수를 이용해 누락된 값을 다른 값으로 대체할 수 있습니다. 만일, 누락된 값을 0으로 대체하고 싶다면 fill_value=0
옵션을 추가하면 됩니다.
기본
pivot_table
의 기본 사용법은 다음과 같습니다.
pythonpd.pivot_table(df, 'age', 'sex', 'alive', aggfunc='count')# 결과alive no yessexfemale 64 197male 360 93
aggfunc
매개변수의 기본값은 mean
입니다. 매개변수의 값을 count
로 주면 crosstab
을 사용할 때와 동일한 교차표를 얻을 수 있습니다. 단, 수치형 변수는 crosstab
함수를 사용할 때와 달리 index
와 columns
매개변수 앞에 위치시켜야 합니다.
아래와 같이 매개변수를 명확히 표기할 수도 있습니다.
pythonpd.pivot_table(df, values='age', index='sex', columns='alive',aggfunc='median')
crosstab
함수에서는 values
매개변수가 index
와 columns
다음에 위치했지만, pivot_table
함수에서는 두 매개변수 앞에 사용합니다. 따라서, 범주형 변수의 연산 결과보다 수치형 변수의 연산 결과에 집중할 수 있습니다.
pivot_table
함수는 DataFrame 수준에서 사용할 수도 있습니다.
pythondf.pivot_table('age', 'sex', 'alive', aggfunc='median')# 결과alive no yessexfemale 24.5 28.0male 29.0 28.0
옵션
pivot_table
함수에서도 다음과 같은 옵션을 사용할 수 있습니다.
- margins=True: 총계를 마지막 행 또는 열에
All
열에 추가- margins_name: 총계열('All') 이름 새로 지정
계층적 색인
계층적 색인이 있는 교차표를 만들고 싶다면
pythondf.pivot_table('age', ['sex', 'class'], ['alive'],aggfunc=['median', 'mean'],margins=True)

지금까지 pivot_table
의 사용법을 알아보았습니다.
3) 긴 형식 ⇒ 넓은 형식: pivot
피봇팅(pivoting)은 특정한 축을 기준으로 데이터를 재정렬하는 것을 말합니다. 한 열의 값을 행 인덱스로 옮기거나, 행 인덱스를 열로 변환하는 것입니다. pivot
함수는 한 컬럼의 값을 열 이름으로 변경하여 데이터를 재구성하는 함수입니다. pivot
함수는 계층적 색인이 없는 일반 DataFrame을 긴 형식에서 넓은 형식으로 신속하게 만들 때 유용합니다. 특히, 시계열 데이터처럼 아래로 긴 형식의 데이터를 간략하게 정리할 때 유용합니다. 실습을 위해서 다음 예제를 사용하겠습니다.
pythondf_kfood = pd.DataFrame({'날짜': ['220501', '220501', '220502', '220502', '220503', '220503'],'음식': ['떡볶이', '어묵', '어묵', '떡볶이', '어묵', '떡볶이'],'가게': ['은희네', '은희네', '한수네', '은희네', '은희네', '한수네'],'날씨' : [30, 31, 32, 29, 32, 29]})df_kfood

이 DataFrame에 pivot
함수 사용해보겠습니다.
기본 사용법
pivot
함수의 기본 사용법은 다음과 같습니다.
pythondf_kfood.pivot('날짜', '음식', '날씨')# 결과음식 떡볶이 어묵날짜220501 30 31220502 29 32220503 29 32
pivot
함수는 인덱스(index), 컬럼(columns), 값(values) 순서대로 매개변수의 값을 지정할 수 있습니다.
- index: 행 색인으로 지정할 열 이름
- columns: 열로 설정할 열 이름
- values: 값에 해당하는 열 이름
pythondf.pivot(index='날짜', columns='음식', values='날씨')
지금까지 pivot
함수의 사용법을 살펴보았습니다. pivot
함수의 반대 개념은 melt
입니다. melt
는 넓은 형식의 데이터를 긴 형식의 데이터로 만들어 줍니다.
4) 넓은 형식 ⇒ 긴 형식: melt
melt
함수는 여러 개의 열을 하나로 합치고 DataFrame을 긴 형태로 만들어냅니다. '녹다'라는 뜻을 지닌 영어 단어에서 유추할 수 있듯 melt
함수는 넓은 모양의 데이터를 녹여서 길이를 길게 만들어 줍니다.
기본 사용법
예제 코드는 다음과 같습니다.
pythoncheese = pd.DataFrame({'key': ['감자', '호박', '당근'],'은희네': [10, 50, 30],'선아네': [20, 40, 10],'한수네': [8, 30, 20]})melted = pd.melt(cheese, ['key'])melted
위 코드를 실행하면 다음과 같이 데이터가 넓은 형식에서 긴 형식으로 바뀝니다.
옵션
특정 데이터 선택
특정 데이터값만 골라서 긴 형식으로 만들고 싶을 경우에는 id_vars
와 value_vars
옵션을 사용하면 됩니다. id_vars
에는 키로 사용할 값을 value_vars
에는 변수로 사용할 값을 넘겨주면 됩니다. 예제 코드는 다음과 같습니다.
pythonmelted2 = pd.melt(cheese, id_vars=['key'], value_vars=['은희네', '선아네'])melted2

복구
녹인 데이터를 원래대로 되돌리고 싶다면 pivot
함수를 사용하면 됩니다.
pythonpivoted = melted.pivot('key', 'variable', 'value').reset_index()pivoted

지금까지 melt
함수의 사용법을 알아보았습니다.
5) 멀티인덱스가 있는 DataFrame 재구성: unstack/stack
unstack
함수와 stack
함수는 멀티인덱스가 있는 다차원 데이터를 재구성하는 데 사용합니다. unstack
함수는 계층적 색인이 있는 DataFrame을 긴 모양에서 수평 방향으로 넓은 모양으로 만들어줍니다. 반대로, stack
함수는 넓은 모양의 데이터를 긴 모양의 데이터로 만들어 줍니다. 먼저 아래 실습 예제를 통해 알아보겠습니다.
pythonvegetables = ['감자', '감자', '당근', '당근']stores = ['선아네', '동석이네', '선아네', '동석이네']vegetables_info = [[v, s] for v, s in zip(vegetables, stores)]df_basic = pd.DataFrame(vegetables_info)rows_index = pd.MultiIndex.from_frame(df_basic) # 계층적 색인 생성columns_index= ['2020', '2021', '2022']df_vegi = pd.DataFrame(np.arange(12).reshape((4, 3)),index=rows_index,columns=columns_index)df_vegi.index.names = ['채소명', '가게명']df_vegi

기본
이 DataFrame에 unstack
함수를 적용하면 아래와 같이 옆으로 넓은 모양의 DataFrame이 만들어집니다.
pythondf_vegi.unstack()

옵션
층위 이동
unstack
함수는 level
이라는 매개변수를 통해 행 인덱스 중 특정 레벨을 열로 이동시킬 수 있습니다.
pythonindex = pd.MultiIndex.from_product([['A', 'B'], ['C', 'D'], ['E', 'F']],names=['idx1', 'idx2', 'idx3'])df_random = pd.DataFrame({'values': [1, 2, 3, 4, 5, 6, 7, 8]},index=index)df_random

이 DataFrame에 각각 level
을 달리 적용해 보겠습니다.
pythondf_random.unstack(level=0) # 행 인덱스의 0번째 레벨을 열 레벨로 이동df_random.unstack(level=1) # 행 인덱스의 1번째 레벨을 열 레벨로 이동df_random.unstack(level=2) # 행 인덱스의 2번째 레벨을 열 레벨로 이동

복구
unstack
함수에 stack
함수를 체이닝하면 원래의 DataFrame 모양으로 되돌릴 수 있습니다.
pythondf_vegi_unstack().stack()
지금까지 DataFrame을 재형성하는 법을 알아보았습니다. 다음으로는 DataFrame에서 데이터를 연산하는 법을 알아보겠습니다.
연산하기(Operating)
판다스에서 데이터 연산은 연산 기호를 사용하는 방법과 메서드를 이용하는 방법으로 나누어집니다. 실습을 위해 먼저 데이터를 준비해 보겠습니다.
pythondf1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'),index=['수박', '참외', '딸기'])df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'),index=['딸기', '수박', '블루베리', '바나나'])
1) 연산 기호
1개
먼저 한 객체와 숫자를 연산할 수 있습니다. 다음은 DataFrame의 'b' 열에 10을 더한 예제입니다.
pythondf1['b'] + 10# 결과수박 10.0참외 13.0딸기 16.0Name: b, dtype: float64
연산 결과를 기존의 DataFrame에 새로운 열로 추가할 수도 있습니다. 다음은 'sum'이라는 이름의 열에 연산 결과를 저장하는 예제입니다.
pythondf1['sum'] = df1['b'] + 10
'sum'과 같이 기존의 변수를 변형해서 만든 변수를 파생변수(derived variable)라고 합니다.
2개
데이터를 연산하는 두 번째 방법은 두 DataFrame에 연산 기호를 사용하는 것입니다. 중복되는 색인이 없으면 해당 부분은 NaN
처리됩니다. 다음 코드에서는 두 DataFrame의 값을 서로 합해보겠습니다.
pythondf1 + df2# 결과b c d e딸기 6.0 NaN 9.0 NaN바나나 NaN NaN NaN NaN블루베리 NaN NaN NaN NaN수박 3.0 NaN 6.0 NaN참외 NaN NaN NaN NaN
연산 기호를 사용하지 않는다면 메서드를 사용하는 방법도 있습니다.
2) 메서드
산술 연산 메서드에는 다음과 같은 종류가 있습니다.
메서드 | 설명 |
---|---|
add, radd | 덧셈(+) |
sub, rsub | 뺄셈(-) |
mul, rmul | 곱셈(*) |
div, rdiv | 나눗셈(/) |
floordiv, rfloordiv | 소숫점 내림(//) |
pow, rpow | 멱승(**) |
여기서 r
로 시작하는 메서드는 계산 인자를 뒤집어 계산합니다. 예를 들어, 1 / df1
은 df1.rdiv(1)
을 사용하는 것과 결과가 같습니다. 만일, 두 DataFrame을 더하고 싶다면 add
함수를 사용하면 됩니다. 다음 코드는 df1
에 df2
를 더하되 fill_value
옵션을 통해 존재하지 않는 값을 처리하는 코드입니다.
pythondf1.add(df2, fill_value=0) # 존재하지 않는 값은 0으로 처리# 결과b c d e딸기 6.0 7.0 9.0 2.0바나나 9.0 NaN 10.0 11.0블루베리 6.0 NaN 7.0 8.0수박 3.0 1.0 6.0 5.0참외 3.0 4.0 5.0 NaN
지금까지 데이터를 연산하는 법을 알아보았습니다. 마지막으로 데이터를 집계하는 법을 알아보겠습니다.
집계하기(Aggregating)
데이터 집계란 데이터의 요약 통계값을 구하고 그룹화(grouping)하는 것입니다. 대량의 데이터를 요약해서 이해하고 비교하기 쉬운 형태로 만드는 것입니다. 구조적으로 데이터 집계란 배열(array)로부터 스칼라값(scala)을 만들어내는 모든 데이터 변환 작업을 의미하기도 합니다.
데이터 집계는 전체 데이터를 대상으로 할 수도 있고, 그룹화된 데이터를 대상으로 할 수도 있습니다. 먼저, 전체 데이터를 집계하는 방법부터 알아보겠습니다.
1) 기본 집계함수
판다스에서 데이터를 집계할 때는 집계함수(aggregating function, 요약 통계값 메서드)를 사용합니다. 집계함수란 입력으로 일련의 값들을 받아들이고, 개수(count), 합(sum), 평균(mean), 최솟값(min), 최댓값(max) 등 단일 요약값(single summary value)을 출력하는 함수를 말합니다. 집계함수는 데이터 그룹의 요약 통계량(summary statistics)을 계산하는 데 사용됩니다.
집계함수의 종류는 다음과 같습니다. 집계시 누락값(missing value)은 제외되고 연산됩니다.
메서드 | 설명 |
---|---|
count | NA가 아닌 값의 수 반환 |
sum | NA가 아닌 값들의 합을 구함 |
mean | NA가 아닌 값들의 평균을 구함 |
median | NA가 아닌 값들의 산술 중간값(50% 분위) 반환 |
min, max | NA가 아닌 값 중 최솟값, 최댓값 계산 |
quantile | 0 부터 1까지의 분위수를 계산 |
std, var | 편향되지 않은(n-1을 분모로 하는) 표준편차와 분산 |
argmin, argmax | 각각 최솟값과 최댓값을 담고 있는 색인의 위치(정수) 반환 |
idxmin, idxmax | 각각 최솟값과 최댓값을 담고 있는 색인의 값 반환 |
mad | 평균값에서 평균절대편차 계산 |
prod | 모든 값의 곱 |
skew | 표본비대칭도(3차 적률)의 값 계산 |
kurt | 표본첨도(4차 적률)의 값 계산 |
cummin, cummax | 각각 누적 최솟값과 누적 최댓값 계산 |
cumsum | 누적합 계산 |
cumprod | 누적곱 계산 |
diff | 1차 산술차 계산(시계열 데이터 처리 시 유용) |
pct_change | 퍼센트 변화율 계산 |
그럼 몇 가지 집계함수를 이용해 요약 통계량을 구해보겠습니다.
개수
열별 데이터의 개수를 구하고 싶다면 count
함수를 이용하면 됩니다. 예제 코드는 다음과 같습니다.
python# 데이터 프레임 생성data = {'Name': ['Alice', 'Bob', 'Charlie', 'Dave', 'Eva'],'Age': [15, 25, None, 42, 31],'City': ['New York', None, 'San Francisco', 'Boston', 'Chicago']}df = pd.DataFrame(data)# 데이터 개수 구하기count_df = df.count()print(count_df)
실행 결과는 다음과 같습니다.
pythonName 5Age 4City 4dtype: int64
합계
열별 합계를 구하고 싶다면 다음 코드를 이용하면 됩니다.
python# 데이터프레임 생성data = {'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]}df = pd.DataFrame(data)# 열별 합계 계산column_sum = df.sum()print(column_sum)
코드를 실행하면 아래와 같은 결과가 만들어집니다.
pythonA 6B 15C 24dtype: int64
지금까지 전체 데이터 집합을 대상으로 데이터를 집계하는 법을 살펴보았습니다. 만일, 특정 기준을 중심으로 데이터를 묶은 다음 집계를 하고 싶다면 groupby
함수를 이용하면 됩니다. 아래에서 자세히 살펴보겠습니다.
2) 그룹화: groupby
groupby
함수는 데이터 집합에서 동일한 값을 가진 대상끼리 모아 데이터 집합을 그룹별로 묶어줍니다. 먼저 실습을 위해서 아래 데이터 프레임을 사용하겠습니다.
pythondata = {'이름' : ['피카츄', '잠만보', '파이리', '꼬부기', '이상해씨', '디그다', '야도란', '뮤'],'학교': ['포켓몬고', '포켓몬고', '포켓몬고', '포켓몬고', '디지몬고', '디지몬고', '포켓몬고', '포켓몬고'],'학년': [3, 1, 2, 1, 2, 1, 3, 3],'반': [1, 1, 5, 3, 2, 4, 6, 6],'과' : ['이과', '이과', '문과', '이과', '문과', '이과', '예체능', '이과'],'국어' : [24, 47, 60, 37, 55, 26, 26, 30],'수학' : [47, 18, 84, 22, 68, 91, 17, 43],'영어' : [63, 5, 22, 31, 10, 80, 27, 33],'사회' : [84, 91, 30, 85, 46, 100, 71, 89],'과학' : [54, 40, 42, 8, 12, 77, 18, 16],'희망전공' : ['전기공학과', '전기공학과', '컴퓨터공학과', '정원학과', '항공학과', '항공학과', '화학과', 'NaN']}df = pd.DataFrame(data)df

특정 값을 가진 데이터 선택하기: get_group, query
특정 값을 가지고 있는 데이터 그룹을 가져오고 싶다면 groupby
함수로 그룹을 나눈 뒤 get_group
메서드를 체이닝(chaining)하거나 query
함수를 이용하면 됩니다.
get_group
다음은 과별로 그룹을 나눈 뒤 그중 '이과'인 데이터만 가져오고 싶다면 다음 코드를 활용할 수 있습니다.
pythondf.groupby('과').get_group('이과') # 과별로 데이터를 묶은 뒤 값이 이과인 데이터 가져오기

query
query
함수를 이용해도 위와 동일한 결과를 얻을 수 있습니다.
pythondf.query('과 == "이과"')
대괄호를 이용해 데이터를 선택하는 방법을 이용해도 같은 결과를 만들 수 있습니다.
pythonfilt = df['과'] == '이과'df[filt]
이 밖에도 데이터를 선택하는 다양한 방법을 알고 싶으시다면 Python pandas 데이터 확인, 정렬, 선택하는 법의 '데이터 선택(Selecting)' 부분을 참조해주세요.
요약 통계값 구하기
이번에는 groupby
함수를 이용해 데이터를 특정 그룹으로 묶은 뒤 요약 통계량을 구해보겠습니다.
평균
데이터를 그룹별로 묶은 뒤 요약 통계량을 구하려면 groupby
함수에 집계함수를 체이닝하면 됩니다.
전체
다음은 학교별 모든 변수의 평균을 구하는 코드입니다.
pythondf.groupby('학교').mean() # 학교별 모든 과목의 평균 구하기

특정 열 1개
만일, 특정 열에 대한 요약 통계량을 구하고 싶다면 요약 통계량을 구하고 싶은 열을 대괄호로 지정하면 됩니다. 다음은 데이터를 학교별로 그룹화한 뒤 특정 과목의 평균을 구하는 코드입니다.
pythondf.groupby('학교')['수학'].mean() # 학교별 수학 평균을 Series로 반환# 결과학교디지몬고 79.5포켓몬고 38.5Name: 수학, dtype: float64
다음 코드를 이용해도 위와 동일한 결과를 얻을 수 있습니다.
pythondf.groupby('학교').mean()['수학']# 또는df['수학'].groupby(df['학교']).mean()
특정 열 2개 이상
다수의 특정 열에 대한 요약 통계량을 구하고 싶다면 다음 코드를 이용하면 됩니다. 여기서 groupby(['학교'])
는 학교를 기준으로 데이터를 그룹화하겠다는 것을 의미하며, [['국어', '수학', '영어']].sum()
은 국어, 영어, 수학 과목의 합계를 구하겠다는 것을 의미합니다.
pythondf.groupby('학교')[['국어', '수학', '영어']].sum()# 결과국어 수학 영어학교디지몬고 81 159 90포켓몬고 224 231 181
하위 집단
만일, 여러 하위 집단별 요약 통계량을 구하고 싶다면 groupby
함수에 대괄호를 이용해 복수의 열을 지정하면 됩니다. 다음은 학교별, 과별 과목 평균을 구하는 코드입니다.
pythondf.groupby(['학교', '과']).mean() # 학교별, 과별 모든 과목의 평균 구하기

하위 집단의 특정 열
다수의 하위 집단별 특정 열에 대한 요약 통계량을 구하고 싶다면 대괄호를 이용해 특정 열을 선택하면 됩니다. 다음은 학교별, 과별 수학 과목의 평균을 구하는 코드입니다.
pythondf.groupby(['학교', '과']).mean()['수학']# 결과학교 과디지몬고 문과 68.0이과 91.0포켓몬고 문과 84.0예체능 17.0이과 32.5Name: 수학, dtype: float64
위 결과를 데이터프레임으로 보기 좋게 만들고 싶다면 아래 코드를 이용하면 됩니다.
pythonpd.DataFrame(df.groupby(['학교', '과']).mean()['수학'])
agg
함수를 이용해도 위와 동일한 결과를 얻을 수 있습니다. agg
함수는 보통 groupby
함수와 같이 각 집단의 요약 통계량을 구할 때 사용할 수 있습니다. 기존의 집계함수와 차이점은 agg
함수는 결괏값을 담는 열의 이름을 지정할 수 있습니다. 다음은 학교별, 과별 수학 과목의 평균을 'mean_math'라는 이름의 열로 만드는 예제 코드입니다.
pythondf.groupby(['학교', '과']).agg(mean_math=('수학', 'mean')) # 학교별, 과별 수학 과목의 평균 구하기

크기
그룹의 크기를 구하고 싶다면 size
함수를 사용하면 됩니다. size
는 그룹의 크기를 담고 있는 Series를 반환하는 함수입니다.
전체
다음은 학교을 기준으로 그룹화하여 각 그룹에 속하는 학생 수를 구하는 예제 코드입니다.
pythondf.groupby('학교').size() # 학교별 학생 수 구하기# 결과학교디지몬고 2포켓몬고 6dtype: int64
특정 행
여기서 특정 학교를 선택하고 싶다면 아래 코드를 이용하면 됩니다.
pythondf.groupby('학교').size()['포켓몬고'] # '포켓몬고' 학생 수 구하기# 결과6
개수
다음은 학교가 '포켓몬고'인 데이터들을 과별로 분류해 각 과에 있는 학생수를 구하는 코드입니다.
pythondf.query('학교 == "포켓몬고"').groupby(['과']).agg(빈도수 = ('과', 'count'))# 결과빈도수과문과 1예체능 1이과 4
유일값
집단별 유일값을 구하려면 groupby
함수에 value_counts
메서드를 체이닝하면 됩니다.
전체
pythondf.groupby('학교')['학년'].value_counts() # 학교별, 학년별 학생수 구하기# 결과학교 학년디지몬고 1 12 1포켓몬고 3 31 22 1Name: 학년, dtype: int64
pythondf.groupby('학교')[['이름', '희망전공']].count() # 학교별 희망전공 수 구하기
특정 열
pythondf.groupby('학교')['학년'].value_counts().loc['포켓몬고'] # '포켓몬고'의 학교별, 학년별 학생수# 결과학년3 31 22 1Name: 학년, dtype: int64
비율
집단별 비율을 구하고 싶다면 value_counts
함수에 normalize=True
를 추가하면 됩니다.
pythondf.groupby('학교')['학년'].value_counts(normalize=True) # 학교별, 학년별 학생수 비율 구하기
다중 인덱스
다중 인덱스(MultiIndex)를 가진 DataFrame에서 데이터를 그룹화할 때는 groupby
함수의 level
예약어를 사용하는 것이 편리합니다. level
예약어를 이용하면 DataFrame에서 특정 level에 따라 데이터를 그룹화할 수 있습니다.
색인 단계로 그룹핑하기: level
pythondf_multiindex = pd.DataFrame({'가게명': ['A', 'A', 'B', 'B', 'C', 'C'],'상품명': ['X', 'Y', 'X', 'Y', 'X', 'Y'],'판매량': [50, 10, 30, 25, 45, 10]})df_multiindex = df_multiindex.set_index(['가게명', '상품명']) # key1, key2로 MultiIndex 생성df_multiindex.groupby(level=1).sum() # 상품명으로 그룹화# 결과판매량상품명X 125Y 45
색인 단계 사용하지 않기: as_index=False
groupby
에서 기준으로 삼은 변수를 인덱스로 사용하지 않으려면 as_index=False
옵션을 사용하면 됩니다.
pythondf.groupby('과', as_index=False).mean()
3) agg
agg
함수는 위에서 집계함수처럼 사용하는 방법 외에도 다양한 목적으로 사용할 수 있습니다. 먼저 실습할 예제 코드로 살펴보겠습니다.
pythondata = {'이름': ['Alice', 'Bob', 'Charlie', 'David', 'Emily'],'학교': ['A', 'B', 'B', 'A', 'B'],'학년': [1, 2, 3, 3, 2],'키': [190, 170, 164, 179, 173],'몸무게': [86, 73, 55, 61, 55]}df = pd.DataFrame(data)df
커스텀 함수 사용하기
먼저 agg
함수를 이용하면 직접 만든 함수를 집계함수처럼 사용할 수 있습니다. 다음은 최댓값에서 최솟값을 뺀 결과를 나타내는 함수를 정의한 뒤 이를 모든 DataFrame의 값에 적용하는 코드입니다.
pythondef peak_to_peak(arr):return arr.max() - arr.min()df.groupby('학교').agg(peak_to_peak)# 결과키 몸무게학교A 11 25B 9 18
단일 열에 다수의 집계함수 사용하기
다수의 집계함수를 사용해서 한 번에 결과를 만들고 싶다면 agg
함수에 사용하고 싶은 집계함수 또는 커스텀 함수의 이름을 넣으면 됩니다.
pythondf.groupby('학교').agg(['mean', 'std', peak_to_peak])
집계결과열의 이름을 따로 지정하고 싶다면 다음과 같이 코드를 작성하면 됩니다.
pythondf.groupby('학교').agg([('평균', 'mean'), # 'mean'의 결과를 평균이라는 이름의 열에 저장('표준편차', np.std), # 'np.std'의 결과를 표준편차라는 이름의 열에 저장('차', peak_to_peak)]) # peak_to_peak의 결과를 차라는 이름의 열에 저장
다수의 열에 다수의 집계함수 사용하기
다수의 열에 다수의 동일한 함수를 적용할 수도 있습니다. 다음은 '열1'과 '열2'에 대해 각각 'count', 'mean', 'max' 함수를 적용해 결괏값(새로운 열)을 만드는 예제 코드입니다.
pythonfunctions = ['count', 'mean', 'max']df.groupby(['학교', '학년']).agg(functions)
아래와 같이 열 이름과 메서드가 담긴 튜플 리스트를 넘길 수도 있습니다.
pythonftuples = [('평균', 'mean'), ('편차', np.var)]df.groupby(['학교', '학년']).agg(ftuples)
pythondf.groupby(['학교', '학년']).agg({'키': 'mean', '몸무게': 'sum'}) # '키' 열에 'mean' 함수 적용, '몸무게' 열에 'sum' 함수 적용
pythondf.groupby(['학교', '학년']).agg({'키': ['max', 'min'], '몸무게': 'mean'})
4) 일괄적으로 적용: apply
pythondf = pd.DataFrame({'주스종류' : ['a', 'a', 'b', 'b', 'a'],'재료' : ['당근', '사과', '당근', '당근', '사과'],'data1' : np.random.randn(5),'data2' : np.random.randn(5)})grouped = df.groupby('주스종류')
describe
메서드는 축소나 누산 등 집계메서드는 아닙니다. 하지만 이 메서드는 아래에서 배울 apply
함수를 이용하면 데이터를 집계하는 것처럼 작동합니다. 코드는 다음과 같습니다.
pythonf = lambda x: x.describe()grouped.apply(f)
apply
메서드는 객체를 여러 조각으로 나누고, 전달된 함수를 각 조각에 일괄 적용한 후 이를 다시 합칩니다. 다음 코드를 실행하신 후 어떤 결과가 나타나는지 실습해 보시길 바랍니다.
pythondef top(df, n=5, column='국어'): # 데이터에서 '국어' 열에서 상위 5개 값 구하기return df.sort_values(by=column)[-n:]top(df, n=6)
기본 사용법
apply
함수를 사용하는 기본 예제 코드는 다음과 같습니다.
pythondf.groupby('과').apply(top) # 과별로 그룹화한 뒤 'top' 함수 적용
만약 학교별, 과별로 상위 1개 값만 가져오고 싶다면 n=1
옵션을 사용하면 됩니다. 다음은 학교별, 과별로 데이터를 그룹화한 뒤, '국어'에서 가장 높은 점수를 가진 데이터만 가져오는 코드입니다.
pythondf.groupby(['학교', '과']).apply(top, n=1, column='국어')
만약 top
함수를 적용할 때 그룹 키를 사용하고 싶지 않다면 아래와 같이 group_keys=False
옵션을 추가하면 됩니다.
pythondf.groupby('과', group_keys=False).apply(top)
결측치 채우기
누락된 값을 그룹의 평균값으로 채우는 코드는 다음과 같습니다.
pythonfill_mean = lambda x: x.fillna(x.mean())df.groupby('과').apply(fill_mean)
무작위 표본 추출하기
무작위 표본을 추출할 때는 다음과 같은 코드를 사용할 수 있습니다.
pythondef sampling(data, sample_pct):N = len(data)sample_n = int(len(data)*sample_pct) # integersample = data.take(np.random.permutation(N)[:sample_n])return samplesample_set = df.groupby('학교').apply(sampling, sample_pct=0.2) # 그룹별로 20% 만큼 무작위 표본 추출
가중 평균 구하기
가중 평균을 구하고 싶다면 아래 코드를 참조해 주세요.
pythondf = pd.DataFrame({'분류': ['차', '커피', '커피', '차', '차', '커피', '커피', '커피'],'데이터': np.random.randn(8),'가중치': np.random.rand(8)})grouped = df.groupby('분류')get_wavg = lambda g: np.average(g['데이터'], weights=g['가중치'])grouped.apply(get_wavg)
상관관계 계산하기
상관관계를 구하고 싶다면 다음 코드를 사용하시면 됩니다.
pythondf.groupby('과').apply(lambda g: g['국어'].corr(g['영어']))
선형회귀 계산하기
선형회귀를 계산하려면 계량경제 라이브러리인 statsmodels
를 사용해서 regress
라는 함수를 작성하고 각 데이터 묶음마다 최소제곱(Ordinary Least Sqaures, OLS)으로 회귀를 수행할 수 있습니다. 영어에 대한 국어 과목의 과별 선형회귀는 다음과 같이 수행할 수 있습니다.
pythonimport statsmodels.api as smdef regress(data, yvar, xvars):Y = data[yvar]X = data[xvars]X['intercept'] = 1.result = sm.OLS(Y, X).fit()return result.paramsdf.groupby('과').apply(regress, '국어', ['영어'])
지금까지 총 4편에 걸쳐 Python pandas의 데이터 프레임 사용법을 알아보았습니다. 다음 시간에는 이 사용법으로 전처리를 거친 데이터를 시각화하는 법을 알아보겠습니다. 모두 수고 많으셨습니다.
참고 문헌
- [1] 유튜브(나도코딩). (2021.10.05). 파이썬 코딩 무료 강의 (활용편5) - 데이터 분석 및 시각화, 이 영상 하나로 끝내세요 [비디오파일]. 검색경로 https://www.youtube.com/watch?v=PjhlUzp_cU0
- [2] 웨스 맥키니, 『파이썬 라이브러리를 활용한 데이터 분석』, 김영근, 한빛미디어. 2019, 334-345, 385-422.
- [3] Rfriend, 「[Python pandas] 그룹 별 무작위 표본 추출 (random sampling by group)」, Rfriend, "https://rfriend.tistory.com/407"
- [4] Zolzaya Luvsandorj, 「3 easy ways to crosstab in pandas」, Towards Data Science, "https://towardsdatascience.com/3-easy-ways-to-crosstab-in-pandas-4123383bfbf2"