【问题标题】:Multi index groupby count including NaN values in pandas and, calculate percentage by single groupby多索引 groupby 计数,包括 pandas 中的 NaN 值,并按单个 groupby 计算百分比
【发布时间】:2021-11-02 21:03:03
【问题描述】:

我有一个如下所示的 df。

df:

Country       Player
Arg           Messi
Bra           Neymar
Arg           NaN
Arg           Messi
Arg           Aguero
Arg           Messi
Bra           Ronaldo
Spain         Xavi
Spain         NaN
Spain         NaN
Bra           Rivaldo
Spain         Iniesta
Bra           NaN
Spain         Xavi

其中 NaN 代表不可用的信息。 从上面的df中,我想执行多个groupby计数,如下所示。

预期输出:

Country    Player    Counts    Percentage_of_country
Arg        NaN       1         20
Arg        Messi     3         60
Arg        Aguero    1         20
Bra        Neymar    1         25
Bra        NaN       1         25
Bra        Ronaldo   1         25
Bra        Rivaldo   1         25
Spain      NaN       2         40
Spain      Xavi      2         40 
Spain      Iniesta   1         20

我试过下面的代码:

df2 = df.groupby(['Country', 'Player']).size().reset_index(name='counts')
df2['prcntg'] = df2['counts']/df2.groupby('Country')['counts'].transform('sum')
df2

【问题讨论】:

  • @Corralien,如果分组是单级的,该解决方案可能会奏效。
  • 看到这篇文章有点晚了。你也可以看看我的解决方案。

标签: python python-3.x pandas dataframe pandas-groupby


【解决方案1】:

另一种方法,真正在单个groupby 中产生所有结果如下:

定义一个辅助函数来计算百分比:

使用dropna=False 保留NaN 值:

f = lambda x: x.size / df.groupby('Country', dropna=False).size()[x.iloc[0]] * 100

第一个size 函数返回['Country', 'Player'] 组下的计数,而第二个size 函数(仅分组在Country 下)返回更大组下的计数。

然后,利用DataFrameGroupBy.aggregate()named aggregation

(df.groupby(['Country', 'Player'], dropna=False)
   .agg(counts=('Player', 'size'), 
        prcntg=('Country', f))
)

结果:

                 counts  prcntg
Country Player                 
Arg     Aguero        1    20.0
        Messi         3    60.0
        NaN           1    20.0
Bra     Neymar        1    25.0
        Rivaldo       1    25.0
        Ronaldo       1    25.0
        NaN           1    25.0
Spain   Iniesta       1    20.0
        Xavi          2    40.0
        NaN           2    40.0

编辑

如果您收到错误 TypeError: groupby() got an unexpected keyword argument 'dropna',可能是您的 Pandas 版本低于 1.1.0。从这个版本开始支持这个 dropna 参数,它允许您保持 NaN 计数。或许您应该考虑升级您的 Pandas 以获得更丰富的 Pandas 功能集。

如果您目前无法升级,解决方法是将 Player 列中的 NaN 替换为其他文本,例如。字符串 '_NaN' 或分组前的一些特殊词。如果需要,您可以在分组后恢复其值。示例代码如下:

import numpy as np

df['Player'] = df['Player'].fillna('_NaN')      # Set `NaN` values to string `_NaN`

# Main processing with all results produced in a single `groupby`:
f = lambda x: x.size / df.groupby('Country').size()[x.iloc[0]] * 100
df_out = (df.groupby(['Country', 'Player'], as_index=False)
            .agg(counts=('Player', 'size'), 
                 prcntg=('Country', f))
         )

df_out['Player'] = df_out['Player'].replace('_NaN', np.nan)     # restore `NaN` values

结果:

print(df_out)

  Country   Player  counts  prcntg
0     Arg   Aguero       1    20.0
1     Arg    Messi       3    60.0
2     Arg      NaN       1    20.0
3     Bra   Neymar       1    25.0
4     Bra  Rivaldo       1    25.0
5     Bra  Ronaldo       1    25.0
6     Bra      NaN       1    25.0
7   Spain  Iniesta       1    20.0
8   Spain     Xavi       2    40.0
9   Spain      NaN       2    40.0

【讨论】:

  • 非常感谢您的回答。我正面临如下图所示。 TypeError: groupby() 得到了一个意外的关键字参数 'dropna'
  • @Danish 您的 Pandas 版本是否早于 1.1.0 版?从这个版本开始支持这个dropna 参数,它允许您保持NaN 计数。或许您应该考虑升级您的 Pandas 以获得更丰富的功能。
  • @Danish 如果您目前无法升级,我认为解决方法是将 Player 列中的 NaN 替换为其他文本,例如。字符串 'NaN' 或分组前的一些特殊单词。如果需要,您可以在分组后恢复其值。如果您希望我为此提供一些示例代码,请告诉我。
  • 非常感谢
  • @Danish 有关示例代码,请参见我上面的编辑。正如您的问题所说,该解决方案在单个.groupby 中生成的所有结果都保持不变。
【解决方案2】:

首先将数据框按CountryPlayer 分组,然后调用size 进行计数,并调用to_frame 传递列名以从中创建数据框。您还需要传递dropna=True,因为您要包含NaN

之后,您可以将计数按level=0 分组,然后调用tranform 以获取级别的总和,然后将计数除以该值。如果需要,您可以最后拨打reset_index

count=df.groupby(['Country', 'Player'], dropna=False).size().to_frame('Counts')
count['Percentage_of_country']=100*count/count.groupby(level=0).transform('sum')

输出:

                 Counts  Percentage_of_country
Country Player                                
Arg     Aguero        1                   20.0
        Messi         3                   60.0
        NaN           1                   20.0
Bra     Neymar        1                   25.0
        Rivaldo       1                   25.0
        Ronaldo       1                   25.0
        NaN           1                   25.0
Spain   Iniesta       1                   20.0
        Xavi          2                   40.0
        NaN           2                   40.0

dropna 参数是在 pandas 版本 1.1.0 中引入的,所以如果您使用的是旧版本,您可以先尝试将 NaN 值替换为其他值,然后在执行所需的操作后恢复到NaN

df['Player'] = df['Player'].fillna('#!Missing!#')  #replace NaN by #!Missing!#'
count=df.groupby(['Country', 'Player']).size().to_frame('Counts')
count['Percentage_of_country']=100*count/count.groupby(level=0).transform('sum')
count.reset_index(inplace=True)
count['Player'] = count['Player'].replace({'#!Missing!#':float('nan')})

【讨论】:

  • 非常感谢您的回答。执行上述代码时遇到错误。
  • TypeError: groupby() got an unexpected keyword argument 'dropna'
  • @Danish,看来你使用的是旧版本,dropna 参数是在版本1.1.0 中引入的
  • 非常感谢
猜你喜欢
  • 1970-01-01
  • 2022-06-13
  • 2022-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-26
  • 2018-03-24
  • 1970-01-01
相关资源
最近更新 更多