对于表示为字符串值的分类数据,sklearn SimpleImputer 太慢了

数据挖掘 Python scikit-学习 熊猫 预处理
2021-10-02 14:27:29

我有一个数据集,其分类特征表示为字符串值,我想在其中填写缺失值。我尝试使用 sklearn SimpleImputer,但与 pandas 相比,完成任务需要太多时间。两种方法产生相同的输出。

这是在合成数据上重现行为的代码:

import numpy as np
import pandas as pd
from sklearn.impute import SimpleImputer

lst = np.array(['a', 'b', np.nan], dtype='object')
arr = np.random.choice(lst, size=(10**6,1), p=[0.6, 0.3, 0.1])
ser = pd.Series(arr.ravel())

使用 SimpleImputer:

%%time
imp = SimpleImputer(missing_values=np.nan, strategy='most_frequent')
imp.fit_transform(arr)

挂壁时间:13 秒

使用熊猫:

%%time
ser.fillna(value=ser.mode()[0])

挂墙时间:64.8 毫秒

当字符串值更长时(例如“abc”而不是一个字母“a”),情况会变得更糟。对于数值数据,pandas 仍然优于 sklearn,但差异并不大。

我究竟做错了什么?

1个回答

在 Sklearn 的源代码中搜索 SimpleImputer(使用 strategy="most_frequent"),最频繁的值是在 python 的循环中计算的,因此这是代码中非常慢的部分。在 SimpleImputer 的源代码中,还有解释为什么他们不使用 scipy.stats.mstats.mode 的注释,它更快:

scipy.stats.mstats.mode 不能使用,因为如果第一个元素被屏蔽并且它的频率等于最频繁的有效元素的频率,它将无法正常工作。https://github.com/scipy/scipy/issues/2636

因此,如果您想将 SimpleImputer 与此策略一起使用,更快的方法是使用“常量”策略并自己传递最频繁的值(ser.mode()[0]),那么时间几乎相同:

t0 = time.time()
imp = SimpleImputer(missing_values=np.nan, strategy='most_frequent')
imp.fit_transform(arr)
print('Simple Imputer (Most Frequent) Time Elapsed:', time.time()-t0)

t0 = time.time()
imp = SimpleImputer(missing_values=np.nan, strategy='constant', fill_value=ser.mode()[0])
imp.fit_transform(arr)
print('Simple Imputer (Constant) Time Elapsed:', time.time()-t0)

t0 = time.time()
ser.fillna(value=ser.mode()[0])
print('Pandas Time Elapsed:', time.time()-t0)

每个策略所用的时间:

Simple Imputer (Most Frequent) Time Elapsed: 14.320188045501709
Simple Imputer (Constant) Time Elapsed: 0.052472829818725586
Pandas Time Elapsed: 0.04726815223693848