按每组平均值填充缺失值

2024-12-04 08:55:00
admin
原创
174
摘要:问题描述:这应该很简单,但我发现最接近的是这篇文章: pandas:填充组内缺失的值,但我仍然无法解决我的问题......假设我有以下数据框df = pd.DataFrame({'value': [1, np.nan, np.nan, 2, 3, 1, 3, np.nan, 3], 'name': ['A',...

问题描述:

这应该很简单,但我发现最接近的是这篇文章:
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()

解决方案 1:

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

解决方案 2:

fillna+ groupby+ transform+mean

这看起来很直观:

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

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

解决方案 3:

@DSM 在我看来是正确答案,但我想分享我对这个问题的概括和优化:多列分组并具有多个值列:

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']\n    .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']\n    .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'])\n    .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']\n    .transform(lambda x: x.fillna(x.mean()))

解决方案 4:

捷径:

Groupby + 应用 + Lambda + Fillna + Mean

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

解决方案 5:

我会这样做

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

解决方案 6:

精选的高排名答案仅适用于只有两列的 pandas Dataframe。如果您有更多列的情况,请改用:

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

解决方案 7:

总结以上关于可能解决方案的效率,我有一个包含 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

因此,选择正确的方法非常重要。请记住,我注意到,一旦列不是数字,时间就会呈指数增长(这很有意义,因为我正在计算中位数)。

解决方案 8:

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

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

我在这里要做的是

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

或者使用 fillna

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

我已经用 timeit 进行了检查(因为,再次,基于 apply/lambda 的解决方案的一致意见让我怀疑我的直觉)。这确实比获得最多支持的方案快 2.5 倍。

解决方案 9:

def groupMeanValue(group):
    group['value'] = group['value'].fillna(group['value'].mean())
    return group

dft = df.groupby("name").transform(groupMeanValue)

解决方案 10:

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

num_cols = df.select_dtypes(exclude='object').columns
df[num_cols] = df.groupby("name").transform(lambda x: x.fillna(x.mean()))

解决方案 11:

这是一种容易理解的方法。

使用groupby++set_indexfillna

import pandas as pd
import numpy as np
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']})
print(df)
name_mean = df.groupby('name').mean()
df.set_index('name', inplace=True)
df = df.fillna(name_mean)
df.reset_index(inplace=True)
print(df)

结果

   value name
0    1.0    A
1    NaN    A
2    NaN    B
3    2.0    B
4    3.0    B
5    1.0    B
6    3.0    C
7    NaN    C
8    3.0    C
  name  value
0    A    1.0
1    A    1.0
2    B    2.0
3    B    2.0
4    B    3.0
5    B    1.0
6    C    3.0
7    C    3.0
8    C    3.0

解决方案 12:

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

解决方案 13:

您也可以使用"dataframe or table_name".apply(lambda x: x.fillna(x.mean()))

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用