首页 > 解决方案 > 根据 groupby 中的比例填充分类 NaN 值

问题描述

我正在使用一个完全由分类特征组成的数据集。

一列只有缺失值:8124 中的 2480 NaN。

我可以根据现有分类值的百分比成功填充 NaN 值:

print(df['stalk-root'].value_counts(normalize=True), '\n')

产量:

b    0.669029
e    0.198441
c    0.098512
r    0.034018

然后我使用这些百分比来填充缺失值:

# https://stackoverflow.com/questions/38934140/fill-missing-values-by-a-ratio-of-other-values-in-pandas
df['stalk-root'] = df['stalk-root'].fillna(pd.Series(np.random.choice(['b', 'e', 'c', 'r'],
                                                     p=[0.669029, 0.198441, 0.098512, 0.034018], size=len(df))))

它完美地工作。

但是,如果我按“类”列分组,我很好奇 df['stalk-root'] 列的 value_counts 是什么样的。

print(df.groupby('class')['stalk-root'].value_counts(normalize=True), '\n')

产量:

e      b             0.550459
       e             0.247706
       c             0.146789
       r             0.055046
p      b             0.860853
       e             0.118738
       c             0.020408

这是一个相当大的区别。足够大,我现在想将我的 NaN 填充过程修改为第一个 groupby 类,然后通过百分比填充,如上所述。

我之前使用数值列和 mean() 完成了此操作,但不同之处在于我根据 value_counts(normalize=True) 的结果手动填充 np.random.choice() 中的百分比。

我不知道怎么说:groupby 类,运行 ['stalk-root'].value_counts(normalize=True),然后像我上面那样获取这些值并输入 fillna(np.random.choice()。

我将有两组完全不同的填充值,而“r”只出现在其中一组中。

一个将是(对于“e”类):

np.random.choice(['b', 'e', 'c', 'r'],
                   p=[0.550459, 0.247706, 0.146789, 0.055046], size=len(df)

另一个(对于“p”类)将是:

np.random.choice(['b', 'e', 'c'],
                   p=[0.860853, 0.118738, 0.020408], size=len(df)

我遇到的第二个问题是 size=len(df)。这必须是每个分组的大小(我假设),并且它们的大小不同。

来自 kaggle 的数据文件

标签: python-3.xpandaspandas-groupby

解决方案


这是一个解决方案groupby

was_null = df['stalk-root'].isna()

for _, gdf in df.groupby('class')['stalk-root']:
    vc = gdf.value_counts(normalize=True)
    df.loc[gdf.loc[gdf.isna()].index, 'stalk-root'] = (
        np.random.choice(vc.index, gdf.isna().sum(), p=vc)
    )

验证输出

# old distribution
print(df[was_null].groupby('class')['stalk-root'].value_counts(normalize=True))

class  stalk-root
e      b             0.561111
       e             0.236111
       c             0.140278
       r             0.062500
p      b             0.865341
       e             0.117045
       c             0.017614
Name: stalk-root, dtype: float64


# new distribution
print(df.groupby('class')['stalk-root'].value_counts(normalize=True))

class  stalk-root
e      b             0.552281
       e             0.245722
       c             0.145675
       r             0.056321
p      b             0.862870
       e             0.117978
       c             0.019152
Name: stalk-root, dtype: float64

推荐阅读