Wie immer in Pandas den ersten Eintrag der Ausgabe zu bevölkern, zu vektorisiert Methoden kleben (dh Vermeidung apply
) ist essentiell für Leistung und Skalierbarkeit.
Die Operation, die Sie tun möchten, ist ein wenig fummelig, da rollierende Operationen auf groupby-Objekten derzeit nicht NaN-fähig sind (Version 0.18.1). Als solche müssen wir ein paar kurze Zeilen Code:
g1 = df.groupby(['var1'])['value'] # group values
g2 = df.fillna(0).groupby(['var1'])['value'] # fillna, then group values
s = g2.rolling(2).sum()/g1.rolling(2).count() # the actual computation
s.reset_index(level=0, drop=True).sort_index() # drop/sort index
Die Idee ist, die Werte im Fenster (mit sum
) zu summieren, die NaN-Werte zählen (unter Verwendung von count
) und teilen sich dann die finden bedeuten. Dieser Code gibt die folgende Ausgabe, die Ihre gewünschte Ausgabe entspricht:
0 NaN
1 NaN
2 2.0
3 2.0
4 2.5
5 3.0
6 3.0
7 2.0
Name: value, dtype: float64
Testing diese auf einem größeren Datenrahmen (etwa 100.000 Zeilen), die Laufzeit unter 100ms war deutlich schneller als alle gelten basierte Methoden, die ich versuchte.
Es kann sich lohnen, die verschiedenen Ansätze Ihrer tatsächlichen Daten zu testen, da die Zeit durch andere Faktoren wie die Anzahl der Gruppen beeinflusst werden kann. Es ist jedoch ziemlich sicher, dass sich vektorisierte Berechnungen durchsetzen werden.
Der oben gezeigte Ansatz eignet sich gut für einfache Berechnungen, z. B. das Rolling Mean.Es wird für kompliziertere Berechnungen (wie das Rolling der Standardabweichung) funktionieren, obwohl die Implementierung komplizierter ist.
Die allgemeine Idee ist Blick auf jede einfache Routine, die in Pandas schnell ist (z. B. sum
) und füllen Sie dann alle Null-Werte mit einem Identitätselement (z. B. 0
). Sie können dann grobpy verwenden und den Walzvorgang durchführen (z. B. .rolling(2).sum()
). Die Ausgabe wird dann mit den Ausgaben anderer Operationen kombiniert.
Um zum Beispiel groupby NaN-bewusste Rollabweichung (von denen Standardabweichung ist die Quadratwurzel) zu implementieren, müssen wir "den Mittelwert der Quadrate minus das Quadrat des Mittelwerts" finden. Hier ist eine Skizze, wie dies aussehen könnte:
def rolling_nanvar(df, window):
"""
Group df by 'var1' values and then calculate rolling variance,
adjusting for the number of NaN values in the window.
Note: user may wish to edit this function to control degrees of
freedom (n), depending on their overall aim.
"""
g1 = df.groupby(['var1'])['value']
g2 = df.fillna(0).groupby(['var1'])['value']
# fill missing values with 0, square values and groupby
g3 = df['value'].fillna(0).pow(2).groupby(df['var1'])
n = g1.rolling(window).count()
mean_of_squares = g3.rolling(window).sum()/n
square_of_mean = (g2.rolling(window).sum()/n)**2
variance = mean_of_squares - square_of_mean
return variance.reset_index(level=0, drop=True).sort_index()
Beachten Sie, dass diese Funktion nicht numerisch stabil sein kann (Quadratur zu Überlauf führen könnte). Pandas verwendet intern Welford's algorithm, um dieses Problem zu beheben.
Wie auch immer, diese Funktion, obwohl sie mehrere Operationen verwendet, ist immer noch sehr schnell. Hier ist ein Vergleich mit der prägnanten anwenden basierten Methode vorgeschlagen von Yakym Pirozhenko:
>>> df2 = pd.concat([df]*10000, ignore_index=True) # 80000 rows
>>> %timeit df2.groupby('var1')['value'].apply(\
lambda gp: gp.rolling(7, min_periods=1).apply(np.nanvar))
1 loops, best of 3: 11 s per loop
>>> %timeit rolling_nanvar(df2, 7)
10 loops, best of 3: 110 ms per loop
Vektorisierung 100 mal in diesem Fall schneller. Natürlich können Sie, je nachdem, wie viele Daten Sie haben, an der Verwendung von apply
festhalten, da dies Ihnen Allgemeingültigkeit/Kürze auf Kosten der Leistung ermöglicht.
Bitte geben Sie einen kleinen, reproduzierbaren Code an, damit ich mit ähnlichen Informationen spielen kann, ohne dass ich mich selbst um etwas kümmern muss. – piRSquared
@piRSquared Ich habe gerade ein Codebeispiel hinzugefügt. Danke – Stergios