按组加权平均多列,舍弃包含 NaN 的列。

4

我有一个类似于Pandas多列分组加权平均的情况,但其中一列的某些值可能是NaN。

也就是说,我正在进行以下操作:

import pandas as pd
import numpy as np

df=pd.DataFrame({'category':['a','a','b','b'],
 'var1':np.random.randint(0,100,4),
 'var2':np.random.randint(0,100,4),
 'weights':np.random.randint(0,10,4)})
df.loc[1,'var1']=np.nan
df


      category  var1  var2  weights
0        a      74.0    99        9
1        a       NaN     8        4
2        b      13.0    86        2
3        b      49.0    38        7

def weighted(x, cols, w="weights"):
    # Following fails when NaNs might be present:
    #return pd.Series(np.average(x[cols], weights=x[w], axis=0), cols)
    return pd.Series([np.nan if x.dropna(subset=[c]).empty else np.average(x.dropna(subset=[c])[c], weights =x.dropna(subset=[c])[w] ) for c in cols], cols)

df.groupby('category').apply(weighted, ['var1', 'var2'])


          var1       var2
category                 
a         74.0  57.846154
b         23.0   8.000000

我希望有一种更好的方法来处理这个问题,但是np.nanmean函数不允许使用权重。np.average函数也不允许控制NaN值的处理方式。


你能先删除 NaN 值吗? - user1558604
1
我无法删除一行,因为我希望var2的意思即使在var1不存在的情况下也存在。 - CPBL
你找到解决方案了吗? - Luis Blanche
@LuisBlanche,我提出的代码似乎比其他任何代码都更简洁,因此我仍然采用它作为解决方案。我只是想问是否有更加简洁/更加规范的方法来实现它。 - CPBL
3个回答

3

在没有更好的解决方案之前,我建议使用下面的函数并不是太糟糕:

import pandas as pd
import numpy as np

def weighted_means_by_column_ignoring_NaNs(x, cols, w="weights"):
    """ This takes a DataFrame and averages each data column (cols),
        weighting observations by column w, but ignoring individual NaN
        observations within each column.
    """
    return pd.Series([np.nan if x.dropna(subset=[c]).empty else \
                      np.average(x.dropna(subset=[c])[c], 
                      weights =x.dropna(subset=[c])[w] )  \
                      for c in cols], cols)

一个示例用法如下:
df=pd.DataFrame({'category':['a','a','b','b'],
 'var1':np.random.randint(0,100,4),
 'var2':np.random.randint(0,100,4),
 'weights':np.random.randint(0,10,4)})
df.loc[1,'var1']=np.nan
df


      category  var1  var2  weights
0        a      74.0    99        9
1        a       NaN     8        4
2        b      13.0    86        2
3        b      49.0    38        7

df.groupby('category').apply(weighted_means_by_column_ignoring_NaNs), 
        ['var1', 'var2'])


          var1       var2
category                 
a         74.0  57.846154
b         23.0   8.000000

0
如何将NaN值设置为零并创建一个新列,该列为var * weight。然后您可以使用groupby来获取结果。

0

在调用applyunstack之前,您可以使用meltdropna对数据框进行预处理。

wa=lambda x: np.average(x.value, weights=x.weights)
df_avg = (df.melt(['category', 'weights']).dropna().groupby(['category', 'variable'])
                                                   .apply(wa).unstack())

Out[40]:
variable  var1       var2
category
a         74.0  71.000000
b         41.0  48.666667

注意:您所期望的输出与示例不符。 (a,'var2') 的值为(99 * 9 + 8 * 4) / (9 + 4) = 71


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接