2016-06-07 9 views
5

Potrzebuję przekształcić kolumnę zmiennych jakościowych w ramce danych Pandy na wartość liczbową odpowiadającą indeksowi w tablicy unikatowych zmiennych jakościowych w kolumnie (! długa historia) i oto fragment kodu, który realizuje, że:Python - Przyspieszenie do konwersji zmiennej kategorialnej na jej numeryczny indeks

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) 

który konwertuje ramkę danych:

col 
0 baked 
1 beans 
2 baked 
3 baked 
4 beans 

w ramce danych:

col 
0 0.0 
1 1.0 
2 0.0 
3 0.0 
4 1.0 

zgodnie z potrzebami. Ale moim problemem jest to, że moja głupia mała pętla (jedyny sposób, w jaki myślałem o tym) jest powolna jak melasa, kiedy próbuję uruchomić podobny kod na dużych plikach danych. Byłem po prostu ciekawy, czy ktoś ma jakiekolwiek przemyślenia na temat tego, czy istnieją sposoby, aby to zrobić bardziej efektywnie. Z góry dziękuję za wszelkie przemyślenia.

Odpowiedz

4

Zastosowanie factorize:

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

Docs

EDIT:

Jak Jeff mowa w komentarzu, wtedy najlepiej przekonwertować kolumnę categorical głównie dlatego mniej memory usage:

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

Timings:

To ciekawe, w dużej df pandas jest szybszy jak numpy. Nie mogę w to uwierzyć.

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 

kod do testowania:

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

Gdybym wiedział pandy więcej, to bym to doceniliśmy więcej może, ale to też działa! Może zrobić coś takiego jak "idx, _ = pd.factorize (df.col)" i może to może być trochę szybciej? Znowu, to jest przeczucie :) – Divakar

+0

Mam nadzieję, że kiedyś zacznę się uczyć 'numpy' - jest dużo fajnej funkcji i jest szybsza. Dziękuję Ci. Tak, zamierzam zrobić kilka testów. – jezrael

+0

Hmmm, ciekawe, w dużych '' pf '' pand' jest szybsze jako 'numpy'. – jezrael

3

Można użyć np.unique jest opcjonalny argument return_inverse zidentyfikować każdy łańcuch w oparciu o ich wyjątkowość wśród innych i ustawić te w dataframe wejściowego, tak jak -

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

Należy pamiętać, że IDs odpowiadać unikalny alfabetycznie uporządkowany układ łańcuchów.Jeśli masz dostać tę wyjątkową tablicę, można zastąpić _ z nim, tak jak -

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

Sample run -

>>> 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 Cóż, mam tylko nadzieję, że "zmienne kategoryczne" nie będą miały "Nones" lub "NaNs" :) – Divakar

+0

Tak, ale w prawdziwych danych jest to możliwe. :) Btw, może ładniej jest 'df ['col'] = np.unique (df ['col'], return_inverse = True) [1]' – jezrael

+2

@jezrael Well '\t ' df ['col'] = np .unique (df ['col'], return_inverse = True) "obliczyłby zarówno unikalne etykiety, jak i identyfikatory, a następnie wybrałby drugi element z' [1] ', co moim zdaniem może zająć trochę wydajności. Tak więc, z '_, idx', myślę, że nie będzie zawracać sobie głowy obliczaniem unikatowych etykiet i które mogą być nieco szybsze. Jest w tym trochę uczucia :) – Divakar