【问题标题】:Pandas groupby mean() not ignoring NaNsPandas groupby mean() 不忽略 NaN
【发布时间】:2019-06-03 23:46:19
【问题描述】:

如果我计算一个 groupby 对象的平均值并且在其中一个组中有一个 NaN(s),那么 NaN 将被忽略。即使应用 np.mean 它仍然只返回所有有效数字的平均值。我希望一旦有一个 NaN 在组内,就会返回 NaN 的行为。这是行为的简化示例

import pandas as pd
import numpy as np
c = pd.DataFrame({'a':[1,np.nan,2,3],'b':[1,2,1,2]})
c.groupby('b').mean()
     a
b     
1  1.5
2  3.0
c.groupby('b').agg(np.mean)
     a
b     
1  1.5
2  3.0

我想收到以下结果:

     a
b     
1  1.5
2  NaN

我知道我可以预先替换 NaN,并且我可能可以编写自己的聚合函数,以便在 NaN 在组内时立即返回 NaN。但是,此功能不会被优化。

您知道使用优化函数实现所需行为的参数吗?

顺便说一句,我认为所需的行为是在以前版本的 pandas 中实现的。

【问题讨论】:

  • Pandas 的这种行为是可怕的......所提出的解决方案都没有使像 np.mean() 这样的标准函数能够正常工作(返回 NaN)

标签: python pandas dataframe nan


【解决方案1】:

使用skipna 选项 -

c.groupby('b').apply(lambda g: g.mean(skipna=False))

【讨论】:

    【解决方案2】:

    默认情况下,pandas 会跳过 Nan 值。您可以通过指定skipna=False 使其包含Nan

    In [215]: c.groupby('b').agg({'a': lambda x: x.mean(skipna=False)})
    Out[215]: 
         a
    b     
    1  1.5
    2  NaN
    

    【讨论】:

      【解决方案3】:

      另一种方法是使用默认不被忽略的 ,例如np.inf:

      >>> c = pd.DataFrame({'a':[1,np.inf,2,3],'b':[1,2,1,2]})
      >>> c.groupby('b').mean()
                a
      b          
      1  1.500000
      2       inf
      

      【讨论】:

      • 在计算平均值之前你可以使用fillna(np.inf),在平均值之后你可以使用.replace([np.inf, -np.inf], np.nan)来恢复nan值。
      【解决方案4】:

      它有三种不同的方法:

      1. 最慢​​
          c.groupby('b').apply(lambda g: g.mean(skipna=False))
      
      1. 比 apply 快,但比默认 sum 慢
          c.groupby('b').agg({'a': lambda x: x.mean(skipna=False)})
      
      1. 最快但需要更多代码
          method3 = c.groupby('b').sum()
          nan_index = c[c['b'].isna()].index.to_list()
          method3.loc[method3.index.isin(nan_index)] = np.nan
      

      【讨论】:

        【解决方案5】:

        mean(skipna=False),但是不工作

        GroupBy 聚合方法(最小值、最大值、平均值、中值等)具有 skipna 参数,该参数适用于这个确切的任务,但目前(2020 年 5 月)似乎有一个 bug(问题于 2020 年 3 月打开),这使其无法正常工作。

        快速解决方法

        基于此 cmets 的完整工作示例:@Serge Ballesta, @RoelAdriaans

        >>> import pandas as pd
        >>> import numpy as np
        >>> c = pd.DataFrame({'a':[1,np.nan,2,3],'b':[1,2,1,2]})
        >>> c.fillna(np.inf).groupby('b').mean().replace(np.inf, np.nan)
        
             a
        b     
        1  1.5
        2  NaN
        

        如需更多信息和更新,请点击上面的链接。

        【讨论】:

          【解决方案6】:

          我来到这里是为了寻找一种快速(矢量化)的方法,但没有找到。此外,在复数的情况下,groupby 的行为有点奇怪:它不喜欢mean(),而sum() 会将所有值为NaN 的组转换为0+0j

          所以,这就是我想出的:

          设置

          df = pd.DataFrame({
              'a': [1, 2, 1, 2],
              'b': [1, np.nan, 2, 3],
              'c': [1, np.nan, 2, np.nan],
              'd': np.array([np.nan, np.nan, 2, np.nan]) * 1j,
          })
          gb = df.groupby('a')
          

          默认行为

          gb.sum()
          
          Out[]:
               b    c                   d
          a                              
          1  3.0  3.0  0.000000+2.000000j
          2  3.0  0.0  0.000000+0.000000j
          

          单个NaN 杀死该组

          cnt = gb.count()
          siz = gb.size()
          mask = siz.values[:, None] == cnt.values
          gb.sum().where(mask)
          
          Out[]:
               b    c   d
          a              
          1  3.0  3.0 NaN
          2  NaN  NaN NaN
          

          只有NaN,如果组中的所有值都是NaN

          cnt = gb.count()
          gb.sum() * (cnt / cnt)
          out
          
          Out[]:
               b    c                   d
          a                              
          1  3.0  3.0  0.000000+2.000000j
          2  3.0  NaN                 NaN
          

          推论:复数的平均值

          cnt = gb.count()
          gb.sum() / cnt
          
          Out[]:
               b    c                   d
          a                              
          1  1.5  1.5  0.000000+2.000000j
          2  3.0  NaN                 NaN
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-10-30
            • 2017-01-11
            • 2016-08-27
            • 2014-11-26
            • 2018-04-06
            • 2021-05-18
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多