Pandas: eigenartigen Leistungsabfall für inplace umbenennen nach dropna
Habe ich berichtet, dies als ein Problem auf pandas Fragen.
In der Zwischenzeit habe ich das hier posten in der Hoffnung andere zu retten, die Zeit, in der Fall Sie treten ähnliche Probleme.
Beim profiling ein Prozess, der benötigt wird, optimiert werden, fand ich, dass die Umbenennung von Spalten NICHT inplace verbessert die Leistung (Ausführungszeit) durch x120.
Profiling zeigt dies bezieht sich auf die garbage collection (siehe unten).
Darüber hinaus die erwartete Leistung, wird wiederhergestellt durch die Vermeidung der dropna Methode.
Folgende kurze Beispiel zeigt eine Faktor-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 loops, best of 3: 15.6 ms pro Schleife
erste Ausgabezeile %%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 loops, best of 3: 1.24 ms pro Schleife
vermeiden dropna
Die erwartete Leistung, wird wiederhergestellt durch die Vermeidung der dropna
Methode:
%%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 loops, best of 3: 865 ľs pro Schleife
%%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 loops, best of 3: 902 µs pro Schleife
Du musst angemeldet sein, um einen Kommentar abzugeben.
Dies ist eine Kopie der Erklärung auf github.
Es ist keine Garantie, dass ein
inplace
Bedienung ist tatsächlich schneller. Oft sind Sie tatsächlich die gleiche operation, die arbeiten auf einer Kopie, aber die top-level-Referenz zugewiesen wird.Den Grund für den Unterschied in der Leistung, in diesem Fall ist wie folgt.
Den
(df1-df2).dropna()
Aufruf erzeugt eine Scheibe des dataframe. Wenn Sie eine neue operation, löst dies eineSettingWithCopy
überprüfen, da es könnte eine Kopie (aber oft nicht ist).Diese Prüfung durchführen muss, die garbage collection zu tilgen, einige cache-Referenzen, um zu sehen, ob es eine Kopie. Leider python-syntax, macht dies unumgänglich.
Sie können nicht diese geschehen, indem man einfach eine Kopie der ersten.
gefolgt von einem
inplace
Vorgang wird so leistungsfähig ist wie zuvor.Meine persönliche Meinung: ich nie verwenden Sie die in-place-Operationen. Die syntax ist schwieriger zu Lesen, und es bietet keine Vorteile.
.copy()
Vorschlag in der Tat löst das Problem. Vielen Dank für deine ausführliche und prompte Antwort!df.dropna().rename(....).sum()
ist sehr intuitiv / lesbar. Wenn Sie Spritzen ein inplace-operation können Sie keine Kette.some_long_complicated_expression[some:long_slice, more_information_here] += 1
hat übersome_long_complicated_expression[some:long_slice, more_information_here] = some_long_complicated_expression[some:long_slice, more_information_here] + 1
.mask
, wo die Bedeutung klar ist. (wenn in deinem Beispiel tatsächlich nicht nötig, auf die Rechte Seite, als der Rahmen angepasst werden, z.B. können Sie einfach:some_long_complicated_expression + 1
(obwohl Ihr vielleicht ein perf Auswirkungen)