python pandas dataframe vectorization lookup

python - Búsqueda vectorizada en un marco de datos de pandas



dataframe vectorization (3)

Hay una función llamada lookup que hace exactamente esto.

df2[''looked_up''] = df1.lookup(df2.animal, df2.letter) df2 0 1 2 3 4 animal letter looked_up 0 0 1 2 3 4 cat a 0 1 5 6 7 8 9 dog b 6 2 10 11 12 13 14 fish c 12 3 15 16 17 18 19 bird d 18

Tengo dos marcos de datos. . .

df1 es una tabla en la que necesito extraer valores del índice, pares de columnas recuperados de varias columnas en df2.

Veo que hay una función get_value que funciona perfectamente cuando se le da un índice y un valor de columna, pero cuando intento vectorizar esta función para crear una nueva columna, estoy fallando ...

df1 = pd.DataFrame(np.arange(20).reshape((4, 5))) df1.columns = list(''abcde'') df1.index = [''cat'', ''dog'', ''fish'', ''bird''] a b c d e cat 0 1 2 3 4 dog 5 6 7 8 9 fish 10 11 12 13 14 bird 15 16 17 18 19 df1.get_value(''bird, ''c'') 17

Ahora lo que tengo que hacer es crear una columna completamente nueva en df2 : cuando indexe df1 función del índice, pares de columnas del animal , las columnas de letter especificadas en df2 vectorizan efectivamente la función pd.get_value anterior.

df2 = pd.DataFrame(np.arange(20).reshape((4, 5))) df2[''animal''] = [''cat'', ''dog'', ''fish'', ''bird''] df2[''letter''] = list(''abcd'') 0 1 2 3 4 animal letter 0 0 1 2 3 4 cat a 1 5 6 7 8 9 dog b 2 10 11 12 13 14 fish c 3 15 16 17 18 19 bird d

Resultando en . . .

0 1 2 3 4 animal letter looked_up 0 0 1 2 3 4 cat a 0 1 5 6 7 8 9 dog b 6 2 10 11 12 13 14 fish c 12 3 15 16 17 18 19 bird d 18


Si busca un enfoque un poco más rápido, zip lo ayudará en el caso de un marco de datos pequeño, es decir

k = list(zip(df2[''animal''].values,df2[''letter''].values)) df2[''looked_up''] = [df1.get_value(*i) for i in k]

Salida:

0 1 2 3 4 animal letter looked_up 0 0 1 2 3 4 cat a 0 1 5 6 7 8 9 dog b 6 2 10 11 12 13 14 fish c 12 3 15 16 17 18 19 bird d 18

Como John sugirió, puede simplificar el código, que será mucho más rápido.

df2[''looked_up''] = [df1.get_value(r, c) for r, c in zip(df2.animal, df2.letter)]

En caso de que falten datos, use si más

df2[''looked_up''] = [df1.get_value(r, c) if not pd.isnull(c) | pd.isnull(r) else pd.np.nan for r, c in zip(df2.animal, df2.letter) ]

Para pequeños marcos de datos

%%timeit df2[''looked_up''] = df1.lookup(df2.animal, df2.letter) 1000 loops, best of 3: 801 µs per loop k = list(zip(df2[''animal''].values,df2[''letter''].values)) df2[''looked_up''] = [df1.get_value(*i) for i in k] 1000 loops, best of 3: 399 µs per loop [df1.get_value(r, c) for r, c in zip(df2.animal, df2.letter)] 10000 loops, best of 3: 87.5 µs per loop

Para grandes marcos de datos

df3 = pd.concat([df2]*10000) %%timeit k = list(zip(df3[''animal''].values,df3[''letter''].values)) df2[''looked_up''] = [df1.get_value(*i) for i in k] 1 loop, best of 3: 185 ms per loop df2[''looked_up''] = [df1.get_value(r, c) for r, c in zip(df3.animal, df3.letter)] 1 loop, best of 3: 165 ms per loop df2[''looked_up''] = df1.lookup(df3.animal, df3.letter) 100 loops, best of 3: 8.82 ms per loop


lookup y get_value son excelentes respuestas si sus valores existen en el marco de datos de búsqueda.

Sin embargo, si los pares (fila, columna) no están presentes en el marco de datos de búsqueda y desea que el valor de búsqueda sea NaN , la merge y el stack es una forma de hacerlo

In [206]: df2.merge(df1.stack().reset_index().rename(columns={0: ''looked_up''}), left_on=[''animal'', ''letter''], right_on=[''level_0'', ''level_1''], how=''left'').drop([''level_0'', ''level_1''], 1) Out[206]: 0 1 2 3 4 animal letter looked_up 0 0 1 2 3 4 cat a 0 1 5 6 7 8 9 dog b 6 2 10 11 12 13 14 fish c 12 3 15 16 17 18 19 bird d 18

Prueba con la adición de un par no existente (animal, letra)

In [207]: df22 Out[207]: 0 1 2 3 4 animal letter 0 0.0 1.0 2.0 3.0 4.0 cat a 1 5.0 6.0 7.0 8.0 9.0 dog b 2 10.0 11.0 12.0 13.0 14.0 fish c 3 15.0 16.0 17.0 18.0 19.0 bird d 4 NaN NaN NaN NaN NaN dummy NaN In [208]: df22.merge(df1.stack().reset_index().rename(columns={0: ''looked_up''}), left_on=[''animal'', ''letter''], right_on=[''level_0'', ''level_1''], how=''left'').drop([''level_0'', ''level_1''], 1) Out[208]: 0 1 2 3 4 animal letter looked_up 0 0.0 1.0 2.0 3.0 4.0 cat a 0.0 1 5.0 6.0 7.0 8.0 9.0 dog b 6.0 2 10.0 11.0 12.0 13.0 14.0 fish c 12.0 3 15.0 16.0 17.0 18.0 19.0 bird d 18.0 4 NaN NaN NaN NaN NaN dummy NaN NaN