파이썬에서 데이터 분석, 처리를 할 때 많이 팬더스(Pandas) 사용합니다.
그중에서 groupby를 사용해야 하는 경우가 있어 정리를 하게 되었습니다.
Python Pandas groupby
# 라이브러리 import
import pandas as pd import numpy as np |
DataFrame으로 샘플 데이터 작성
df = pd.DataFrame({ 'city': ['부산', '부산', '부산', '부산', '서울', '서울', '서울'], 'fruits': ['apple', 'orange', 'banana', 'banana', 'apple', 'apple', 'banana'], 'price': [100, 200, 250, 300, 150, 200, 400], 'quantity': [1, 2, 3, 4, 5, 6, 7] }) df |
city | fruits | price | quantity | |
0 | 부산 | apple | 100 | 1 |
1 | 부산 | orange | 200 | 2 |
2 | 부산 | banana | 250 | 3 |
3 | 부산 | banana | 300 | 4 |
4 | 서울 | apple | 150 | 5 |
5 | 서울 | apple | 200 | 6 |
6 | 서울 | banana | 400 | 7 |
groupby 사용 방법
같은 값을 하나로 묶어 통계 또는 집계 결과를 얻기 위해 사용하는 것이 groupby입니다.
예를 들어 도시(city) 별로 가격(price) 평균을 구하고 싶은 경우 다음과 같이 groupby를 사용하여 평균값을 구할 수 있습니다.
평균값을 구해주는 메서드로 mean을 사용합니다.
df.groupby('city').mean() |
price | quantity | |
city | ||
부산 | 212.5 | 2.5 |
서울 | 250.0 | 6.0 |
그룹 지정은 여러 개를 지정할 수도 있습니다.
도시(city)와 과일(fruits)로 평균을 구해보겠습니다.
df.groupby(['city', 'fruits']).mean()
price | quantity | ||
city | fruits | ||
부산 | apple | 100.0 | 1.0 |
banana | 275.0 | 3.5 | |
orange | 200.0 | 2.0 | |
서울 | apple | 175.0 | 5.5 |
banana | 400.0 | 7.0 |
도시별로 그룹화하고 다시 과일 종류별로 그룹이 된 평균값을 얻을 수 있습니다.
groupby를 사용하면 기본으로 그룹 라벨이 index가 됩니다.
index를 사용하고 싶은 않은 경우에는 as_index=False 를 설정하면 됩니다.
df.groupby(['city', 'fruits'], as_index=False).mean() |
city | fruits | price | quantity | |
0 | 부산 | apple | 100.0 | 1.0 |
1 | 부산 | banana | 275.0 | 3.5 |
2 | 부산 | orange | 200.0 | 2.0 |
3 | 서울 | apple | 175.0 | 5.5 |
4 | 서울 | banana | 400.0 | 7.0 |
GroupBy 오브젝트 특성
get_group()
그룹 안에 데이터를 확인하고 싶은 경우 사용합니다.
df.groupby('city').get_group('부산') |
city | fruits | price | quantity | |
0 | 부산 | apple | 100 | 1 |
1 | 부산 | orange | 200 | 2 |
2 | 부산 | banana | 250 | 3 |
3 | 부산 | banana | 300 | 4 |
size()
각 그룹의 사이즈를 취득할 수 있습니다.
df.groupby('city').size() |
>>>
city
부산 4
서울 3
dtype: int64
size() 결과는 Series 이라는 1차원 배열 오브젝트로 반환합니다.
반환된 배열에서 특정 그룹의 사이즈만을 취득할 수 있습니다.
df.groupby('city').size()['부산'] |
>>>
4
Aggregation
GroupBy.mean()처럼 그룹별로 결과를 얻는 조작을 Aggregation이라고 부릅니다.
GroupBy 오브젝트에는 Aggregation에 사용할 수 있는 함수가 있습니다.
Aggregation를 사용하고 싶은 경우에는 agg()를 사용해서 처리할 수 있습니다.
df.groupby('city').agg(np.mean) |
price | quantity | |
city | ||
부산 | 212.5 | 2.5 |
서울 | 250.0 | 6.0 |
agg에 mean()을 사용하여 도시별 가격과 수량의 평균값을 구했습니다.
만약 가격의 평균과 수량의 합계를 동시에 구하고 싶은 경우에는 어떻게 해야 할까요?
agg를 사용하여 동시에 처리할 수 있습니다.
def my_mean(s): return np.mean(s) df.groupby('city').agg({'price': my_mean, 'quantity': np.sum}) |
price | quantity | |
city | ||
부산 | 212.5 | 10 |
서울 | 250.0 | 18 |
apply
Aggregation을 사용하여 그룹 별로 결과를 얻을 수 있지만 더욱 활용하기 편리한 결과를 만들고 싶을 때 apply를 사용합니다.
apply를 사용하면 그룹별로 DataFrame를 사용할 수 있습니다.
apply 결과를 스칼라로 반환한 경우
・groupby로 작성한 lable이 row index가 됨. ・행수는 그룹수와 동일 ・as_index 적용 안됨 |
df.groupby(['city', 'fruits'], as_index=False).apply(lambda d: (d.price * d.quantity).sum()) |
city fruits
부산 apple 100
banana 1950
orange 400
서울 apple 1950
banana 2800
dtype: int64
그룹명에 접근하고 싶은 경우
df.groupby(['city', 'fruits'], as_index=False).apply(lambda d: d.name) |
city fruits
부산 apple (부산, apple)
banana (부산, banana)
orange (부산, orange)
서울 apple (서울, apple)
banana (서울, banana)
dtype: object
apply 결과를 Series로 반환한 경우
・groupby로 작성한 label과 더해져 apply 함수 결과 index가 결과 전체의 row index가 됨. ・전체 행수는 결과에 따라 다름. ・as_index=False를 지정하여 index를 삭제 가능 |
def total_series(d): return d.price * d.quantity df.groupby(['city', 'fruits']).apply(total_series) |
city fruits
city fruits
부산 apple 0 100
banana 2 750
3 1200
orange 1 400
서울 apple 4 750
5 1200
banana 6 2800
dtype: int64
allpy 결과에 row index를 지정하여 DataFrame으로 반환한 경우.
・apply 결과를 연결한 DataFrame을 작성 가능. ・groupby에 사용한 label은 index가 되지 않음. ・as_index=False 사용 불가. |
def total_keepindex(d): return pd.DataFrame({ 'total': d.price * d.quantity # 여기서 반환된 DataFrame row index와 d의 row index는 같음 }) df.groupby(['city', 'fruits']).apply(total_keepindex) |
total | |
0 | 100 |
1 | 400 |
2 | 750 |
3 | 1200 |
4 | 750 |
5 | 1200 |
6 | 2800 |
allpy 결과에 row index를 지정 안하고 DataFrame으로 반환한 경우.
・groupby에 사용한 label이 index가 됨. ・as_index=False 사용 가능. |
def total_keepnoindex(d): return pd.DataFrame({ 'total': (d.price * d.quantity).sum() }, index=['hoge']) df.groupby(['city', 'fruits']).apply(total_keepnoindex) |
total | |||
city | fruits | ||
부산 | apple | hoge | 100 |
banana | hoge | 1950 | |
orange | hoge | 400 | |
서울 | apple | hoge | 1950 |
banana | hoge | 2800 |
allpy 함수 사용시 주의점
데이터 레코드 행수가 하나도 없는 DataFrame의 경우 apply 결과에 컬럼이 작성되지 않습니다.
먼저 데이터가 있는 경우를 보겠습니다.
pd.DataFrame({'hoge': [1,1,3], 'fuga': [10, 20, 30]}).groupby('hoge').apply(np.sum) |
hoge | fuga | |
hoge | ||
1 | 2 | 30 |
3 | 3 |
이번에는 DataFrame에 데이터가 없는 경우를 보겠습니다.
pd.DataFrame({'hoge': [], 'fuga': []}).groupby('hoge').apply(np.sum) |
hoge |
'fuga' 컬럼이 안 만들어졌습니다.
apply를 사용하여 DataFrame을 처리를 하는 경우에 데이터가 존재하는지 확인을 먼저 해주는 것이 좋을거 같습니다.
안 그러면 apply 실행 후 값을 취득하려고 할 때 해당 컬럼명이 존재하지 않아 예상치 못한 에러가 발생할 수도 있습니다.
'코딩' 카테고리의 다른 글
Jupyter 주요 단축키 모음 (0) | 2021.07.26 |
---|---|
[SQL] INNER 조인과 OUTER조인이 무엇인가요?출처: https://stanleykou.tistory.com/entry/SQL-INNER-조인과-OUTER조인이-무엇인가요 [StanleyKou의 개인작업실] (0) | 2021.07.26 |
순서도(flowchart) (0) | 2021.07.13 |
주피터 노트북Jupyter Notebook 매직 명령어 (0) | 2021.07.13 |
[Python] pass, continue, break 차이점 (0) | 2021.07.13 |
댓글