Ich habe folgenden Datenrahmen:
Index_Date A B C D
===============================
2015-01-31 10 10 Nan 10
2015-02-01 2 3 Nan 22
2015-02-02 10 60 Nan 280
2015-02-03 10 100 Nan 250
Benötigen:
Index_Date A B C D
===============================
2015-01-31 10 10 10 10
2015-02-01 2 3 23 22
2015-02-02 10 60 290 280
2015-02-03 10 100 3000 250
Column C
wird abgeleitet 2015-01-31
durch Nehmen value
von D
.
Dann muss ich das value
von C
für verwenden 2015-01-31
und mit dem value
von A
on multiplizieren 2015-02-01
und hinzufügen B
.
Ich habe versucht, eine apply
und eine shift
Verwendung if else
von dies gibt einen Schlüsselfehler.
A
undB
?A
und SpalteD
?apply()
in der die Benutzerfunktion im Rahmen ihrer Berechnung auf einen oder mehrere Werte aus der vorherigen Zeile zugreifen oder zumindest einen Wert zurückgeben kann, der dann bei der nächsten Iteration an sich selbst übergeben wird. Würde dies nicht einige Effizienzgewinne im Vergleich zu einer for-Schleife ermöglichen?numba
ist hier oft eine gute Option.Antworten:
Erstellen Sie zunächst den abgeleiteten Wert:
df.loc[0, 'C'] = df.loc[0, 'D']
Durchlaufen Sie dann die verbleibenden Zeilen und füllen Sie die berechneten Werte aus:
for i in range(1, len(df)): df.loc[i, 'C'] = df.loc[i-1, 'C'] * df.loc[i, 'A'] + df.loc[i, 'B'] Index_Date A B C D 0 2015-01-31 10 10 10 10 1 2015-02-01 2 3 23 22 2 2015-02-02 10 60 290 280
quelle
apply
eine Funktion verwenden, die die gleiche Berechnung wie die Schleife ausführt, aber hinter den Kulissen wäre dies auch eine Schleife. pandas.pydata.org/pandas-docs/version/0.17.1/generated/…C
?Gegeben eine Spalte von Zahlen:
lst = [] cols = ['A'] for a in range(100, 105): lst.append([a]) df = pd.DataFrame(lst, columns=cols, index=range(5)) df A 0 100 1 101 2 102 3 103 4 104
Sie können die vorherige Zeile mit Shift referenzieren:
df['Change'] = df.A - df.A.shift(1) df A Change 0 100 NaN 1 101 1.0 2 102 1.0 3 103 1.0 4 104 1.0
quelle
numba
Bei rekursiven Berechnungen, die nicht vektorisierbar sind
numba
, die JIT-Kompilierung verwenden und mit Objekten niedrigerer Ebene arbeiten, ergeben sich häufig große Leistungsverbesserungen. Sie müssen nur eine regulärefor
Schleife definieren und den Dekorator verwenden@njit
oder (für ältere Versionen)@jit(nopython=True)
:Bei einem Datenrahmen mit angemessener Größe ergibt sich eine ~ 30-fache Leistungsverbesserung gegenüber einer regulären
for
Schleife:from numba import jit @jit(nopython=True) def calculator_nb(a, b, d): res = np.empty(d.shape) res[0] = d[0] for i in range(1, res.shape[0]): res[i] = res[i-1] * a[i] + b[i] return res df['C'] = calculator_nb(*df[list('ABD')].values.T) n = 10**5 df = pd.concat([df]*n, ignore_index=True) # benchmarking on Python 3.6.0, Pandas 0.19.2, NumPy 1.11.3, Numba 0.30.1 # calculator() is same as calculator_nb() but without @jit decorator %timeit calculator_nb(*df[list('ABD')].values.T) # 14.1 ms per loop %timeit calculator(*df[list('ABD')].values.T) # 444 ms per loop
quelle
Das Anwenden der rekursiven Funktion auf Numpy-Arrays ist schneller als die aktuelle Antwort.
df = pd.DataFrame(np.repeat(np.arange(2, 6),3).reshape(4,3), columns=['A', 'B', 'D']) new = [df.D.values[0]] for i in range(1, len(df.index)): new.append(new[i-1]*df.A.values[i]+df.B.values[i]) df['C'] = new
Ausgabe
A B D C 0 1 1 1 1 1 2 2 2 4 2 3 3 3 15 3 4 4 4 64 4 5 5 5 325
quelle
Obwohl es eine Weile her ist, dass diese Frage gestellt wurde, werde ich meine Antwort veröffentlichen, in der Hoffnung, dass sie jemandem hilft.
Haftungsausschluss: Ich weiß, dass diese Lösung kein Standard ist , aber ich denke, dass sie gut funktioniert.
import pandas as pd import numpy as np data = np.array([[10, 2, 10, 10], [10, 3, 60, 100], [np.nan] * 4, [10, 22, 280, 250]]).T idx = pd.date_range('20150131', end='20150203') df = pd.DataFrame(data=data, columns=list('ABCD'), index=idx) df A B C D ================================= 2015-01-31 10 10 NaN 10 2015-02-01 2 3 NaN 22 2015-02-02 10 60 NaN 280 2015-02-03 10 100 NaN 250 def calculate(mul, add): global value value = value * mul + add return value value = df.loc['2015-01-31', 'D'] df.loc['2015-01-31', 'C'] = value df.loc['2015-02-01':, 'C'] = df.loc['2015-02-01':].apply(lambda row: calculate(*row[['A', 'B']]), axis=1) df A B C D ================================= 2015-01-31 10 10 10 10 2015-02-01 2 3 23 22 2015-02-02 10 60 290 280 2015-02-03 10 100 3000 250
Im Grunde genommen verwenden wir ein
apply
From Pandas und die Hilfe einer globalen Variablen, die den zuvor berechneten Wert verfolgt.Zeitvergleich mit einer
for
Schleife:data = np.random.random(size=(1000, 4)) idx = pd.date_range('20150131', end='20171026') df = pd.DataFrame(data=data, columns=list('ABCD'), index=idx) df.C = np.nan df.loc['2015-01-31', 'C'] = df.loc['2015-01-31', 'D'] %%timeit for i in df.loc['2015-02-01':].index.date: df.loc[i, 'C'] = df.loc[(i - pd.DateOffset(days=1)).date(), 'C'] * df.loc[i, 'A'] + df.loc[i, 'B']
3,2 s ± 114 ms pro Schleife (Mittelwert ± Standardabweichung von 7 Läufen, jeweils 1 Schleife)
data = np.random.random(size=(1000, 4)) idx = pd.date_range('20150131', end='20171026') df = pd.DataFrame(data=data, columns=list('ABCD'), index=idx) df.C = np.nan def calculate(mul, add): global value value = value * mul + add return value value = df.loc['2015-01-31', 'D'] df.loc['2015-01-31', 'C'] = value %%timeit df.loc['2015-02-01':, 'C'] = df.loc['2015-02-01':].apply(lambda row: calculate(*row[['A', 'B']]), axis=1)
1,82 s ± 64,4 ms pro Schleife (Mittelwert ± Standardabweichung von 7 Läufen, jeweils 1 Schleife)
Also durchschnittlich 0,57 mal schneller.
quelle
Im Allgemeinen besteht der Schlüssel zum Vermeiden einer expliziten Schleife darin, zwei Instanzen des Datenrahmens auf rowindex-1 == rowindex zu verbinden (zusammenzuführen).
Dann hätten Sie einen großen Datenrahmen mit Zeilen von r und r-1, von wo aus Sie eine df.apply () -Funktion ausführen könnten.
Der Aufwand für die Erstellung des großen Datensatzes kann jedoch die Vorteile der Parallelverarbeitung ausgleichen ...
HTH Martin
quelle