파이썬-pandas-데이터-프레임-레드-판다2
Python pandas, 레드 판다스

지난 시간에는 Python pandas 데이터 병합, 정제, 변형하는 법을 살펴보았습니다. 이번 시간에는 DataFrame을 재형성하고, DataFrame 간 연산하기, 그리고 데이터를 집계하는 법을 알아보겠습니다.

준비하기

실습을 위해서 먼저 데이터를 준비하겠습니다. 여기서는 Seaborn 라이브러리의 내장 데이터셋 중 타이타닉호(Titanic) 데이터셋을 사용하겠습니다.

python
import pandas as pd
from seaborn import load_dataset
df = load_dataset('titanic')
df.head(5)
실습에서 사용할 타이타닉 데이터셋
실습에서 사용할 타이타닉 데이터셋  - Snug Archive

먼저 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 함수의 기본적인 사용법은 아래와 같습니다.

python
pd.crosstab(df['sex'], df['alive'])
# 결과
alive no yes
sex
female 81 233
male 468 109

매개변수를 명확히 표기하면 다음과 같습니다.

python
pd.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') 이름 새로 지정

다음은 타이타닉호의 성별 생존자수를 만드는 코드입니다.

python
crosstb = pd.crosstab(df['sex'], df['alive'],
normalize='index', margins=True).round(3) * 100
crosstb
# 결과
alive no yes
sex
female 25.8 74.2
male 81.1 18.9
All 61.6 38.4

결과를 다중 막대그래프로 보면 다음과 같습니다.

python
crosstb.plot.bar(rot=0)
성별 생존자 수를 나타낸 다중 막대그래프
성별 생존자 수를 나타낸 다중 막대그래프  - Snug Archive
수치형 변수 추가

범주형 변수에 수치형 변수를 추가하고 싶다면, aggfunc 매개변수에 집계함수를 지정하면 됩니다.

python
pd.crosstab(df['sex'], df['alive'], df['age'], aggfunc='median')
# 결과
alive no yes
sex
female 24.5 28.0
male 29.0 28.0
계층 세분화

계층을 세분화하고 싶다면 대괄호를 이용해서 색인 단계를 높이면 됩니다.

python
pd.crosstab([df['sex'], df['class']],
[df['alive'], df['sibsp']],
df['age'],
aggfunc='median')
여러 층위를 가진 계층적 교차표
여러 층위를 가진 계층적 교차표  - Snug Archive
여러 개의 집계함수 사용

여러 개의 집계함수를 적용하고 싶다면 aggfunc 매개변수에 집계함수의 리스트를 추가하면 됩니다.

python
pd.crosstab(df['sex'], df['alive'], df['sibsp'],
aggfunc=['median', 'mean'])
2개의 집계함수를 사용한 교차표
2개의 집계함수를 사용한 교차표  - Snug Archive

지금까지 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의 기본 사용법은 다음과 같습니다.

python
pd.pivot_table(df, 'age', 'sex', 'alive', aggfunc='count')
# 결과
alive no yes
sex
female 64 197
male 360 93

aggfunc 매개변수의 기본값은 mean입니다. 매개변수의 값을 count로 주면 crosstab을 사용할 때와 동일한 교차표를 얻을 수 있습니다. 단, 수치형 변수는 crosstab 함수를 사용할 때와 달리 indexcolumns 매개변수 앞에 위치시켜야 합니다.

아래와 같이 매개변수를 명확히 표기할 수도 있습니다.

python
pd.pivot_table(df, values='age', index='sex', columns='alive',
aggfunc='median')

crosstab 함수에서는 values 매개변수가 indexcolumns 다음에 위치했지만, pivot_table 함수에서는 두 매개변수 앞에 사용합니다. 따라서, 범주형 변수의 연산 결과보다 수치형 변수의 연산 결과에 집중할 수 있습니다.

pivot_table 함수는 DataFrame 수준에서 사용할 수도 있습니다.

python
df.pivot_table('age', 'sex', 'alive', aggfunc='median')
# 결과
alive no yes
sex
female 24.5 28.0
male 29.0 28.0

