J'ai un DataFrame Pandas comme le suivant:
col1 col2 col3
1 0.2 0.3 0.3
2 0.2 0.3 0.3
3 0 0.4 0.4
4 0 0 0.3
5 0 0 0
6 0.1 0.4 0.4
Je souhaite remplacer les valeurs col1
par les valeurs de la deuxième colonne (col2
) uniquement si les valeurs col1
sont égales à 0 et après (pour les valeurs nulles restantes), recommencez l'opération mais avec la troisième colonne (col3
). Le résultat souhaité est le suivant:
col1 col2 col3
1 0.2 0.3 0.3
2 0.2 0.3 0.3
3 0.4 0.4 0.4
4 0.3 0 0.3
5 0 0 0
6 0.1 0.4 0.4
Je l’ai fait en utilisant la fonction pd.replace
, mais cela semble trop lent .. Je pense que cela doit être un moyen plus rapide d’accomplir cela.
df.col1.replace(0,df.col2,inplace=True)
df.col1.replace(0,df.col3,inplace=True)
existe-t-il un moyen plus rapide de le faire?, en utilisant une autre fonction au lieu de la fonction pd.replace
?
Utiliser np.where
est plus rapide. En utilisant un modèle similaire à celui que vous avez utilisé avec replace
:
df['col1'] = np.where(df['col1'] == 0, df['col2'], df['col1'])
df['col1'] = np.where(df['col1'] == 0, df['col3'], df['col1'])
Cependant, utiliser un np.where
imbriqué est légèrement plus rapide:
df['col1'] = np.where(df['col1'] == 0,
np.where(df['col2'] == 0, df['col3'], df['col2']),
df['col1'])
Timings
Utilisation de la configuration suivante pour produire un échantillon plus volumineux de DataFrame et de fonctions de minutage:
df = pd.concat([df]*10**4, ignore_index=True)
def root_nested(df):
df['col1'] = np.where(df['col1'] == 0, np.where(df['col2'] == 0, df['col3'], df['col2']), df['col1'])
return df
def root_split(df):
df['col1'] = np.where(df['col1'] == 0, df['col2'], df['col1'])
df['col1'] = np.where(df['col1'] == 0, df['col3'], df['col1'])
return df
def pir2(df):
df['col1'] = df.where(df.ne(0), np.nan).bfill(axis=1).col1.fillna(0)
return df
def pir2_2(df):
slc = (df.values != 0).argmax(axis=1)
return df.values[np.arange(slc.shape[0]), slc]
def andrew(df):
df.col1[df.col1 == 0] = df.col2
df.col1[df.col1 == 0] = df.col3
return df
def pablo(df):
df['col1'] = df['col1'].replace(0,df['col2'])
df['col1'] = df['col1'].replace(0,df['col3'])
return df
Je reçois les horaires suivants:
%timeit root_nested(df.copy())
100 loops, best of 3: 2.25 ms per loop
%timeit root_split(df.copy())
100 loops, best of 3: 2.62 ms per loop
%timeit pir2(df.copy())
100 loops, best of 3: 6.25 ms per loop
%timeit pir2_2(df.copy())
1 loop, best of 3: 2.4 ms per loop
%timeit andrew(df.copy())
100 loops, best of 3: 8.55 ms per loop
J'ai essayé de chronométrer votre méthode, mais elle fonctionne depuis plusieurs minutes sans terminer. À titre de comparaison, le chronométrage de votre méthode sur l'exemple de DataFrame à 6 lignes (et non la beaucoup plus grande testée ci-dessus) a pris 12,8 ms.
Je ne sais pas si c'est plus rapide, mais vous avez raison, vous pouvez découper le cadre de données pour obtenir le résultat souhaité.
df.col1[df.col1 == 0] = df.col2
df.col1[df.col1 == 0] = df.col3
print(df)
Sortie:
col1 col2 col3
0 0.2 0.3 0.3
1 0.2 0.3 0.3
2 0.4 0.4 0.4
3 0.3 0.0 0.3
4 0.0 0.0 0.0
5 0.1 0.4 0.4
Alternativement, si vous voulez que ce soit plus concis (même si je ne sais pas si c'est plus rapide), vous pouvez combiner ce que vous avez fait avec ce que j'ai fait.
df.col1[df.col1 == 0] = df.col2.replace(0, df.col3)
print(df)
Sortie:
col1 col2 col3
0 0.2 0.3 0.3
1 0.2 0.3 0.3
2 0.4 0.4 0.4
3 0.3 0.0 0.3
4 0.0 0.0 0.0
5 0.1 0.4 0.4
approche en utilisant pd.DataFrame.where
et pd.DataFrame.bfill
df['col1'] = df.where(df.ne(0), np.nan).bfill(axis=1).col1.fillna(0)
df
Une autre approche utilisant np.argmax
def pir2(df):
slc = (df.values != 0).argmax(axis=1)
return df.values[np.arange(slc.shape[0]), slc]
Je sais qu’il existe un meilleur moyen d’utiliser numpy
pour trancher. Je ne peux tout simplement pas y penser pour le moment.