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



这应该很简单,但我发现的最接近的东西是这篇文章:熊猫:在一组中填充缺失值,我仍然无法解决我的问题......

假设我有以下数据帧

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

谢谢一堆。

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

groupby + transform + mean

这似乎很直观:

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

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

@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

在这种广义的情况下,我们希望按categoryname分组,并且仅对value进行插补。

这可以按如下方式解决:

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

请注意 group-by 子句中的列列表,并且我们选择紧跟在 group-by 之后的value列。这使得转换仅在该特定列上运行。您可以将其添加到末尾,但随后您将对所有列运行它,只是为了在末尾抛弃除一个度量值列之外的所有列。标准的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()))

快捷方式:

分组依据 + 应用 + λ + Fillna + 平均值

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

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

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

我会这样做

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

排名答案仅适用于只有两列的熊猫数据帧。如果您有更多列大小写,请改用:

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

总结以上所有关于可能解决方案的效率我有一个包含 97 906 行和 48 列的数据集。我想用每组的中位数填写 4 列。我要分组的列有 26 200 个组。

第一个解决方案

start = time.time()
x = df_merged[continuous_variables].fillna(df_merged.groupby('domain_userid')[continuous_variables].transform('median'))
print(time.time() - start)
0.10429811477661133 seconds

第二种解决方案

start = time.time()
for col in continuous_variables:
    df_merged.loc[df_merged[col].isnull(), col] = df_merged.groupby('domain_userid')[col].transform('median')
print(time.time() - start)
0.5098445415496826 seconds

下一个解决方案我只在一个子集上执行,因为它运行的时间太长。

start = time.time()
for col in continuous_variables:
    x = df_merged.head(10000).groupby('domain_userid')[col].transform(lambda x: x.fillna(x.median()))
print(time.time() - start)
11.685635566711426 seconds

以下解决方案遵循与上述相同的逻辑。

start = time.time()
x = df_merged.head(10000).groupby('domain_userid')[continuous_variables].transform(lambda x: x.fillna(x.median()))
print(time.time() - start)
42.630549907684326 seconds

因此,选择正确的方法非常重要。请记住,我注意到一旦一列不是数字,时间就会呈指数级上升(当我计算中位数时是有道理的)。

def groupMeanValue(group):
    group['value'] = group['value'].fillna(group['value'].mean())
    return group
dft = df.groupby("name").transform(groupMeanValue)
我知道

这是一个老问题。但我对这里的apply/lambda答案的一致性感到非常惊讶。

一般来说,从时间的角度来看,这是迭代行之后第二糟糕的事情。

我在这里要做的是

df.loc[df['value'].isna(), 'value'] = df.groupby('name')['value'].transform('mean')

或使用菲尔纳

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

我已经检查了timeit(因为,再次,基于apply/lambda的解决方案的一致让我怀疑我的直觉)。这确实比最受好评的解决方案快 2.5 倍。

用按"名称"分组的平均值填充所有数字空值

num_cols = df.select_dtypes(exclude='object').columns
df[num_cols] = df.groupby("name").transform(lambda x: x.fillna(x.mean()))
df.fillna(df.groupby(['name'], as_index=False).mean(), inplace=True)
您也可以

使用 "dataframe or table_name".apply(lambda x: x.fillna(x.mean())) .

相关内容

  • 没有找到相关文章

最新更新