2017-09-08 3 views
0

J'ai deux DataFrames. . .Recherche vectorisée sur une base de données pandas

df1 est un tableau dont j'ai besoin pour extraire des valeurs à partir de l'utilisation de paires de colonnes d'index extraites de plusieurs colonnes dans df2.

Je vois qu'il ya une fonction get_value qui fonctionne parfaitement lorsque donné une valeur d'index et de la colonne, mais en essayant de vectoriser cette fonction pour créer une nouvelle colonne Je manquerais ...

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 

Maintenant ce que je besoin de faire est de créer une nouvelle colonne entière sur df2 - lors de l'indexation df1 sur la base de l'index, les paires de colonnes des colonnes animal, letter spécifiées dans df2 efficacement vectoriser la fonction pd.get_value ci-dessus.

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 

résultant dans. . .

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 

Répondre

3

Il y a une fonction bien nommée lookup qui fait exactement cela.

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

Le verrouillage est le gagnant ici. Ne peut pas penser à une approche différente quand il y a un intégré. Et l'application tue la performance si elle est essayée. – Dark

+0

J'ai déjà essayé de chercher, mais après que votre réponse a trouvé que mon problème était lié à des valeurs qui ne regardent pas dans le tableau - existe-t-il un moyen de gérer les valeurs qui ne recherchent pas? –

+0

@AranFreel Je pense que vous pourriez jeter un coup d'oeil à la réponse de John Galt. –

3

Si vous cherchez un peu plus rapide approche zip puis aidera en cas de petits dataframe-à-dire

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

Sortie:

 
    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 

Comme John suggère que vous pouvez simplifier le code qui sera être beaucoup plus rapide.

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

En cas d'utilisation de données manquantes si les autres i.e.

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

Pour les petits dataframes

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

Pour les grandes dataframe

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

Autre façon d'écrire '[df1.get_value (r, c) pour r, c dans zip (df2.animal, df2.letter)]'? – Zero

+0

Qu'est-ce que 'df2.shape' ici? J'attends 'lookup' beaucoup plus rapidement pour des données plus volumineuses. – Zero

+0

Oui en effet. La méthode zip est rapide si df est petit. Ici df2.shape est (4,6). Comme je l'ai dit, la recherche est le gagnant parce que intégré. – Dark

1

lookup et get_value sont d'excellentes réponses si vos valeurs existent dans la base de données de recherche.

Cependant, si vous avez (ligne, colonne) paires ne sont pas présents dans la trame de données de recherche, et que vous voulez la valeur de recherche soit NaN - merge et stack est une façon de le faire

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 

test avec ajout d'une paire (animale, lettre) inexistante

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

L'approche que j'ai utilisée était de trouver l'intersection entre les colonnes 'df1' et les valeurs de la colonne' df2' ... à partir de là, je divise la 'dataframe' en alignées, et les données non alignées ... utilisé' lookup' sur l'alignement dataframe - puis les concaténés ensemble ... –