Die Top-Antwort ist meiner Meinung nach fehlerhaft. Hoffentlich importiert niemand alle Pandas in ihren Namespace mit from pandas import *
. Außerdem sollte die map
Methode für diese Zeiten reserviert werden, wenn ein Wörterbuch oder eine Reihe übergeben wird. Es kann eine Funktion annehmen, aber dafür wird apply
es verwendet.
Wenn Sie also den obigen Ansatz verwenden müssen, würde ich ihn so schreiben
df["A1"], df["A2"] = zip(*df["a"].apply(calculate))
Es gibt hier eigentlich keinen Grund, zip zu verwenden. Sie können dies einfach tun:
df["A1"], df["A2"] = calculate(df['a'])
Diese zweite Methode ist auch bei größeren DataFrames viel schneller
df = pd.DataFrame({'a': [1,2,3] * 100000, 'b': [2,3,4] * 100000})
DataFrame mit 300.000 Zeilen erstellt
%timeit df["A1"], df["A2"] = calculate(df['a'])
2.65 ms ± 92.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit df["A1"], df["A2"] = zip(*df["a"].apply(calculate))
159 ms ± 5.24 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
60x schneller als Reißverschluss
Vermeiden Sie im Allgemeinen die Verwendung von apply
Das Anwenden ist im Allgemeinen nicht viel schneller als das Durchlaufen einer Python-Liste. Lassen Sie uns die Leistung einer for-Schleife testen, um dasselbe wie oben zu tun
%%timeit
A1, A2 = [], []
for val in df['a']:
A1.append(val**2)
A2.append(val**3)
df['A1'] = A1
df['A2'] = A2
298 ms ± 7.14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Das ist also doppelt so langsam, was keine schreckliche Leistungsregression ist, aber wenn wir das oben Gesagte zythonisieren, erhalten wir eine viel bessere Leistung. Angenommen, Sie verwenden ipython:
%load_ext cython
%%cython
cpdef power(vals):
A1, A2 = [], []
cdef double val
for val in vals:
A1.append(val**2)
A2.append(val**3)
return A1, A2
%timeit df['A1'], df['A2'] = power(df['a'])
72.7 ms ± 2.16 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Direktes Zuweisen ohne zutreffen
Sie können noch größere Geschwindigkeitsverbesserungen erzielen, wenn Sie die direkten vektorisierten Operationen verwenden.
%timeit df['A1'], df['A2'] = df['a'] ** 2, df['a'] ** 3
5.13 ms ± 320 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Dies nutzt die extrem schnellen vektorisierten Operationen von NumPy anstelle unserer Schleifen. Wir haben jetzt eine 30-fache Beschleunigung gegenüber dem Original.
Der einfachste Geschwindigkeitstest mit apply
Das obige Beispiel sollte deutlich zeigen, wie langsam es sein apply
kann, aber nur damit es besonders klar ist, schauen wir uns das grundlegendste Beispiel an. Lassen Sie uns eine Reihe von 10 Millionen Zahlen mit und ohne Anwendung quadrieren
s = pd.Series(np.random.rand(10000000))
%timeit s.apply(calc)
3.3 s ± 57.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Ohne Anwendung ist 50x schneller
%timeit s ** 2
66 ms ± 2 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)