옵션

pivot_table 함수에서도 다음과 같은 옵션을 사용할 수 있습니다.

  • margins=True: 총계를 마지막 행 또는 열에 All 열에 추가
  • margins_name: 총계열('All') 이름 새로 지정
계층적 색인

계층적 색인이 있는 교차표를 만들고 싶다면

python
df.pivot_table('age', ['sex', 'class'], ['alive'],
aggfunc=['median', 'mean'],
margins=True)
pivot_table 함수로 만든 다층 교차테이블
pivot_table 함수로 만든 다층 교차테이블  - Snug Archive

지금까지 pivot_table의 사용법을 알아보았습니다.

3) 긴 형식 ⇒ 넓은 형식: pivot

피봇팅(pivoting)은 특정한 축을 기준으로 데이터를 재정렬하는 것을 말합니다. 한 열의 값을 행 인덱스로 옮기거나, 행 인덱스를 열로 변환하는 것입니다. pivot 함수는 한 컬럼의 값을 열 이름으로 변경하여 데이터를 재구성하는 함수입니다. pivot 함수는 계층적 색인이 없는 일반 DataFrame을 긴 형식에서 넓은 형식으로 신속하게 만들 때 유용합니다. 특히, 시계열 데이터처럼 아래로 긴 형식의 데이터를 간략하게 정리할 때 유용합니다. 실습을 위해서 다음 예제를 사용하겠습니다.

python
df_kfood = pd.DataFrame({
'날짜': ['220501', '220501', '220502', '220502', '220503', '220503'],
'음식': ['떡볶이', '어묵', '어묵', '떡볶이', '어묵', '떡볶이'],
'가게': ['은희네', '은희네', '한수네', '은희네', '은희네', '한수네'],
'날씨' : [30, 31, 32, 29, 32, 29]
})
df_kfood
예제 시계열 데이터
예제 시계열 데이터  - Snug Archive

이 DataFrame에 pivot 함수 사용해보겠습니다.

기본 사용법

pivot 함수의 기본 사용법은 다음과 같습니다.

python
df_kfood.pivot('날짜', '음식', '날씨')
# 결과
음식 떡볶이 어묵
날짜
220501 30 31
220502 29 32
220503 29 32

pivot 함수는 인덱스(index), 컬럼(columns), 값(values) 순서대로 매개변수의 값을 지정할 수 있습니다.

  • index: 행 색인으로 지정할 열 이름
  • columns: 열로 설정할 열 이름
  • values: 값에 해당하는 열 이름
python
df.pivot(index='날짜', columns='음식', values='날씨')

지금까지 pivot 함수의 사용법을 살펴보았습니다. pivot 함수의 반대 개념은 melt입니다. melt는 넓은 형식의 데이터를 긴 형식의 데이터로 만들어 줍니다.

4) 넓은 형식 ⇒ 긴 형식: melt

melt 함수는 여러 개의 열을 하나로 합치고 DataFrame을 긴 형태로 만들어냅니다. '녹다'라는 뜻을 지닌 영어 단어에서 유추할 수 있듯 melt 함수는 넓은 모양의 데이터를 녹여서 길이를 길게 만들어 줍니다.

기본 사용법

예제 코드는 다음과 같습니다.

python
cheese = pd.DataFrame({'key': ['감자', '호박', '당근'],
'은희네': [10, 50, 30],
'선아네': [20, 40, 10],
'한수네': [8, 30, 20]})
melted = pd.melt(cheese, ['key'])
melted

위 코드를 실행하면 다음과 같이 데이터가 넓은 형식에서 긴 형식으로 바뀝니다.

넓은 형식을 긴 형식의 데이터로 만드는 melt 함수  - Snug Archive

옵션

특정 데이터 선택

특정 데이터값만 골라서 긴 형식으로 만들고 싶을 경우에는 id_varsvalue_vars 옵션을 사용하면 됩니다. id_vars에는 키로 사용할 값을 value_vars에는 변수로 사용할 값을 넘겨주면 됩니다. 예제 코드는 다음과 같습니다.

