3 votes

Remplacer les valeurs d'un dataframe par celles d'un autre dataframe avec Pandas

J'ai 3 cadres de données : df1 , df2 , df3 . J'essaie de remplir NaN valeurs de df1 avec certaines valeurs contenues dans df2 . Les valeurs choisies parmi df2 sont également sélectionnés en fonction de la sortie d'une fonction simple ( mul_val ) qui traite certaines données stockées dans df3 .

J'ai pu obtenir ce résultat mais j'aimerais trouver un code plus simple, plus facile et plus lisible.

Voici ce que j'ai jusqu'à présent :

import pandas as pd
import numpy as np

# simple function
def mul_val(a,b):
    return a*b

# dataframe 1
data = {'Name':['PINO','PALO','TNCO' ,'TNTO','CUCO' ,'FIGO','ONGF','LABO'],
        'Id'  :[  10  ,  9   ,np.nan ,  14   , 3    ,np.nan,  7   ,np.nan]}
df1 = pd.DataFrame(data)

# dataframe 2
infos = {'Info_a':[10,20,30,40,70,80,90,50,60,80,40,50,20,30,15,11],
         'Info_b':[10,30,30,60,10,85,99,50,70,20,30,50,20,40,16,17]}
df2 = pd.DataFrame(infos)

dic = {'Name': {0: 'FIGO', 1: 'TNCO'}, 
       'index': {0: [5, 6], 1: [11, 12, 13]}}
df3 = pd.DataFrame(dic)

#---------------Modify from here in the most efficient way!-----------------

for idx,row in df3.iterrows():
    store_val = []
    print(row['Name'])
    for j in row['index']:
        store_val.append([mul_val(df2['Info_a'][j],df2['Info_b'][j]),j])
    store_val = np.asarray(store_val)

    # - Identify which is the index of minimum value of the first column
    indx_min_val = np.argmin(store_val[:,0])

    # - Get the value relative number contained in the second column
    col_value = row['index'][indx_min_val]

    # Identify value to be replaced in df1
    value_to_be_replaced = df1['Id'][df1['Name']==row['Name']]

    # - Replace such value into the df1 having the same row['Name']
    df1['Id'].replace(to_replace=value_to_be_replaced,value=col_value, inplace=True)

Par impression store_val à chaque itération que je reçois :

FIGO
[[6800    5]   
 [8910    6]]
TNCO
[[2500   11]
 [ 400   12]
 [1200   13]]

Faisons un exemple simple : considérant FIGO j'identifie 6800 comme le nombre minimum entre 6800 et 8910 . Je choisis donc le nombre 5 qui est placé dans df1 . En répétant cette opération pour les rangées restantes de df3 (dans ce cas, je n'ai que 2 lignes mais il pourrait y en avoir beaucoup plus), le résultat final devrait être comme ceci :

In[0]: before           In[0]: after
Out[0]:                 Out[0]: 
     Id  Name                Id  Name
0  10.0  PINO           0  10.0  PINO
1   9.0  PALO           1   9.0  PALO
2   NaN  TNCO  ----->   2  12.0  TNCO
3  14.0  TNTO           3  14.0  TNTO
4   3.0  CUCO           4   3.0  CUCO
5   NaN  FIGO  ----->   5   5.0  FIGO
6   7.0  ONGF           6   7.0  ONGF
7   NaN  LABO           7   NaN  LABO

Nore : vous pouvez également supprimer les boucles for si nécessaire et utiliser différents types de formats pour stocker les données (liste, tableaux...) ; l'important est que le résultat final soit toujours un dataframe.

1voto

Julien Marrec Points 6598

Je peux vous proposer deux options similaires qui permettent d'obtenir le même résultat que votre boucle en quelques lignes :

1.Utiliser appliquer et fillna() ( fillna est plus rapide que combine_first par un facteur de deux) :

  df3['Id'] = df3.apply(lambda row: (df2.Info_a*df2.Info_b).loc[row['index']].argmin(), axis=1)
  df1 = df1.set_index('Name').fillna(df3.set_index('Name')).reset_index()

2. l'utilisation d'une fonction (lambda ne supporte pas l'assignation, donc vous devez appliquer un func)

def f(row):
    df1.ix[df1.Name==row['Name'], 'Id'] = (df2.Info_a*df2.Info_b).loc[row['index']].argmin()
df3.apply(f, axis=1)

ou une légère variante ne reposant pas sur des définitions globales :

def f(row, df1, df2):
    df1.ix[df1.Name==row['Name'], 'Id'] = (df2.Info_a*df2.Info_b).loc[row['index']].argmin()
df3.apply(f, args=(df1,df2,), axis=1)

Notez que votre solution, bien que beaucoup plus verbeuse, prendra le moins de temps avec ce petit ensemble de données (7,5 ms contre 9,5 ms pour les deux miennes). Il est logique que la vitesse soit similaire, puisque dans les deux cas il s'agit de boucler sur les lignes du fichier df3

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X