[python] 여러 그룹 별 열에 여러 함수 적용

문서 는 출력 열 이름을 키로 사용하는 dict를 사용하여 한 번에 groupby 객체에 여러 기능을 적용하는 방법을 보여줍니다.

In [563]: grouped['D'].agg({'result1' : np.sum,
   .....:                   'result2' : np.mean})
   .....:
Out[563]:
      result2   result1
A
bar -0.579846 -1.739537
foo -0.280588 -1.402938

그러나 이것은 Series groupby 객체에서만 작동합니다. 또한 dict이 DataFrame에 의해 그룹으로 유사하게 전달되면 키가 함수가 적용되는 열 이름이 될 것으로 예상합니다.

내가하고 싶은 것은 여러 열에 여러 기능을 적용하는 것입니다 (그러나 특정 열은 여러 번 운영됩니다). 또한 일부 함수는 sumby 함수와 같은 groupby 객체의 다른 열에 따라 다릅니다 . 내 현재 솔루션은 열별로 이동하고 다른 행에 의존하는 함수에 람다를 사용하여 위의 코드와 같은 작업을 수행하는 것입니다. 그러나 이것은 오랜 시간이 걸립니다 (그룹 별 객체를 반복하는 데 시간이 오래 걸린다고 생각합니다). 한 번의 실행으로 전체 그룹 별 객체를 반복하도록 변경해야하지만 팬더에이를 깨끗하게 수행하는 방법이 내장되어 있는지 궁금합니다.

예를 들어, 나는 다음과 같은 것을 시도했다.

grouped.agg({'C_sum' : lambda x: x['C'].sum(),
             'C_std': lambda x: x['C'].std(),
             'D_sum' : lambda x: x['D'].sum()},
             'D_sumifC3': lambda x: x['D'][x['C'] == 3].sum(), ...)

그러나 예상대로 KeyError가 발생합니다 ( aggDataFrame에서 호출 되면 키가 열 이어야하기 때문에).

내가하고 싶은 일을 수행하는 방법이 있거나이 기능이 추가 될 가능성이 있습니까? 아니면 그룹별로 수동으로 반복해야합니까?

감사



답변

현재 허용되는 답변의 후반부는 구식이며 두 가지 더 이상 사용되지 않습니다. 가장 중요한 것은 더 이상 사전 사전을 agggroupby 메소드에 전달할 수 없다는 것입니다. 둘째, 절대 사용하지 마십시오 .ix.

두 개의 별도 열을 동시에 사용하려면 applyDataFrame을 적용된 함수에 암시 적으로 전달하는 메서드를 사용하는 것이 좋습니다 . 위에서와 비슷한 데이터 프레임을 사용합시다

df = pd.DataFrame(np.random.rand(4,4), columns=list('abcd'))
df['group'] = [0, 0, 1, 1]
df

          a         b         c         d  group
0  0.418500  0.030955  0.874869  0.145641      0
1  0.446069  0.901153  0.095052  0.487040      0
2  0.843026  0.936169  0.926090  0.041722      1
3  0.635846  0.439175  0.828787  0.714123      1

열 이름에서 집계 함수로 매핑 된 사전은 여전히 ​​집계를 수행하는 데 가장 좋은 방법입니다.

df.groupby('group').agg({'a':['sum', 'max'],
                         'b':'mean',
                         'c':'sum',
                         'd': lambda x: x.max() - x.min()})

              a                   b         c         d
            sum       max      mean       sum  <lambda>
group
0      0.864569  0.446069  0.466054  0.969921  0.341399
1      1.478872  0.843026  0.687672  1.754877  0.672401

못생긴 람다 열 이름이 마음에 들지 않으면 일반 함수를 사용하고 다음과 __name__같이 특수 속성에 사용자 지정 이름을 제공 할 수 있습니다 .

def max_min(x):
    return x.max() - x.min()

max_min.__name__ = 'Max minus Min'

df.groupby('group').agg({'a':['sum', 'max'],
                         'b':'mean',
                         'c':'sum',
                         'd': max_min})

              a                   b         c             d
            sum       max      mean       sum Max minus Min
