熊猫:在每组中按平均值填充缺失值

Blu*_*eet 57 python pandas imputation fillna pandas-groupby

这应该是直截了当的,但我发现的最接近的是这篇文章: 熊猫:填写组内的缺失值,我仍然无法解决我的问题....

假设我有以下数据帧

df = pd.DataFrame({'value': [1, np.nan, np.nan, 2, 3, 1, 3, np.nan, 3], 'name': ['A','A', 'B','B','B','B', 'C','C','C']})

  name  value
0    A      1
1    A    NaN
2    B    NaN
3    B      2
4    B      3
5    B      1
6    C      3
7    C    NaN
8    C      3
Run Code Online (Sandbox Code Playgroud)

并且我想在每个"名称"组中填写"NaN",其中包含平均值

      name  value
0    A      1
1    A      1
2    B      2
3    B      2
4    B      3
5    B      1
6    C      3
7    C      3
8    C      3
Run Code Online (Sandbox Code Playgroud)

我不确定去哪里:

grouped = df.groupby('name').mean()
Run Code Online (Sandbox Code Playgroud)

谢谢一堆.

DSM*_*DSM 74

一种方法是使用transform:

>>> df
  name  value
0    A      1
1    A    NaN
2    B    NaN
3    B      2
4    B      3
5    B      1
6    C      3
7    C    NaN
8    C      3
>>> df["value"] = df.groupby("name").transform(lambda x: x.fillna(x.mean()))
>>> df
  name  value
