熊猫:通过每组平均值填充缺失值


83

这应该很简单,但是我发现的最接近的内容是这篇文章: pandas:填充组中的缺失值,但我仍然无法解决我的问题...。

假设我有以下数据框

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

我想在每个“名称”组中用平均值填写“ 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

我不确定该去哪里:

grouped = df.groupby('name').mean()

谢谢你

Answers:


91

一种方法是使用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

3
当我开始坐下来阅读文档时,我发现它很有帮助。本groupby节介绍了这一点。有太多的事情要记住,但是您会选择诸如“转换是针对每个组操作的规则,这些操作要像原始帧一样被索引”之类的规则。
DSM

还要寻找韦斯·麦金尼(Wes McKinney)的书。我个人认为groupby上的文档非常糟糕,这本书稍好一些。
Woody Pride

35
如果您有两列以上,请确保指定列名称df [“ value”] = df.groupby(“ name”)。transform(lambda x:x.fillna(x.mean()))['value ']
劳伦(Lauren)

16
@Lauren好点。我想补充一下,出于性能方面的考虑,您可能考虑将value列的说明进一步移到group-by子句。这样,lambda函数仅针对该特定列(而不是每个列)中的值进行调用,然后选择列。做了一个测试,使用两根色谱柱的速度是它的两倍。:自然你会得到更好的性能,你不需要转嫁给了更多的列df["value"] = df.groupby("name")["value"].transform(lambda x: x.fillna(x.mean()))
安德烈C.安徒生

我已经搜索了两天。。只是您的一个问题。为什么用循环很难做到这一点?因为在我的情况下,有两个多重索引,即StateAge_Group然后我试图用组均值填充那些组中的缺失值(来自同一年龄组中的同一州取均值并填充组中的缺失)。.谢谢
Ozkan Serttas

45

fillna+ groupby+ transform+mean

这看起来很直观:

df['value'] = df['value'].fillna(df.groupby('name')['value'].transform('mean'))

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


25

@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],
    }
)

...给...

  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

在这种一般情况下,我们希望按category和分组name,并且仅对进行归因value

可以解决以下问题:

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

请注意group-by子句中的列列表,并且我们value在group-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

...确认这将使速度与您不必估算的列数成正比:

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

最后要指出的是,如果您要推算多个而不是全部的列,则可以进一步推广:

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

感谢您的出色工作。我想知道如何使用for循环来成功完成相同的转换。速度与我无关,因为我正在尝试寻找手动方法。感谢@AndréC.Andersen
厄兹坎Serttas

12

我会这样

df.loc[df.value.isnull(), 'value'] = df.groupby('group').value.transform('mean')

1
与此版本略有不同df['value_imputed'] = np.where(df.value.isnull(), df.groupby('group').value.transform('mean'), df.value)
tsando

9

上述大多数答案都涉及使用“ groupby”和“ transform”填充缺失值。

但是我更喜欢使用“ groupby”和“ apply”来填充缺失的值,这对我来说更直观。

>>> df['value']=df.groupby('name')['value'].apply(lambda x:x.fillna(x.mean()))
>>> df.isnull().sum().sum()
    0 

快捷方式:Groupby + Apply / Lambda + Fillna + Mean

如果要按多列分组以替换缺少的值,则此解决方案仍然有效。

     >>> 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 name   class
0    1.0    A     p
1    NaN    A     p
2    NaN    B     q
3    2.0    B     q
4    3.0    B     r
5    NaN    B     r
6    NaN    C     s
7    4.0    C     s
8    3.0    C     s

>>> 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

5

精选的高答案仅适用于只有两列的熊猫数据框。如果您有更多的列,请改用:

df['Crude_Birth_rate'] = df.groupby("continent").Crude_Birth_rate.transform(
    lambda x: x.fillna(x.mean()))

谢谢,这个答案对我有用。此外,对于df.groupby("continent")['Crude_Birth_rate']... 刚接触大熊猫的任何人,也可以使用切片符号编制索引,我相信这是建议的建议
Adam Hughes


0
df.fillna(df.groupby(['name'], as_index=False).mean(), inplace=True)

5
请给您的答案一些解释。为什么在Google偶然发现此页面的人将您的解决方案用于其他6个答案?
divibisan

1
@vino请添加一些解释
Nursnaaz

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.