고급 GroupBy 사용
고급 GroupBy 사용
● 그룹 변환과 GroupBy 객체 풀어내기
● 시계열 그룹 리샘플링
그룹 변환과 GroupBy 객체 풀어내기
transform 이라는 내장 메서드를 이용하면 apply 메서드와 유사하게 동작하면서도 사용할 수 있는 함수의 종류에 대해 좀 더 많은 제한을 포함할 수 있다.
. 그룹 형태로 브로드캐스트할 수 있는 스칼라값을 생성해야 한다.
. 입력 그룹과 같은 형태의 객체를 반환해야 한다.
. 입력을 변경하지 않아야 한다.
df = pd.DataFrame({'key':['a','b','c'] * 4, 'value':np.arange(12.)})
df
key에 따른 그룹의 평균을 구해보자.
g = df.groupby('key').value
g.mean()
key
a 4.5
b 5.5
c 6.5
Name: value, dtype: float64
df['value']와 같은 형태의 Series를 원한 것이 아니라 'key'에 따른 그룹의 평균값으로 값을 변경하기 원했다고 가정한다면 transform에 람다 함수 lambda x:x.mean()을 넘기면 된다.
g.transform(lambda x:x.mean())
0 4.5
1 5.5
2 6.5
3 4.5
4 5.5
5 6.5
6 4.5
7 5.5
8 6.5
9 4.5
10 5.5
11 6.5
Name: value, dtype: float64
내장 요약함수에 대해서는 agg 메서드에서처럼 문자열 그룹 연산 이름을 넘기면 된다.
g.transform('mean')
0 4.5
1 5.5
2 6.5
3 4.5
4 5.5
5 6.5
6 4.5
7 5.5
8 6.5
9 4.5
10 5.5
11 6.5
Name: value, dtype: float64
apply와 마찬가지로 transform은 Series를 반환하는 함수만 사용할 수 있지만 결과는 입력과 똑같은 크기여야 한다. 예를 들어 람다 함수를 이용해서 각 그룹에 모두 2를 곱할 수 있다.
g.transform(lambda x: x* 2)
0 0.0
1 2.0
2 4.0
3 6.0
4 8.0
5 10.0
6 12.0
7 14.0
8 16.0
9 18.0
10 20.0
11 22.0
Name: value, dtype: float64
좀 더 복핮ㅂ한 예제로, 각 그룹에 대해 내림차순으로 순위를 계산할 수도 있다.
g.transform(lambda x:x.rank(ascending=False))
0 4.0
1 4.0
2 4.0
3 3.0
4 3.0
5 3.0
6 2.0
7 2.0
8 2.0
9 1.0
10 1.0
11 1.0
Name: value, dtype: float64
간단한 요약을 통해 그룹 변환을 수행하는 함수를 살펴보자.
def normalize(x):
return (x - x.mean()) / x.std()
이 경우에는 transform이나 apply를 이용해서 같은 결과를 얻을 수 있다.
g.transform(normalize)
0 -1.161895
1 -1.161895
2 -1.161895
3 -0.387298
4 -0.387298
5 -0.387298
6 0.387298
7 0.387298
8 0.387298
9 1.161895
10 1.161895
11 1.161895
Name: value, dtype: float64
g.apply(normalize)
0 -1.161895
1 -1.161895
2 -1.161895
3 -0.387298
4 -0.387298
5 -0.387298
6 0.387298
7 0.387298
8 0.387298
9 1.161895
10 1.161895
11 1.161895
Name: value, dtype: float64
mean이나 sum 같은 내장 요약함수는 일반적인 apply 함수보다 더 빠르게 동작한다. 또한 이 함수들을 transform 과 함께 사용하면 뒤로 되돌릴 수 있는데 이를 통해 그룹 연산을 풀어낼 수 있다.
g.transform('mean')
normalized = (df['value'] - g.transform('mean')) / g.transform('std')
normalized
0 -1.161895
1 -1.161895
2 -1.161895
3 -0.387298
4 -0.387298
5 -0.387298
6 0.387298
7 0.387298
8 0.387298
9 1.161895
10 1.161895
11 1.161895
Name: value, dtype: float64
그룹 연산을 풀어내면 수차례의 그룹 연산을 수행하게 되지만 벡터 연산의 장점이 더 크다.
시계열 그룹 리샘플링
시계열 데이터에서 resample 메서드는 의미적으로 시간 간격에 기반한 그룹 연산이다.
N = 15
times = pd.date_range('2017-05-20 00:00',freq='1min', periods=N)
times
DatetimeIndex(['2017-05-20 00:00:00', '2017-05-20 00:01:00',
'2017-05-20 00:02:00', '2017-05-20 00:03:00',
'2017-05-20 00:04:00', '2017-05-20 00:05:00',
'2017-05-20 00:06:00', '2017-05-20 00:07:00',
'2017-05-20 00:08:00', '2017-05-20 00:09:00',
'2017-05-20 00:10:00', '2017-05-20 00:11:00',
'2017-05-20 00:12:00', '2017-05-20 00:13:00',
'2017-05-20 00:14:00'],
dtype='datetime64[ns]', freq='T')
df = pd.DataFrame({'time':times, 'value':np.arange(N)})
df
여기서 time으로 색인한 후 리샘플해보자.
df.set_index('time').resample('5min').count()
key 컬럼으로 구분되는 여러 시계열 데이터를 담고 있는 DataFrame을 생각해보자.
df2 = pd.DataFrame({'time':times.repeat(3),
'key':np.tile(['a','b','c'], N),
'value':np.arange(N*3.)})