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

지난 시간은 Python pandas 데이터 병합, 정제, 변형하는 법을 살펴보았습니다. 이번 시간은 pandas 사용법의 마지막 세션으로 Python pandas의 데이터 프레임(DataFrame)을 재형성하고, 데이터 프레임 간 연산하기, 그리고 데이터값을 집계하는 법을 알아보겠습니다.

재형성하기(reshaping)

DataFrame을 재형성하는 것은 DataFrame 안에 있는 데이터의 물리적 배치를 바꾸는 일입니다. DataFrame을 재형성하는 일은 데이터를 보는 시각을 넓혀줍니다. 기존에 발견하지 못했던 부분을 새롭게 재조명할 수 있기 때문이죠. DataFrame을 재형성하는 대표적인 작업은 데이터 피벗(pivoting)입니다.

데이터 피벗은 데이터를 요약하는 통계표를 만드는 일입니다. 피벗은 말 그대로 데이터를 회전시킨다는 의미입니다. 표 형식의 데이터를 회전시켜 재배치하는 일을 재형성 또는 피벗 연산이라고 하죠.

이 피벗에는 두 가지 방법이 있습니다. 하나는 길이가 긴 형식의 데이터를 옆으로 넓은 형식으로 만드는 것이고, 다른 하나는 옆으로 넓은 형식의 데이터를 긴 형식으로 만드는 것입니다. 하나씩 차례로 살펴보겠습니다.

1) 긴 형식 ⇒ 넓은 형식: pivot, unstack

먼저 길이가 긴 형식의 데이터를 넓은 형식으로 만들 때는 pivot, unstack 함수를 사용할 수 있습니다. pivot 함수는 DataFrame에 색인이 없고, 색인을 직접 지정하고 싶을 때 사용합니다. unstack 함수는 DataFrame에 계층적 색인이 있는 경우에 사용합니다.

지정된 색인이 없을 때: pivot

지정된 색인이 없는 경우에는 pivot 함수를 사용할 수 있습니다. pivot 함수는 시계열 데이터처럼 같은 날 측정한 데이터 종류가 여러 개여서 길이가 긴 데이터를 옆으로 넓게 만드는 데 유용합니다.

이 함수는 index, columns, values 등의 매개변수를 사용합니다. index에는 행 색인으로 지정할 열 이름을, columns에는 열 색인으로 사용할 열 이름을, values에는 값에 해당하는 열 이름을 전달합니다. 실습을 위해 DataFrame을 만들어보겠습니다. 실행할 코드는 다음과 같습니다.

python
data = pd.DataFrame({
'날짜': ['220501', '220501', '220502', '220502', '220503', '220503'],
'음식': ['떡볶이', '어묵', '어묵', '떡볶이', '어묵', '떡볶이'],
'가게': ['은희네', '은희네', '한수네', '한수네', '은희네', '한수네'],
'날씨' : [30, 31, 32, 29, 32, 29]
})

위 코드를 실행하면 아래와 같은 시계열 데이터가 만들어집니다.

pandas-데이터-생성-시계열데이터
만든 시계열 데이터 | Snug Archive

여기에 pivot 함수를 써보겠습니다. 실행할 코드와 결과는 아래와 같습니다.

python
data.pivot(index='날짜', columns='음식', values='날씨')
# 결과
음식 떡볶이 어묵
날짜
220501 30 31
220502 29 32
220503 29 32
220504 33 28

DataFrame의 데이터가 행 색인으로 지정한 '날짜'와 열 색인으로 지정한 '음식'에 따라 피벗되었습니다. 지금까지는 계층적 색인이 없는 DataFrame을 재형성하는데 pivot 함수를 사용했습니다. 만약 계층적 색인이 이미 있는 DataFrame에서 긴 데이터를 옆으로 만들 때는 unstack 함수를 사용하면 됩니다.

지정된 색인이 있을 경우: unstack

unstack 함수는 계층적 색인이 있는 DataFrame의 데이터를 수직으로 긴 방향에서 옆으로 넓게 만들어줍니다. 실습을 위해 계층적 색인이 있는 DataFrame을 준비해보겠습니다. 다음 코드를 실행하면 계층적 색인이 있는 DataFrame이 만들어집니다.

