Pandas:在每组中用平均值填充缺失值比转换快

2024-10-01 13:26:36 发布

您现在位置:Python中文网/ 问答频道 /正文

我需要用每组的平均值来填充pandas数据帧中缺失的值。根据this questiontransform可以实现这一点。在

但是,transform对我来说太慢了。在

例如,使用具有100个不同组和70%NaN值的大型数据帧进行以下设置:

import pandas as pd
import numpy as np

size = 10000000  # DataFrame length
ngroups = 100  # Number of Groups

randgroups = np.random.randint(ngroups, size=size)  # Creation of groups
randvals = np.random.rand(size) * randgroups * 2    # Random values with mean like group number
nan_indices = np.random.permutation(range(size))    # NaN indices
nanfrac = 0.7                                       # Fraction of NaN values
nan_indices = nan_indices[:int(nanfrac*size)]       # Take fraction of NaN indices
randvals[nan_indices] = np.NaN                      # Set NaN values

df = pd.DataFrame({'value': randvals, 'group': randgroups})  # Create data frame

通过使用transform

^{pr2}$

在我的电脑上已经花了3秒多。我需要一个数量级更快的东西(买一台更大的机器不是一个选择:-D)。在

那么如何更快地填充缺失的值呢?在


Tags: of数据importpandassizeasnptransform
3条回答

这里有一个使用^{}的NumPy方法,对于这种基于bin的求和/平均操作非常有效-

ids = df.group.values                    # Extract 2 columns as two arrays
vals = df.value.values

m = np.isnan(vals)                             # Mask of NaNs
grp_sums = np.bincount(ids,np.where(m,0,vals)) # Group sums with NaNs as 0s
avg_vals = grp_sums*(1.0/np.bincount(ids,~m))        # Group averages
vals[m] = avg_vals[ids[m]]              # Set avg values into NaN positions

注意,这将更新value列。在

运行时测试

数据大小:

^{pr2}$

时间安排:

In [17]: %timeit df.groupby("group").transform(lambda x: x.fillna(x.mean()))
1 loops, best of 3: 276 ms per loop

In [18]: %timeit bincount_based(df)
100 loops, best of 3: 13.6 ms per loop

In [19]: 276.0/13.6  # Speedup
Out[19]: 20.294117647058822

20x+加速!在

使用排序索引+fillna()

你说得对-你的代码需要3.18秒才能运行。@piRSquared提供的代码需要2.78秒才能运行。在

  1. 示例代码 %%timeit df2 = df1.groupby("group").transform(lambda x: x.fillna(x.mean())) Output: 1 loop, best of 3: 3.18 s per loop`

  2. piRSquared的改进 %%timeit df[['value']].fillna(df.groupby('group').transform('mean')) Output: 1 loop, best of 3: 2.78 s per loop

  3. 稍微更有效的方法(使用排序索引和fillna

您可以将group列设置为dataframe的索引,并对其进行排序。在

df = df.set_index('group').sort_index()

现在已经有了排序索引,通过使用df.loc[x,:]按组号访问数据帧的子集是非常便宜的

因为您需要用每个组的平均值进行插补,所以您需要所有唯一的组id。对于本例,您可以使用range(因为组是从0到99),但更一般地说,您可以使用:

groups = np.unique(set(df.index))

在这之后,您可以迭代这些组并使用fillna()进行插补: %%timeit for x in groups: df.loc[x,'value'] = df.loc[x,'value'].fillna(np.mean(df.loc[x,'value'])) Output: 1 loop, best of 3: 231 ms per loop

注意:set_indexsort_index和{}操作是一次性成本。公平地说,在我的机器上,总时间(包括这些操作)是2.26秒,但插补只花了231毫秒

你做错了。它很慢,因为您正在使用lambda

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

相关问题 更多 >