python
melted2 = pd.melt(cheese, id_vars=['key'], value_vars=['은희네', '선아네'])
melted2
특정 데이터값만 골라서 만든 DataFrame
특정 데이터값만 골라서 만든 DataFrame  - Snug Archive
복구

녹인 데이터를 원래대로 되돌리고 싶다면 pivot 함수를 사용하면 됩니다.

python
pivoted = melted.pivot('key', 'variable', 'value').reset_index()
pivoted
pivot 함수로 다시 원상 복구한 DataFrame
pivot 함수로 다시 원상 복구한 DataFrame  - Snug Archive

지금까지 melt 함수의 사용법을 알아보았습니다.

5) 멀티인덱스가 있는 DataFrame 재구성: unstack/stack

unstack 함수와 stack 함수는 멀티인덱스가 있는 다차원 데이터를 재구성하는 데 사용합니다. unstack 함수는 계층적 색인이 있는 DataFrame을 긴 모양에서 수평 방향으로 넓은 모양으로 만들어줍니다. 반대로, stack 함수는 넓은 모양의 데이터를 긴 모양의 데이터로 만들어 줍니다. 먼저 아래 실습 예제를 통해 알아보겠습니다.

python
vegetables = ['감자', '감자', '당근', '당근']
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 예제
다층 색인이 있는 DataFrame 예제  - Snug Archive

기본

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

python
df_vegi.unstack()
unstack 함수를 적용해서 넓게 만든 DataFrame
unstack 함수를 적용해서 넓게 만든 DataFrame  - Snug Archive

옵션

층위 이동

unstack 함수는 level이라는 매개변수를 통해 행 인덱스 중 특정 레벨을 열로 이동시킬 수 있습니다.

python
index = 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
3층의 색인이 있는 DataFrame
3층의 색인이 있는 DataFrame  - Snug Archive

이 DataFrame에 각각 level을 달리 적용해 보겠습니다.

python
df_random.unstack(level=0) # 행 인덱스의 0번째 레벨을 열 레벨로 이동
df_random.unstack(level=1) # 행 인덱스의 1번째 레벨을 열 레벨로 이동
df_random.unstack(level=2) # 행 인덱스의 2번째 레벨을 열 레벨로 이동
unstack함수에 level 옵션을 적용한 DataFrame 차이
unstack함수에 level 옵션을 적용한 DataFrame 차이  - Snug Archive
복구

unstack 함수에 stack 함수를 체이닝하면 원래의 DataFrame 모양으로 되돌릴 수 있습니다.

python
df_vegi_unstack().stack()

지금까지 DataFrame을 재형성하는 법을 알아보았습니다. 다음으로는 DataFrame에서 데이터를 연산하는 법을 알아보겠습니다.

연산하기(Operating)

판다스에서 데이터 연산은 연산 기호를 사용하는 방법과 메서드를 이용하는 방법으로 나누어집니다. 실습을 위해 먼저 데이터를 준비해 보겠습니다.

python
df1 = 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을 더한 예제입니다.

python
df1['b'] + 10
# 결과
수박 10.0
참외 13.0
딸기 16.0
Name: b, dtype: float64

연산 결과를 기존의 DataFrame에 새로운 열로 추가할 수도 있습니다. 다음은 'sum'이라는 이름의 열에 연산 결과를 저장하는 예제입니다.

python
df1['sum'] = df1['b'] + 10

'sum'과 같이 기존의 변수를 변형해서 만든 변수를 파생변수(derived variable)라고 합니다.

2개

데이터를 연산하는 두 번째 방법은 두 DataFrame에 연산 기호를 사용하는 것입니다. 중복되는 색인이 없으면 해당 부분은 NaN 처리됩니다. 다음 코드에서는 두 DataFrame의 값을 서로 합해보겠습니다.

python
df1 + 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 / df1df1.rdiv(1)을 사용하는 것과 결과가 같습니다. 만일, 두 DataFrame을 더하고 싶다면 add 함수를 사용하면 됩니다. 다음 코드는 df1df2를 더하되 fill_value 옵션을 통해 존재하지 않는 값을 처리하는 코드입니다.