python
vegetables = ['감자', '감자', '당근', '당근']
stores = ['선아네', '동석이네', '선아네', '동석이네']
vegetables_info = [[v, s] for v, s in zip(vegetables, stores)]
df = pd.DataFrame(vegetables_info)
rows_index = pd.MultiIndex.from_frame(df) # 계층적 색인 생성
columns_index= ['2020', '2021', '2022']
data = pd.DataFrame(np.arange(12).reshape((4, 3)),
index=rows_index,
columns=columns_index)
data.index.names = ['채소명', '가게명']

코드를 실행한 결과는 다음과 같습니다.

pandas-데이터-생성-DataFrame1
생성한 DataFrame | Snug Archive

이 DataFrame에 아래와 같이 unstack 함수를 사용해보겠습니다.

python
data.unstack()

코드를 실행하면 다음과 같은 결과가 나타납니다.

pandas-데이터-피벗-unstack
DataFrame에 unstack함수를 적용한 결과 | Snug Archive

옆으로 긴 형식의 데이터가 새로 만들어진 것을 알 수 있습니다.

unstack 함수의 또 다른 쓰임새는 stack 함수를 사용한 후 만들어진 Series를 원래대로 돌려놓는 일입니다. stack 함수는 데이터를 아래 방향으로 길게 쌓는 함수입니다. 영어 동사 'stack'이 '쌓다'라는 의미를 지닌 것처럼 아래서 위로 데이터를 쌓는 것이죠. DataFrame에 stack 메서드를 사용하면 다음과 같이 열이 행으로 피벗 됩니다.

python
result = data.stack()

이 코드를 실행하면 다음과 같은 결과를 얻습니다.

pandas-데이터-피벗-stack
DataFrame에 stack 메서드를 적용한 결과 | Snug Archive

이 피벗된 DataFrame에 unstack 메서드를 사용하면 계층적 색인을 가진 Series로부터 원래의 DataFrame으로 돌려놓을 수 있습니다. 이때 레벨 숫자나 이름을 사용해서 끄집어낼 단계를 정할 수 있습니다. 예를 들어, stack 함수로 만든 Series를 원래 DataFrame으로 돌려놓고 싶다면 unstack 함수에 -1을 인자로 주면 됩니다.

여기서 -1은 DataFrame의 초기 레벨, 최상위 그룹을 뜻합니다. 레벨은 -1부터 시작해 숫자가 커질수록 하위그룹입니다. 예를 들어 DataFrame이 2단계로 색인되어 있다면, -1은 상위그룹이고 0은 하위그룹을 의미합니다. 계층적 색인이 3단계인 경우도 각각 -1, 0, 1로 레벨을 나눕니다. -1은 최상위 그룹, 1은 최하위 그룹을 뜻합니다. 계층적 색인이 없고 열 1개만 있다면 기본 레벨은 -1입니다.

레벨이 아닌 이름을 인자로 줄 수도 있습니다. 이때 인자는 끄집어낼 열의 이름을 의미합니다. 다음 코드는 각각 -1과 '채소명', '가게명'으로 데이터를 끄집어냅니다.

python
result.unstack(-1) # 원래 DataFrame으로 돌아가기
result.unstack('채소명') # unstack(0)
result.unstack('가게명') # unstack(1)

위 코드를 입력하면 아래와 같은 결과를 얻게 됩니다.

pandas-데이터-피벗-stack-레벨
DataFrame에 unstack 함수를 달리 적용한 결과 | Snug Archive

만약, 계층적 색인이 서로 다른 두 Series에서 동일한 계층적 색인을 만들고 싶다면 unstack으로 데이터를 옆으로 길게 만들고 다시 stack 메서드를 사용하면 됩니다. 다음 코드와 실행 결과를 참고해주세요.

python
s1 = pd.Series([0, 1, 2], index=['a', 'b', 'c'])
s2 = pd.Series([4, 5, 6, 7, 8], index=['c', 'd', 'e', 'f', 'g'])
data2 = pd.concat([s1, s2], keys=['GroupA', 'GroupB'])
data2.unstack().stack(dropna=False)
# 결과
GroupA a 0.0
b 1.0
c 2.0
d NaN
e NaN
f NaN
g NaN
GroupB a NaN
b NaN
c 4.0
d 5.0
e 6.0
f 7.0
g 8.0
dtype: float64

