Programing

Pandas 집계 함수에서 반환 된 열의 이름을 지정 하시겠습니까?

crosscheck 2020. 11. 10. 07:51
반응형

Pandas 집계 함수에서 반환 된 열의 이름을 지정 하시겠습니까?


이 질문에 이미 답변이 있습니다.

Pandas의 groupby 기능에 문제가 있습니다. 내가 읽은 문서를 ,하지만 난 여러 컬럼에 집계 함수를 적용하는 방법을 알아 내기 위해 볼 수 그 열에 대한 사용자 정의 이름이 있습니다.

이것은 매우 비슷하지만 반환 된 데이터 구조에는 중첩 된 열 제목이 있습니다.

data.groupby("Country").agg(
        {"column1": {"foo": sum()}, "column2": {"mean": np.mean, "std": np.std}})

(예 : column2의 평균과 표준을 취하고 싶지만 해당 열을 "평균"및 "표준"으로 반환)

내가 무엇을 놓치고 있습니까?


이렇게하면 계층 열 인덱스에서 가장 바깥 쪽 수준이 삭제됩니다.

df = data.groupby(...).agg(...)
df.columns = df.columns.droplevel(0)

가장 바깥 쪽 수준을 유지하려면 다중 수준 열에서 ravel () 함수를 사용하여 새 레이블을 만들 수 있습니다.

df.columns = ["_".join(x) for x in df.columns.ravel()]

예를 들면 :

import pandas as pd
import pandas.rpy.common as com
import numpy as np

data = com.load_data('Loblolly')
print(data.head())
#     height  age Seed
# 1     4.51    3  301
# 15   10.89    5  301
# 29   28.72   10  301
# 43   41.74   15  301
# 57   52.70   20  301

df = data.groupby('Seed').agg(
    {'age':['sum'],
     'height':['mean', 'std']})
print(df.head())
#       age     height           
#       sum        std       mean
# Seed                           
# 301    78  22.638417  33.246667
# 303    78  23.499706  34.106667
# 305    78  23.927090  35.115000
# 307    78  22.222266  31.328333
# 309    78  23.132574  33.781667

df.columns = df.columns.droplevel(0)
print(df.head())

수확량

      sum        std       mean
Seed                           
301    78  22.638417  33.246667
303    78  23.499706  34.106667
305    78  23.927090  35.115000
307    78  22.222266  31.328333
309    78  23.132574  33.781667

또는 색인의 첫 번째 레벨을 유지하려면 다음을 수행하십시오.

df = data.groupby('Seed').agg(
    {'age':['sum'],
     'height':['mean', 'std']})
df.columns = ["_".join(x) for x in df.columns.ravel()]

수확량

      age_sum   height_std  height_mean
Seed                           
301        78    22.638417    33.246667
303        78    23.499706    34.106667
305        78    23.927090    35.115000
307        78    22.222266    31.328333
309        78    23.132574    33.781667

판다> = 0.25