python
df1.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)은 제외되고 연산됩니다.

메서드설명
countNA가 아닌 값의 수 반환
sumNA가 아닌 값들의 합을 구함
meanNA가 아닌 값들의 평균을 구함
medianNA가 아닌 값들의 산술 중간값(50% 분위) 반환
min, maxNA가 아닌 값 중 최솟값, 최댓값 계산
quantile0 부터 1까지의 분위수를 계산
std, var편향되지 않은(n-1을 분모로 하는) 표준편차와 분산
argmin, argmax각각 최솟값과 최댓값을 담고 있는 색인의 위치(정수) 반환
idxmin, idxmax각각 최솟값과 최댓값을 담고 있는 색인의 값 반환
mad평균값에서 평균절대편차 계산
prod모든 값의 곱
skew표본비대칭도(3차 적률)의 값 계산
kurt표본첨도(4차 적률)의 값 계산
cummin, cummax각각 누적 최솟값과 누적 최댓값 계산
cumsum누적합 계산
cumprod누적곱 계산
diff1차 산술차 계산(시계열 데이터 처리 시 유용)
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)

실행 결과는 다음과 같습니다.

python
Name 5
Age 4
City 4
dtype: 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)

코드를 실행하면 아래와 같은 결과가 만들어집니다.

python
A 6
B 15
C 24
dtype: int64

지금까지 전체 데이터 집합을 대상으로 데이터를 집계하는 법을 살펴보았습니다. 만일, 특정 기준을 중심으로 데이터를 묶은 다음 집계를 하고 싶다면 groupby 함수를 이용하면 됩니다. 아래에서 자세히 살펴보겠습니다.

2) 그룹화: groupby

groupby 함수는 데이터 집합에서 동일한 값을 가진 대상끼리 모아 데이터 집합을 그룹별로 묶어줍니다. 먼저 실습을 위해서 아래 데이터 프레임을 사용하겠습니다.

