2016-06-07 48 views
5

Ho bisogno di convertire una colonna di variabili categoriali in un frame di dati di Pandas in un valore numerico che corrisponde all'indice in una matrice delle variabili categoriali univoche nella colonna (! lunga storia) ed ecco un frammento di codice che riesce a far questo:Python - Velocizza la conversione di una variabile categoriale nel suo indice numerico

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) 

che converte il frame di dati:

col 
0 baked 
1 beans 
2 baked 
3 baked 
4 beans 

nella cornice di dati:

col 
0 0.0 
1 1.0 
2 0.0 
3 0.0 
4 1.0 

come desiderato. Ma il mio problema è che il mio stupido loop per il ciclo (l'unico modo in cui ho pensato di farlo) è lento come melassa quando provo a eseguire un codice simile su file di big data. Ero solo curioso di sapere se qualcuno avesse qualche idea sul fatto che ci fossero dei modi per farlo in modo più efficiente. Grazie in anticipo per ogni pensiero.

risposta

4

Uso factorize:

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

Docs

EDIT:

Come Jeff menzionato nel commento, quindi la cosa migliore è di convertire colonna per categorical soprattutto perché meno memory usage:

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

Timings:

E 'interessante, in gran df pandas è più veloce come numpy. Non posso crederci.

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 

Codice per la prova:

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

Se avessi saputo panda di più, avrei apprezzato più forse, ma anche questo funziona! Forse fare qualcosa come 'idx, _ = pd.factorize (df.col)' e forse potrebbe essere un po 'più veloce? Ancora una volta, è una sensazione istintiva :) – Divakar

+0

Spero di iniziare una volta a imparare "numpy" - c'è una funzione molto bella ed è più veloce. Grazie. Sì, esattamente, farò dei test. – jezrael

+0

Hmmm, interessante, in grandi 'df'' pandas' è più veloce come 'numpy'. – jezrael

3

È possibile utilizzare s' np.unique argomento opzionale return_inverse a ID ciascuna stringa in base alla loro unicità tra gli altri, e impostare quelli del dataframe di ingresso, in questo modo -

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

Si prega di notare che il IDs corrispondono a una matrice di stringhe in ordine alfabetico univoco.Se dovete ottenere tale matrice unica, è possibile sostituire _ con esso, in questo modo -

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

Campione 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 Bene, sto solo sperando che le 'variabili categoriali 'non abbiano quei' Nones' o 'NaNs' :) – Divakar

+0

Sì, ma in dati reali è possibile. :) Btw, forse più bello è 'df ['col'] = np.unique (df ['col'], return_inverse = True) [1]' – jezrael

+2

@jezrael Bene '\t ' df ['col'] = np .unique (df ['col'], return_inverse = True) 'calcola sia le etichette univoche che gli ID e poi seleziono il secondo elemento con' [1] ', che a mio avviso potrebbe richiedere un po 'di successo nelle prestazioni. Quindi, con '_, idx', penso che non si preoccuperà di calcolare le etichette univoche e che potrebbe essere un po 'più veloce. C'è però un po 'di istinto dentro :) :) – Divakar