group
0      0.864569  0.446069  0.466054  0.969921      0.341399
1      1.478872  0.843026  0.687672  1.754877      0.672401

apply시리즈 사용 및 반환

이제 서로 상호 작용해야하는 여러 열이 있으면를 사용할 수 없습니다 agg.이 함수는 Series를 암시 적으로 집계 함수에 전달합니다. 사용하는 경우 apply전체 그룹 DataFrame은 함수에 전달됩니다있다.

일련의 모든 집계를 반환하는 단일 사용자 지정 함수를 만드는 것이 좋습니다. 시리즈 색인을 새 열의 레이블로 사용하십시오.

def f(x):
    d = {}
    d['a_sum'] = x['a'].sum()
    d['a_max'] = x['a'].max()
    d['b_mean'] = x['b'].mean()
    d['c_d_prodsum'] = (x['c'] * x['d']).sum()
    return pd.Series(d, index=['a_sum', 'a_max', 'b_mean', 'c_d_prodsum'])

df.groupby('group').apply(f)

         a_sum     a_max    b_mean  c_d_prodsum
group
0      0.864569  0.446069  0.466054     0.173711
1      1.478872  0.843026  0.687672     0.630494

MultiIndexes를 좋아한다면 다음과 같이 Series를 반환 할 수 있습니다.

    def f_mi(x):
        d = []
        d.append(x['a'].sum())
        d.append(x['a'].max())
        d.append(x['b'].mean())
        d.append((x['c'] * x['d']).sum())
        return pd.Series(d, index=[['a', 'a', 'b', 'c_d'],
                                   ['sum', 'max', 'mean', 'prodsum']])

df.groupby('group').apply(f_mi)

              a                   b       c_d
            sum       max      mean   prodsum
group
0      0.864569  0.446069  0.466054  0.173711
1      1.478872  0.843026  0.687672  0.630494


답변

첫 번째 부분에서는 키에 대한 열 이름과 값에 대한 함수 목록을 전달할 수 있습니다.

In [28]: df
Out[28]:
          A         B         C         D         E  GRP
0  0.395670  0.219560  0.600644  0.613445  0.242893    0
1  0.323911  0.464584  0.107215  0.204072  0.927325    0
2  0.321358  0.076037  0.166946  0.439661  0.914612    1
3  0.133466  0.447946  0.014815  0.130781  0.268290    1

In [26]: f = {'A':['sum','mean'], 'B':['prod']}

In [27]: df.groupby('GRP').agg(f)
Out[27]:
            A                   B
          sum      mean      prod
GRP
0    0.719580  0.359790  0.102004
1    0.454824  0.227412  0.034060

업데이트 1 :

집계 함수는 Series에서 작동하므로 다른 열 이름에 대한 참조는 손실됩니다. 이 문제를 해결하려면 전체 데이터 프레임을 참조하고 람다 함수 내의 그룹 색인을 사용하여 색인화하십시오.

해키 해결 방법은 다음과 같습니다.

In [67]: f = {'A':['sum','mean'], 'B':['prod'], 'D': lambda g: df.loc[g.index].E.sum()}

In [69]: df.groupby('GRP').agg(f)
Out[69]:
            A                   B         D
          sum      mean      prod  <lambda>
GRP
0    0.719580  0.359790  0.102004  1.170219
1    0.454824  0.227412  0.034060  1.182901

여기서 결과 ‘D’열은 합산 된 ‘E’값으로 구성됩니다.

업데이트 2 :

여기에 내가 원하는 모든 것을 할 것이라고 생각되는 방법이 있습니다. 먼저 커스텀 람다 함수를 만드십시오. 아래에서 g는 그룹을 나타냅니다. 집계 할 때 g는 시리즈가됩니다. df에서 현재 그룹 g.indexdf.ix[]선택하여 전달 합니다 . 그런 다음 C 열이 0.5보다 작은 지 테스트합니다. 반환 된 부울 계열이 전달되어 g[]조건을 충족하는 행만 선택합니다.

In [95]: cust = lambda g: g[df.loc[g.index]['C'] < 0.5].sum()