반환 된 집계 열의 이름을 지정하는 기능 은 master 브랜치에서 다시 도입되었으며 Pandas 0.25를 대상으로합니다. 새로운 구문은 .agg(new_col_name=('col_name', 'agg_func')입니다. 위에 링크 된 PR의 자세한 예 :

In [2]: df = 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 [3]: df
Out[3]:
  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 [4]: df.groupby('kind').agg(min_height=('height', 'min'), 
                               max_weight=('weight', 'max'))
Out[4]:
      min_height  max_weight
kind
cat          9.1         9.9
dog          6.0       198.0

이 구문 과이 PR에 따라 이전에 제안한 2 단계 이름 바꾸기 구문 (아래)을 사용하여 여러 람다 식을 사용할 수도 있습니다 . 다시 PR의 예에서 복사합니다.

In [2]: df = pd.DataFrame({"A": ['a', 'a'], 'B': [1, 2], 'C': [3, 4]})

In [3]: df.groupby("A").agg({'B': [lambda x: 0, lambda x: 1]})
Out[3]:
         B
  <lambda> <lambda 1>
A
a        0          1

그런 다음 .rename()또는 한 번에 :

In [4]: df.groupby("A").agg(b=('B', lambda x: 0), c=('B', lambda x: 1))
Out[4]:
   b  c
A
a  0  0

판다 <0.25

unutbu에서 현재 받아 들여지는 답변은 pandas 버전 <= 0.20에서 이것을 수행하는 좋은 방법입니다. 그러나 Pandas 0.20부터이 메서드를 사용하면 이후 버전의 Pandas에서 구문을 사용할 수 없다는 경고가 발생합니다.

시리즈:

FutureWarning : 집계를 위해 Series에서 dict를 사용하는 것은 더 이상 사용되지 않으며 향후 버전에서 제거됩니다.

데이터 프레임 :

FutureWarning : dict를 이름 변경과 함께 사용하는 것은 더 이상 사용되지 않으며 향후 버전에서 제거됩니다.

Pandas 0.20 changelog 에 따르면 집계하는 동안 열 이름을 바꾸는 권장 방법은 다음과 같습니다.

# Create a sample data frame
df = pd.DataFrame({'A': [1, 1, 1, 2, 2],
                   'B': range(5),
                   'C': range(5)})

# ==== SINGLE COLUMN (SERIES) ====
# Syntax soon to be deprecated
df.groupby('A').B.agg({'foo': 'count'})
# Recommended replacement syntax
df.groupby('A').B.agg(['count']).rename(columns={'count': 'foo'})

# ==== MULTI COLUMN ====
# Syntax soon to be deprecated
df.groupby('A').agg({'B': {'foo': 'sum'}, 'C': {'bar': 'min'}})
# Recommended replacement syntax
df.groupby('A').agg({'B': 'sum', 'C': 'min'}).rename(columns={'B': 'foo', 'C': 'bar'})
# As the recommended syntax is more verbose, parentheses can
# be used to introduce line breaks and increase readability
(df.groupby('A')
    .agg({'B': 'sum', 'C': 'min'})
    .rename(columns={'B': 'foo', 'C': 'bar'})
)

자세한 내용은 0.20 변경 로그 를 참조하십시오.

@JunkMechanic의 의견에 대한 응답으로 2017-01-03을 업데이트하십시오.

With the old style dictionary syntax, it was possible to pass multiple lambda functions to .agg, since these would be renamed with the key in the passed dictionary:

>>> df.groupby('A').agg({'B': {'min': lambda x: x.min(), 'max': lambda x: x.max()}})

    B    
  max min
A        
1   2   0
2   4   3

Multiple functions can also be passed to a single column as a list:

>>> df.groupby('A').agg({'B': [np.min, np.max]})

     B     
  amin amax
A          
1    0    2
2    3    4

However, this does not work with lambda functions, since they are anonymous and all return <lambda>, which causes a name collision:

>>> df.groupby('A').agg({'B': [lambda x: x.min(), lambda x: x.max]})
SpecificationError: Function names must be unique, found multiple named <lambda>

To avoid the SpecificationError, named functions can be defined a priori instead of using lambda. Suitable function names also avoid calling .rename on the data frame afterwards. These functions can be passed with the same list syntax as above:

>>> def my_min(x):
>>>     return x.min()

>>> def my_max(x):
>>>     return x.max()

>>> df.groupby('A').agg({'B': [my_min, my_max]})

       B       
  my_min my_max
A              
1      0      2
2      3      4

If you want to have a behavior similar to JMP, creating column titles that keep all info from the multi index you can use:

newidx = []
for (n1,n2) in df.columns.ravel():
    newidx.append("%s-%s" % (n1,n2))
df.columns=newidx

It will change your dataframe from:

    I                       V
    mean        std         first
V
4200.0  25.499536   31.557133   4200.0
4300.0  25.605662   31.678046   4300.0
4400.0  26.679005   32.919996   4400.0
4500.0  26.786458   32.811633   4500.0

to

    I-mean      I-std       V-first
V
4200.0  25.499536   31.557133   4200.0
4300.0  25.605662   31.678046   4300.0
4400.0  26.679005   32.919996   4400.0
4500.0  26.786458   32.811633   4500.0

I agree with the OP that it seems more natural and consistent to name and define the output columns in the same place (e.g. as is done with tidyverse's summarize in R), but a work-around in pandas for now is to create the new columns with desired names via assign before doing the aggregation:

data.assign(
    f=data['column1'],
    mean=data['column2'],
    std=data['column2']
).groupby('Country').agg(dict(f=sum, mean=np.mean, std=np.std)).reset_index()

(Using reset_index turns 'Country', 'f', 'mean', and 'std' all into regular columns with a separate integer index.)


With the inspiration of @Joel Ostblom

For those who already have a workable dictionary for merely aggregation, you can use/modify the following code for the newer version aggregation, separating aggregation and renaming part. Please be aware of the nested dictionary if there are more than 1 item.

def agg_translate_agg_rename(input_agg_dict):
    agg_dict = {}
    rename_dict = {}
    for k, v in input_agg_dict.items():
        if len(v) == 1:
            agg_dict[k] = list(v.values())[0]
            rename_dict[k] = list(v.keys())[0]
        else:
            updated_index = 1
            for nested_dict_k, nested_dict_v in v.items():
                modified_key = k + "_" + str(updated_index)
                agg_dict[modified_key] = nested_dict_v
                rename_dict[modified_key] = nested_dict_k
                updated_index += 1
    return agg_dict, rename_dict

one_dict = {"column1": {"foo": 'sum'}, "column2": {"mean": 'mean', "std": 'std'}}
agg, rename = agg_translator_aa(one_dict)

We get

agg = {'column1': 'sum', 'column2_1': 'mean', 'column2_2': 'std'}
rename = {'column1': 'foo', 'column2_1': 'mean', 'column2_2': 'std'}

Please let me know if there is a smarter way to do it. Thanks.


such as this kind of dataframe, there are two levels of thecolumn name:

 shop_id  item_id   date_block_num item_cnt_day       
                                  target              
0   0       30          1            31               

we can use this code:

df.columns = [col[0] if col[-1]=='' else col[-1] for col in df.columns.values]

result is:

 shop_id  item_id   date_block_num target              
0   0       30          1            31 

참고URL : https://stackoverflow.com/questions/19078325/naming-returned-columns-in-pandas-aggregate-function

반응형