命名返回Pandas聚合函数中的列?

时间:2013-09-29 13:00:06

标签: python group-by pandas aggregate-functions

我遇到了Pandas的groupby功能问题。我已阅读the documentation,但我无法弄清楚如何将聚合函数应用于多个列具有这些列的自定义名称。

这非常接近,但返回的数据结构具有嵌套的列标题:

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

(即我想取column2的mean和std,但将这些列作为“mean”和“std”返回)

我错过了什么?

6 个答案:

答案 0 :(得分:81)

这将从分层列索引中删除最外层:

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

答案 1 :(得分:50)

unutbu当前接受的答案是在pandas版本< = 0.20中执行此操作的好方法。但是,从pandas 0.20开始,使用此方法会发出警告,指示语法在将来的pandas版本中不可用。

系列:

  

FutureWarning:不推荐在系列上使用dict进行聚合,并将在以后的版本中删除

DataFrames:

  

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 changelog

更新2017-01-03以回应@ JunkMechanic的评论。

使用旧样式字典语法,可以将多个lambda函数传递给.agg,因为这些函数将使用传递的字典中的键重命名:

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

多个函数也可以作为列表传递给单个列:

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

     B     
  amin amax
A          
1    0    2
2    3    4

但是,这不适用于lambda函数,因为它们是匿名的并且都返回<lambda>,这会导致名称冲突:

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

为避免SpecificationError,可以先验地定义命名函数,而不是使用lambda。合适的函数名称也可以避免以后在数据框上调用.rename。这些函数可以使用与上面相同的列表语法传递:

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

答案 2 :(得分:6)

如果您希望获得类似于JMP的行为,请创建列标题,以保留您可以使用的多索引中的所有信息:

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

它会改变您的数据框架:

    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

    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

答案 3 :(得分:4)

我同意OP认为在同一个地方命名和定义输出列似乎更自然和一致(例如用tidyverse's summarize in R完成),但现在大熊猫的解决办法是在进行聚合之前,通过assign 创建具有所需名称的新列:

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

(使用reset_index'Country''f''mean''std'全部转换为带有单独整数索引的常规列。)

答案 4 :(得分:0)

借助@Joel Ostblom的灵感

对于那些已经只有聚合的可用字典的人,可以使用/修改以下代码用于较新的版本聚合,分离聚合和重命名部分。如果有多个项目,请注意嵌套字典。

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)

我们得到了

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

如果有更聪明的方法,请告诉我。感谢。

答案 5 :(得分:0)

例如这种数据框,列名称有两个级别:

 shop_id  item_id   date_block_num item_cnt_day       
                                  target              
0   0       30          1            31               

我们可以使用此代码:

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

结果是:

 shop_id  item_id   date_block_num target              
0   0       30          1            31