In [96]: f = {'A':['sum','mean'], 'B':['prod'], 'D': {'my name': cust}}

In [97]: df.groupby('GRP').agg(f)
Out[97]:
            A                   B         D
          sum      mean      prod   my name
GRP
0    0.719580  0.359790  0.102004  0.204072
1    0.454824  0.227412  0.034060  0.570441


답변

Ted Petrou의 답변에 대한 대안 (주로 미학에 대한 대안)으로, 나는 조금 더 작은 목록을 선호한다는 것을 알았습니다. 수락하는 것을 고려하지 마십시오. Ted의 답변과 코드 / 데이터에 대한 자세한 설명 일뿐입니다. 파이썬 / 팬더는 내 첫 번째 / 최고가 아니지만 잘 읽습니다.

df.groupby('group') \
  .apply(lambda x: pd.Series({
      'a_sum'       : x['a'].sum(),
      'a_max'       : x['a'].max(),
      'b_mean'      : x['b'].mean(),
      'c_d_prodsum' : (x['c'] * x['d']).sum()
  })
)

          a_sum     a_max    b_mean  c_d_prodsum
group
0      0.530559  0.374540  0.553354     0.488525
1      1.433558  0.832443  0.460206     0.053313

dplyr파이프와 data.table연쇄 명령을 연상시키는 것으로 나타 났습니다. 그들이 더 나아 졌다고 말하지 않고, 나에게 더 친숙합니다. (나는 분명히 def이러한 유형의 작업에 더 공식화 된 기능을 사용하는 힘과 많은 사람들의 선호를 인식하고 있습니다. 이것은 반드시 대안 일 뿐이며 반드시 더 나은 것은 아닙니다.)


Ted와 같은 방식으로 데이터를 생성했습니다. 재현성을 위해 시드를 추가하겠습니다.

import numpy as np
np.random.seed(42)
df = pd.DataFrame(np.random.rand(4,4), columns=list('abcd'))
df['group'] = [0, 0, 1, 1]
df

          a         b         c         d  group
0  0.374540  0.950714  0.731994  0.598658      0
1  0.156019  0.155995  0.058084  0.866176      0
2  0.601115  0.708073  0.020584  0.969910      1
3  0.832443  0.212339  0.181825  0.183405      1


답변

Pandas >= 0.25.0, 명명 된 집계

팬더 버전 0.25.0이상 이므로 사전 기반 집계 및 이름 변경에서 벗어나 이름이 지정된 집계로 이동 하고 tuple있습니다. 이제 더 많은 정보를 제공하는 열 이름으로 집계 + 이름 바꾸기를 동시에 수행 할 수 있습니다.

:

df = pd.DataFrame(np.random.rand(4,4), columns=list('abcd'))
df['group'] = [0, 0, 1, 1]

          a         b         c         d  group
0  0.521279  0.914988  0.054057  0.125668      0
1  0.426058  0.828890  0.784093  0.446211      0
2  0.363136  0.843751  0.184967  0.467351      1
3  0.241012  0.470053  0.358018  0.525032      1

GroupBy.agg명명 된 집계로 적용 :

df.groupby('group').agg(
             a_sum=('a', 'sum'),
             a_mean=('a', 'mean'),
             b_mean=('b', 'mean'),
             c_sum=('c', 'sum'),
             d_range=('d', lambda x: x.max() - x.min())
)

          a_sum    a_mean    b_mean     c_sum   d_range
group
0      0.947337  0.473668  0.871939  0.838150  0.320543
1      0.604149  0.302074  0.656902  0.542985  0.057681


답변

버전 0.25.0의 새로운 기능.

