簡體   English   中英

熊貓:在每組中按平均值填充缺失值比轉換更快

[英]Pandas: Fill missing values by mean in each group faster than transform

我需要通過每組中的平均值填充pandas DataFrame中的缺失值。 根據這個問題, transform可以實現這一點。

但是, transform對我來說太慢了。

例如,對具有100個不同組和70% NaN值的大型DataFrame進行以下設置:

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

df.groupby("group").transform(lambda x: x.fillna(x.mean())) # Takes too long

我的電腦上已經超過3秒鍾了。 我需要更快一些數量級的東西(購買更大的機器不是一種選擇:-D)。

那么我怎樣才能更快地填補缺失值?

你這樣做是錯的。 它很慢因為你正在使用lambda

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

使用排序索引+ 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列設置為數據幀的索引,並對其進行排序。

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_indexnp.unique操作是一次性成本。 為了公平對待每個人,我的機器上的總時間(包括這些操作)是2.26秒,但是估算片只花了231毫秒。

這是使用np.bincount的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列。

運行時測試

數據量:

size = 1000000  # DataFrame length
ngroups = 10  # Number of Groups

時間:

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+加速!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM