функция pandas Rolling () с ежемесячным смещением

Я пытаюсь использовать функцию Rolling () для фрейма данных pandas с ежемесячными данными. Однако я отказался от некоторых значений NaN, поэтому теперь в моих временных рядах есть пробелы. Таким образом, основной параметр окна дает вводящий в заблуждение ответ, поскольку он просто смотрит на предыдущее наблюдение:

import pandas as pd
import numpy as np
import random
dft = pd.DataFrame(np.random.randint(0,10,size=len(dt)),index=dt)
dft.columns = ['value']
dft['value'] = np.where(dft['value'] < 3,np.nan,dft['value'])
dft = dft.dropna()
dft['basic'] = dft['value'].rolling(2).sum()

См., Например, запись 2017-08-31, которая суммирует 3.0 и 9.0, но предыдущая запись - 2017-03-31.

In [57]: dft.tail()
Out[57]:
            value  basic
2017-02-28    8.0   12.0
2017-03-31    3.0   11.0
2017-08-31    9.0   12.0
2017-10-31    7.0   16.0
2017-11-30    7.0   14.0

Естественное решение (я думал) - использовать смещение 2M, но оно дает ошибку:

In [58]: dft['basic2M'] = dft['value'].rolling('2M').sum()
...<output omitted>...
ValueError: <2 * MonthEnds> is a non-fixed frequency

Если я перемещу ежедневное смещение, я могу заставить его работать, но это кажется странным обходным путем:

In [59]: dft['basic32D'] = dft['value'].rolling('32D', min_periods=2).sum()

In [61]: dft.tail()
Out[61]:
            value  basic  basic32D
2017-02-28    8.0   12.0      12.0
2017-03-31    3.0   11.0      11.0
2017-08-31    9.0   12.0       NaN
2017-10-31    7.0   16.0       NaN
2017-11-30    7.0   14.0      14.0

Я также попытался преобразовать в PeriodIndex:

dfp = dft.to_period(freq='M')

но это дает ту же ошибку:

dfp['basic2M'] = dfp['value'].rolling('2M').sum()

и это очень неожиданно:

dfp['basic32Dp'] = dfp['value'].rolling('32D', min_periods=2).sum()
In [68]: dfp
Out[68]:
         value  basic  basic32D  basic32Dp
2016-02    9.0    NaN       NaN        NaN
2016-03    3.0   12.0      12.0       12.0
2016-04    7.0   10.0      10.0       19.0
2016-05    3.0   10.0      10.0       22.0
2016-06    4.0    7.0       7.0       26.0
2016-07    7.0   11.0      11.0       33.0
2016-08    3.0   10.0      10.0       36.0
2016-09    9.0   12.0      12.0       45.0
2016-11    5.0   14.0       NaN       50.0
2017-01    4.0    9.0       NaN       54.0
2017-02    8.0   12.0      12.0       62.0
2017-03    3.0   11.0      11.0       65.0
2017-08    9.0   12.0       NaN       74.0
2017-10    7.0   16.0       NaN       81.0
2017-11    7.0   14.0      14.0       88.0

Возможно, смещение «32D» с индексом периода «M» трактуется как «32M»? Похоже, это просто увеличивающаяся сумма для всей серии.

Возможно, я неправильно понимаю, как использовать смещения? Очевидно, я мог бы решить эту проблему, сохранив NaN в исходном столбце value и просто используя параметр окна, но смещения кажутся весьма полезными.

Что бы это ни стоило, если я генерирую ежечасные данные с помощью DateTimeIndex, все, похоже, работает, как ожидалось (то есть смещение `` 2D '' с данными каждые 12 часов дает правильный ответ для отсутствующих строк).


person Jesse Blocher    schedule 05.06.2018    source источник
comment
Проблема с окном переменного размера заключается в том, что нет явно превосходного способа его применения, когда окно охватывает два месяца с разной длиной.   -  person Mad Physicist    schedule 05.06.2018
comment
Я понимаю. Вот почему я попробовал PeriodIndex - мне это кажется намного проще, потому что это всего лишь базовый счетчик - 12 месяцев в году.   -  person Jesse Blocher    schedule 05.06.2018
comment
Первоначальный блок кода, который вы указали, приводит к ошибке NameError: имя 'dt' не определено   -  person Sid Kwakkel    schedule 05.02.2021


Ответы (1)


Вот функция, которая дает вам скользящую сумму за указанное количество месяцев. В приведенном выше коде вы не указали переменную dt, поэтому я просто создал список времени (включая код).

from datetime import datetime
from dateutil.relativedelta import relativedelta
import pandas as pd
import numpy as np
import random

def date_range(start_date, end_date, increment, period):
    result = []
    nxt = start_date
    delta = relativedelta(**{period:increment})
    while nxt <= end_date:
        result.append(nxt)
        nxt += delta
    return result

def MonthRollSum(df, offset, sumColumn):
    #must have DateTimeIndex
    df2 = df.copy()
    df2.index = df2.index + pd.DateOffset(days = -offset)
    return df2.groupby([df2.index.year, df2.index.month])[sumColumn].sum()

# added this part to generate the dt list for 8hour interval for 1000 days
start_date = datetime.now()
end_date = start_date + relativedelta(days=1000)
end_date = end_date.replace(hour=19, minute=0, second=0, microsecond=0)
dt = date_range(start_date, end_date, 8, 'hours')

# the following was given by the questioner
dft = pd.DataFrame(np.random.randint(0,10,size=len(dt)),index=dt)
dft.columns = ['value']
dft['value'] = np.where(dft['value'] < 3,np.nan,dft['value'])
dft = dft.dropna()

# Call the solution function
dft = MonthRollSum(dft, 2, 'value')
dft

Результаты могут отличаться, потому что первоначальный список значений генерируется случайным образом:

2021  2     290.0
      3     379.0
      4     414.0
      5     368.0
      6     325.0
      7     405.0
      8     425.0
      9     380.0
      10    393.0
      11    370.0
      12    419.0
2022  1     377.0
      2     275.0
      3     334.0
      4     350.0
      5     395.0
      6     376.0
      7     420.0
      8     419.0
      9     359.0
      10    328.0
      11    394.0
      12    345.0
2023  1     381.0
      2     335.0
      3     352.0
      4     355.0
      5     376.0
      6     350.0
      7     401.0
      8     443.0
      9     394.0
      10    394.0
person Sid Kwakkel    schedule 05.02.2021