Ответ 1
Я предлагаю вам взглянуть на исходный код, чтобы понять, что происходит. В частности, я предлагаю вам взглянуть на функции rolling
в generic.py и window.py. Оттуда вы можете посмотреть Window
класс, который используется, если вы укажете тип окна или значение по умолчанию rolling
class. Последний наследует от _Rolling_and_Expanding
и в конечном итоге _Rolling
и _Window
.
Тем не менее, я дам свои два цента: Pandas 'весь механизм катания полагается на функцию numpy apply_along_axis
. В частности, здесь используется здесь pandas. Он используется в сочетании с windows.pyx
cython module. В вашей серии выходим агрегированное окно. Для типичных функций агрегации он обрабатывает их для вас эффективно, но для пользовательских (с использованием apply()
) он использует roll_generic()
в windows.pyx
.
Функция качения в pandas работает независимо от столбцов фрейма pandas. Это не итератор python, и он ленив загружен, то есть ничего не вычисляется до тех пор, пока вы не примените к нему функцию агрегации. Функции, которые на самом деле применяют окно качения данных, не используются до начала агрегации.
Источником путаницы может быть то, что вы думаете о подвижном объекте как о кадре данных. (Вы назвали катящийся объект df
в своем последнем фрагменте кода). Это действительно не так. Это объект, который может создавать dataframes, применяя агрегации по логике окна, в которой он находится.
Лямбда, которую вы поставляете, применяется для каждой ячейки вашего нового фрейма. Он занимает окно назад (вдоль каждого столбца) в вашем старом фрейме данных и объединяет его в одну ячейку в новом фреймворке данных. Агрегация может быть такой, как sum
, mean
, что-то обычное, что вы сделали и т.д., Над некоторым размером окна, скажем 3. Вот несколько примеров:
a = np.arange(5)
df = pd.DataFrame(a, columns=['a'])
df.rolling(3).mean().dropna()
... что также можно сделать:
df.rolling(3).apply(np.mean).dropna()
... и производит:
a
2 3.0
3 6.0
4 9.0
(Первый столбец является значением индекса и может быть проигнорирован здесь и для следующих примеров.)
Обратите внимание, как мы предоставили существующую функцию агрегации numpy. Это идея. Предполагается, что мы можем поставлять все, что хотим, до тех пор, пока оно соответствует тем, какие функции агрегации выполняются, т.е. Принимают вектор значений и производят от него одно значение. Вот еще одна, где мы создаем пользовательскую функцию агрегации, в этом случае норма L2 окна:
df.rolling(3).apply(lambda x: np.sqrt(x.dot(x))).dropna()
если вы не знакомы с лямбда-функциями, это то же самое, что:
def euclidean_dist(x):
return np.sqrt(x.dot(x))
df.rolling(3).apply(euclidean_dist).dropna()
... yielding:
a
2 2.236068
3 3.741657
4 5.385165
Чтобы убедиться, что мы можем вручную проверить, что np.sqrt(0**2 + 1**2 + 2**2)
действительно 2.236068
.
[В вашем первоначальном редактировании, в последнем фрагменте кода, ваш код, вероятно, не работает раньше, чем вы ожидаете. Он не работает перед вызовом df.apply(...)
. Вы пытаетесь добавить катящийся объект с именем df
к номеру 2 до его передачи в df.apply(...)
. Скользящий объект - это не то, над чем вы делаете операции. Функция агрегации, которую вы предоставили, также не соответствует функции агрегации в целом. a
- это список со значениями окна, b
будет постоянным дополнительным параметром, в который вы проходите. Он может быть скользящим объектом, если вы хотите, но обычно это не то, что вы хотели бы сделать, Чтобы сделать это более понятным, вот что похоже на то, что вы делали в своем первоначальном редактировании, но работает:
a = np.arange(8)
df = pd.DataFrame(a, columns=['a'])
n = 4
rol = df.rolling(n)
def prod(window_list, constant_rol):
return window_list.dot(constant_rol.sum().dropna().head(n))
rol.apply(prod, args=(rol,)).dropna()
# [92.0, 140.0, 188.0, 236.0, 284.0]
Это надуманный пример, но я показываю его, чтобы указать, что вы можете передать все, что захотите, в качестве константы, даже для подвижного объекта, который вы используете сами. Динамическая часть - это первый аргумент a
в вашем случае или window_list
в моем случае. Все определенные окна в виде отдельных списков передаются в эту функцию по очереди.
Основываясь на ваших последующих комментариях, это может быть то, что вы ищете:
import numpy as np
import pandas as pd
n = 3
a = np.arange(5)
df = pd.DataFrame(a, columns=['a'])
def keep(window, windows):
windows.append(window.copy())
return window[-1]
windows = list()
df['a'].rolling(n).apply(keep, args=(windows,))
df = df.tail(n)
df['a_window'] = windows
который добавляет массивы/векторы к каждому блоку прокатки, создавая таким образом:
a a_window
2 2 [0.0, 1.0, 2.0]
3 3 [1.0, 2.0, 3.0]
4 4 [2.0, 3.0, 4.0]
Обратите внимание, что он работает только, если вы делаете это по столбцу за раз. Если вы хотите сделать некоторую математику в окне, прежде чем хранить ее в keep
, это тоже хорошо.
Тем не менее, без дополнительных сведений о том, чего вы пытаетесь достичь, сложно построить пример, который подходит вашим потребностям.
Если ваша конечная цель состоит в том, чтобы создать dataframe из отстающих переменных, я бы пошел на использование реальных столбцов, используя shift()
:
import numpy as np
import pandas as pd
a = np.arange(5)
df = pd.DataFrame(a, columns=['a'])
for i in range(1,3):
df['a-%s' % i] = df['a'].shift(i)
df.dropna()
...:
a a-1 a-2
2 2 1.0 0.0
3 3 2.0 1.0
4 4 3.0 2.0
(Может быть, есть еще более красивый способ сделать это, но он выполняет свою работу.)
Что касается вашей переменной b
в вашем первом фрагменте кода, помните, что DataFrames в pandas обычно не обрабатываются как тензоры произвольных измерений/объекта. Вы, вероятно, можете наполнить все, что захотите, но в конечном итоге ожидаются строки, объекты времени, ints и floats. Это могут быть причины, по которым дизайнеры pandas не беспокоились, позволяя прокачивать агрегацию нескалярным значениям. Это даже не похоже, что простая строка разрешена как вывод функции агрегации.
В любом случае, я надеюсь, что это ответ на некоторые из ваших вопросов. Если не сообщите мне, и я попытаюсь помочь вам в комментариях или обновлении.
Заключительная записка о функции _create_blocks()
для прокатки объектов.
Функция _create_blocks()
обрабатывает переиндексирование и бининг, когда вы используете аргумент freq
rolling
.
Если вы используете частоту с, скажем, неделями, чтобы freq=W
:
import pandas as pd
a = np.arange(50)
df = pd.DataFrame(a, columns=['a'])
df.index = pd.to_datetime('2016-01-01') + pd.to_timedelta(df['a'], 'D')
blocks, obj, index = df.rolling(4, freq='W')._create_blocks(how=None)
for b in blocks:
print(b)
... затем мы получаем забитые (не прокатные) исходные данные по неделям:
a
a
2016-01-03 2.0
2016-01-10 9.0
2016-01-17 16.0
2016-01-24 23.0
2016-01-31 30.0
2016-02-07 37.0
2016-02-14 44.0
2016-02-21 NaN
Обратите внимание, что это не результат агрегированной прокатки. Это просто новые блоки, над которыми он работает. После этого. Мы делаем агрегацию типа sum
и получаем:
a
a
2016-01-03 NaN
2016-01-10 NaN
2016-01-17 NaN
2016-01-24 50.0
2016-01-31 78.0
2016-02-07 106.0
2016-02-14 134.0
2016-02-21 NaN
..., который проверяется с помощью тестового суммирования: 50 = 2 + 9 + 16 + 23.
Если вы не используете freq
в качестве аргумента, он просто возвращает исходную структуру данных:
import pandas as pd
a = np.arange(5)
df = pd.DataFrame(a, columns=['a'])
blocks, obj, index = df.rolling(3)._create_blocks(how=None)
for b in blocks:
print(b)
... который производит...
a
a
2016-01-01 0
2016-01-02 1
2016-01-03 2
2016-01-04 3
2016-01-05 4
... и используется для агрегации скользящего окна.