Пользовательская функция прокрутки Pandas DataFrame, которая видит индекс

Я хочу применить функцию смещения скользящего окна к DataFrame с индексом даты. Вот пример:

rng = pd.date_range('2017-01-03', periods=20, freq='W')
df = pd.DataFrame(np.random.randn(20), rng, columns=['Val'])
df.index.name = 'Date'
r = df.rolling('15D')

Это создает DataFrame df, например:

                 Val
Date                
2017-01-08  0.592210
2017-01-15 -1.243938
2017-01-22 -0.713988
2017-01-29  1.554777
...

Но я не могу понять, как увидеть дату, связанную с каждым Val, в любой функции, которую я применяю к окну Rolling. Например, следующее:

def f(data=None): # I really want to reference the Date associated with each Val in here!
    print('f(%s) data=%s' % (str(type(data)), data))        
    return 1
r.apply(lambda x: f(x))

показывает, что все, что я вижу, это ndarray для каждого вызова:

f(<class 'numpy.ndarray'>) data=[0.59220959]
f(<class 'numpy.ndarray'>) data=[ 0.59220959 -1.24393841]
f(<class 'numpy.ndarray'>) data=[ 0.59220959 -1.24393841 -0.71398767]
f(<class 'numpy.ndarray'>) data=[-1.24393841 -0.71398767  1.55477737]
...

Есть ли способ вызвать скользящее окно смещения времени в DataFrame таким образом, чтобы функция агрегации видела индекс, связанный с каждым значением в окне?

Например, чтобы я мог применить функцию, которая видит что-то вроде:

f(<class 'DataFrame'>) data=[{2017-01-08, 0.59221}]
f(<class 'DataFrame'>) data=[{2017-01-08, 0.59221}, {2017-01-15, -1.243938}]
...

person feetwet    schedule 05.03.2018    source источник


Ответы (2)


Это возможно в последней версии с .apply(..., raw=False)

Хитрость заключается в том, чтобы определить функцию, которая имеет доступ ко всему вашему фрейму данных. Затем вы выполняете проверку любого столбца и вызываете apply(), передавая эту функцию. Функция будет иметь доступ к данным окна, которые являются подмножеством столбца фрейма данных. Из этого подмножества вы можете извлечь нужный вам индекс. (Это предполагает, что ваш индекс строго увеличивается. Таким образом, обычный целочисленный индекс будет работать, как и большинство временных рядов.) Вы можете использовать индекс для доступа ко всему фрейму данных со всеми столбцами.

def dataframe_roll(df):
    def my_fn(window_series):
        # Note: you can do any kind of offset here
        window_df = df[(df.index >= window_series.index[0]) & (df.index <= window_series.index[-1])]
        return window_df["col1"] + window_df["col2"]
    return my_fn

df["result"] = df["any_col"].rolling(24).apply(dataframe_roll(df), raw=False)
person Alexei Andreev    schedule 29.07.2019

Я не думаю, что есть способ сделать это только с помощью pd.rolling. Вот обходной путь, вдохновленный недавним вопросом SO:

s = pd.Series([df.loc[d - pd.offsets.DateOffset(days=15):d, 'Val'] for d in df.index])

Это создаст серию серий, где каждая подсерия содержит дату и значение, которые вы хотите видеть в своей функции. То есть, с вашей типовой функцией s.apply(f) производит:

f(<class 'pandas.core.series.Series'>) data=Date
2017-01-08   -1.662699
Freq: W-SUN, Name: Val, dtype: float64
f(<class 'pandas.core.series.Series'>) data=Date
2017-01-08   -1.662699
2017-01-15    0.478471
Freq: W-SUN, Name: Val, dtype: float64
f(<class 'pandas.core.series.Series'>) data=Date
2017-01-08   -1.662699
2017-01-15    0.478471
2017-01-22   -0.552616
Freq: W-SUN, Name: Val, dtype: float64
f(<class 'pandas.core.series.Series'>) data=Date
2017-01-15    0.478471
2017-01-22   -0.552616
2017-01-29   -2.190669
...
person Peter Leimbigler    schedule 05.03.2018