10 minutes to pandas - Group by (split - apply - combine)
Group By 개요
Splitting the data into groups based on some criteria
Applying a function to each group independently
- Aggregation: 각 그룹에 대하여 요약통계를 처리할 수 있다.
- Transformation: 그룹 특화 연산을 수행하고, 전체 데이터에 대해 그 값을 적용할 수 있다.
- Filtration: 그룹 단위 연산으로 T/F 를 평가한다. True 인 대상을 추출할 수 있다.
Combining the results into a data structure.
Splitting an object into groups
Groupby 를 독립적으로 이해하는 것이 중요하다.
주요기능
# 컬럼명의 값 기준으로 그룹핑하기
grouped = df.groupby("A")
grouped = df.groupby(["A", "B"])
grouped = df.groupby("first")
grouped = df.groupby(["first", "second"])
grouped = df.groupby(["first", "A"])
# 인덱스 명/위치를 통해 인덱스의 값을 기준으로 그룹핑하기
grouped = df.groupby(level=0)
grouped = df.groupby(level=["first", "second"])
# 컬럼 및 인덱스의 값 기준으로 그룹핑하기
grouped = df.groupby([pd.Grouper(level=0), "A"])
grouped = df.groupby([pd.Grouper(freq="1M", key="Date"), "Buyer"]).sum()
# Date 컬럼으로 그룹핑할 경우, index 로 옮긴후 그룹핑하기
grouped = df.groupby([df.index.year, df.index.month])
주의. 만약 그룹의 기준에 NaN 또는 NaT 가 있다면, 자동적으로 배제된다. 즉 NA 그룹이나 NaT 그룹은 없다!
만약 NA 그룹이나 NaT 그룹이 필요하면 dropna = False 를 사용하자!
참고. pd.Grouper 는 특히 datetime-like object 에 대한 다양한 기능을 제공한다. 필요할 경우 더 공부해보자.
부가기능
# group by 속성
grouped = df.groupby("A", dropna=False)
grouped = df.groupby("A", as_index=False)
# Groupby Object 상세 확인
grouped.groups #그룹 이름과 행의 인덱스 배열 딕셔너리
grouped.ngroups # 몇 개의 그룹
grouped.get_group("bar") # 'bar 라는 이름을 가지는 그룹
for name, group in grouped: #그룹 이름과 그룹의 DataFrame Chunk
print(name)
print(group)
Applying
Aggregation
각 그룹에 대해 연산 (주로 통계치 계산) 을 수행할 수 있다. 스프레드시트의 Pivot Table 기능과 흡사하다!
주의. 그룹의 연산 대상에 NaN 값이 있을 경우 연산대상에서 제외된다.
grouped = df.groupby("A")
# 연산이 가능한 모든 컬럼에 대해서 다음 연산을 수행한다.
grouped.sum()
grouped.mean()
grouped.size()
grouped.describe()
grouped.nunique()
grouped.nlargest(3)
# 연산이 가능한 모든 컬럼에 대해서 연산을 1개 또는 N개 수행한다.
grouped.aggregate(np.sum)
grouped.aggregate([np.sum, np.mean, np.std])
# 사용자정의 함수를 수행한다.
grouped.agg(lambda x: x.max() - x.min())
grouped.agg([lambda x: x.max() - x.min(), lambda x: x.median() - x.mean()])
# df 의 특정 컬럼에 대해서만 연산을 수애한다.
grouped["C"].agg([np.sum, np.mean, np.std])
grouped[["C", "D"]].agg([np.sum, np.mean, np.std])
# df 의 컬럼마다 다른 연산을 수행한다.
grouped.agg({"C": np.sum, "D": lambda x: np.std()})
Transformation
전체 데이터에 그룹 단위의 연산값을 활용하여 특정 값을 변환할 수 있다.
# z score : 그룹의 평균과 표준편차 값을 가지고 z score 를 구한다.
index = pd.date_range("10/1/1999", periods=1100)
ts = pd.Series(np.random.normal(0.5, 2, 1100), index)
grouped = ts.groupby(lambda x: x.year)
grouped.transform(lambda x: (x - x.mean()) / x.std())
# fillna : NA 를 각 그룹의 평균값으로 채운다.
grouped = df.groupby('A')
grouped.transform(lambda x: x.fillna(x.mean()))
grouped.ffill()
Filteration
특정 조건을 만족하는 subset 만 추출할 수 있다.
# 그룹에 속한 값의 합이 4 초과인 그룹만 남긴다.
sf = pd.Series([1, 2, 2, 3, 3, 3, 4, 4, 4, 4])
sf.groupby(sf).filter(lambda x : x.sum() > 4)
# 그룹 크기가 2 초과인 그룹만 남긴다.
dff = pd.DataFrame({"A": np.arange(8), "B": list("aabbbbcc")})
dff.groupby('B').filter(lambda x : len(x) > 2)
응용
Flexible Apply
하나의 컬럼에 대해 값을 다양하게 변환하고 싶은 경우 다음과 같이 적용할 수 있다.
# A 컬럼 값을 기준으로 그룹핑한다. 그 중 C 컬럼에 대해 관심이 있다.
grouped = df.groupby('A')['C']
# group 을 인자로 받고, 새로운 DataFrame 을 제공하는 함수이다.
def f(group):
return pd.DataFrame({'original': group, # 원래값
'demeaned': group - group.mean()}) # 평균과의 거리(편차)
# GroupBy Object 에 대해 f 함수를 호출한다.
grouped.apply(f)
'''
original demeaned
0 -0.575247 -0.215962
1 0.254161 0.123181
2 -1.143704 -0.784420
3 0.215897 0.084917
4 1.193555 1.552839
'''
Piping Function Calls
Functions that take GroupBy objects can be chained together using a pipe method to allow for a cleaner, more readable syntax.
GroupBy Object 를 인자로 받아서 함수를 수행할 수 있다.
n = 1000
df = pd.DataFrame(
{
"Store": np.random.choice(["Store_1", "Store_2"], n),
"Product": np.random.choice(["Product_1", "Product_2"], n),
"Revenue": (np.random.random(n) * 50 + 10).round(2),
"Quantity": np.random.randint(1, 10, size=n),
}
)
'''
Store Product Revenue Quantity
0 Store_2 Product_1 26.12 1
1 Store_2 Product_1 28.86 1
'''
# 여러 컬럼의 값에 대한 연산을 활용하여 결과를 제공한다.
df.groupby(["Store", "Product"]).pipe(
lambda grp: grp.Revenue.sum() / grp.Quantity.sum()
).unstack().round(2)
'''
Product Product_1 Product_2
Store
Store_1 6.82 7.05
Store_2 6.30 6.64
'''
# GroupBy Object 에 대해 사용자 정의 함수를 처리할 수 있다.
def mean(groupby):
return groupby.mean()
df.groupby(["Store", "Product"]).pipe(mean)
'''
Revenue Quantity
Store Product
Store_1 Product_1 34.622727 5.075758
Product_2 35.482815 5.029630
Store_2 Product_1 32.972837 5.237589
Product_2 34.684360 5.224000
'''