Pandas Groupby:同一列上的聚合,但总计基于两个不同的标准/数据框

Posted

技术标签:

【中文标题】Pandas Groupby:同一列上的聚合,但总计基于两个不同的标准/数据框【英文标题】:Pandas Groupby: Aggregations on the same column but totals based on two different critera / dataframes 【发布时间】:2018-12-16 21:34:59 【问题描述】:

我的数据框:

display_name    security_type1  currency_str     state
         A            GOVT           USD         Done
         B            CORP           NZD         Passed
         B            CORP           USD         Done
         C            CORP           EUR         Done
         C            CORP           EUR         Traded Away
         C            CORP           GBP         Done
         C            CORP           GBP         Done
         C            CORP           USD         Done

我想要的结果是:

一个。分组display_namesecurity_type1currency_str

b.然后计算column state包含Done的行数并更新列Done_RFQ

c。显示每个display_namesecurity_type1currency_str组合的总行数并更新列Total_RFQ

d。最后显示完成占总数的百分比,即Done_Pct = Done_RFQ / Total_RFQ

display_name    security_type1  currency_str   Done_RFQ Total_RFQ Done_Pct
A               GOVT             USD           1           1      100%
B               CORP             USD           1           2      50%
C               CORP             EUR           1           5      20%
C               CORP             GBP           2           5      40%
C               CORP             USD           1           5      20%

我的代码除了Total_RFQDone_Pct 之外都可以工作

d = [('Done_RFQ', 'size')]
df_Done_Client = df[
                    df['state'].str.contains('Done')
                ][['display_name','security_type1','currency_str','state']].copy()

df_Done_Client =    
    df_Done_Client.groupby(['display_name','security_type1','currency_str'])['state'].agg(d).reset_index()
    # Sum of all Done RFQ's per display_name
    Sum_of_Done_For_Month = df_Done_Client.groupby('display_name')['Done_RFQ'].transform('sum')
    df_Done_Client['Total_Done_RFQ'] = Sum_of_Done_For_Month
    df_Done_Client['Done_Pct'] = df_Done_Client['Done_RFQ_For_Month'].div(Sum_of_Done_For_Month).round(5)
    display(df_Done_Client)

我不清楚如何计算这个总数,因为它需要来自另一个数据框,即相同的字段但没有“完成”标准。

df_All_Client = df[['display_name','security_type1','currency_str','state']].copy()

【问题讨论】:

【参考方案1】:

我认为需要Total_RFQ 列和size - 总计数和Done_RFQ 按布尔掩码计数 - 与Trues 的Donesum 比较:

d = [('Total_RFQ', 'size'), ('Done_RFQ', lambda x: x.eq('Done').sum())]
df=df.groupby(['display_name','security_type1','currency_str'])['state'].agg(d).reset_index()
df['Done_Pct'] = df['Done_RFQ'] / df['Total_RFQ'] * 100
print (df)
  display_name security_type1 currency_str  Total_RFQ  Done_RFQ  Done_Pct
0            A           GOVT          USD          1         1     100.0
1            B           CORP          NZD          1         0       0.0
2            B           CORP          USD          1         1     100.0
3            C           CORP          EUR          2         1      50.0
4            C           CORP          GBP          2         2     100.0
5            C           CORP          USD          1         1     100.0

如果需要检查子字符串:

d = [('Total_RFQ', 'size'), ('Done_RFQ', lambda x: x.str.contains('Done').sum())]
df=df.groupby(['display_name','security_type1','currency_str'])['state'].agg(d).reset_index()
df['Done_Pct'] = df['Done_RFQ'] / df['Total_RFQ'] * 100
print (df)
  display_name security_type1 currency_str  Total_RFQ  Done_RFQ  Done_Pct
0            A           GOVT          USD          1         1     100.0
1            B           CORP          NZD          1         0       0.0
2            B           CORP          USD          1         1     100.0
3            C           CORP          EUR          2         1      50.0
4            C           CORP          GBP          2         2     100.0
5            C           CORP          USD          1         1     100.0

【讨论】:

有效!! state 列可以包含我们想要包含的“Done”和“Tied Done”。看来 .contains 方法不可用。可以将 x.eq 更新为什么以包含任何带有“完成”的字符串? @PeterLucas - 这取决于需要什么,如果需要检查子字符串更好的是x.str.contains('Done').sum(),如果需要检查字符串x.eq('Done').sum()【参考方案2】:

这是一种方法。类似于@jezrael 的解决方案,但保留您的逻辑来检查子字符串Done 并过滤Done_RFQ > 0

另外,我相信你需要2次groupby计算才能得到你想要的结果,即Total_RFQ是由display_name计算的。

# function to calcuate Done_RFQ
d = 'Done_RFQ': lambda x: x.str.contains('Done', na=False, regex=False).sum()

# apply 2 groupby calculations
df['Total_RFQ'] = df.groupby('display_name')['display_name'].transform('size')

group_cols = ['display_name', 'security_type1', 'currency_str', 'Total_RFQ']
res = df.groupby(group_cols)['state'].agg(d).reset_index()

# calculate Done_Pct
res['Done_Pct'] = res['Done_RFQ'] / res['Total_RFQ']

# filter for Done_RFQ > 0
res = res[res['Done_RFQ'] > 0]

print(res)

  display_name security_type1 currency_str  Total_RFQ  Done_RFQ  Done_Pct
0            A           GOVT          USD          1         1       1.0
2            B           CORP          USD          2         1       0.5
3            C           CORP          EUR          5         1       0.2
4            C           CORP          GBP          5         2       0.4
5            C           CORP          USD          5         1       0.2

【讨论】:

以上是关于Pandas Groupby:同一列上的聚合,但总计基于两个不同的标准/数据框的主要内容,如果未能解决你的问题,请参考以下文章

如何在一个 groupby 列上执行聚合选项,给出两列输出

pyspark:groupby 和聚合 avg 和 first 在多个列上

Dataframe GroupBy 在包含模式的列上聚合

提高性能(矢量化?) pandas.groupby.aggregate

如何在单个列上使用 groupby 并对 Pandas 中的多个列进行比较?

到目前为止,按同一组中的聚合元素分组 - Pandas