【问题标题】:Rename result columns from Pandas aggregation ("FutureWarning: using a dict with renaming is deprecated")从 Pandas 聚合中重命名结果列(“FutureWarning:不推荐使用带有重命名的字典”)
【发布时间】:2017-11-21 23:15:00
【问题描述】:

我正在尝试对 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:不推荐使用带有重命名的字典,并将 在未来的版本中删除 return super(DataFrameGroupBy, self).aggregate(arg, *args, **kwargs)

我怎样才能避免这种情况?

【问题讨论】:

  • 我很想知道为什么要贬值(我相信这是有充分理由的)。有人有讨论的链接吗?
  • 为了关注解决方案的关键字而不仅仅是现有的警告,我重新命名了“重命名聚合结果列” 并加了标签。现在人们甚至可能会在(比如说)不那么规范的Naming returned columns in Pandas aggregate function? 之前找到这个问题 :)
  • 希望这将在github.com/pandas-dev/pandas/issues/18366得到解决
  • 如果我不执行“groupby”但我正在执行“pivot”,这将如何工作?
  • 见熊猫 0.25.0 aggregation relabeling

标签: python pandas aggregate rename


【解决方案1】:

这对我有用,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 name1 name2_mean name2_custom_std
0 1 3 13 1.5
1 2 1 6 0.0
2 3 5 4 0.0

【讨论】:

    【解决方案2】:

    熊猫更新 0.25+ Aggregation relabeling

    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
    

    【讨论】:

    • 但在这种情况下,名称 SumCount 必须是有效的 Python 名称。您不能将 Sum 替换为 Sum of foos。 :(
    • @Dror 你可以使用这种格式df.groupby('User').agg(**{'sum of foos':pd.NamedAgg('Amount','sum'), 'count of foos':pd.NamedAgg('Amount','count')})
    • 确实,这行得通,但不幸的是,与基于字典的聚合 API 相比,它要麻烦得多。
    • @Dror 您不需要包含pd.NamedAgg 部分,这会缩短代码量。现在这是我的首选方式。传入前面有** 的字典。
    【解决方案3】:

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

    要重命名我正在使用这个实用函数的函数:

    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 提出的 this solution 所示
    • ...但在其他情况下,在聚合规范项本身中携带别名很有用。所以我保留原样。
    【解决方案4】:

    这就是我所做的:

    创建一个假数据集:

    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
    

    O/P:

        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 设为索引,然后是 groupby:

    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
    

    【讨论】:

      【解决方案5】:

      如果将内部字典替换为元组列表,则会消除警告消息

      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,我不知道。但是如果警告信息不存在,那么大概(如你所说)它将继续得到支持。
      • 这是一个未记录的意外功能,我强烈建议不要使用这种语法,因为它在未来可能无法使用。
      • 感谢@TedPetrou 提供的信息。并感谢您回答中讨论的链接。听起来很难找到正确的语法。
      • 这确实像@TedPetrou 提到的那样不稳定吗?
      【解决方案6】:

      使用 groupby apply 并返回一个 Series 来重命名列

      使用 groupby apply 方法执行聚合

      • 重命名列
      • 名称中允许有空格
      • 允许您以您选择的任何方式对返回的列进行排序
      • 允许列之间的交互
      • 返回单级索引而不是多级索引

      为此:

      • 创建一个您传递给apply的自定义函数
      • 此自定义函数作为 DataFrame 传递给每个组
      • 返回一个系列
      • 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]})
      

      创建返回系列的自定义函数
      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 aggregationsagg慢很多

      使用带有 groupby agg 方法的字典

      由于字典的复杂性和含糊不清的性质,已删除使用字典。 github上有一个ongoing discussion关于以后如何改进这个功能这里可以直接访问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
      

      【讨论】:

      • 但假设您确实希望重命名方面也具有与聚合后的默认值不同的列名。是否有一些语法仍然可以获得此功能?
      • 对命名有同样的疑问,因为我使用相同的 col 两次(最少一次和最多一次),并且当我将结果放回对象时需要一种唯一引用它们的方法。
      • 如果要重命名列,则必须手动进行。要么使用列表替换所有 df.columns = ['your', 'new', 'col', 'names'],要么使用 rename 方法,这会有点困难,因为你的结果是 MultiIndex。
      • @ErnestScribbler 我已经更新了这个答案,以展示一种相当简单的方法来执行聚合、重命名和返回单级索引。
      • apply 方法如何与 'first' 和 'last' 一起使用?
      猜你喜欢
      • 2018-03-28
      • 1970-01-01
      • 2020-05-01
      • 2021-08-21
      • 1970-01-01
      • 2015-06-10
      • 2016-07-31
      • 2020-06-22
      相关资源
      最近更新 更多