【问题标题】:why does pandas rolling use single dimension ndarray为什么熊猫滚动使用一维ndarray
【发布时间】:2016-09-25 23:19:28
【问题描述】:

我有动力使用 pandas rolling 功能来执行滚动多因素回归(这个问题不是关于滚动多因素回归)。我希望我能够在df.rolling(2) 之后使用apply 并获取结果pd.DataFrame.values 提取ndarray 并执行必要的矩阵乘法。结果不是这样。

这是我发现的:

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'

好的,所以 pandas 在其 rolling 实现中直接使用 ndarray。我可以处理。而不是使用.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 正在为每一列生成长度为 window 的单维 ndarray,而不是我预期的形状为 (window, len(df.columns))ndarray

问题是为什么?

我现在没有办法轻松运行滚动多因素回归。

【问题讨论】:

  • 这是known issue。我最近问过 Jeff,你可以在 cmets 中看到他的回答!
  • 从 Pandas 0.20 开始,最先进的解决方案是什么?似乎已经进行了很多改进。直接使用 rolling().apply() 可以实现 OP 中的目标吗?

标签: python pandas numpy group-by pandas-groupby


【解决方案1】:

我想分享一下我为解决这个问题所做的工作。

给定一个pd.DataFrame 和一个窗口,我使用np.dstack (see answer) 生成一个堆叠的ndarray。然后我将其转换为pd.Panel 并使用pd.Panel.to_frame 将其转换为pd.DataFrame。此时,我有一个pd.DataFrame,它的索引相对于原始pd.DataFrame 有一个额外的级别,新级别包含有关每个滚动周期的信息。例如,如果滚动窗口为 3,则新索引级别将包含 [0, 1, 2]。每个时期的一个项目。我现在可以groupbylevel=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

【讨论】:

  • 这很有帮助,谢谢。我在使用 nan 值时遇到了一点麻烦,但更新 roll 函数的最后一行以使用 .to_frame(filter_observations=False) 解决了我的问题。
  • 这很有帮助。但是有没有办法让'roll'列保持原始索引?例如0.46 0.47 总是与“1”相关联。谢谢。
  • 这很酷,但为什么这不是 Pandas 的一个功能呢?
【解决方案2】:

使用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 ]])

那里有巨大的改进,我希望在更大的阵列上能保持明显!

【讨论】:

  • 我在尝试使用 get_sliding_window 时遇到未解决的参考错误
【解决方案3】:

对上述答案进行了以下修改,因为我需要像在 pd.DataFrame.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)

【讨论】:

    【解决方案4】:

    Since pandas v0.23 it is now possible to pass a Series instead of a ndarray to Rolling.apply()。只需设置raw=False

    raw : bool,默认无

    False :将每一行或每一列作为一个系列传递给函数。

    TrueNone :传递的函数将接收 ndarray 对象。如果您只是应用 NumPy 缩减功能,这将获得更好的性能。 raw 参数是必需的,如果未传递,将显示 FutureWarning。未来 raw 将默认为 False。

    0.23.0 版中的新功能。

    如前所述;如果您只需要一个单一维度,则将其原始传递显然更有效。这可能是您问题的答案; Rolling.apply() 最初是为了传递 ndarray 而构建的,只是因为这是最有效的。

    【讨论】:

      猜你喜欢
      • 2021-12-22
      • 2018-07-30
      • 2018-09-07
      • 2018-06-26
      • 2017-03-30
      • 2019-04-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多