0    A      1
1    A      1
2    B      2
3    B      2
4    B      3
5    B      1
6    C      3
7    C      3
8    C      3
Run Code Online (Sandbox Code Playgroud)

  • 如果您有两列以上,请确保指定列名称df ["value"] = df.groupby("name").transform(lambda x:x.fillna(x.mean()))['value "] (25认同)
  • @Lauren好点.我想补充一点,出于性能原因,您可能会考虑将值列规范进一步移至group-by子句.这样,lambda函数只调用该特定列中的值,而不是每列都调用,然后选择列.进行了测试,使用两列时速度提高了一倍.你自然会得到更好的表现,你不需要输入更多的列:`df ["value"] = df.groupby("name")["value"].transform(lambda x:x.fillna(x.意思是()))` (10认同)
  • 我开始坐下来阅读文档时发现它很有用.这个部分包含在[`groupby`](http://pandas.pydata.org/pandas-docs/stable/groupby.html)部分.要记住的东西太多了,但你会选择"像每个组操作一样转换的规则,你想要像原始帧一样索引"等规则. (2认同)

And*_*sen 18

@DSM让IMO得到正确的答案,但我想分享我对问题的概括和优化:多列分组并具有多个值列:

df = pd.DataFrame(
    {
        'category': ['X', 'X', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y'],
        'name': ['A','A', 'B','B','B','B', 'C','C','C'],
        'other_value': [10, np.nan, np.nan, 20, 30, 10, 30, np.nan, 30],
        'value': [1, np.nan, np.nan, 2, 3, 1, 3, np.nan, 3],
    }
)
Run Code Online (Sandbox Code Playgroud)

......给...

  category name  other_value value
0        X    A         10.0   1.0
1        X    A          NaN   NaN
2        X    B          NaN   NaN
3        X    B         20.0   2.0
4        X    B         30.0   3.0
5        X    B         10.0   1.0
6        Y    C         30.0   3.0
7        Y    C          NaN   NaN
8        Y    C         30.0   3.0
Run Code Online (Sandbox Code Playgroud)

在这种情况下,广义我们想通过组categoryname,只有在推诿value.

这可以解决如下:

df['value'] = df.groupby(['category', 'name'])['value']\
    .transform(lambda x: x.fillna(x.mean()))
Run Code Online (Sandbox Code Playgroud)

注意group-by子句中的列列表,我们选择valuegroup-by之后的列.这使得转换仅在该特定列上运行.您可以将它添加到最后,但是然后您将为所有列运行它,以便在结尾处丢弃除一个度量列之外的所有列.标准的SQL查询规划器可能已经能够优化它,但是pandas(0.19.2)似乎没有这样做.

通过做...来增加数据集进行性能测试

big_df = None
for _ in range(10000):
    if big_df is None:
        big_df = df.copy()
    else:
        big_df = pd.concat([big_df, df])
df = big_df
Run Code Online (Sandbox Code Playgroud)

...确认这会增加速度与您不必估算的列数成比例:

import pandas as pd
from datetime import datetime

def generate_data():
    ...

t = datetime.now()
df = generate_data()
df['value'] = df.groupby(['category', 'name'])['value']\
    .transform(lambda x: x.fillna(x.mean()))
print(datetime.now()-t)

# 0:00:00.016012

t = datetime.now()
df = generate_data()
df["value"] = df.groupby(['category', 'name'])\
    .transform(lambda x: x.fillna(x.mean()))['value']
print(datetime.now()-t)

# 0:00:00.030022
Run Code Online (Sandbox Code Playgroud)

最后一点,如果你想要输入多个列,而不是全部列,你可以进一步概括:

df[['value', 'other_value']] = df.groupby(['category', 'name'])['value', 'other_value']\
    .transform(lambda x: x.fillna(x.mean()))
Run Code Online (Sandbox Code Playgroud)


jpp*_*jpp 14

fillna+ groupby+ transform+mean

这看起来很直观:

df['value'] = df['value'].fillna(df.groupby('name')['value'].transform('mean'))
Run Code Online (Sandbox Code Playgroud)

groupby+ transform语法的GroupWise平均映射到原始数据帧的指数。这大致相当于@DSM的解决方案,但避免了定义匿名lambda函数的需要。

  • 谢谢!,我发现 lambda 函数有点令人困惑,而你的函数更容易理解。 (2认同)
  • 很好的解决方案。我的 groupby 返回 73k 组。换句话说,它需要找到 73k 组的平均值才能填充每组的 NA 值。我主要关心的是时间安排,因为我想轻松地将其扩展到超过 73k 组。lambda 解决方案需要 21.39 秒才能完成,而此解决方案需要 0.27 秒。强烈建议采用此解决方案! (2认同)
  • df = df.fillna(df.groupby('name').transform('mean')) 是否对所有列成功执行此操作?我正在使用它,它看起来不错,但我担心我做错了什么,就像这里每列所做的那样? (2认同)

Ash*_*and 13

捷径:

Groupby + Apply + Lambda + Fillna + Mean

>>> df['value1']=df.groupby('name')['value'].apply(lambda x:x.fillna(x.mean()))
>>> df.isnull().sum().sum()
    0 
Run Code Online (Sandbox Code Playgroud)

如果您想按多列分组以替换缺失值,此解决方案仍然有效。

>>> df = pd.DataFrame({'value': [1, np.nan, np.nan, 2, 3, np.nan,np.nan, 4, 3], 
    'name': ['A','A', 'B','B','B','B', 'C','C','C'],'class':list('ppqqrrsss')})  

    
>>> df['value']=df.groupby(['name','class'])['value'].apply(lambda x:x.fillna(x.mean()))
       
>>> df
        value name   class
    0    1.0    A     p
    1    1.0    A     p
    2    2.0    B     q
    3    2.0    B     q
    4    3.0    B     r
    5    3.0    B     r
    6    3.5    C     s
    7    4.0    C     s
    8    3.0    C     s
 
Run Code Online (Sandbox Code Playgroud)


piR*_*red 10

我这样做

df.loc[df.value.isnull(), 'value'] = df.groupby('group').value.transform('mean')
Run Code Online (Sandbox Code Playgroud)


Phi*_*arz 5

特色高排名答案仅适用于只有两列的熊猫数据框。如果您有更多列案例,请改用:

df['Crude_Birth_rate'] = df.groupby("continent").Crude_Birth_rate.transform(
    lambda x: x.fillna(x.mean()))
Run Code Online (Sandbox Code Playgroud)