2016-09-13 6 views
5

у меня есть большой файл CSV с двумя строками в строке в таком виде:Как ускорить LabelEncoder до перекодировки категориальной переменной в целые

g,k 
a,h 
c,i 
j,e 
d,i 
i,h 
b,b 
d,d 
i,a 
d,h 

Я прочитал в первых двух столбцах и перекодировать строки в целых чисел следующим образом :

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) 

Этот код от https://stackoverflow.com/a/39419342/2179021.

Код работает очень хорошо, но медленный, когда df большой. Я приурочил каждый шаг, и результат был неожиданным для меня.

  • pd.read_csv занимает около 40 секунд.
  • le.fit(df.values.flat) занимает около 30 секунд
  • df = df.apply(le.transform) занимает около 250 секунд.

Есть ли способ ускорить этот последний шаг? Похоже, это должен быть самый быстрый шаг для всех!


Больше тайминги для шага перекодировки на компьютере с 4 ГБ оперативной памяти

Ответ ниже по 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 

Обратите внимание, что «d» отображается 3 в первой колонке, но 2 во втором.

Я попробовал решение от https://stackoverflow.com/a/39356398/2179021 и получил следующее.

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 

Тогда я увеличил размер dataframe на коэффициент 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) 

Этот метод, как представляется, использовать столько оперативной памяти, пытаясь перевести эту относительно небольшую dataframe, что он выходит из строя.

Я также приурочил LabelEncoder к более крупному набору данных с 10 миллионами строк. Он работает без сбоев, но только линия подгонки заняла 50 секунд. Шаг df.apply (le.transform) занял около 80 секунд.

Как я могу:

  1. Получить что-то примерно скорости ответа maxymoo в и примерно использование памяти LabelEncoder, но что дает правильный ответ, когда dataframe имеет две колонки.
  2. Храните сопоставление, чтобы я мог повторно использовать его для разных данных (как это может сделать LabelEncoder)?
+0

Как велика ваша CSV - строк и столбцов? Сколько у вас le.class? – wwii

+0

@wwii Около 10 миллионов строк и 1 млн. Le.classes. – eleanora

+0

Что делает '' 'groupby ....' '' выполнить? – wwii

ответ

4

Похоже, что использовать pandas будет гораздо быстрее, если вы используете pandas category datatype; внутри этого использует хэш-таблицу, а в то время как 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 

EDIT: Вот это пользовательский класс трансформатора, который, что вы могли бы использовать (вы, вероятно, не будете видеть это в официальной scikit учиться релиз так сопроводителей не хочет иметь панда как зависимость)

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

Спасибо, хотя мне нужно согласованное кодирование в двух столбцах, и у меня не будет каждого значения, представленного в обоих столбцах. – eleanora

+0

Мне нужно иметь возможность применить это сопоставление к другому кадру данных. Есть ли способ сохранить его как labelencoder? – eleanora

+0

Спасибо, что редактировали! Это выглядит очень интересно, – eleanora

3

Я попытался это с DataFrame:

In [xxx]: import string 
In [xxx]: letters = np.array([c for c in string.ascii_lowercase]) 
In [249]: df = pd.DataFrame({'ID_0': np.random.choice(letters, 10000000), 'ID_1':np.random.choice(letters, 10000000)}) 

Это выглядит так:

In [261]: df.head() 
Out[261]: 
    ID_0 ID_1 
0 v z 
1 i i 
2 d n 
3 z r 
4 x x 

In [262]: df.shape 
Out[262]: (10000000, 2) 

Итак, 10 миллионов строк. Локально мои тайминги:

In [257]: % timeit le.fit(df.values.flat) 
1 loops, best of 3: 17.2 s per loop 

In [258]: % timeit df2 = df.apply(le.transform) 
1 loops, best of 3: 30.2 s per loop 

Тогда я сделал отображение ДИКТ письма чисел и использовать pandas.Series.map:

In [248]: letters = np.array([l for l in string.ascii_lowercase]) 
In [263]: d = dict(zip(letters, range(26))) 

In [273]: %timeit for c in df.columns: df[c] = df[c].map(d) 
1 loops, best of 3: 1.12 s per loop 

In [274]: df.head() 
Out[274]: 
    ID_0 ID_1 
0 21 25 
1  8  8 
2  3 13 
3 25 17 
4 23 23 

Так что может быть вариант. У dict просто должны быть все значения, которые происходят в данных.

EDIT: ОП задал вопрос, какое время у меня есть для этого второго варианта, с категориями. Это то, что я получаю:

In [40]: %timeit x=df.stack().astype('category').cat.rename_categories(np.arange(len(df.stack().unique()))).unstack() 
1 loops, best of 3: 13.5 s per loop 

EDIT: за 2 комментария:

In [45]: %timeit uniques = np.sort(pd.unique(df.values.ravel())) 
1 loops, best of 3: 933 ms per loop 

In [46]: %timeit dfc = df.apply(lambda x: x.astype('category', categories=uniques)) 
1 loops, best of 3: 1.35 s per loop 
+0

Это отличный ответ, я не знал, что вы могли бы отобразить такой словарь, очень хороший трюк. btw более функциональным синтаксисом будет 'df.apply (lambda c: c.map (d))' (хотя по какой-то причине это медленнее на 0,5 с) – maxymoo

+0

Спасибо. Можете ли вы время 'uniques = np.sort (pd.unique (df.values.ravel())) df.apply (lambda x: x.astype ('category', categories = uniques)) 'тоже, пожалуйста? – eleanora

+0

Благодарим вас за то, что я не имел в виду код, который вы использовали. Я имел в виду буквально две строки, которые я вложил в комментарий. Вам не нужно что-либо другое, если вы (например, стека и стека)? – eleanora

Смежные вопросы