Ich bin neu bei Pandas ... Ich habe eine Reihe von Umfragedaten. Ich möchte einen rollierenden Mittelwert berechnen, um eine Schätzung für jeden Tag basierend auf einem dreitägigen Fenster zu erhalten. Wie ich aus dieser Frage verstehe , berechnen die Funktionen rolling_ * das Fenster basierend auf einer bestimmten Anzahl von Werten und nicht auf einem bestimmten Datums- / Uhrzeitbereich.
Gibt es eine andere Funktion, die diese Funktionalität implementiert? Oder stecke ich fest, meine eigenen zu schreiben?
BEARBEITEN:
Beispiel für Eingabedaten:
polls_subset.tail(20)
Out[185]:
favorable unfavorable other
enddate
2012-10-25 0.48 0.49 0.03
2012-10-25 0.51 0.48 0.02
2012-10-27 0.51 0.47 0.02
2012-10-26 0.56 0.40 0.04
2012-10-28 0.48 0.49 0.04
2012-10-28 0.46 0.46 0.09
2012-10-28 0.48 0.49 0.03
2012-10-28 0.49 0.48 0.03
2012-10-30 0.53 0.45 0.02
2012-11-01 0.49 0.49 0.03
2012-11-01 0.47 0.47 0.05
2012-11-01 0.51 0.45 0.04
2012-11-03 0.49 0.45 0.06
2012-11-04 0.53 0.39 0.00
2012-11-04 0.47 0.44 0.08
2012-11-04 0.49 0.48 0.03
2012-11-04 0.52 0.46 0.01
2012-11-04 0.50 0.47 0.03
2012-11-05 0.51 0.46 0.02
2012-11-07 0.51 0.41 0.00
Die Ausgabe würde nur eine Zeile für jedes Datum haben.
EDIT x2: Tippfehler behoben
python
pandas
time-series
Anov
quelle
quelle
rolling_*
Funktionen normalerweise recht langsam ist .Antworten:
In der Zwischenzeit wurde eine Zeitfensterfunktion hinzugefügt. Siehe diesen Link .
In [1]: df = DataFrame({'B': range(5)}) In [2]: df.index = [Timestamp('20130101 09:00:00'), ...: Timestamp('20130101 09:00:02'), ...: Timestamp('20130101 09:00:03'), ...: Timestamp('20130101 09:00:05'), ...: Timestamp('20130101 09:00:06')] In [3]: df Out[3]: B 2013-01-01 09:00:00 0 2013-01-01 09:00:02 1 2013-01-01 09:00:03 2 2013-01-01 09:00:05 3 2013-01-01 09:00:06 4 In [4]: df.rolling(2, min_periods=1).sum() Out[4]: B 2013-01-01 09:00:00 0.0 2013-01-01 09:00:02 1.0 2013-01-01 09:00:03 3.0 2013-01-01 09:00:05 5.0 2013-01-01 09:00:06 7.0 In [5]: df.rolling('2s', min_periods=1).sum() Out[5]: B 2013-01-01 09:00:00 0.0 2013-01-01 09:00:02 1.0 2013-01-01 09:00:03 3.0 2013-01-01 09:00:05 3.0 2013-01-01 09:00:06 7.0
quelle
rolling
kann hier sein: pandas.pydata.org/pandas-docs/stable/user_guide/…Was ist mit so etwas:
Abtasten Sie zuerst den Datenrahmen in 1D-Intervallen. Dies ist der Mittelwert der Werte für alle doppelten Tage. Verwenden Sie die
fill_method
Option, um fehlende Datumswerte einzugeben. Übergeben Sie als Nächstes den neu abgetasteten Framepd.rolling_mean
mit einem Fenster von 3 und min_periods = 1:pd.rolling_mean(df.resample("1D", fill_method="ffill"), window=3, min_periods=1) favorable unfavorable other enddate 2012-10-25 0.495000 0.485000 0.025000 2012-10-26 0.527500 0.442500 0.032500 2012-10-27 0.521667 0.451667 0.028333 2012-10-28 0.515833 0.450000 0.035833 2012-10-29 0.488333 0.476667 0.038333 2012-10-30 0.495000 0.470000 0.038333 2012-10-31 0.512500 0.460000 0.029167 2012-11-01 0.516667 0.456667 0.026667 2012-11-02 0.503333 0.463333 0.033333 2012-11-03 0.490000 0.463333 0.046667 2012-11-04 0.494000 0.456000 0.043333 2012-11-05 0.500667 0.452667 0.036667 2012-11-06 0.507333 0.456000 0.023333 2012-11-07 0.510000 0.443333 0.013333
UPDATE : Wie Ben in den Kommentaren betont , hat sich bei Pandas 0.18.0 die Syntax geändert . Mit der neuen Syntax wäre dies:
df.resample("1d").sum().fillna(0).rolling(window=3, min_periods=1).mean()
quelle
ffill
steht für Forward Fill und propagiert einfach den neuesten nicht fehlenden Wert. Ähnliches giltbfill
für das Rückwärtsfüllen in umgekehrter Reihenfolge.df.resample("1D").ffill(limit=0).rolling(window=3, min_periods=1).mean()
df.resample("1d").mean().rolling(window=3, min_periods=1).mean()
Ich hatte nur die gleiche Frage, aber mit unregelmäßig verteilten Datenpunkten. Resample ist hier eigentlich keine Option. Also habe ich meine eigene Funktion erstellt. Vielleicht ist es auch für andere nützlich:
from pandas import Series, DataFrame import pandas as pd from datetime import datetime, timedelta import numpy as np def rolling_mean(data, window, min_periods=1, center=False): ''' Function that computes a rolling mean Parameters ---------- data : DataFrame or Series If a DataFrame is passed, the rolling_mean is computed for all columns. window : int or string If int is passed, window is the number of observations used for calculating the statistic, as defined by the function pd.rolling_mean() If a string is passed, it must be a frequency string, e.g. '90S'. This is internally converted into a DateOffset object, representing the window size. min_periods : int Minimum number of observations in window required to have a value. Returns ------- Series or DataFrame, if more than one column ''' def f(x): '''Function to apply that actually computes the rolling mean''' if center == False: dslice = col[x-pd.datetools.to_offset(window).delta+timedelta(0,0,1):x] # adding a microsecond because when slicing with labels start and endpoint # are inclusive else: dslice = col[x-pd.datetools.to_offset(window).delta/2+timedelta(0,0,1): x+pd.datetools.to_offset(window).delta/2] if dslice.size < min_periods: return np.nan else: return dslice.mean() data = DataFrame(data.copy()) dfout = DataFrame() if isinstance(window, int): dfout = pd.rolling_mean(data, window, min_periods=min_periods, center=center) elif isinstance(window, basestring): idx = Series(data.index.to_pydatetime(), index=data.index) for colname, col in data.iterkv(): result = idx.apply(f) result.name = colname dfout = dfout.join(result, how='outer') if dfout.columns.size == 1: dfout = dfout.ix[:,0] return dfout # Example idx = [datetime(2011, 2, 7, 0, 0), datetime(2011, 2, 7, 0, 1), datetime(2011, 2, 7, 0, 1, 30), datetime(2011, 2, 7, 0, 2), datetime(2011, 2, 7, 0, 4), datetime(2011, 2, 7, 0, 5), datetime(2011, 2, 7, 0, 5, 10), datetime(2011, 2, 7, 0, 6), datetime(2011, 2, 7, 0, 8), datetime(2011, 2, 7, 0, 9)] idx = pd.Index(idx) vals = np.arange(len(idx)).astype(float) s = Series(vals, index=idx) rm = rolling_mean(s, window='2min')
quelle
s.rolling('2min', min_periods=1).mean()
Der Code von user2689410 war genau das, was ich brauchte. Bereitstellung meiner Version (Credits für Benutzer 2689410), die schneller ist, da der Mittelwert für ganze Zeilen im DataFrame sofort berechnet wird.
Hoffe, meine Suffix-Konventionen sind lesbar: _s: string, _i: int, _b: bool, _ser: Series und _df: DataFrame. Wenn Sie mehrere Suffixe finden, kann der Typ beides sein.
import pandas as pd from datetime import datetime, timedelta import numpy as np def time_offset_rolling_mean_df_ser(data_df_ser, window_i_s, min_periods_i=1, center_b=False): """ Function that computes a rolling mean Credit goes to user2689410 at http://stackoverflow.com/questions/15771472/pandas-rolling-mean-by-time-interval Parameters ---------- data_df_ser : DataFrame or Series If a DataFrame is passed, the time_offset_rolling_mean_df_ser is computed for all columns. window_i_s : int or string If int is passed, window_i_s is the number of observations used for calculating the statistic, as defined by the function pd.time_offset_rolling_mean_df_ser() If a string is passed, it must be a frequency string, e.g. '90S'. This is internally converted into a DateOffset object, representing the window_i_s size. min_periods_i : int Minimum number of observations in window_i_s required to have a value. Returns ------- Series or DataFrame, if more than one column >>> idx = [ ... datetime(2011, 2, 7, 0, 0), ... datetime(2011, 2, 7, 0, 1), ... datetime(2011, 2, 7, 0, 1, 30), ... datetime(2011, 2, 7, 0, 2), ... datetime(2011, 2, 7, 0, 4), ... datetime(2011, 2, 7, 0, 5), ... datetime(2011, 2, 7, 0, 5, 10), ... datetime(2011, 2, 7, 0, 6), ... datetime(2011, 2, 7, 0, 8), ... datetime(2011, 2, 7, 0, 9)] >>> idx = pd.Index(idx) >>> vals = np.arange(len(idx)).astype(float) >>> ser = pd.Series(vals, index=idx) >>> df = pd.DataFrame({'s1':ser, 's2':ser+1}) >>> time_offset_rolling_mean_df_ser(df, window_i_s='2min') s1 s2 2011-02-07 00:00:00 0.0 1.0 2011-02-07 00:01:00 0.5 1.5 2011-02-07 00:01:30 1.0 2.0 2011-02-07 00:02:00 2.0 3.0 2011-02-07 00:04:00 4.0 5.0 2011-02-07 00:05:00 4.5 5.5 2011-02-07 00:05:10 5.0 6.0 2011-02-07 00:06:00 6.0 7.0 2011-02-07 00:08:00 8.0 9.0 2011-02-07 00:09:00 8.5 9.5 """ def calculate_mean_at_ts(ts): """Function (closure) to apply that actually computes the rolling mean""" if center_b == False: dslice_df_ser = data_df_ser[ ts-pd.datetools.to_offset(window_i_s).delta+timedelta(0,0,1): ts ] # adding a microsecond because when slicing with labels start and endpoint # are inclusive else: dslice_df_ser = data_df_ser[ ts-pd.datetools.to_offset(window_i_s).delta/2+timedelta(0,0,1): ts+pd.datetools.to_offset(window_i_s).delta/2 ] if (isinstance(dslice_df_ser, pd.DataFrame) and dslice_df_ser.shape[0] < min_periods_i) or \ (isinstance(dslice_df_ser, pd.Series) and dslice_df_ser.size < min_periods_i): return dslice_df_ser.mean()*np.nan # keeps number format and whether Series or DataFrame else: return dslice_df_ser.mean() if isinstance(window_i_s, int): mean_df_ser = pd.rolling_mean(data_df_ser, window=window_i_s, min_periods=min_periods_i, center=center_b) elif isinstance(window_i_s, basestring): idx_ser = pd.Series(data_df_ser.index.to_pydatetime(), index=data_df_ser.index) mean_df_ser = idx_ser.apply(calculate_mean_at_ts) return mean_df_ser
quelle
Dieses Beispiel scheint einen gewichteten Mittelwert zu erfordern, wie in @ andyhaydens Kommentar vorgeschlagen. Zum Beispiel gibt es zwei Umfragen am 25.10. Und jeweils eine am 26.10. Und 27.10. Wenn Sie nur eine Neuabtastung durchführen und dann den Mittelwert ermitteln, werden die Umfragen am 26.10. Und 27.10. Im Vergleich zu den Umfragen am 25.10. Effektiv doppelt so gewichtet.
Um jeder Umfrage das gleiche Gewicht zu geben und nicht jedem Tag das gleiche Gewicht , können Sie Folgendes tun.
>>> wt = df.resample('D',limit=5).count() favorable unfavorable other enddate 2012-10-25 2 2 2 2012-10-26 1 1 1 2012-10-27 1 1 1 >>> df2 = df.resample('D').mean() favorable unfavorable other enddate 2012-10-25 0.495 0.485 0.025 2012-10-26 0.560 0.400 0.040 2012-10-27 0.510 0.470 0.020
Das gibt Ihnen die Rohstoffe für die Erstellung eines umfragebasierten Mittelwerts anstelle eines tagesbasierten Mittelwerts. Nach wie vor werden die Umfragen am 25.10. Gemittelt, aber das Gewicht für 25.10. Wird ebenfalls gespeichert und ist doppelt so hoch wie am 26.10. Oder 27.10., Um anzuzeigen, dass am 25.10. Zwei Umfragen durchgeführt wurden.
>>> df3 = df2 * wt >>> df3 = df3.rolling(3,min_periods=1).sum() >>> wt3 = wt.rolling(3,min_periods=1).sum() >>> df3 = df3 / wt3 favorable unfavorable other enddate 2012-10-25 0.495000 0.485000 0.025000 2012-10-26 0.516667 0.456667 0.030000 2012-10-27 0.515000 0.460000 0.027500 2012-10-28 0.496667 0.465000 0.041667 2012-10-29 0.484000 0.478000 0.042000 2012-10-30 0.488000 0.474000 0.042000 2012-10-31 0.530000 0.450000 0.020000 2012-11-01 0.500000 0.465000 0.035000 2012-11-02 0.490000 0.470000 0.040000 2012-11-03 0.490000 0.465000 0.045000 2012-11-04 0.500000 0.448333 0.035000 2012-11-05 0.501429 0.450000 0.032857 2012-11-06 0.503333 0.450000 0.028333 2012-11-07 0.510000 0.435000 0.010000
Beachten Sie, dass der rollierende Mittelwert für 10/27 jetzt 0,51500 (umfragegewichtet) und nicht 52,1667 (taggewichtet) beträgt.
Beachten Sie auch, dass Änderungen an den APIs für
resample
undrolling
ab Version 0.18.0 vorgenommen wurden.Rollen (was ist neu in Pandas 0.18.0)
Resample (was ist neu in Pandas 0.18.0)
quelle
Um es einfach zu halten, habe ich eine Schleife und so etwas verwendet, um Ihnen den Einstieg zu erleichtern (mein Index ist datetimes):
import pandas as pd import datetime as dt #populate your dataframe: "df" #... df[df.index<(df.index[0]+dt.timedelta(hours=1))] #gives you a slice. you can then take .sum() .mean(), whatever
und dann können Sie Funktionen auf diesem Slice ausführen. Sie können sehen, wie das Hinzufügen eines Iterators, um den Start des Fensters zu einem anderen Wert als dem ersten Wert in Ihrem Datenrahmenindex zu machen, das Fenster dann rollt (Sie könnten beispielsweise auch eine> -Regel für den Start verwenden).
Beachten Sie, dass dies für SUPER große Datenmengen oder sehr kleine Schritte möglicherweise weniger effizient ist, da Ihr Slicing möglicherweise anstrengender wird (funktioniert für mich gut genug für Hunderttausende von Datenzeilen und mehrere Spalten, jedoch für stündliche Fenster über einige Wochen).
quelle
Ich habe festgestellt, dass der Code von user2689410 beim Versuch mit window = '1M' fehlerhaft war, da das Delta im Geschäftsmonat diesen Fehler verursachte:
AttributeError: 'MonthEnd' object has no attribute 'delta'
Ich habe die Option hinzugefügt, ein relatives Zeitdelta direkt zu übergeben, damit Sie ähnliche Dinge für benutzerdefinierte Zeiträume tun können.
Vielen Dank für die Hinweise, hier ist mein Versuch - hoffe, es ist von Nutzen.
def rolling_mean(data, window, min_periods=1, center=False): """ Function that computes a rolling mean Reference: http://stackoverflow.com/questions/15771472/pandas-rolling-mean-by-time-interval Parameters ---------- data : DataFrame or Series If a DataFrame is passed, the rolling_mean is computed for all columns. window : int, string, Timedelta or Relativedelta int - number of observations used for calculating the statistic, as defined by the function pd.rolling_mean() string - must be a frequency string, e.g. '90S'. This is internally converted into a DateOffset object, and then Timedelta representing the window size. Timedelta / Relativedelta - Can directly pass a timedeltas. min_periods : int Minimum number of observations in window required to have a value. center : bool Point around which to 'center' the slicing. Returns ------- Series or DataFrame, if more than one column """ def f(x, time_increment): """Function to apply that actually computes the rolling mean :param x: :return: """ if not center: # adding a microsecond because when slicing with labels start # and endpoint are inclusive start_date = x - time_increment + timedelta(0, 0, 1) end_date = x else: start_date = x - time_increment/2 + timedelta(0, 0, 1) end_date = x + time_increment/2 # Select the date index from the dslice = col[start_date:end_date] if dslice.size < min_periods: return np.nan else: return dslice.mean() data = DataFrame(data.copy()) dfout = DataFrame() if isinstance(window, int): dfout = pd.rolling_mean(data, window, min_periods=min_periods, center=center) elif isinstance(window, basestring): time_delta = pd.datetools.to_offset(window).delta idx = Series(data.index.to_pydatetime(), index=data.index) for colname, col in data.iteritems(): result = idx.apply(lambda x: f(x, time_delta)) result.name = colname dfout = dfout.join(result, how='outer') elif isinstance(window, (timedelta, relativedelta)): time_delta = window idx = Series(data.index.to_pydatetime(), index=data.index) for colname, col in data.iteritems(): result = idx.apply(lambda x: f(x, time_delta)) result.name = colname dfout = dfout.join(result, how='outer') if dfout.columns.size == 1: dfout = dfout.ix[:, 0] return dfout
Und das Beispiel mit einem 3-Tage-Zeitfenster zur Berechnung des Mittelwerts:
from pandas import Series, DataFrame import pandas as pd from datetime import datetime, timedelta import numpy as np from dateutil.relativedelta import relativedelta idx = [datetime(2011, 2, 7, 0, 0), datetime(2011, 2, 7, 0, 1), datetime(2011, 2, 8, 0, 1, 30), datetime(2011, 2, 9, 0, 2), datetime(2011, 2, 10, 0, 4), datetime(2011, 2, 11, 0, 5), datetime(2011, 2, 12, 0, 5, 10), datetime(2011, 2, 12, 0, 6), datetime(2011, 2, 13, 0, 8), datetime(2011, 2, 14, 0, 9)] idx = pd.Index(idx) vals = np.arange(len(idx)).astype(float) s = Series(vals, index=idx) # Now try by passing the 3 days as a relative time delta directly. rm = rolling_mean(s, window=relativedelta(days=3)) >>> rm Out[2]: 2011-02-07 00:00:00 0.0 2011-02-07 00:01:00 0.5 2011-02-08 00:01:30 1.0 2011-02-09 00:02:00 1.5 2011-02-10 00:04:00 3.0 2011-02-11 00:05:00 4.0 2011-02-12 00:05:10 5.0 2011-02-12 00:06:00 5.5 2011-02-13 00:08:00 6.5 2011-02-14 00:09:00 7.5 Name: 0, dtype: float64
quelle
Überprüfen Sie, ob Ihr Index wirklich
datetime
nicht ist.str
Kann hilfreich sein:data.index = pd.to_datetime(data['Index']).values
quelle