Programing

그룹 객체에 변형 대 적용

crosscheck 2020. 6. 25. 08:15
반응형

그룹 객체에 변형 대 적용


다음 데이터 프레임을 고려하십시오.

     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)})

.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. 때문에 .applyA가 감소 반환 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

이것이 좀 더 명확 해지기를 바랍니다.


두 가지 주요 차이점 사이 applytransform

transformapplygroupby 메소드 에는 두 가지 주요 차이점이 있습니다.

  • apply각 그룹의 모든 열을 사용자 정의 함수에 DataFrame 으로 암시 적으로 전달 하는 한편 transform각 그룹의 각 열을 Series 로 사용자 정의 함수에 전달
  • 전달 된 사용자 지정 함수 apply는 스칼라 또는 Series 또는 DataFrame (또는 numpy 배열 또는 목록)을 반환 수 있습니다. 전달 된 사용자 정의 함수 transform는 그룹과 동일한 길이의 시퀀스 (1 차원 시리즈, 배열 또는 목록)를 반환해야합니다.

따라서 transform한 번에 하나의 Series에서만 apply작동하고 전체 DataFrame에서 한 번에 작동합니다.

사용자 정의 기능 검사

apply또는에 전달 된 사용자 정의 함수에 대한 입력을 검사하는 데 도움이 될 수 있습니다 transform.

샘플 데이터를 만들고 그룹을 검사하여 내가 말하는 것을 볼 수 있습니다.

df = pd.DataFrame({'State':['Texas', 'Texas', 'Florida', 'Florida'], 
                   'a':[4,5,1,3], 'b':[6,10,3,11]})
df

암시 적으로 전달 된 객체의 유형을 인쇄 한 다음 실행을 중지 할 수 있도록 오류를 발생시키는 간단한 사용자 정의 함수를 만들어 봅시다.

def inspect(x):
    print(type(x))
    raise

이제이 함수를 groupby applytransform메소드 모두에 전달하여 어떤 객체가 전달되는지 확인하십시오.

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이며 원래 인덱스가 유지되므로 약간 혼란 스럽지만 모든 열에 액세스 할 수 있습니다.


전달 된 팬더 객체 표시

사용자 정의 기능 내에서 전체 팬더 객체를 표시하는 데 도움이되므로 작동중인 것을 정확하게 볼 수 있습니다. 모듈 printdisplay함수 를 사용 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

Returning a single scalar object also works for transform

If you return just a single scalar from your custom function, then transform will use it for each of the rows in the group:

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

I am going to use a very simple snippet to illustrate the difference:

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']

The DataFrame looks like this:

    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   

There are 3 customer IDs in this table, each customer made three transactions and paid 1,2,3 dollars each time.

Now, I want to find the minimum payment made by each customer. There are two ways of doing it:

  1. Using apply:

    grouping.min()

The return looks like this:

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
  1. Using transform:

    grouping.transform(min)

The return looks like this:

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    

Both methods return a Series object, but the length of the first one is 3 and the length of the second one is 9.

If you want to answer What is the minimum price paid by each customer, then the apply method is the more suitable one to choose.

에 대답 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)

참고 URL : https://stackoverflow.com/questions/27517425/apply-vs-transform-on-a-group-object

반응형