Python – Acelera para convertir una variable categórica a su índice numérico

Necesito convertir una columna de variables categóricas en un dataframe de Pandas en un valor numérico que corresponda al índice en una matriz de las variables categóricas únicas en la columna (¡historia larga!) Y aquí hay un fragmento de código que lo logra:

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) 

que convierte el dataframe:

  col 0 baked 1 beans 2 baked 3 baked 4 beans 

en el dataframe:

  col 0 0.0 1 1.0 2 0.0 3 0.0 4 1.0 

como se desee. Pero mi problema es que mi tonto para el bucle (la única forma en que he pensado hacer esto) es tan lento como la melaza cuando bash ejecutar un código similar en archivos de datos grandes. Tenía curiosidad por saber si alguien tenía alguna idea sobre si había alguna forma de hacerlo de manera más eficiente. Gracias de antemano por cualquier pensamiento.

Utilice factorize :

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

Docs

EDITAR:

Como Jeff mencionó en el comentario, lo mejor es convertir la columna en categorical principalmente porque se usa menos memoria :

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

Tiempos :

Es interesante, en grandes df los pandas son más rápidos como el numpy . No puedo creerlo.

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 

Código de prueba :

 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)) 

Puede usar el argumento opcional de return_inverse para identificar cada cadena en función de su unicidad entre otras y establecerlas en el dataframe de entrada, como así:

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

Tenga en cuenta que los IDs corresponden a una matriz única ordenada alfabéticamente de las cadenas. Si tiene que obtener esa matriz única, puede reemplazar _ con ella, como así:

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

Ejecución de la muestra

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