почему pandas rolling использует одномерный ndarray
я был мотивирован использовать панд rolling
функция для выполнения скользящей многофакторной регрессии (этот вопрос не о прокатке многофакторной регрессии). Я ожидал, что смогу использовать apply
после df.rolling(2)
и получившийся pd.DataFrame
извлечь ndarray с .values
и выполните необходимое умножение матрицы. Это не сработало.
вот что я нашел:
import pandas as pd
import numpy as np
np.random.seed([3,1415])
df = pd.DataFrame(np.random.rand(5, 2).round(2), columns=['A', 'B'])
X = np.random.rand(2, 1).round(2)
как выглядят объекты например:
print "ndf = n", df
print "nX = n", X
print "ndf.shape =", df.shape, ", X.shape =", X.shape
df =
A B
0 0.44 0.41
1 0.46 0.47
2 0.46 0.02
3 0.85 0.82
4 0.78 0.76
X =
[[ 0.93]
[ 0.83]]
df.shape = (5, 2) , X.shape = (2L, 1L)
умножение матрицы ведет себя нормально:
df.values.dot(X)
array([[ 0.7495],
[ 0.8179],
[ 0.4444],
[ 1.4711],
[ 1.3562]])
использование apply для выполнения строки за строкой точка продукта ведет себя так, как ожидалось:
df.apply(lambda x: x.values.dot(X)[0], axis=1)
0 0.7495
1 0.8179
2 0.4444
3 1.4711
4 1.3562
dtype: float64
Groupby - > Apply ведет себя так, как я ожидал:
df.groupby(level=0).apply(lambda x: x.values.dot(X)[0, 0])
0 0.7495
1 0.8179
2 0.4444
3 1.4711
4 1.3562
dtype: float64
но когда я запускаю:
df.rolling(1).apply(lambda x: x.values.dot(X))
я:
AttributeError: 'numpy.объект ndarray не имеет атрибута "values"
хорошо, поэтому панды используют straight ndarray
в пределах своей rolling
реализация. Я справлюсь с этим. Вместо использования .values
для получения ndarray
давайте попробуем:
df.rolling(1).apply(lambda x: x.dot(X))
фигуры (1,) и (2,1) не выровнены: 1 (dim 0)!= 2 (dim 0)
подождите! Что?!
поэтому я создал пользовательскую функцию, чтобы посмотреть, что делает Роллинг.
def print_type_sum(x):
print type(x), x.shape
return x.sum()
побежал:
print df.rolling(1).apply(print_type_sum)
<type 'numpy.ndarray'> (1L,)
<type 'numpy.ndarray'> (1L,)
<type 'numpy.ndarray'> (1L,)
<type 'numpy.ndarray'> (1L,)
<type 'numpy.ndarray'> (1L,)
<type 'numpy.ndarray'> (1L,)
<type 'numpy.ndarray'> (1L,)
<type 'numpy.ndarray'> (1L,)
<type 'numpy.ndarray'> (1L,)
<type 'numpy.ndarray'> (1L,)
A B
0 0.44 0.41
1 0.46 0.47
2 0.46 0.02
3 0.85 0.82
4 0.78 0.76
моя в результате pd.DataFrame
то же самое, это хорошо. Но он распечатал 10 одномерных ndarray
объекты. Насчет rolling(2)
print df.rolling(2).apply(print_type_sum)
<type 'numpy.ndarray'> (2L,)
<type 'numpy.ndarray'> (2L,)
<type 'numpy.ndarray'> (2L,)
<type 'numpy.ndarray'> (2L,)
<type 'numpy.ndarray'> (2L,)
<type 'numpy.ndarray'> (2L,)
<type 'numpy.ndarray'> (2L,)
<type 'numpy.ndarray'> (2L,)
A B
0 NaN NaN
1 0.90 0.88
2 0.92 0.49
3 1.31 0.84
4 1.63 1.58
то же самое, ожидайте выхода, но он напечатал 8 ndarray
объекты. rolling
производит одномерный ndarray
длиной window
для каждого столбца, в отличие от того, что я ожидал, который был ndarray
формы (window, len(df.columns))
.
вопрос в том, почему?
теперь у меня нет способа легко запустить многофакторную регрессию.
4 ответов
С помощью strides views concept on dataframe
, вот векторизованный подход -
get_sliding_window(df, 2).dot(X) # window size = 2
испытаний
In [101]: df = pd.DataFrame(np.random.rand(5, 2).round(2), columns=['A', 'B'])
In [102]: X = np.array([2, 3])
In [103]: rolled_df = roll(df, 2)
In [104]: %timeit rolled_df.apply(lambda df: pd.Series(df.values.dot(X)))
100 loops, best of 3: 5.51 ms per loop
In [105]: %timeit get_sliding_window(df, 2).dot(X)
10000 loops, best of 3: 43.7 µs per loop
проверить результаты -
In [106]: rolled_df.apply(lambda df: pd.Series(df.values.dot(X)))
Out[106]:
0 1
1 2.70 4.09
2 4.09 2.52
3 2.52 1.78
4 1.78 3.50
In [107]: get_sliding_window(df, 2).dot(X)
Out[107]:
array([[ 2.7 , 4.09],
[ 4.09, 2.52],
[ 2.52, 1.78],
[ 1.78, 3.5 ]])
огромное улучшение там, которое я надеюсь, останется заметным на больших массивах!
я хотел поделиться тем, что я сделал, чтобы обойти эту проблему.
дали pd.DataFrame
и окно, я генерирую stacked ndarray
используя np.dstack
(ответ). Затем я преобразую его в pd.Panel
и с помощью pd.Panel.to_frame
преобразовать его в pd.DataFrame
. На данный момент у меня есть pd.DataFrame
который имеет дополнительный уровень на своем индексе относительно оригинала pd.DataFrame
и новый уровень содержит информацию о каждом свернутом периоде. Например, если roll окно 3, новый уровень будет содержать [0, 1, 2]
. Элемент для каждого периода. Теперь я могу!--14--> level=0
и возвращает объект groupby. Теперь это дает мне объект, которым я могу гораздо более интуитивно манипулировать.
Функция Крена
import pandas as pd
import numpy as np
def roll(df, w):
roll_array = np.dstack([df.values[i:i+w, :] for i in range(len(df.index) - w + 1)]).T
panel = pd.Panel(roll_array,
items=df.index[w-1:],
major_axis=df.columns,
minor_axis=pd.Index(range(w), name='roll'))
return panel.to_frame().unstack().T.groupby(level=0)
демонстрация
np.random.seed([3,1415])
df = pd.DataFrame(np.random.rand(5, 2).round(2), columns=['A', 'B'])
print df
A B
0 0.44 0.41
1 0.46 0.47
2 0.46 0.02
3 0.85 0.82
4 0.78 0.76
давайте sum
rolled_df = roll(df, 2)
print rolled_df.sum()
major A B
1 0.90 0.88
2 0.92 0.49
3 1.31 0.84
4 1.63 1.58
чтобы заглянуть под капот, мы можем увидеть конструкцию:
print rolled_df.apply(lambda x: x)
major A B
roll
1 0 0.44 0.41
1 0.46 0.47
2 0 0.46 0.47
1 0.46 0.02
3 0 0.46 0.02
1 0.85 0.82
4 0 0.85 0.82
1 0.78 0.76
но как насчет цели, для которой я построил это, прокатная многофакторная регрессия. Но пока я ограничусь матричным умножением.
X = np.array([2, 3])
print rolled_df.apply(lambda df: pd.Series(df.values.dot(X)))
0 1
1 2.11 2.33
2 2.33 0.98
3 0.98 4.16
4 4.16 3.84
сделал следующие изменения в приведенном выше ответе, так как мне нужно было вернуть все скользящее окно, как это делается в pd.Фрейм данных.rolling ()
def roll(df, w):
roll_array = np.dstack([df.values[i:i+w, :] for i in range(len(df.index) - w + 1)]).T
roll_array_full_window = np.vstack((np.empty((w-1 ,len(df.columns), w)), roll_array))
panel = pd.Panel(roll_array_full_window,
items=df.index,
major_axis=df.columns,
minor_axis=pd.Index(range(w), name='roll'))
return panel.to_frame().unstack().T.groupby(level=0)
начиная с pandas v0.23 теперь можно пройти Series
вместо ndarray
качению.apply (). Просто установите raw=False
.
raw : bool, по умолчанию нет
False
: передает в функцию каждую строку или столбец в виде ряда.
True
илиNone
: переданная функция вместо этого получит объекты ndarray. Если вы просто применяете функцию уменьшения NumPy, это будет намного лучше спектакль. Требуемый параметр RAW-это и покажет FutureWarning если не прошло. В будущем raw по умолчанию будет False.новое в версии 0.23.0.
как уже отмечалось; Если вам нужно только одно измерение, передача его raw, очевидно, более эффективна. Это, вероятно, ответ на ваш вопрос; Роллинг.apply () изначально был построен для передачи ndarray
только потому, что это наиболее эффективный.