파이썬에서 데이터 분석, 처리 할 때 많이 팬더스(Pandas) 사용합니다.
그중에서 groupby를 사용해야 하는 경우가 있어 정리하게 되었습니다.
Python Pandas groupby
라이브러리 import
import pandas as pd
import numpy as np
DataFrame으로 샘플 데이터 작성
import pandas as pd
import numpy as np
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]
})
print(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()
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를 사용하여 동시에 처리할 수 있습니다.
import pandas as pd
import numpy as np
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]
})
def my_mean(s):
return np.mean(s)
print(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 적용 안됨
import pandas as pd
import numpy as np
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]
})
print(df.groupby(['city', 'fruits'], as_index=False).apply(lambda d: (d.price * d.quantity).sum()))
결과
city fruits None
0 부산 apple 100
1 부산 banana 1950
2 부산 orange 400
3 서울 apple 1950
4 서울 banana 2800
apply 결과를 Series로 반환한 경우
・groupby로 작성한 label과 더해져 apply 함수 결과 index가 결과 전체의 row index가 됨.
・전체 행수는 결과에 따라 다름.
・as_index=False를 지정하여 index를 삭제 가능
import pandas as pd
import numpy as np
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]
})
def total_series(d):
return d.price * d.quantity
print(df.groupby(['city', 'fruits']).apply(total_series))
결과
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 사용 불가.
import pandas as pd
import numpy as np
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]
})
def total_keepindex(d):
return pd.DataFrame({
'total': d.price * d.quantity # 여기서 반환된 DataFrame row index와 d의 row index는 같음
})
print(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 사용 가능.
import pandas as pd
import numpy as np
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]
})
def total_keepnoindex(d):
return pd.DataFrame({
'total': (d.price * d.quantity).sum()
}, index=['hoge'])
print(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 30
이번에는 DataFrame에 데이터가 없는 경우를 보겠습니다.
pd.DataFrame({'hoge': [], 'fuga': []}).groupby('hoge').apply(np.sum)
결과
Empty DataFrame
Columns: [hoge, fuga]
Index: []
‘fuga’ 컬럼이 안 만들어졌습니다.
apply를 사용하여 DataFrame을 처리를 하는 경우에 데이터가 존재하는지 확인을 먼저 해주는 것이 좋을거 같습니다.
안 그러면 apply 실행 후 값을 취득하려고 할 때 해당 컬럼명이 존재하지 않아 예상치 못한 에러가 발생할 수도 있습니다.
댓글