Генерация скользящих средних из временного ряда, но при выборе на основе месяца

#python #pandas

#python #pandas

Вопрос:

У меня есть длинные временные ряды еженедельных данных. Для данного наблюдения я хочу вычислить значение за эту неделю по сравнению со средним значением за три предыдущих года за тот же месяц.

Конкретный пример: Для точки данных 2019-02-15 я хочу сравнить ее со средним значением всех точек данных за февраль 2018, февраль 2017 и февраль 2016.

Я хочу заполнить все временные ряды таким образом. (конечно, первые три года будут np.nans )

Я сделал действительно грубый пример вычисления с одной точкой данных, которое я хочу выполнить, но я не уверен, как реализовать это в векторизованном решении. Я также не впечатлен тем, что мне пришлось использовать эту промежуточную вспомогательную таблицу «mth_avg».

 import pandas as pd
ix = pd.date_range(freq='W-FRI',start="20100101", end='20190301' )
df  = pd.DataFrame({"foo": [x for x in range(len(ix))]}, index=ix) #weekly data
mth_avg = df.resample("M").mean() #data as a monthly average over time
mth_avg['month_hack'] = mth_avg.index.month

#average of previous three years' same-month averages
df['avg_prev_3_year_same-month'] = "?"

#single arbitrary example of my intention
df.loc['2019-02-15', "avg_prev_3_year_same-month"]= (
    mth_avg[mth_avg.month_hack==2]
                    .loc[:'2019-02-15']
                    .iloc[-3:]
                    .loc[:,'foo']
                    .mean() 
                    )


df[-5:]
  

Ответ №1:

Я думаю, что на самом деле это нетривиальная проблема — для этого нет существующей функциональности, о которой я знаю в Pandas. Создание вспомогательной таблицы экономит время вычисления, фактически я использовал две. Мое решение использует цикл (а именно понимание списка) и осведомленность Pandas о дате и времени, чтобы избежать вашего month_hack . В остальном я думаю, что это было хорошее начало. Был бы рад увидеть что-то более элегантное!

 # your code
ix = pd.date_range(freq='W-FRI',start="20100101", end='20190301' )
df  = pd.DataFrame({"foo": [x for x in range(len(ix))]}, index=ix)
mth_avg = df.resample("M").mean()

# use multi-index of month/year with month first
mth_avg.index = [mth_avg.index.month, mth_avg.index.year]
tmp = mth_avg.sort_index().groupby(level=0).rolling(3).foo.mean()
tmp.index = tmp.index.droplevel(0)

# get rolling value from tmp
res = [tmp.xs((i.month, i.year - 1)) for i in df[df.index > '2010-12-31'].index]

# NaNs for 2010
df['avg_prev_3_year_same-month'] = np.NaN
df.loc[df.index > '2010-12-31', 'avg_prev_3_year_same-month'] = res

# output
df.sort_index(ascending=False).head()

            foo     avg_prev_3_year_same-month
2019-03-01  478     375.833333
2019-02-22  477     371.500000
2019-02-15  476     371.500000
2019-02-08  475     371.500000
2019-02-01  474     371.500000