팬더 : 시간 간격 별 롤링 평균


85

저는 Pandas를 처음 사용합니다 .... 많은 폴링 데이터가 있습니다. 3 일 창을 기준으로 매일의 추정치를 얻기 위해 이동 평균을 계산하고 싶습니다. 이 질문 에서 이해 했듯이 rolling_ * 함수는 특정 날짜 시간 범위가 아닌 지정된 수의 값을 기반으로 창을 계산합니다.

이 기능을 구현하는 다른 기능이 있습니까? 아니면 내 자신의 글을 쓰고있는 걸까?

편집하다:

샘플 입력 데이터 :

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

출력에는 각 날짜에 대해 하나의 행만 있습니다.

x2 수정 : 오타 수정


2
Pandas 버그 추적기에이 기능을 요청하는 미해결 문제가 있습니다 : github.com/pydata/pandas/issues/936 . 기능은 아직 존재하지 않습니다. 답변 이 질문은 원하는 효과를 얻을 수있는 방법을 설명하지만, 일반적으로 매우 느린 내장에 비교됩니다 rolling_*기능.
— BrenBarn 2013

답변:


73

그 동안 시간 창 기능이 추가되었습니다. 이 링크를 참조하십시오 .

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

이것이 최고의 답변이어야합니다.
— Ivan

6
오프셋 (예 : '2s') 인수에 대한 문서는 다음과 rolling같습니다. pandas.pydata.org/pandas-docs/stable/user_guide/…
— Guilherme Salomé

2
데이터 프레임에 여러 열이 있으면 어떻게됩니까? 특정 열을 어떻게 지정합니까?
— Brain_overflowed jul.

@Brain_overflowed가 인덱스로 설정 됨
— jamfie

min_period는이 방법으로 신뢰할 수없는 것 같습니다. min_periods> 1의 경우 타임 스탬프 정밀도 / 가변 샘플링 속도로 인해 예상치 못한 NaN을 얻을 수 있습니다
— Albert James Teddy

50

다음과 같은 것은 어떻습니까?

먼저 데이터 프레임을 1D 간격으로 리샘플링합니다. 이것은 모든 중복 날짜에 대한 값의 평균을 취합니다. fill_method옵션을 사용하여 누락 된 날짜 값을 입력하십시오. 다음으로, pd.rolling_mean3과 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

업데이트 : Ben이 주석에서 지적했듯이 pandas 0.18.0에서는 구문이 변경되었습니다 . 새 구문을 사용하면 다음과 같습니다.

df.resample("1d").sum().fillna(0).rolling(window=3, min_periods=1).mean()

죄송합니다, Pandas newb, ffill은 누락 된 값을 제공하기 위해 규칙으로 정확히 무엇을 사용합니까?
— Anov 2013-04-02

1
몇 가지 채우기 옵션이 있습니다. ffill전방 채우기를 나타내며 가장 최근의 비결 측 값을 전파합니다. bfill역방향 채우기와 마찬가지로 역순으로 동일 하게 수행합니다.
— Zelazny7

9
아마도 (... 롤링은 당신이 한 것보다 더 많은 무게를 수행하기 위해 두 개의 판독 값을 기대하는 의미 촬영시) 내가 여기 착각 해요,하지만 당신은 같은 날 여러 측정 값을 무시하는
— 앤디 헤이든

4
좋은 대답입니다. pandas 0.18.0에서 구문이 변경 되었음을 주목하십시오 . 새 구문은 다음과 같습니다.df.resample("1D").ffill(limit=0).rolling(window=3, min_periods=1).mean()
— Ben

1
팬더 버전 0.18.1에서 원래 답변의 결과를 복제하려면 다음을 사용하고 있습니다. df.resample("1d").mean().rolling(window=3, min_periods=1).mean()
— JohnE

33

나는 똑같은 질문을했지만 불규칙한 간격의 데이터 포인트가 있습니다. Resample은 실제로 여기에서 옵션이 아닙니다. 그래서 저는 제 기능을 만들었습니다. 아마도 다른 사람들에게도 유용 할 것입니다.

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')

관련 수입품을 포함 할 수 있습니까?
— Bryce Drennan 2014

시간 간격 슬라이딩 윈도우를 계산할 때 작동하는 예제 입력 데이터 프레임을
— 제공해 주시겠습니까?

원본 게시물에 예제를 추가했습니다.
— user2689410 2014-04-09

5
이제 동일한 작업을 수행 할 수 있습니다.s.rolling('2min', min_periods=1).mean()
— kampta

8

user2689410의 코드는 정확히 필요한 것입니다. DataFrame의 전체 행에 대해 한 번에 평균을 계산하기 때문에 더 빠른 내 버전 (user2689410에 대한 크레딧)을 제공합니다.

내 접미사 규칙을 읽을 수 있기를 바랍니다 : _s : string, _i : int, _b : bool, _ser : Series 및 _df : DataFrame. 여러 접미사가있는 경우 type은 둘 다일 수 있습니다.

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

3

이 예는 @andyhayden의 의견에서 제안 된 가중치 평균을 요구하는 것 같습니다. 예를 들어 10/25에 두 개의 투표가 있고 10/26과 10/27에 각각 하나씩 있습니다. 리샘플링 한 다음 평균을 취하면 10/26 및 10/27의 설문 조사에 10/25의 설문 조사에 비해 두 배 더 많은 가중치를 부여합니다.

매일 같은 가중치가 아닌 각 투표에 동일한 가중치를 부여하려면 다음과 같이 할 수 있습니다.

>>> 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

그것은 당신에게 일 기반 평균 대신 투표 기반 평균을 수행하기위한 원시 성분을 제공합니다. 이전과 마찬가지로 설문 조사의 평균은 10/25이지만 10/25에 대한 가중치도 저장되며 10/25에 두 개의 설문 조사가 수행되었음을 반영하기 위해 10/26 또는 10/27에 가중치의 두 배입니다.

>>> 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

10/27에 대한 이동 평균은 이제 52.1667 (일 가중)이 아니라 0.51500 (폴더 가중)입니다.

또한 대한 API에되어 변화가 있는지주의 resample및 rolling버전 0.18.0의 등을.

롤링 (pandas 0.18.0의 새로운 기능)

리샘플링 (Pandas 0.18.0의 새로운 기능)


3

기본적으로 유지하기 위해 루프와 다음과 같은 것을 사용하여 시작했습니다 (내 인덱스는 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

그런 다음 해당 슬라이스에서 함수를 실행할 수 있습니다. 반복자를 추가하여 데이터 프레임 인덱스의 첫 번째 값이 아닌 다른 값으로 창을 시작하는 방법을 볼 수 있습니다 (예를 들어 시작에도> 규칙을 사용할 수 있음).

슬라이싱이 더 힘들어 질 수 있으므로 초대형 데이터 또는 매우 작은 증분의 경우 효율성이 떨어질 수 있습니다 (몇 주에 걸쳐 시간별 창에 대해 수십만 개의 데이터 행과 여러 열에 대해 충분히 작동합니다).


2

영업 월의 델타에서이 오류가 발생하여 window = '1M'으로 시도했을 때 user2689410 코드가 손상되었음을 발견했습니다.

AttributeError: 'MonthEnd' object has no attribute 'delta'

상대 시간 델타를 직접 전달하는 옵션을 추가하여 사용자 정의 기간에 대해 유사한 작업을 수행 할 수 있습니다.

포인터 주셔서 감사합니다. 여기에 내 시도가 있습니다. 유용하기를 바랍니다.

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

평균을 계산하는 3 일 기간의 예 :

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

0

색인이 실제로 유용 datetime하지 않은지 확인하십시오 str.

data.index = pd.to_datetime(data['Index']).values
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.