출력 열 이름을 제어하여 열별 집계를 지원하기 위해 pandas는 GroupBy.agg () 에서 “named aggregation”이라는 특수 구문을 허용합니다 . 여기서

  • 키워드는 출력 열 이름입니다.
  • 값은 첫 번째 요소가 선택할 열이고 두 번째 요소가 해당 열에 적용 할 집계 인 튜플입니다. Pandas는 pandas.NamedAgg라는 이름의 tuple을 필드 [ ‘column’, ‘aggfunc’]와 함께 제공하여 인수가 무엇인지 더 명확하게 만듭니다. 일반적으로 집계는 호출 가능 또는 문자열 별명 일 수 있습니다.
    In [79]: animals = pd.DataFrame({'kind': ['cat', 'dog', 'cat', 'dog'],
       ....:                         'height': [9.1, 6.0, 9.5, 34.0],
       ....:                         'weight': [7.9, 7.5, 9.9, 198.0]})
       ....:

    In [80]: animals
    Out[80]:
      kind  height  weight
    0  cat     9.1     7.9
    1  dog     6.0     7.5
    2  cat     9.5     9.9
    3  dog    34.0   198.0

    In [81]: animals.groupby("kind").agg(
       ....:     min_height=pd.NamedAgg(column='height', aggfunc='min'),
       ....:     max_height=pd.NamedAgg(column='height', aggfunc='max'),
       ....:     average_weight=pd.NamedAgg(column='weight', aggfunc=np.mean),
       ....: )
       ....:
    Out[81]:
          min_height  max_height  average_weight
    kind
    cat          9.1         9.5            8.90
    dog          6.0        34.0          102.75

pandas.NamedAgg는 단지 명명 된 튜플입니다. 일반 튜플도 허용됩니다.

    In [82]: animals.groupby("kind").agg(
       ....:     min_height=('height', 'min'),
       ....:     max_height=('height', 'max'),
       ....:     average_weight=('weight', np.mean),
       ....: )
       ....:
    Out[82]:
          min_height  max_height  average_weight
    kind
    cat          9.1         9.5            8.90
    dog          6.0        34.0          102.75

추가 키워드 인수는 집계 함수로 전달되지 않습니다. (열, aggfunc) 쌍만 ** kwargs로 전달해야합니다. 집계 함수에 추가 인수가 필요한 경우 functools.partial ()을 사용하여 인수를 부분적으로 적용하십시오.

명명 된 집계는 시리즈 그룹 별 집계에도 유효합니다. 이 경우 열 선택이 없으므로 값은 함수일뿐입니다.

    In [84]: animals.groupby("kind").height.agg(
       ....:     min_height='min',
       ....:     max_height='max',
       ....: )
       ....:
    Out[84]:
          min_height  max_height
    kind
    cat          9.1         9.5
    dog          6.0        34.0


답변

테드의 대답은 놀랍습니다. 누군가 관심이 있으시면 더 작은 버전을 사용했습니다. 여러 열의 값에 따라 하나의 집계를 찾을 때 유용합니다.

데이터 프레임 만들기

df=pd.DataFrame({'a': [1,2,3,4,5,6], 'b': [1,1,0,1,1,0], 'c': ['x','x','y','y','z','z']})


   a  b  c
0  1  1  x
1  2  1  x
2  3  0  y
3  4  1  y
4  5  1  z
5  6  0  z

apply를 사용하여 그룹화 및 집계 (여러 열 사용)

df.groupby('c').apply(lambda x: x['a'][(x['a']>1) & (x['b']==1)].mean())

c
x    2.0
y    4.0
z    5.0

집계를 사용하여 그룹화 및 집계 (여러 열 사용)

나는 여전히 집계를 사용할 수 있기 때문에이 접근법을 좋아합니다. 아마도 사람들은 그룹에서 집계를 수행 할 때 여러 열을 가져 오기 위해 apply가 필요한 이유를 알려줄 것입니다.

지금은 분명해 보이지만 groupby 바로 다음 에 관심있는 열을 선택하지 않으면 집계 함수 내에서 데이터 프레임의 모든 열에 액세스 할 수 있습니다.

선택한 열에 만 액세스

df.groupby('c')['a'].aggregate(lambda x: x[x>1].mean())

선택은 모든 마법 이후 모든 열에 액세스

df.groupby('c').aggregate(lambda x: x[(x['a']>1) & (x['b']==1)].mean())['a']

또는 유사하게

df.groupby('c').aggregate(lambda x: x['a'][(x['a']>1) & (x['b']==1)].mean())

이게 도움이 되길 바란다.


답변