【问题标题】:How to write complicated function to aggregate DataFrame如何编写复杂的函数来聚合 DataFrame
【发布时间】:2021-03-23 00:57:32
【问题描述】:

我在 Python 中有一个如下所示的 DataFrame,它显示了客户的协议:

df = pd.DataFrame({"ID"         : [1,2,1,1,3],
                   "amount"     : [100,200,300,400,500],
                   "status"     : ["active", "finished", "finished", 
                                  "active", "finished"]})

我需要用 Python 编写 FUNCTION,它将计算: 1.每个“ID”的合约数量(NumAg)和金额(AmAg) 2.每个ID的数量(NumAct)和活跃(AmAct)合约数量 3.每个ID的数量(NumFin)和已完成(AmFin)合约的数量

为了更精确,我需要通过这个函数创建 DataFrame,如下所示:

【问题讨论】:

  • 如果太复杂,只需将其分解为单行代码即可。
  • 但是如何?你能推荐点什么吗?
  • 前 2 个例如:groupby('ID').agg(NumAg = pd.NamedAgg(column='amount',aggfunc='count'),AmAg = pd.NamedAgg(column='amount',aggfunc='sum'))
  • 谢谢,您对第 3 点有什么想法吗?

标签: python pandas dataframe aggregation


【解决方案1】:

以下解决方案应该适合您的用例。

import pandas as pd

def summarise_df(df):  
    # Define mask to filter df by 'active' value in 'status' column for 'NumAct', 'AmAct', 'NumFin', and 'AmFin' columns
    active_mask = df['status'].str.contains('active') 
    return df.groupby('ID').agg( # Create first columns in output df using agg (no mask needed) 
            NumAg=pd.NamedAgg(column='amount', aggfunc='count'),
            AmAg=pd.NamedAgg(column='amount', aggfunc='sum'
        )).join( # Add columns using values with 'active' status
        df[active_mask].groupby('ID').agg( 
            NumAct=pd.NamedAgg(column='amount', aggfunc='count'),
            AmAct=pd.NamedAgg(column='amount', aggfunc='sum')
        )).join( # Add columns using values with NOT 'active' (i.e. 'finished') status
        df[~active_mask].groupby('ID').agg( 
            NumFin=pd.NamedAgg(column='amount', aggfunc='count'),
            AmFin=pd.NamedAgg(column='amount', aggfunc='sum')
        )).fillna(0) # Replace nan values with 0

我建议您阅读此函数及其 cmets 以及 groupby()join() 的文档,以便您可以更好地了解这里正在做什么。依赖您没有很好掌握的代码很少是一个明智的决定。

【讨论】:

    【解决方案2】:

    在添加两个使聚合更容易的bool 列之后,您可以在ID 上使用groupbyagg

    df['AmAct'] = df.amount[df.status.eq('active')]
    df['AmFin'] = df.amount[df.status.eq('finished')]
    df = df.groupby('ID').agg(
            NumAg = ('ID', 'count'),
            AmAg = ('amount', 'sum'),
            NumAct = ('status', lambda col: col.eq('active').sum()),
            AmAct = ('AmAct', 'sum'),
            NumFin = ('status', lambda col: col.eq('finished').sum()),
            AmFin = ('AmFin', 'sum')
         )
    

    结果:

        NumAg  AmAg  NumAct  AmAct  NumFin  AmFin
    ID                                           
    1       3   800       2  500.0       1  300.0
    2       1   200       0    0.0       1  200.0
    3       1   500       0    0.0       1  500.0
    

    或在df 中添加更多列,以在ID 上使用sum 做一个更简单的groupby

    df.insert(1, 'NumAg', 1)
    df['NumAct'] = df.status.eq('active')
    df['AmAct'] = df.amount[df.NumAct]
    df['NumFin'] = df.status.eq('finished')
    df['AmFin'] = df.amount[df.NumFin]
    df.drop(columns=['status'], inplace=True)
    df = df.groupby('ID').sum().rename(columns={'amount': 'AmAg'})
    

    结果相同。

    或者,也许是最简单的方法,让pivot_table 完成大部分工作,在count 列添加到df 之后,然后进行一些列重新排列:

    df['count'] = 1
    df = df.pivot_table(index='ID', columns='status', values=['count', 'amount'],
                        aggfunc=sum, fill_value=0, margins=True).drop('All')
    df.columns = ['AmAct', 'AmFin', 'AmAg', 'NumAct', 'NumFin', 'NumAg']
    df = df[['NumAg', 'AmAg', 'NumAct', 'AmAct', 'NumFin', 'AmFin']]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-06
      • 2012-07-22
      • 1970-01-01
      • 2011-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-14
      相关资源
      最近更新 更多