Pandas聚合操作中重命名结果列(“FutureWarning:使用字典进行重命名已被弃用”)

64

我正在尝试对一个Pandas数据框进行一些聚合操作。以下是样本代码:

import pandas as pd

df = pd.DataFrame({"User": ["user1", "user2", "user2", "user3", "user2", "user1"],
                  "Amount": [10.0, 5.0, 8.0, 10.5, 7.5, 8.0]})

df.groupby(["User"]).agg({"Amount": {"Sum": "sum", "Count": "count"}})

Out[1]: 
      Amount      
         Sum Count
User              
user1   18.0     2
user2   20.5     3
user3   10.5     1

如何避免下面的警告:

FutureWarning: 使用字典进行重命名已被弃用并将在将来的版本中删除 返回 super(DataFrameGroupBy, self).aggregate(arg, *args, **kwargs)

我该怎么做才能避免这个警告?


10
我很想知道为什么这个东西被废弃了(我相信一定有一个好的理由)。有人有关于此事的讨论链接吗? - Stephen McAteer
为了聚焦于解决方案的关键词,而不仅仅是现有的警告,我重新命名了“从聚合重命名结果列”并进行了标记。现在人们甚至可能会发现这个问题 :) 超过(比如说)不太规范的Pandas聚合函数中返回列的命名? - smci
2
希望这个问题能够在 https://github.com/pandas-dev/pandas/issues/18366 得到解决。 - Nickolay
如果我不使用"groupby"而是使用"pivot",这会如何工作? - avloss
请参阅pandas 0.25.0的聚合重标记 - Scott Boston
6个回答

94

使用groupby方法和apply方法,返回一个Series以重命名列名

使用groupby方法的apply函数执行聚合操作:

  • 重命名列名
  • 允许列名中有空格
  • 可按任意顺序排序返回的列
  • 允许列之间的交互
  • 返回单层索引而不是多层索引

实现方法如下:

  • 创建一个自定义函数并将其传递给apply函数
  • 该自定义函数作用于每个分组的DataFrame数据
  • 返回一个Series
  • Series的索引将成为新的列名

创建虚假数据

df = pd.DataFrame({"User": ["user1", "user2", "user2", "user3", "user2", "user1", "user3"],
                  "Amount": [10.0, 5.0, 8.0, 10.5, 7.5, 8.0, 9],
                  'Score': [9, 1, 8, 7, 7, 6, 9]})

图片描述

创建一个返回Series的自定义函数
my_agg内的变量x是一个DataFrame。

def my_agg(x):
    names = {
        'Amount mean': x['Amount'].mean(),
        'Amount std':  x['Amount'].std(),
        'Amount range': x['Amount'].max() - x['Amount'].min(),
        'Score Max':  x['Score'].max(),
        'Score Sum': x['Score'].sum(),
        'Amount Score Sum': (x['Amount'] * x['Score']).sum()}

    return pd.Series(names, index=['Amount range', 'Amount std', 'Amount mean',
                                   'Score Sum', 'Score Max', 'Amount Score Sum'])

将此自定义函数传递给groupby apply方法

df.groupby('User').apply(my_agg)

在此输入图像描述

缺点是这个函数比cythonized聚合agg方法慢得多。

使用字典与groupby的agg方法

由于其复杂性和含糊不清的特性,已经删除了使用字典的方法。 关于如何改进这一功能,现在正在github上进行持续讨论。 在这里,您可以在groupby调用之后直接访问聚合列。 只需传递您希望应用的所有聚合函数的列表即可。

df.groupby('User')['Amount'].agg(['sum', 'count'])

输出

       sum  count
User              
user1  18.0      2
user2  20.5      3
user3  10.5      1

仍然可以使用词典来明确指定不同列的不同聚合方式,就像这里如果有另一个名为Other的数字列一样。

df = pd.DataFrame({"User": ["user1", "user2", "user2", "user3", "user2", "user1"],
              "Amount": [10.0, 5.0, 8.0, 10.5, 7.5, 8.0],
              'Other': [1,2,3,4,5,6]})

df.groupby('User').agg({'Amount' : ['sum', 'count'], 'Other':['max', 'std']})

输出

      Amount       Other          
         sum count   max       std
User                              
user1   18.0     2     6  3.535534
user2   20.5     3     5  1.527525
user3   10.5     1     4       NaN

21
如果您希望进行重命名,使用不同于默认聚合后的列名,那么是否存在某种语法仍然可以实现此功能?请注意,不要改变原来的意思。 - ErnestScribbler
2
对于命名问题我也有同样的疑问,因为我使用了两次相同的列(一个最小值和一个最大值),需要一种唯一地引用它们的方法,以便在将结果放回对象时使用。 - mgmonteleone
1
如果你想重命名列,你必须手动完成。可以使用列表替换所有列 df.columns = ['your', 'new', 'col', 'names'] 或者使用 rename 方法,但由于结果是多级索引,这可能会有些困难。 - Ted Petrou
2
应用方法如何与“first”和“last”一起使用? - Gregory Saxton
为什么在调用my_agg时不需要传递参数? - Snow
显示剩余3条评论

21

Pandas 0.25+ 更新 聚合重命名

import pandas as pd