python
data = {'이름' : ['피카츄', '잠만보', '파이리', '꼬부기', '이상해씨', '디그다', '야도란', '뮤'],
'학교': ['포켓몬고', '포켓몬고', '포켓몬고', '포켓몬고', '디지몬고', '디지몬고', '포켓몬고', '포켓몬고'],
'학년': [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
데이터 그룹화를 위해 사용할 예제 데이터프레임
데이터 그룹화를 위해 사용할 예제 데이터프레임  - Snug Archive

특정 값을 가진 데이터 선택하기: get_group, query

특정 값을 가지고 있는 데이터 그룹을 가져오고 싶다면 groupby 함수로 그룹을 나눈 뒤 get_group 메서드를 체이닝(chaining)하거나 query 함수를 이용하면 됩니다.

get_group

다음은 과별로 그룹을 나눈 뒤 그중 '이과'인 데이터만 가져오고 싶다면 다음 코드를 활용할 수 있습니다.

python
df.groupby('과').get_group('이과') # 과별로 데이터를 묶은 뒤 값이 이과인 데이터 가져오기
전체 데이터프레임에서 특정 값을 가진 데이터 그룹 가져오기
전체 데이터프레임에서 특정 값을 가진 데이터 그룹 가져오기  - Snug Archive
query

query 함수를 이용해도 위와 동일한 결과를 얻을 수 있습니다.

python
df.query('과 == "이과"')

대괄호를 이용해 데이터를 선택하는 방법을 이용해도 같은 결과를 만들 수 있습니다.

python
filt = df['과'] == '이과'
df[filt]

이 밖에도 데이터를 선택하는 다양한 방법을 알고 싶으시다면 Python pandas 데이터 확인, 정렬, 선택하는 법의 '데이터 선택(Selecting)' 부분을 참조해주세요.

요약 통계값 구하기

이번에는 groupby 함수를 이용해 데이터를 특정 그룹으로 묶은 뒤 요약 통계량을 구해보겠습니다.

평균

데이터를 그룹별로 묶은 뒤 요약 통계량을 구하려면 groupby 함수에 집계함수를 체이닝하면 됩니다.

전체

다음은 학교별 모든 변수의 평균을 구하는 코드입니다.

python
df.groupby('학교').mean() # 학교별 모든 과목의 평균 구하기
학교별 모든 과목의 평균 구하기
학교별 모든 과목의 평균 구하기  - Snug Archive
특정 열 1개

만일, 특정 열에 대한 요약 통계량을 구하고 싶다면 요약 통계량을 구하고 싶은 열을 대괄호로 지정하면 됩니다. 다음은 데이터를 학교별로 그룹화한 뒤 특정 과목의 평균을 구하는 코드입니다.

python
df.groupby('학교')['수학'].mean() # 학교별 수학 평균을 Series로 반환
# 결과
학교
디지몬고 79.5
포켓몬고 38.5
Name: 수학, dtype: float64

다음 코드를 이용해도 위와 동일한 결과를 얻을 수 있습니다.

python
df.groupby('학교').mean()['수학']
# 또는
df['수학'].groupby(df['학교']).mean()
특정 열 2개 이상

다수의 특정 열에 대한 요약 통계량을 구하고 싶다면 다음 코드를 이용하면 됩니다. 여기서 groupby(['학교'])는 학교를 기준으로 데이터를 그룹화하겠다는 것을 의미하며, [['국어', '수학', '영어']].sum()은 국어, 영어, 수학 과목의 합계를 구하겠다는 것을 의미합니다.

python
df.groupby('학교')[['국어', '수학', '영어']].sum()
# 결과
국어 수학 영어
학교
디지몬고 81 159 90
포켓몬고 224 231 181
하위 집단

만일, 여러 하위 집단별 요약 통계량을 구하고 싶다면 groupby 함수에 대괄호를 이용해 복수의 열을 지정하면 됩니다. 다음은 학교별, 과별 과목 평균을 구하는 코드입니다.

python
df.groupby(['학교', '과']).mean() # 학교별, 과별 모든 과목의 평균 구하기
학교별, 과별 모든 과목의 평균 구하기
학교별, 과별 모든 과목의 평균 구하기  - Snug Archive
하위 집단의 특정 열

다수의 하위 집단별 특정 열에 대한 요약 통계량을 구하고 싶다면 대괄호를 이용해 특정 열을 선택하면 됩니다. 다음은 학교별, 과별 수학 과목의 평균을 구하는 코드입니다.

python
df.groupby(['학교', '과']).mean()['수학']
# 결과
학교 과
디지몬고 문과 68.0
이과 91.0
포켓몬고 문과 84.0
예체능 17.0
이과 32.5
Name: 수학, dtype: float64

위 결과를 데이터프레임으로 보기 좋게 만들고 싶다면 아래 코드를 이용하면 됩니다.

python
pd.DataFrame(df.groupby(['학교', '과']).mean()['수학'])

agg 함수를 이용해도 위와 동일한 결과를 얻을 수 있습니다. agg 함수는 보통 groupby 함수와 같이 각 집단의 요약 통계량을 구할 때 사용할 수 있습니다. 기존의 집계함수와 차이점은 agg 함수는 결괏값을 담는 열의 이름을 지정할 수 있습니다. 다음은 학교별, 과별 수학 과목의 평균을 'mean_math'라는 이름의 열로 만드는 예제 코드입니다.

python
df.groupby(['학교', '과']).agg(mean_math=('수학', 'mean')) # 학교별, 과별 수학 과목의 평균 구하기
학교별, 과별 수학 과목의 평균 구하기
학교별, 과별 수학 과목의 평균 구하기  - Snug Archive
크기

그룹의 크기를 구하고 싶다면 size 함수를 사용하면 됩니다. size는 그룹의 크기를 담고 있는 Series를 반환하는 함수입니다.

전체

다음은 학교을 기준으로 그룹화하여 각 그룹에 속하는 학생 수를 구하는 예제 코드입니다.

python
df.groupby('학교').size() # 학교별 학생 수 구하기
# 결과
학교
디지몬고 2
포켓몬고 6
dtype: int64
특정 행

여기서 특정 학교를 선택하고 싶다면 아래 코드를 이용하면 됩니다.

python
df.groupby('학교').size()['포켓몬고'] # '포켓몬고' 학생 수 구하기
# 결과
6
개수

다음은 학교가 '포켓몬고'인 데이터들을 과별로 분류해 각 과에 있는 학생수를 구하는 코드입니다.

python
df.query('학교 == "포켓몬고"').groupby(['과']).agg(빈도수 = ('과', 'count'))
# 결과
빈도수
문과 1
예체능 1
이과 4
유일값

집단별 유일값을 구하려면 groupby 함수에 value_counts 메서드를 체이닝하면 됩니다.

전체
python
df.groupby('학교')['학년'].value_counts() # 학교별, 학년별 학생수 구하기
# 결과
학교 학년
디지몬고 1 1
2 1
포켓몬고 3 3
1 2
2 1
Name: 학년, dtype: int64
특정 열
python
df.groupby('학교')['학년'].value_counts().loc['포켓몬고'] # '포켓몬고'의 학교별, 학년별 학생수
# 결과
학년
3 3
1 2
2 1
Name: 학년, dtype: int64
비율

집단별 비율을 구하고 싶다면 value_counts 함수에 normalize=True를 추가하면 됩니다.

python
df.groupby('학교')['학년'].value_counts(normalize=True) # 학교별, 학년별 학생수 비율 구하기

다중 인덱스

다중 인덱스(MultiIndex)를 가진 DataFrame에서 데이터를 그룹화할 때는 groupby 함수의 level 예약어를 사용하는 것이 편리합니다. level 예약어를 이용하면 DataFrame에서 특정 level에 따라 데이터를 그룹화할 수 있습니다.

색인 단계로 그룹핑하기: level
python
df_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 125
Y 45
색인 단계 사용하지 않기: as_index=False

groupby에서 기준으로 삼은 변수를 인덱스로 사용하지 않으려면 as_index=False 옵션을 사용하면 됩니다.

python
df.groupby('과', as_index=False).mean()

3) agg

agg 함수는 위에서 집계함수처럼 사용하는 방법 외에도 다양한 목적으로 사용할 수 있습니다. 먼저 실습할 예제 코드로 살펴보겠습니다.

python
data = {'이름': ['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의 값에 적용하는 코드입니다.

python
def peak_to_peak(arr):
return arr.max() - arr.min()
df.groupby('학교').agg(peak_to_peak)
# 결과
키 몸무게
학교
A 11 25
B 9 18

단일 열에 다수의 집계함수 사용하기

다수의 집계함수를 사용해서 한 번에 결과를 만들고 싶다면 agg 함수에 사용하고 싶은 집계함수 또는 커스텀 함수의 이름을 넣으면 됩니다.

python
df.groupby('학교').agg(['mean', 'std', peak_to_peak])

집계결과열의 이름을 따로 지정하고 싶다면 다음과 같이 코드를 작성하면 됩니다.

python
df.groupby('학교').agg([
('평균', 'mean'), # 'mean'의 결과를 평균이라는 이름의 열에 저장
('표준편차', np.std), # 'np.std'의 결과를 표준편차라는 이름의 열에 저장
('차', peak_to_peak)]) # peak_to_peak의 결과를 차라는 이름의 열에 저장

다수의 열에 다수의 집계함수 사용하기

다수의 열에 다수의 동일한 함수를 적용할 수도 있습니다. 다음은 '열1'과 '열2'에 대해 각각 'count', 'mean', 'max' 함수를 적용해 결괏값(새로운 열)을 만드는 예제 코드입니다.

python
functions = ['count', 'mean', 'max']
df.groupby(['학교', '학년']).agg(functions)

아래와 같이 열 이름과 메서드가 담긴 튜플 리스트를 넘길 수도 있습니다.

python
ftuples = [('평균', 'mean'), ('편차', np.var)]
df.groupby(['학교', '학년']).agg(ftuples)
python
df.groupby(['학교', '학년']).agg({'키': 'mean', '몸무게': 'sum'}) # '키' 열에 'mean' 함수 적용, '몸무게' 열에 'sum' 함수 적용
python
df.groupby(['학교', '학년']).agg({'키': ['max', 'min'], '몸무게': 'mean'})

4) 일괄적으로 적용: apply

python
df = pd.DataFrame({'주스종류' : ['a', 'a', 'b', 'b', 'a'],
'재료' : ['당근', '사과', '당근', '당근', '사과'],
'data1' : np.random.randn(5),
'data2' : np.random.randn(5)})
grouped = df.groupby('주스종류')

describe메서드는 축소나 누산 등 집계메서드는 아닙니다. 하지만 이 메서드는 아래에서 배울 apply 함수를 이용하면 데이터를 집계하는 것처럼 작동합니다. 코드는 다음과 같습니다.

python
f = lambda x: x.describe()
grouped.apply(f)

apply 메서드는 객체를 여러 조각으로 나누고, 전달된 함수를 각 조각에 일괄 적용한 후 이를 다시 합칩니다. 다음 코드를 실행하신 후 어떤 결과가 나타나는지 실습해 보시길 바랍니다.

python
def top(df, n=5, column='국어'): # 데이터에서 '국어' 열에서 상위 5개 값 구하기
return df.sort_values(by=column)[-n:]
top(df, n=6)

기본 사용법

apply 함수를 사용하는 기본 예제 코드는 다음과 같습니다.

python
df.groupby('과').apply(top) # 과별로 그룹화한 뒤 'top' 함수 적용

만약 학교별, 과별로 상위 1개 값만 가져오고 싶다면 n=1 옵션을 사용하면 됩니다. 다음은 학교별, 과별로 데이터를 그룹화한 뒤, '국어'에서 가장 높은 점수를 가진 데이터만 가져오는 코드입니다.

python
df.groupby(['학교', '과']).apply(top, n=1, column='국어')

만약 top 함수를 적용할 때 그룹 키를 사용하고 싶지 않다면 아래와 같이 group_keys=False 옵션을 추가하면 됩니다.

python
df.groupby('과', group_keys=False).apply(top)

결측치 채우기

누락된 값을 그룹의 평균값으로 채우는 코드는 다음과 같습니다.

python
fill_mean = lambda x: x.fillna(x.mean())
df.groupby('과').apply(fill_mean)

무작위 표본 추출하기

무작위 표본을 추출할 때는 다음과 같은 코드를 사용할 수 있습니다.

python
def sampling(data, sample_pct):
N = len(data)
sample_n = int(len(data)*sample_pct) # integer
sample = data.take(np.random.permutation(N)[:sample_n])
return sample
sample_set = df.groupby('학교').apply(sampling, sample_pct=0.2) # 그룹별로 20% 만큼 무작위 표본 추출

가중 평균 구하기

가중 평균을 구하고 싶다면 아래 코드를 참조해 주세요.

python
df = 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)

상관관계 계산하기

상관관계를 구하고 싶다면 다음 코드를 사용하시면 됩니다.

python
df.groupby('과').apply(lambda g: g['국어'].corr(g['영어']))

선형회귀 계산하기

선형회귀를 계산하려면 계량경제 라이브러리인 statsmodels를 사용해서 regress라는 함수를 작성하고 각 데이터 묶음마다 최소제곱(Ordinary Least Sqaures, OLS)으로 회귀를 수행할 수 있습니다. 영어에 대한 국어 과목의 과별 선형회귀는 다음과 같이 수행할 수 있습니다.

python
import statsmodels.api as sm
def regress(data, yvar, xvars):
Y = data[yvar]
X = data[xvars]
X['intercept'] = 1.
result = sm.OLS(Y, X).fit()
return result.params
df.groupby('과').apply(regress, '국어', ['영어'])

지금까지 총 4편에 걸쳐 Python pandas의 데이터 프레임 사용법을 알아보았습니다. 다음 시간에는 이 사용법으로 전처리를 거친 데이터를 시각화하는 법을 알아보겠습니다. 모두 수고 많으셨습니다.

참고 문헌

...

©2023 Snug Archive. All rights reserved.

Contact me at snugarchive@gmail.com.