dropna - pandas python
Pandas: caída de rendimiento peculiar para cambio de nombre in situ después de dropna (1)
He informado de esto como un problema en temas de pandas . Mientras tanto, publico esto aquí con la esperanza de ahorrar tiempo a otros, en caso de que encuentren problemas similares.
Al perfilar un proceso que necesitaba ser optimizado, encontré que cambiar el nombre de las columnas NO en el lugar mejora el rendimiento (tiempo de ejecución) en x120. El perfil indica que esto está relacionado con la recolección de basura (ver más abajo).
Además, el rendimiento esperado se recupera al evitar el método dropna.
El siguiente ejemplo corto demuestra un factor 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 bucles, lo mejor de 3: 15.6 ms por bucle
Primera línea de salida de %%prun
:
ncalls tottime percall cumtime percall filename: lineno (función)
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 bucles, lo mejor de 3: 1.24 ms por bucle
evitar dropna
El rendimiento esperado se recupera al evitar el método 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 bucles, lo mejor de 3: 865 µs por bucle
%%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 bucles, lo mejor de 3: 902 µs por bucle
Esta es una copia de la explicación en github.
No hay garantía de que una operación inplace
sea realmente más rápida. A menudo, en realidad son la misma operación que funciona en una copia, pero la referencia de nivel superior se reasigna.
La razón de la diferencia en el rendimiento en este caso es la siguiente.
La llamada (df1-df2).dropna()
crea una porción del marco de datos. Cuando aplica una nueva operación, esto activa una verificación de SettingWithCopy
porque podría ser una copia (pero a menudo no lo es).
Esta verificación debe realizar una recolección de basura para borrar algunas referencias de caché para ver si es una copia. Desafortunadamente la sintaxis de Python hace esto inevitable.
No puedes hacer que esto suceda, simplemente haciendo una copia primero.
df = (df1-df2).dropna().copy()
seguido de una operación inplace
tendrá el mismo rendimiento que antes.
Mi opinión personal: nunca uso operaciones in situ. La sintaxis es más difícil de leer y no ofrece ninguna ventaja.