print(pd.__version__)
#0.25.0

df = pd.DataFrame({"User": ["user1", "user2", "user2", "user3", "user2", "user1"],
                  "Amount": [10.0, 5.0, 8.0, 10.5, 7.5, 8.0]})

df.groupby("User")['Amount'].agg(Sum='sum', Count='count')

输出:

        Sum  Count
User              
user1  18.0      2
user2  20.5      3
user3  10.5      1

5
但在这种情况下,'Sum'和'Count'必须是有效的Python名称。你不能用'Sum of foos'替换'Sum'。:( - Dror
2
@Dror,你可以使用以下格式:df.groupby('User').agg(**{'sum of foos':pd.NamedAgg('Amount','sum'),'count of foos':pd.NamedAgg('Amount','count')}) - Scott Boston
确实,这个方法可行,但与基于字典的聚合API相比,它不幸地更加繁琐。 - Dror
2
@Dror,您不需要包含 pd.NamedAgg 部分以缩短代码量。这是我现在做这件事情的首选方法。传递一个带有 ** 前缀的字典即可。 - Corey Levinson

17

如果用元组的列表替换内部字典,则可以消除警告消息。

import pandas as pd

df = pd.DataFrame({"User": ["user1", "user2", "user2", "user3", "user2", "user1"],
                  "Amount": [10.0, 5.0, 8.0, 10.5, 7.5, 8.0]})

df.groupby(["User"]).agg({"Amount": [("Sum", "sum"), ("Count", "count")]})

你知道这个方法是否也可以在未来的版本中使用,还是只是为了解决警告信息而已? - Peanut
@Peanut,我不知道。但是如果没有警告信息,那么就像你所说的,它将继续得到支持。 - Jacob Stevenson
8
这是一个未记录且意外的功能,我强烈建议不要使用这个语法,因为它在将来可能无法正常工作。 - Ted Petrou
感谢@TedPetrou提供的信息。还有感谢您在答案中提供了讨论链接。听起来这是一个很难找到正确语法的问题。 - Jacob Stevenson
这确实是@TedPetrou提到的不稳定行为吗? - Dror

12

这对我有用,Pandas版本为1.2.4

对于每一列,我们添加一个由元组组成的列表:

df.groupby('column to group by').agg(
{'column name': [('new column name', 'function to apply')]})

例子

# Create DataFrame
df=pd.DataFrame(data={'id':[1,1,2,3],'col1': [1,2,1,5], 'col2':[5,8,6,4]})

# Apply grouping 
grouped = df.groupby('id').agg({
                             'col1': [('name1', 'sum')], 
                             'col2': [('name2_mean', 'sum'), ('name2_custom_std', lambda x: np.std(x))]})

# Drop multi-index for columns and reset index
grouped.columns = grouped.columns.droplevel()
grouped.reset_index(inplace=True)

结果:

id 姓名1 姓名2(含义) 姓名2(自定义标准差)
0 1 3 13 1.5
1 2 1 6 0.0
2 3 5 4 0.0

3
这是我的步骤:

创建一个虚假数据集:

import pandas as pd
df = pd.DataFrame({"User": ["user1", "user2", "user2", "user3", "user2", "user1", "user3"],
                  "Amount": [10.0, 5.0, 8.0, 10.5, 7.5, 8.0, 9],
                  'Score': [9, 1, 8, 7, 7, 6, 9]})
df

输出:

    Amount  Score   User
0   10.0    9   user1
1   5.0 1   user2
2   8.0 8   user2
3   10.5    7   user3
4   7.5 7   user2
5   8.0 6   user1
6   9.0 9   user3

我首先将“User”设置为索引,然后进行了分组操作:
ans = df.set_index('User').groupby(level=0)['Amount'].agg([('Sum','sum'),('Count','count')])
ans

解决方案:
    Sum Count
User        
user1   18.0    2
user2   20.5    3
user3   19.5    2

0

将内部字典替换为正确命名函数的列表。

为了重命名函数,我正在使用此实用程序函数:

def aliased_aggr(aggr, name):
    if isinstance(aggr,str):
        def f(data):
            return data.agg(aggr)
    else:
        def f(data):
            return aggr(data)
    f.__name__ = name
    return f

然后,group-by语句变成了:


df.groupby(["User"]).agg({"Amount": [ 
    aliased_aggr("sum","Sum"),
    aliased_aggr("count","Count")
]

如果您有更大、可重用的聚合规范,可以使用以下方法进行转换:

def convert_aggr_spec(aggr_spec):
    return {
        col : [ 
            aliased_aggr(aggr,alias) for alias, aggr in aggr_map.items() 
        ]  
        for col, aggr_map in aggr_spec.items() 
    }

所以你可以这样说

df.groupby(["User"]).agg(convert_aggr_spec({"Amount": {"Sum": "sum", "Count": "count"}}))

参见 https://github.com/pandas-dev/pandas/issues/18366#issuecomment-476597674

好的,我有点过度沉迷于这个问题了:这本质上是在重新创建已经存在于pandas中的内容,就像@jacob-stevenson提出的这个解决方案所示。 - plankthom
但是在其他情况下,将别名放在聚合规范项本身中也很有用。因此,我保留答案不变。 - plankthom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接