지금까지 pivot 함수와 unstack 함수를 사용해 긴 형식의 데이터를 넓은 형식으로 만드는 방법을 알아보았습니다. 이번에는 반대로 넓은 형식의 데이터를 긴 형식의 데이터로 만드는 법을 알아보겠습니다.

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

넓은 형식의 데이터를 긴 형식의 데이터로 만드는 함수는 melt 입니다. melt는 '녹다'라는 뜻을 지닌 영어 단어입니다. 즉, melt 함수는 말 그대로 데이터를 녹입니다. 데이터를 녹이니 길이가 길어진다고 연상할 수 있습니다. melt는 여러 개의 열을 하나로 합치고 DataFrame을 긴 형태로 만들어냅니다. 예제 코드는 다음과 같습니다.

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

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

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

만약, 녹인 데이터를 원래대로 되돌리고 싶다면 다음 코드를 사용할 수 있습니다.

python
reshaped = melted.pivot('key', 'variable', 'value').reset_index()

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

python
melted2 = pd.melt(cheese, id_vars=['key'], value_vars=['은희네', '선아네'])

지금까지 pivot 함수와 unstack 함수, melt 함수로 데이터를 재형성하는 법을 알아보았습니다. 다음으로는 데이터를 연산하는 법을 알아보겠습니다.

연산하기(operating)

pandas에서 데이터 연산은 1) 한 객체에 직접 연산하는 법 2) 연산 기호를 통해 두 객체를 연산하는 법 3) 산술 연산 메서드를 이용하는 방법으로 나뉩니다. 실습을 위해 먼저 데이터를 준비해보겠습니다.

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) 단일 객체 연산

먼저 한 객체와 숫자를 연산할 수 있습니다. 다음은 DataFrame의 'b' 열에 10을 더한 예제입니다.

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

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

연산 기호를 사용하지 않는다면 메서드를 사용하는 방법도 있습니다.

3) 메서드 연산

산술 연산 메서드에는 다음과 같은 종류가 있습니다.

메서드설명
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)

데이터 집계는 배열로부터 스칼라값을 만들어내는 모든 데이터 변환 작업을 말합니다. 먼저 데이터를 집계하는 요약 통계 메서드부터 알아보겠습니다.

1) 요약 통계

요약 통계에는 다음과 같은 메서드가 있습니다.

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

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

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

집계 함수를 사용할 때는 바로 함수를 호출하거나 agg 메서드를 사용하는 법이 있습니다. 여기서는 agg 메서드를 사용하는 법을 알아보겠습니다. 실습 DataFrame은 다음 코드를 실행한 결과를 사용하겠습니다. 코드에서 사용한 groupby는 그룹화에서 자세히 다루겠습니다. 여기서는 어떻게 사용하는지만 간단히 알아보겠습니다.

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

직접 고안한 함수 사용

먼저 직접 고안한 함수를 사용할 수 있습니다. 다음은 최댓값에서 최솟값을 뺀 결과를 나타내는 함수를 정의한 뒤 이를 모든 DataFrame의 값에 적용하는 코드입니다.

python
def peak_to_peak(arr):
return arr.max() - arr.min()
grouped.agg(peak_to_peak)

단일 열에 단일 함수 사용

어떤 데이터 그룹의 평균을 구하고 싶다면 다음과 같은 코드를 사용할 수 있습니다.

python
grouped.agg('mean')

단일 열에 다수의 함수 사용

단일 열에 다수의 함수를 적용해 원하는 결과를 얻을 수도 있습니다. 예제 코드는 다음과 같습니다.

python
grouped.agg(['mean', 'std', peak_to_peak])

추가되는 열 이름을 다른 이름으로 변경하고 싶다면 다음과 같은 코드를 사용하면 됩니다.

python
grouped.agg([('평균', 'mean'), ('표준편차', np.std)]) # 'mean'을 '평균'으로 변경, 'np.std'를 '표준편차'로 수정

다수 열에 다수의 동일한 함수 적용

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

