다음 데이터 프레임을 고려하십시오.
A B C D
0 foo one 0.162003 0.087469
1 bar one -1.156319 -1.526272
2 foo two 0.833892 -1.666304
3 bar three -2.026673 -0.322057
4 foo two 0.411452 -0.954371
5 bar two 0.765878 -0.095968
6 foo one -0.654890 0.678091
7 foo three -1.789842 -1.130922
다음 명령이 작동합니다.
> df.groupby('A').apply(lambda x: (x['C'] - x['D']))
> df.groupby('A').apply(lambda x: (x['C'] - x['D']).mean())
그러나 다음 작업은 없습니다.
> df.groupby('A').transform(lambda x: (x['C'] - x['D']))
ValueError: could not broadcast input array from shape (5) into shape (5,3)
> df.groupby('A').transform(lambda x: (x['C'] - x['D']).mean())
TypeError: cannot concatenate a non-NDFrame object
왜? 문서의 예제transform
는 그룹 을 호출 하면 행 단위 작업 처리를 수행 할 수 있다고 제안합니다 .
# Note that the following suggests row-wise operation (x.mean is the column mean)
zscore = lambda x: (x - x.mean()) / x.std()
transformed = ts.groupby(key).transform(zscore)
다시 말해, 변환은 본질적으로 특정 유형의 적용 (집계되지 않는 적용)이라고 생각했습니다. 내가 어디 틀렸어?
참고로 아래는 위의 원래 데이터 프레임 구성입니다.
df = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar',
'foo', 'bar', 'foo', 'foo'],
'B' : ['one', 'one', 'two', 'three',
'two', 'two', 'one', 'three'],
'C' : randn(8), 'D' : randn(8)})
답변
두 가지 주요 차이점 사이 apply
와transform
transform
와 apply
groupby 메소드 에는 두 가지 주요 차이점이 있습니다.
- 입력:
apply
암시 적으로 각 그룹의 모든 열을 DataFrame 으로 사용자 지정 함수에 전달합니다.- 상태
transform
A와 개별적으로 각 그룹에 대해 각각의 열 전달 시리즈 맞춤 기능.
- 산출:
- 전달 된 사용자 지정 함수
apply
는 스칼라 또는 Series 또는 DataFrame (또는 numpy 배열 또는 목록)을 반환 할 수 있습니다 . - 전달 된 사용자 정의 함수 는 그룹과 길이가 같은
transform
시퀀스 (1 차원 시리즈, 배열 또는 목록)를 반환해야합니다 .
- 전달 된 사용자 지정 함수
따라서 transform
한 번에 하나의 Series에서만 apply
작동하고 전체 DataFrame에서 한 번에 작동합니다.
사용자 정의 기능 검사
apply
또는에 전달 된 사용자 정의 함수에 대한 입력을 검사하는 데 도움이 될 수 있습니다 transform
.
예
샘플 데이터를 만들고 그룹을 검사하여 내가 말하는 것을 볼 수 있습니다.
import pandas as pd
import numpy as np
df = pd.DataFrame({'State':['Texas', 'Texas', 'Florida', 'Florida'],
'a':[4,5,1,3], 'b':[6,10,3,11]})
State a b
0 Texas 4 6
1 Texas 5 10
2 Florida 1 3
3 Florida 3 11
암시 적으로 전달 된 객체의 유형을 인쇄 한 다음 오류를 발생시켜 실행을 중지 할 수있는 간단한 사용자 지정 함수를 만들어 보겠습니다.
def inspect(x):
print(type(x))
raise
이제이 함수를 groupby apply
와 transform
메소드 모두에 전달하여 어떤 객체가 전달되는지 확인하십시오.
df.groupby('State').apply(inspect)
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>
RuntimeError
보시다시피 DataFrame은 inspect
함수 로 전달됩니다 . 왜 DataFrame 유형이 두 번 인쇄되었는지 궁금 할 것입니다. 팬더는 첫 번째 그룹을 두 번 실행합니다. 계산을 완료하는 빠른 방법이 있는지 여부를 판별하기 위해이를 수행합니다. 걱정할 필요가없는 사소한 세부 사항입니다.
자, 같은 작업을하겠습니다 transform
df.groupby('State').transform(inspect)
<class 'pandas.core.series.Series'>
<class 'pandas.core.series.Series'>
RuntimeError
완전히 다른 Pandas 객체 인 Series를 전달합니다.
따라서 transform
한 번에 하나의 시리즈로만 작업 할 수 있습니다. 그것은 것입니다 하지 가 동시에 두 개의 열을 행동 불가능. 따라서 사용자 정의 함수 내부 a
에서 열 을 빼려고 b
하면에 오류가 발생합니다 transform
. 아래를보십시오 :
def subtract_two(x):
return x['a'] - x['b']
df.groupby('State').transform(subtract_two)
KeyError: ('a', 'occurred at index a')
팬더가 a
존재하지 않는 Series 인덱스를 찾으려고 할 때 KeyError 가 발생합니다. apply
전체 DataFrame이 있으므로이 작업을 완료 할 수 있습니다 .
df.groupby('State').apply(subtract_two)
State
Florida 2 -2
3 -8
Texas 0 -2
1 -5
dtype: int64
출력은 Series이며 원래 인덱스가 유지되므로 약간 혼란 스럽지만 모든 열에 액세스 할 수 있습니다.
전달 된 팬더 객체 표시
사용자 정의 기능 내에서 전체 팬더 객체를 표시하는 데 도움이되므로 작동중인 것을 정확하게 볼 수 있습니다. 당신이 사용할 수있는 print
사용에처럼에 의해 문을 display
로부터 기능을 IPython.display
DataFrames가 멋지게 jupyter 노트북에 HTML에 출력 얻을 그래서 모듈 :
from IPython.display import display
def subtract_two(x):
display(x)
return x['a'] - x['b']
변환은 그룹과 동일한 크기의 단일 차원 시퀀스를 반환해야합니다.
다른 차이점은 transform
그룹과 동일한 크기의 단일 차원 시퀀스를 반환해야 한다는 것 입니다. 이 특정 인스턴스에서 각 그룹에는 두 개의 행이 있으므로 transform
두 개의 행 시퀀스를 리턴해야합니다. 그렇지 않으면 오류가 발생합니다.
def return_three(x):
return np.array([1, 2, 3])
df.groupby('State').transform(return_three)
ValueError: transform must return a scalar value for each group
오류 메시지는 실제로 문제를 설명하지 않습니다. 그룹과 동일한 길이의 시퀀스를 반환해야합니다. 따라서 이와 같은 기능이 작동합니다.
def rand_group_len(x):
return np.random.rand(len(x))
df.groupby('State').transform(rand_group_len)
a b
0 0.962070 0.151440
1 0.440956 0.782176
2 0.642218 0.483257
3 0.056047 0.238208
단일 스칼라 객체를 반환해도 작동합니다. transform
사용자 정의 함수에서 단일 스칼라 만 반환 transform
하면 그룹의 각 행에 대해 스칼라를 사용합니다.
def group_sum(x):
return x.sum()
df.groupby('State').transform(group_sum)
a b
0 9 16
1 9 16
2 4 14
3 4 14
답변
.transform
작업과 비슷하게 혼란스러워 .apply
하면서 문제에 대한 약간의 답변을 발견했습니다. 예를 들어이 답변 은 매우 도움이되었습니다.
지금까지의 테이크 아웃 은 서로 분리되어.transform
작동 (또는 Series
열) 할 것 입니다 . 이것이 의미하는 것은 마지막 두 번의 호출에서입니다.
df.groupby('A').transform(lambda x: (x['C'] - x['D']))
df.groupby('A').transform(lambda x: (x['C'] - x['D']).mean())
.transform
두 열에서 값을 가져 오라고 요청 했지만 실제로는 두 열을 동시에 볼 수 없습니다. transform
데이터 프레임 열을 하나씩보고 반복되는 반복되는 스칼라로 구성된 시리즈 (또는 시리즈 그룹)를 다시 반환 len(input_column)
합니다.
사용해야이 스칼라이므로 .transform
하게는 Series
일부 감소 함수의 결과는 입력에인가 Series
(한 번에 단지 하나 시리즈 / 열).
데이터 프레임에서이 예제를 고려하십시오.
zscore = lambda x: (x - x.mean()) / x.std() # Note that it does not reference anything outside of 'x' and for transform 'x' is one column.
df.groupby('A').transform(zscore)
산출 할 것이다 :
C D
0 0.989 0.128
1 -0.478 0.489
2 0.889 -0.589
3 -0.671 -1.150
4 0.034 -0.285
5 1.149 0.662
6 -1.404 -0.907
7 -0.509 1.653
한 번에 하나의 열에서만 사용하는 것과 정확히 동일합니다.
df.groupby('A')['C'].transform(zscore)
굽힐 수 있는:
0 0.989
1 -0.478
2 0.889
3 -0.671
4 0.034
5 1.149
6 -1.404
7 -0.509
참고 것을 .apply
마지막 예에서 ( df.groupby('A')['C'].apply(zscore)
) 정확히 같은 방식으로 작동합니다,하지만 당신은 dataframe에 그것을 사용하려고한다면 실패 할 것이다 :
df.groupby('A').apply(zscore)
오류를 준다 :
ValueError: operands could not be broadcast together with shapes (6,) (2,)
다른 .transform
유용한 곳은 어디 입니까? 가장 간단한 경우는 축소 함수의 결과를 원래 데이터 프레임에 다시 할당하는 것입니다.
df['sum_C'] = df.groupby('A')['C'].transform(sum)
df.sort('A') # to clearly see the scalar ('sum') applies to the whole column of the group
굽힐 수 있는:
A B C D sum_C
1 bar one 1.998 0.593 3.973
3 bar three 1.287 -0.639 3.973
5 bar two 0.687 -1.027 3.973
4 foo two 0.205 1.274 4.373
2 foo two 0.128 0.924 4.373
6 foo one 2.113 -0.516 4.373
7 foo three 0.657 -1.179 4.373
0 foo one 1.270 0.201 4.373
와 같은 시도는 .apply
줄 것이라고 NaNs
에서 sum_C
. 때문에 .apply
A가 감소 반환 Series
이 다시 방송하는 방법을 알고하지 않는 :
df.groupby('A')['C'].apply(sum)
기부:
A
bar 3.973
foo 4.373
.transform
데이터를 필터링하는 데 사용되는 경우도 있습니다 .
df[df.groupby(['B'])['D'].transform(sum) < -1]
A B C D
3 bar three 1.287 -0.639
7 foo three 0.657 -1.179
이것이 좀 더 명확 해지기를 바랍니다.
답변
차이점을 설명하기 위해 매우 간단한 스 니펫을 사용하겠습니다.
test = pd.DataFrame({'id':[1,2,3,1,2,3,1,2,3], 'price':[1,2,3,2,3,1,3,1,2]})
grouping = test.groupby('id')['price']
DataFrame은 다음과 같습니다.
id price
0 1 1
1 2 2
2 3 3
3 1 2
4 2 3
5 3 1
6 1 3
7 2 1
8 3 2
이 표에는 3 개의 고객 ID가 있으며, 각 고객은 3 번의 거래를하고 매번 1,2,3 달러를 지불했습니다.
이제 각 고객의 최소 결제 금액을 찾고 싶습니다. 이를 수행하는 두 가지 방법이 있습니다.
-
사용
apply
:grouping.min ()
리턴은 다음과 같습니다.
id
1 1
2 1
3 1
Name: price, dtype: int64
pandas.core.series.Series # return type
Int64Index([1, 2, 3], dtype='int64', name='id') #The returned Series' index
# lenght is 3
-
사용
transform
:그룹화 변환 (분)
리턴은 다음과 같습니다.
0 1
1 1
2 1
3 1
4 1
5 1
6 1
7 1
8 1
Name: price, dtype: int64
pandas.core.series.Series # return type
RangeIndex(start=0, stop=9, step=1) # The returned Series' index
# length is 9
두 방법 모두 Series
객체를 반환 하지만 length
첫 번째 방법은 3이고 length
두 번째 방법은 9입니다.
에 대답 What is the minimum price paid by each customer
하려면 apply
방법이 더 적합합니다.
에 대답 What is the difference between the amount paid for each transaction vs the minimum payment
하려면 다음을 사용하십시오 transform
.
test['minimum'] = grouping.transform(min) # ceates an extra column filled with minimum payment
test.price - test.minimum # returns the difference for each row
Apply
여기서는 Series 3 크기를 반환하기 때문에 여기서는 작동하지 않지만 원본 df의 길이는 9입니다. 원본 df에 쉽게 통합 할 수 없습니다.
답변
tmp = df.groupby(['A'])['c'].transform('mean')
처럼
tmp1 = df.groupby(['A']).agg({'c':'mean'})
tmp = df['A'].map(tmp1['c'])
또는
tmp1 = df.groupby(['A'])['c'].mean()
tmp = df['A'].map(tmp1)