
Python pandas 데이터 재형성, 연산, 집계하는 법
데이터 분석 4단계
지난 시간에는 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을 만들어보겠습니다. 실행할 코드는 다음과 같습니다.
pythondata = pd.DataFrame({'날짜': ['220501', '220501', '220502', '220502', '220503', '220503'],'음식': ['떡볶이', '어묵', '어묵', '떡볶이', '어묵', '떡볶이'],'가게': ['은희네', '은희네', '한수네', '한수네', '은희네', '한수네'],'날씨' : [30, 31, 32, 29, 32, 29]})
위 코드를 실행하면 아래와 같은 시계열 데이터가 만들어집니다.

여기에 pivot
함수를 써보겠습니다. 실행할 코드와 결과는 아래와 같습니다.
pythondata.pivot(index='날짜', columns='음식', values='날씨')# 결과음식 떡볶이 어묵날짜220501 30 31220502 29 32220503 29 32220504 33 28
DataFrame의 데이터가 행 색인으로 지정한 '날짜'와 열 색인으로 지정한 '음식'에 따라 피벗되었습니다. 지금까지는 계층적 색인이 없는 DataFrame을 재형성하는데 pivot
함수를 사용했습니다. 만약 계층적 색인이 이미 있는 DataFrame에서 긴 데이터를 옆으로 만들 때는 unstack
함수를 사용하면 됩니다.
지정된 색인이 있을 경우: unstack
unstack
함수는 계층적 색인이 있는 DataFrame의 데이터를 수직으로 긴 방향에서 옆으로 넓게 만들어줍니다. 실습을 위해 계층적 색인이 있는 DataFrame을 준비해보겠습니다. 다음 코드를 실행하면 계층적 색인이 있는 DataFrame이 만들어집니다.
pythonvegetables = ['감자', '감자', '당근', '당근']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 = ['채소명', '가게명']
코드를 실행한 결과는 다음과 같습니다.

이 DataFrame에 아래와 같이 unstack
함수를 사용해보겠습니다.
pythondata.unstack()
코드를 실행하면 다음과 같은 결과가 나타납니다.

옆으로 긴 형식의 데이터가 새로 만들어진 것을 알 수 있습니다.
unstack
함수의 또 다른 쓰임새는 stack
함수를 사용한 후 만들어진 Series를 원래대로 돌려놓는 일입니다. stack
함수는 데이터를 아래 방향으로 길게 쌓는 함수입니다. 영어 동사 'stack'이 '쌓다'라는 의미를 지닌 것처럼 아래서 위로 데이터를 쌓는 것이죠. DataFrame에 stack
메서드를 사용하면 다음과 같이 열이 행으로 피벗 됩니다.
pythonresult = data.stack()
이 코드를 실행하면 다음과 같은 결과를 얻습니다.

이 피벗된 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과 '채소명', '가게명'으로 데이터를 끄집어냅니다.
pythonresult.unstack(-1) # 원래 DataFrame으로 돌아가기result.unstack('채소명') # unstack(0)result.unstack('가게명') # unstack(1)
위 코드를 입력하면 아래와 같은 결과를 얻게 됩니다.

만약, 계층적 색인이 서로 다른 두 Series에서 동일한 계층적 색인을 만들고 싶다면 unstack
으로 데이터를 옆으로 길게 만들고 다시 stack
메서드를 사용하면 됩니다. 다음 코드와 실행 결과를 참고해주세요.
pythons1 = 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.0b 1.0c 2.0d NaNe NaNf NaNg NaNGroupB a NaNb NaNc 4.0d 5.0e 6.0f 7.0g 8.0dtype: float64
지금까지 pivot
함수와 unstack
함수를 사용해 긴 형식의 데이터를 넓은 형식으로 만드는 방법을 알아보았습니다. 이번에는 반대로 넓은 형식의 데이터를 긴 형식의 데이터로 만드는 법을 알아보겠습니다.
2) 넓은 형식 ⇒ 긴 형식: melt
넓은 형식의 데이터를 긴 형식의 데이터로 만드는 함수는 melt
입니다. melt
는 '녹다'라는 뜻을 지닌 영어 단어입니다. 즉, melt
함수는 말 그대로 데이터를 녹입니다. 데이터를 녹이니 길이가 길어진다고 연상할 수 있습니다. melt
는 여러 개의 열을 하나로 합치고 DataFrame을 긴 형태로 만들어냅니다. 예제 코드는 다음과 같습니다.
pythoncheese = pd.DataFrame({'key': ['감자', '호박', '당근'],'은희네': [10, 50, 30],'선아네': [20, 40, 10],'한수네': [8, 30, 20]})melted = pd.melt(cheese, ['key'])
위 코드를 실행하면 다음과 같이 데이터가 넓은 형식에서 긴 형식으로 바뀝니다.

