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