2017-06-02 5 views
1

Je me demande s'il existe un moyen plus efficace de faire une fonction de type "index & match" qui est populaire dans Excel. Par exemple, - donné deux pandas DataFrames, mettez à jour le df_1 avec l'information trouvée dans df_2:pandas dataframe index match

import pandas as pd 

df_1 = pd.DataFrame({'num_a':[1, 2, 3, 4, 5], 
        'num_b':[2, 4, 1, 2, 3]})  
df_2 = pd.DataFrame({'num':[1, 2, 3, 4, 5], 
        'name':['a', 'b', 'c', 'd', 'e']}) 

Je travaille avec des ensembles de données qui ont ~ 80.000 lignes dans les deux df_1 et df_2 et mon but est de créer deux nouvelles colonnes dans df_1, "nom_a" et "nom_b".

Ci-dessous est la méthode la plus efficace que je pourrais trouver. Il a pour être un meilleur moyen!

name_a = [] 
name_b = [] 
for i in range(len(df_1)): 

    name_a.append(df_2.name.iloc[df_2[ 
        df_2.num == df_1.num_a.iloc[i]].index[0]]) 
    name_b.append(df_2.name.iloc[df_2[ 
        df_2.num == df_1.num_b.iloc[i]].index[0]]) 

df_1['name_a'] = name_a 
df_1['name_b'] = name_b 

Entraînant:

>>> df_1.head() 
    num_a num_b name_a name_b 
0  1  2  a  b 
1  2  4  b  d 
2  3  1  c  a 
3  4  2  d  b 
4  5  3  e  c 

Répondre

1

haut niveau

  • Créer un dictionnaire à utiliser dans un replace
  • replace, rename colonnes et join

m = dict(zip(
    df_2.num.values.tolist(), 
    df_2.name.values.tolist() 
)) 

df_1.join(
    df_1.replace(m).rename(
     columns=lambda x: x.replace('num', 'name') 
    ) 
) 

    num_a num_b name_a name_b 
0  1  2  a  b 
1  2  4  b  d 
2  3  1  c  a 
3  4  2  d  b 
4  5  3  5  c 

Répartition

replace avec un dictionnaire devrait être assez rapide. Il existe un tas de façons de créer un formulaire de dictionnaire df_2. En fait, nous aurions pu utiliser un pd.Series. J'ai choisi de construire avec dict et zip parce que je trouve que c'est plus rapide.

Building m

Option 1

m = df_2.set_index('num').name 

Option 2

m = df_2.set_index('num').name.to_dict() 

Option 3

m = dict(zip(df_2.num, df_2.name)) 

Option 4 (Mon choix)

m = dict(zip(df_2.num.values.tolist(), df_2.name.values.tolist())) 

m fois construire

1000 loops, best of 3: 325 µs per loop 
1000 loops, best of 3: 376 µs per loop 
10000 loops, best of 3: 32.9 µs per loop 
100000 loops, best of 3: 10.4 µs per loop 

%timeit df_2.set_index('num').name 
%timeit df_2.set_index('num').name.to_dict() 
%timeit dict(zip(df_2.num, df_2.name)) 
%timeit dict(zip(df_2.num.values.tolist(), df_2.name.values.tolist())) 

Remplacement num

Encore une fois, nous avons des choix, voici quelques-uns et leurs temps.

%timeit df_1.replace(m) 
%timeit df_1.applymap(lambda x: m.get(x, x)) 
%timeit df_1.stack().map(lambda x: m.get(x, x)).unstack() 

1000 loops, best of 3: 792 µs per loop 
1000 loops, best of 3: 959 µs per loop 
1000 loops, best of 3: 925 µs per loop 

Je choisis ...

df_1.replace(m) 

    num_a num_b 
0  a  b 
1  b  d 
2  c  a 
3  d  b 
4  5  c 

Renommer des colonnes

df_1.replace(m).rename(columns=lambda x: x.replace('num', 'name')) 

    name_a name_b <-- note the column name change 
0  a  b 
1  b  d 
2  c  a 
3  d  b 
4  5  c 

Rejoignez

df_1.join(df_1.replace(m).rename(columns=lambda x: x.replace('num', 'name'))) 

    num_a num_b name_a name_b 
0  1  2  a  b 
1  2  4  b  d 
2  3  1  c  a 
3  4  2  d  b 
4  5  3  5  c 
0

Juste essayer une déclaration conditionnelle:

import pandas as pd 
import numpy as np 
df_1 = pd.DataFrame({'num_a':[1, 2, 3, 4, 5], 
        'num_b':[2, 4, 1, 2, 3]})  
df_2 = pd.DataFrame({'num':[1, 2, 3, 4, 5], 
        'name':['a', 'b', 'c', 'd', 'e']}) 
df_1["name_a"] = df_2["num_b"] 
df_1["name_b"] = np.array(df_1["name_a"][df_1["num_b"]-1]) 
print(df_1) 

    num_a num_b name_a name_b 
0  1  2  a  b 
1  2  4  b  d 
2  3  1  c  a 
3  4  2  d  b 
4  5  3  e  c