만약, 녹인 데이터를 원래대로 되돌리고 싶다면 다음 코드를 사용할 수 있습니다.
pythonreshaped = melted.pivot('key', 'variable', 'value').reset_index()
참고로 특정 데이터값만 골라서 긴 형식으로 만들고 싶을 경우에는 id_vars
와 value_vars
옵션을 사용하면 됩니다. id_vars
에는 키로 사용할 값을 value_vars
에는 변수로 사용할 값을 넘겨주면 됩니다. 예제 코드는 다음과 같습니다.
pythonmelted2 = pd.melt(cheese, id_vars=['key'], value_vars=['은희네', '선아네'])
지금까지 pivot
함수와 unstack
함수, melt
함수로 데이터를 재형성하는 법을 알아보았습니다. 다음으로는 데이터를 연산하는 법을 알아보겠습니다.
연산하기(Operating)
pandas에서 데이터 연산은 1) 한 객체에 직접 연산하는 법 2) 연산 기호를 통해 두 객체를 연산하는 법 3) 산술 연산 메서드를 이용하는 방법으로 나뉩니다. 실습을 위해 먼저 데이터를 준비해보겠습니다.
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) 단일 객체
먼저 한 객체와 숫자를 연산할 수 있습니다. 다음은 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
연산 기호를 사용하지 않는다면 메서드를 사용하는 방법도 있습니다.
3) 메서드 연산
산술 연산 메서드에는 다음과 같은 종류가 있습니다.
메서드 | 설명 |
---|---|
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)
데이터를 집계한다는 것은 요약 통계값을 구하는 일입니다. 구조적으로 말하면 데이터 집계는 배열로부터 스칼라값을 만들어내는 모든 데이터의 변환 작업을 말합니다. 이때 누락값은 제외되고 연산됩니다. 요약 통계값 메서드(집계 함수)의 종류는 다음과 같습니다.
메서드 | 설명 |
---|---|
count | NA가 아닌 값의 수 반환 |
sum | NA가 아닌 값들의 합을 구함 |
mean | NA가 아닌 값들의 평균을 구함 |
median | NA가 아닌 값들의 산술 중간값(50% 분위) 반환 |
min, max | NA가 아닌 값들 중 최솟값, 최댓값 계산 |
quantile | 0 부터 1까지의 분위수를 계산 |
std, var | 편향되지 않은(n-1을 분모로 하는) 표준편차와 분산 |
cumsum | 누적합 계산 |
argmin, argmax | 각각 최솟값과 최댓값을 담고 있는 색인의 위치(정수) 반환 |
idxmin, idxmax | 각각 최솟값과 최댓값을 담고 있는 색인의 값 반환 |
mad | 평균값에서 평균절대편차 계산 |
prod | 모든 값의 곱 |
skew | 표본비대칭도(3차 적률)의 값 계산 |
kurt | 표본첨도(4차 적률)의 값 계산 |
cummin, cummax | 각각 누적 최솟값과 누적 최댓값 계산 |
cumprod | 누적곱 계산 |
diff | 1차 산술차 계산(시계열 데이터 처리 시 유용) |
pct_change | 퍼센트 변화율 계산 |
집계 함수는 agg
함수와 함께 사용할 수 있습니다. 예를 들어 전체 평균을 구하고 싶다면 다음과 같은 코드를 이용할 수 있습니다.
pythondf['과학'].mean() # 전체 데이터의 과학 평균 구하기# 또는df.agg(평균_과학=('과학', 'mean'))
agg
함수는 보통 groupby
함수와 같이 각 집단을 요약한 값, 즉 집단별 요약 통계값을 구하고 싶을 때 사용합니다. 먼저 groupby
함수의 사용법부터 살펴보겠습니다.
1) 그룹화: groupby
groupby
는 데이터 집합에서 동일한 값을 가진 대상끼리 묶어서 새로운 통계값을 구하는 함수입니다. groupby
함수는 데이터 집합을 자연스럽게 나누고 요약합니다. 그룹화를 알아보기 위해 아래 데이터로 DataFrame을 만들어보겠습니다.
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)
특정 값을 가지고 있는 데이터 선택하기: get_group
특정 값을 가지고 있는 데이터만 선별해서 가져오려면 groupby
함수로 그룹을 나눈 뒤 get_group
메서드를 체이닝(chaining)하면 됩니다. 예를 들어, 과별로 그룹을 나눈 뒤 그중 '이과'인 데이터만 가져오고 싶다면 다음 코드를 활용할 수 있습니다.
pythondf.groupby('과').get_group('이과') # 과별로 집단을 묶은 뒤 값이 이과인 데이터 가져오기
get_group
외에도 데이터를 선택하는 다양한 방법을 알고 싶은 분들은 Python pandas 데이터 확인, 정렬, 선택하는 법의 데이터 선택 부분을 참조해주세요.
요약 통계값 구하기
이번에는 데이터를 특정 그룹으로 묶은 뒤 요약 통계를 구해보겠습니다. 먼저 평균을 구해보겠습니다.
평균
전체 열
열별로 요약 통계값을 구하려면 groupby
함수에 집계합수를 체이닝하면 됩니다. 다음은 학교별 모든 변수의 평균을 구하는 코드입니다.
pythondf.groupby('학교').mean() # 학교별 모든 과목의 평균 구하기
groupby
함수로 여러 하위 집단을 만들 수도 있습니다. 다음은 학교별, 과별로 평균을 구하는 코드입니다.
pythondf.groupby(['학교', '과']).mean() # 학교별, 과별 모든 과목의 평균 구하기
특정 열
특정 열에 대해서만 선택적으로 요약 통계값을 구하고 싶다면 groupby
함수와 agg
함수를 조합해서 사용하면 됩니다. 다음은 집단을 학교별로 그룹화한 뒤 특정 과목의 평균을 구하는 코드입니다. 아래 코드는 모두 동일한 결과를 반환합니다.
pythondf.groupby('학교').mean()['수학'] # 학교별 수학 평균을 Series로 반환df.groupby('학교')['수학'].mean()df['수학'].groupby(df['학교']).mean()
agg
함수를 이용해도 동일한 결과를 얻을 수 있습니다.
pythondf.groupby('학교').agg(평균_수학=('수학', 'mean')) # 학교별 수학 평균을 DataFrame으로 반환
특정 집단
여러 개의 하위 집단별 요약 통계값을 구할 수도 있습니다. 다음은 학교별, 과별 수학 과목의 평균을 구하는 코드입니다.
pythondf.groupby(['학교', '과']).agg(평균_수학=('수학', 'mean')) # 학교별, 과별 수학 과목의 평균 구하기
빈도수
pythondf.groupby('학교')[['이름', '희망전공']].count() # 학교별 희망전공 수 구하기
크기
그룹의 크기를 구하고 싶다면 size
함수를 사용하면 됩니다. size
는 그룹의 크기를 담고 있는 Series를 반환하는 함수입니다.
전체
학교별 학생 수를 구하고 싶다면 아래 코드를 이용할 수 있습니다.
pythondf.groupby('학교').size() # 학교별 학생 수 구하기df.groupby('학교').size()['포켓몬고'] # '포켓몬고' 학생 수 구하기
특정 집단
다음은 학교가 '포켓몬고'인 데이터들을 과별로 분류해 각 과에 있는 학생수를 구하는 코드입니다.
pythondf.query('학교 == "포켓몬고"').groupby(['과']).agg(빈도수 = ('과', 'count'))
유일값
집단별 유일값을 구하려면 groupby
함수에 value_counts
메서드를 체이닝하면 됩니다.
pythondf.groupby('학교')['학년'].value_counts() # 학교별, 학년별 학생수 구하기
value_counts
함수에 normalize=True
를 추가하면 집단별 비율을 구할 수 있습니다.
pythondf.groupby('학교')['학년'].value_counts(normalize=True) # 학교별, 학년별 학생수 비율 구하기df.groupby('학교')['학년'].value_counts(normalize=True).loc['포켓몬고'] # '포켓몬고'의 학교별, 학년별 학생수 비율 구하기
색인 단계로 그룹핑하기: level
계층적으로 색인된 데이터는 축 색인의 단계 중 하나를 사용해서 집계할 수 있습니다. 계층적 색인이 있는 경우, 색인 단계로 그룹핑하고 싶다면 level
예약어를 사용할 수 있습니다. 예제 코드는 다음과 같습니다.
pythoncolumns = 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
지금까지 살펴본 모든 예제에서는는 집계 기준으로 삼은 열이 행색인으로 처리되었습니다. 만약 기준으로 삼은 열을 행색인화 하지 않고 일반 열로 만들고 싶다면 as_index=False
옵션을 사용하면 됩니다.
pythondf.groupby('과', as_index=False).mean()
지금까지 groupby
함수를 통해 데이터를 그룹화하는 법을 알아보았습니다. 이번에는 apply
함수를 사용해 데이터를 집계하는 법을 알아보겠습니다.
2) agg
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)
커스텀 함수 사용
먼저 직접 고안한 함수를 사용할 수 있습니다. 다음은 최댓값에서 최솟값을 뺀 결과를 나타내는 함수를 정의한 뒤 이를 모든 DataFrame의 값에 적용하는 코드입니다.
pythondef peak_to_peak(arr):return arr.max() - arr.min()grouped.agg(peak_to_peak)
단일 열에 단일 함수 사용
어떤 데이터 그룹의 평균을 구하고 싶다면 다음과 같은 코드를 사용할 수 있습니다.
pythongrouped.agg('mean')
단일 열에 다수의 함수 사용
단일 열에 다수의 함수를 적용해 원하는 결과를 얻을 수도 있습니다. 예제 코드는 다음과 같습니다.
pythongrouped.agg(['mean', 'std', peak_to_peak])
추가되는 열 이름을 다른 이름으로 변경하고 싶다면 다음과 같은 코드를 사용하면 됩니다.
pythongrouped.agg([('평균', 'mean'), ('표준편차', np.std)]) # 'mean'을 '평균'으로 변경, 'np.std'를 '표준편차'로 수정
다수 열에 다수의 동일한 함수 적용
다수의 열에 다수의 동일한 함수를 적용할 수도 있습니다. 다음은 '열1'과 '열2'에 대해 각각 'count', 'mean', 'max' 함수를 적용해 결괏값(새로운 열)을 만드는 예제 코드입니다.
pythonfunctions = ['count', 'mean', 'max']grouped['열1', '열2'].agg(functions)
아래와 같이 열 이름과 메서드가 담긴 튜플 리스트를 넘길 수도 있습니다.
pythonftuples = [('평균', 'mean'), ('편차', np.var)]grouped['열1', '열2'].agg(ftuples)
열마다 다른 함수 적용하기
열마다 서로 다른 함수를 사용할 수도 있습니다. 예제 코드는 다음과 같습니다.
pythongrouped.agg({'평균': 'mean', '합': 'sum'}) # '평균' 열에 'mean' 함수 적용, '합' 열에 'sum' 함수 적용grouped.agg({'요약 통계': ['mean', 'count', 'sum'], '표준편차': 'std'})
3) 함수 적용: apply
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) 일반 피벗테이블 만들기: pivot_table
피벗테이블은 데이터를 하나 이상의 키로 수집해서 어떤 키는 행에, 어떤 키는 열에 나열해서 데이터를 정렬합니다. 피벗테이블은 groupby
기능을 사용해서 계층적 색인을 활용한 재형성 연산을 가능하게 해줍니다.
기본
index
는 만들어지는 피벗테이블의 행을 그룹으로 묶을 열 이름이나 그룹 키를 뜻합니다.
pythondf.pivot_table(index=['학교', '학년'])
columns
는 만들어지는 피벗테이블의 열을 그룹으로 묶을 열 이름이나 그룹 키를 뜻합니다. 다음은 학교별, 학년별로 국어와 영어 과목을 과에 따라 집계하는 코드입니다.
pythondf.pivot_table(['국어', '영어'], index=['학교', '학년'], columns='과')
부분합 얻기: margins=True
여기에 margins=True
를 넘기면 부분합을 포함하도록 확장할 수 있습니다. 그렇게 하면 All 열과 All 행이 추가되어 그룹 통계를 얻을 수 있습니다.
pythondf.pivot_table(['국어', '영어'], index=['학교', '학년'], columns='과', margins=True)
집계함수 지정하기: aggfunc
다른 집계함수를 사용하려면 aggfunc
매개변수를 사용하면 됩니다. 기본값으로는 mean
이 사용됩니다. groupby
컨텍스트 안에서 유효한 어떤 함수라도 가능합니다. 예를 들어, len
또는 'count'
는 그룹 크기의 교차일람표(총 개수나 빈도)를 반환합니다.
pythondf.pivot_table(['국어', '영어'], index=['학교', '학년'], columns='과', aggfunc=len, margins=True)df.pivot_table(['국어', '영어'], index=['학교', '학년'], columns='과', aggfunc='count', margins=True)
결측치 채우기: fill_value
fill_value
매개변수는 결측치(NA)를 처리합니다. 다음 코드는 결측치를 0으로 대체합니다.
pythondf.pivot_table(['국어', '영어'], index=['학교', '학년'], columns='과', aggfunc='mean', fill_value=0)
5) 교차표 만들기: crosstab
교차표(cross table) 피벗테이블의 특수한 경우입니다. 한국어로는 교차일람표 영어로는 contingency table이라고도 불립니다. 교차표는 교차분석 시 만들어지는 결과물입니다. 교차분석이란 질적 독립변수와 질적 종속변수의 관계를 분석할 때 사용하는 통계 기법입니다. 질적 변수라는 것은 명목척도, 범주형 척도처럼 수치가 아닌 척도를 의미합니다. 예를 들어, 날씨와 선풍기 판매 사이의 관계가 있는지 알고 싶을 때 하는 분석이 바로 교차분석입니다.
crosstab
은 이 교차표를 만들 때 사용하는 함수입니다.
일반 색인 사용하는 경우
pythonpd.crosstab(df['학교'], df['과'], margins=True)
계층적 색인 사용하는 경우
pythonpd.crosstab([df['학교'], df['학년']], df['과'] , margins=True)
지금까지 총 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"