Pandas: chute de performance particulière pour inplace renommer après dropna

, j'ai signalé ce problème sur pandas "problèmes . Dans l'intervalle, je poste ceci ici dans l'espoir de sauver d'autres temps, au cas où ils rencontrent des problèmes similaires.

après avoir établi le profil d'un processus qui devait être optimisé, j'ai constaté que le renommage des colonnes non en place améliore les performances (temps d'exécution) de x120. Le profilage indique que cela est lié à la collecte des ordures (voir ci-dessous).

en outre, les prévisions la performance est récupérée en évitant la méthode dropna.

le bref exemple suivant démontre un facteur x12:

import pandas as pd
import numpy as np

inplace=True

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.rand(r)
df1 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.rand(len(indx))
df2 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
df = (df1-df2).dropna()
## inplace rename:
df.rename(columns={col:'d{}'.format(col) for col in df.columns}, inplace=True)

100 boucles, best of 3: 15.6 ms par boucle

première ligne de sortie de %%prun :

ncalls tottime percall cumtime percall filename: lineno(function)

1  0.018 0.018 0.018 0.018 {gc.collect}

inplace=False

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.rand(r)
df1 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.rand(len(indx))
df2 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
df = (df1-df2).dropna()
## avoid inplace:
df = df.rename(columns={col:'d{}'.format(col) for col in df.columns})

1000 boucles, best of 3: 1.24 ms par boucle

eviter dropna

la performance attendue est récupérée en évitant la méthode dropna :

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.rand(r)
df1 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.rand(len(indx))
df2 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
#no dropna:
df = (df1-df2)#.dropna()
## inplace rename:
df.rename(columns={col:'d{}'.format(col) for col in df.columns}, inplace=True)

1000 boucles, best of 3: 865 µs par boucle

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.rand(r)
df1 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.rand(len(indx))
df2 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
## no dropna
df = (df1-df2)#.dropna()
## avoid inplace:
df = df.rename(columns={col:'d{}'.format(col) for col in df.columns})

1000 boucles, best of 3: 902 µs par boucle

18
demandé sur eldad-a 2014-03-20 15:59:42

1 réponses

Ceci est une copie de l'explication sur github.

il y a aucune garantie qu'une opération inplace est en fait plus rapide. Souvent, il s'agit en fait de la même opération qui fonctionne sur une copie, mais la référence de haut niveau est réassignée.

La raison de la différence de performance dans ce cas, est comme suit.

l'appel (df1-df2).dropna() crée une tranche de la base de données. Lorsque vous appliquez un nouveau opération, cela déclenche un SettingWithCopy vérifier parce qu'il pourrait être une copie (mais n'est souvent pas).

ce contrôle doit effectuer une collecte des ordures pour effacer certaines références de cache pour voir si c'est une copie. Malheureusement, la syntaxe python rend cela inévitable.

vous ne pouvez pas avoir cela se produire, en faisant simplement une copie d'abord.

df = (df1-df2).dropna().copy()

suivie d'une opération inplace sera aussi performant que avant.

Mon opinion personnelle: je jamais l'utilisation sur place des opérations. La syntaxe est plus difficile à lire et n'offre aucun avantage.

27
répondu Jeff 2017-06-08 22:12:53