python
functions = ['count', 'mean', 'max']
grouped['열1', '열2'].agg(functions)

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

python
ftuples = [('평균', 'mean'), ('편차', np.var)]
grouped['열1', '열2'].agg(ftuples)

열마다 다른 함수 적용하기

열마다 서로 다른 함수를 사용할 수도 있습니다. 예제 코드는 다음과 같습니다.

python
grouped.agg({'평균': 'mean', '합': 'sum'}) # '평균' 열에 'mean' 함수 적용, '합' 열에 'sum' 함수 적용
grouped.agg({'요약 통계': ['mean', 'count', 'sum'], '표준편차': 'std'})

지금까지 요약 통계 메서드(집계 함수)와 agg 함수를 사용해 요약 통계 메서드를 사용하는 법을 알아보았습니다. 이번에는 그룹화를 알아보겠습니다.

2) 그룹화: groupby

groupby는 데이터 집합에서 동일한 값을 가진 대상끼리 묶어서 새로운 통계 계산을 하는 데 사용하는 함수입니다. 데이터 집합을 자연스럽게 나누고 요약하죠. 그룹화를 알아보기 위해 아래 데이터로 DataFrame을 만들어보겠습니다.

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)

특정 값을 가지고 있는 데이터 선택하기: get_group

특정 값을 가지고 있는 데이터만 가져오려면 groupby 함수로 그룹을 나눈 뒤 get_group 함수를 통해 특정 값만 가져올 수 있습니다. 예를 들어, 과별로 그룹을 나눈 뒤 그중 '이과'인 데이터만 가져오고 싶다면 다음 코드를 활용할 수 있습니다.

python
df.groupby('과').get_group('이과') # '과'로 그룹화한 뒤 '이과'인 데이터 가져오기

특정 그룹으로 묶기

이번에는 데이터를 특정 그룹으로 묶은 뒤 요약 통계를 구해보겠습니다. 먼저 평균을 구해보겠습니다.

평균

예를 들어, 학교별 평균을 구하고 싶다면 다음 코드를 이용할 수 있습니다.

python
df.groupby('학교').mean() # 학교별 평균 구하기

만약 학교별, 과별로 평균을 구하고 싶다면 아래 코드를 이용할 수 있습니다.

python
df.groupby(['학교', '과']).mean() # 학교별, 과별 평균 구하기
크기

그룹의 크기를 구하고 싶다면 size 함수를 사용하면 됩니다. size는 그룹의 크기를 담고 있는 Series를 반환하는 함수입니다. 만약 학교별 학생 수를 구하고 싶다면 아래 코드를 이용할 수 있습니다.

python
df.groupby('학교').size() # 학교별 학생 수 구하기
df.groupby('학교').size()['포켓몬고'] # '포켓몬고' 학생 수 구하기
유일값

학교별, 학년별 학생수를 구하고 싶다면 다음 코드를 이용할 수 있습니다.

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

열의 일부만 선택하기

데이터 중에서 열의 일부만 선택해서 값을 구하고 싶은 경우도 있습니다. 예를 들어, 학교별 '국어' 과목의 평균만 구하고 싶다면 다음 코드를 이용하시면 됩니다.

python
df.groupby('학교')['국어'].mean() # 학교별 국어 과목의 평균을 구하기
# df['국어'].groupby(df['학교']).mean()와 결과 동일

학교별 '희망전공'의 수를 알고 싶다면 다음 코드를 이용할 수 있죠.

python
df.groupby('학교')[['이름', '희망전공']].count() # 학교별 희망전공 수 구하기

색인 단계로 그룹핑하기: level

계층적으로 색인된 데이터는 축 색인의 단계 중 하나를 사용해서 집계할 수 있습니다. 계층적 색인이 있는 경우, 색인 단계로 그룹핑하고 싶다면 level 예약어를 사용할 수 있습니다. 예제 코드는 다음과 같습니다.

python
columns = pd.MultiIndex.from_arrays([['은희네', '미란이네', '은희네', '은희네', '미란이네'],
[2, 4, 1, 1, 2]],
names=['가게', '개수'])
hier_df = pd.DataFrame(np.random.randn(4, 5), columns=columns)
hier_df.groupby(level='가게', axis=1).count()

