如何加速LabelEncoder将分类变量重新编码为整数

ele*_*ora 5 python pandas scikit-learn

我有一个大的csv,每行有两个字符串:

g,k
a,h
c,i
j,e
d,i
i,h
b,b
d,d
i,a
d,h
Run Code Online (Sandbox Code Playgroud)

我在前两列中读取并将字符串重新编码为整数,如下所示:

import pandas as pd
df = pd.read_csv("test.csv", usecols=[0,1], prefix="ID_", header=None)
from sklearn.preprocessing import LabelEncoder

# Initialize the LabelEncoder.
le = LabelEncoder()
le.fit(df.values.flat)

# Convert to digits.
df = df.apply(le.transform)
Run Code Online (Sandbox Code Playgroud)

此代码来自/sf/answers/2759353971/.

代码工作得很好,但是当df很大时代码很慢.我计划每一步,结果令我感到惊讶.

  • pd.read_csv 大约需要40秒.
  • le.fit(df.values.flat) 大约需要30秒
  • df = df.apply(le.transform) 大约需要250秒.

有没有办法加快这最后一步?感觉它应该是他们所有人中最快的一步!


在具有4GB RAM的计算机上进行重新编码步骤的更多时间

maxymoo下面的答案很快,但没有给出正确的答案.以问题顶部的示例csv为例,将其转换为:

   0  1
0  4  6
1  0  4
2  2  5
3  6  3
4  3  5
5  5  4
6  1  1
7  3  2
8  5  0
9  3  4
Run Code Online (Sandbox Code Playgroud)

请注意,'d'在第一列中映射到3,在第二列中映射到2.

我尝试了/sf/answers/2754947891/的解决方案,并获得以下内容.

df = pd.DataFrame({'ID_0':np.random.randint(0,1000,1000000), 'ID_1':np.random.randint(0,1000,1000000)}).astype(str)
df.info()
memory usage: 7.6MB
%timeit x = (df.stack().astype('category').cat.rename_categories(np.arange(len(df.stack().unique()))).unstack())
1 loops, best of 3: 1.7 s per loop
Run Code Online (Sandbox Code Playgroud)

然后我将数据帧大小增加了10倍.

df = pd.DataFrame({'ID_0':np.random.randint(0,1000,10000000), 'ID_1':np.random.randint(0,1000,10000000)}).astype(str) 
df.info()
memory usage: 76.3+ MB
%timeit x = (df.stack().astype('category').cat.rename_categories(np.arange(len(df.stack().unique()))).unstack())
MemoryError                               Traceback (most recent call last)
Run Code Online (Sandbox Code Playgroud)

这种方法似乎使用了大量的RAM来尝试翻译它崩溃的相对较小的数据帧.

我还为LabelEncoder定时了一个包含1000万行的更大数据集.它运行没有崩溃,但单独的拟合线需要50秒.df.apply(le.transform)步骤大约需要80秒.

我怎么能够:

  1. 得到一些大致与maxymoo的答案速度大致相当于LabelEncoder的内存使用量,但是当数据帧有两列时,它给出了正确的答案.
  2. 存储映射,以便我可以将其重用于不同的数据(就像LabelEncoder允许我这样做的那样)?

max*_*moo 8

看起来使用 pandascategory数据类型会快得多;在内部,它使用哈希表,而 LabelEncoder 使用排序搜索:

In [87]: df = pd.DataFrame({'ID_0':np.random.randint(0,1000,1000000), 
                            'ID_1':np.random.randint(0,1000,1000000)}).astype(str)

In [88]: le.fit(df.values.flat) 
         %time x = df.apply(le.transform)
CPU times: user 6.28 s, sys: 48.9 ms, total: 6.33 s
Wall time: 6.37 s

In [89]: %time x = df.apply(lambda x: x.astype('category').cat.codes)
CPU times: user 301 ms, sys: 28.6 ms, total: 330 ms
Wall time: 331 ms
Run Code Online (Sandbox Code Playgroud)

编辑:这是您可以使用的自定义转换器类(您可能不会在官方的 scikit-learn 版本中看到它,因为维护者不想将 Pandas 作为依赖项)

import pandas as pd
from pandas.core.nanops import unique1d
from sklearn.base import BaseEstimator, TransformerMixin

class PandasLabelEncoder(BaseEstimator, TransformerMixin):
    def fit(self, y):
        self.classes_ = unique1d(y)
        return self

    def transform(self, y):
        s = pd.Series(y).astype('category', categories=self.classes_)
        return s.cat.codes
Run Code Online (Sandbox Code Playgroud)