2016-06-07 2 views
5

Мне нужно преобразовать столбец категориальных переменных в кадре данных Pandas в числовое значение, соответствующее индексу, в массив уникальных категориальных переменных в столбце (! длинная история), и вот фрагмент кода, который выполняет следующее:Python - ускорить преобразование категориальной переменной в ее числовой индекс

import pandas as pd 
import numpy as np 

d = {'col': ["baked","beans","baked","baked","beans"]} 
df = pd.DataFrame(data=d) 
uniq_lab = np.unique(df['col']) 

for lab in uniq_lab: 
    df['col'].replace(lab,np.where(uniq_lab == lab)[0][0].astype(float),inplace=True) 

, который преобразует фрейм данных:

col 
0 baked 
1 beans 
2 baked 
3 baked 
4 beans 

в кадр данных:

col 
0 0.0 
1 1.0 
2 0.0 
3 0.0 
4 1.0 

по желанию. Но моя проблема заключается в том, что мой немой маленький цикл (единственный способ, который я думал сделать для этого), медленный, как меласса, когда я пытаюсь запустить аналогичный код в больших файлах данных. Мне было просто любопытно, есть ли у кого-нибудь мысли о том, есть ли способы сделать это более эффективно. Заранее благодарим за любые мысли.

ответ

4

Использование factorize:

df['col'] = pd.factorize(df.col)[0] 
print (df) 
    col 
0 0 
1 1 
2 0 
3 0 
4 1 

Docs

EDIT:

Как Jeff упоминалось в комментариях, то лучше всего преобразовать столбец categorical в основном потому, что меньше memory usage:

df['col'] = df['col'].astype("category") 

Timings:

Интересно, что в значительной ФР pandas скорее как numpy. Я не верю в это.

len(df)=500k:

In [29]: %timeit (a(df1)) 
100 loops, best of 3: 9.27 ms per loop 

In [30]: %timeit (a1(df2)) 
100 loops, best of 3: 9.32 ms per loop 

In [31]: %timeit (b(df3)) 
10 loops, best of 3: 24.6 ms per loop 

In [32]: %timeit (b1(df4)) 
10 loops, best of 3: 24.6 ms per loop 

len(df)=5k:

In [38]: %timeit (a(df1)) 
1000 loops, best of 3: 274 µs per loop 

In [39]: %timeit (a1(df2)) 
The slowest run took 6.71 times longer than the fastest. This could mean that an intermediate result is being cached. 
1000 loops, best of 3: 273 µs per loop 

In [40]: %timeit (b(df3)) 
The slowest run took 5.15 times longer than the fastest. This could mean that an intermediate result is being cached. 
1000 loops, best of 3: 295 µs per loop 

In [41]: %timeit (b1(df4)) 
1000 loops, best of 3: 294 µs per loop 

len(df)=5:

In [46]: %timeit (a(df1)) 
1000 loops, best of 3: 206 µs per loop 

In [47]: %timeit (a1(df2)) 
1000 loops, best of 3: 204 µs per loop 

In [48]: %timeit (b(df3)) 
The slowest run took 6.30 times longer than the fastest. This could mean that an intermediate result is being cached. 
10000 loops, best of 3: 164 µs per loop 

In [49]: %timeit (b1(df4)) 
The slowest run took 6.44 times longer than the fastest. This could mean that an intermediate result is being cached. 
10000 loops, best of 3: 164 µs per loop 

Код для тестирования:

d = {'col': ["baked","beans","baked","baked","beans"]} 
df = pd.DataFrame(data=d) 
print (df) 
df = pd.concat([df]*100000).reset_index(drop=True) 
#test for 5k 
#df = pd.concat([df]*1000).reset_index(drop=True) 


df1,df2,df3, df4 = df.copy(),df.copy(),df.copy(),df.copy() 

def a(df): 
    df['col'] = pd.factorize(df.col)[0] 
    return df 

def a1(df): 
    idx,_ = pd.factorize(df.col) 
    df['col'] = idx 
    return df 

def b(df): 
    df['col'] = np.unique(df['col'],return_inverse=True)[1] 
    return df 

def b1(df): 
    _,idx = np.unique(df['col'],return_inverse=True) 
    df['col'] = idx  
    return df 

print (a(df1))  
print (a1(df2)) 
print (b(df3)) 
print (b1(df4)) 
+0

Если бы я знал панд больше, я бы оценил его больше, может быть, но это тоже работает! Возможно, что-то вроде 'idx, _ = pd.factorize (df.col)' и, может быть, это может быть немного быстрее? Опять же, это ощущение кишки :) – Divakar

+0

Надеюсь, я когда-нибудь начну учиться «numpy» - есть много приятных функций, и это быстрее. Спасибо. Да, именно, я собираюсь сделать некоторые тесты. – jezrael

+0

Хм, интересно, в больших 'df'' pandas' быстрее, чем 'numpy'. – jezrael

3

Вы можете использовать np.unique «s дополнительный аргумент return_inverse для ID каждую строку, основанную на их уникальность среди других и установить те во входном dataframe, как так -

_,idx = np.unique(df['col'],return_inverse=True) 
df['col'] = idx 

Пожалуйста, обратите внимание, что IDs соответствуют уникальный сортированный по алфавиту массив строк.Если у вас есть, чтобы получить этот уникальный массив, вы можете заменить _ с ним, как так -

uniq_lab,idx = np.unique(df['col'],return_inverse=True) 

Sample пробег -

>>> d = {'col': ["baked","beans","baked","baked","beans"]} 
>>> df = pd.DataFrame(data=d) 
>>> df 
    col 
0 baked 
1 beans 
2 baked 
3 baked 
4 beans 
>>> _,idx = np.unique(df['col'],return_inverse=True) 
>>> df['col'] = idx 
>>> df 
    col 
0 0 
1 1 
2 0 
3 0 
4 1 
+0

@jezrael Ну, я просто надеюсь, что «категориальные переменные» не будут иметь эти «Nones» или «NaNs» :) – Divakar

+0

Да, но в реальных данных это возможно. :) Btw, возможно, более приятным является 'df ['col'] = np.unique (df ['col'], return_inverse = True) [1]' – jezrael

+2

@jezrael Well' \t 'df ['col'] = np .unique (df ['col'], return_inverse = True) 'будет вычислять как уникальные ярлыки, так и идентификаторы, а затем выбирать второй элемент с помощью' [1] ', что, по моему мнению, может немного пострадать. Итак, с '_, idx', я думаю, что он не потрудится рассчитать уникальные метки, и это может быть немного быстрее. В нем немного ощущения кишки, хотя :) – Divakar

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