색인 단계 사용하지 않기: as_index=False

지금까지 살펴본 모든 예제에서 집계된 데이터는 유일한 그룹키 조합으로 색인(어떤 경우에는 계층적 색인)되어 반환되었습니다. 만약 색인되지 않도록 하고 싶다면 다음 옵션을 사용하면 됩니다.

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

지금까지 groupby 함수를 통해 데이터를 그룹화하는 법을 알아보았습니다. 이번에는 apply 함수를 사용해 데이터를 집계하는 법을 알아보겠습니다.

3) 함수 적용: apply

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) 일반 피벗테이블 만들기: pivot_table

피벗테이블은 데이터를 하나 이상의 키로 수집해서 어떤 키는 행에, 어떤 키는 열에 나열해서 데이터를 정렬합니다. 피벗테이블은 groupby 기능을 사용해서 계층적 색인을 활용한 재형성 연산을 가능하게 해줍니다.

기본

index는 만들어지는 피벗테이블의 행을 그룹으로 묶을 열 이름이나 그룹 키를 뜻합니다.

python
df.pivot_table(index=['학교', '학년'])

columns는 만들어지는 피벗테이블의 열을 그룹으로 묶을 열 이름이나 그룹 키를 뜻합니다. 다음은 학교별, 학년별로 국어와 영어 과목을 과에 따라 집계하는 코드입니다.

python
df.pivot_table(['국어', '영어'], index=['학교', '학년'], columns='과')

부분합 얻기: margins=True

여기에 margins=True를 넘기면 부분합을 포함하도록 확장할 수 있습니다. 그렇게 하면 All 열과 All 행이 추가되어 그룹 통계를 얻을 수 있습니다.

python
df.pivot_table(['국어', '영어'], index=['학교', '학년'], columns='과', margins=True)

집계함수 지정하기: aggfunc

다른 집계함수를 사용하려면 aggfunc 매개변수를 사용하면 됩니다. 기본값으로는 mean이 사용됩니다. groupby 컨텍스트 안에서 유효한 어떤 함수라도 가능합니다. 예를 들어, len 또는 'count'는 그룹 크기의 교차일람표(총 개수나 빈도)를 반환합니다.

python
df.pivot_table(['국어', '영어'], index=['학교', '학년'], columns='과', aggfunc=len, margins=True)
df.pivot_table(['국어', '영어'], index=['학교', '학년'], columns='과', aggfunc='count', margins=True)

결측치 채우기: fill_value

fill_value 매개변수는 결측치(NA)를 처리합니다. 다음 코드는 결측치를 0으로 대체합니다.

python
df.pivot_table(['국어', '영어'], index=['학교', '학년'], columns='과', aggfunc='mean', fill_value=0)

5) 교차표 만들기: crosstab

교차표(cross table) 피벗테이블의 특수한 경우입니다. 한국어로는 교차일람표 영어로는 contingency table이라고도 불립니다. 교차표는 교차분석 시 만들어지는 결과물입니다. 교차분석이란 질적 독립변수와 질적 종속변수의 관계를 분석할 때 사용하는 통계 기법입니다. 질적 변수라는 것은 명목척도, 범주형 척도처럼 수치가 아닌 척도를 의미합니다. 예를 들어, 날씨와 선풍기 판매 사이의 관계가 있는지 알고 싶을 때 하는 분석이 바로 교차분석입니다.

crosstab은 이 교차표를 만들 때 사용하는 함수입니다.

일반 색인 사용하는 경우

python
pd.crosstab(df['학교'], df['과'], margins=True)

계층적 색인 사용하는 경우

python
pd.crosstab([df['학교'], df['학년']], df['과'] , margins=True)

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

참고 문헌

  • [1] 웨스 맥키니, 『파이썬 라이브러리를 활용한 데이터 분석』, 김영근, 한빛미디어. 2019, 334-345, 385-422.
  • [2] Rfriend, 「[Python pandas] 그룹 별 무작위 표본 추출 (random sampling by group)」, Rfriend, "https://rfriend.tistory.com/407"
...

©2022 Snug Archive. All rights reserved.

Contact me at snugarchive@gmail.com.