pandas滚动对象如何工作?

Bra*_*mon 33 python numpy cython dataframe pandas

编辑:我强调了这个问题,因为它可能太复杂了.问题的内容在下面以粗体显示.

我想更多地了解使用时实际创建的对象DataFrame.rollingSeries.rolling:

print(type(df.rolling))
<class 'pandas.core.window.Rolling'>
Run Code Online (Sandbox Code Playgroud)

一些背景:考虑常用的替代品np.as_strided.此代码段本身并不重要,但其结果是我在提出此问题时的参考点.

def rwindows(a, window):
    if a.ndim == 1:
        a = a.reshape(-1, 1)
    shape = a.shape[0] - window + 1, window, a.shape[-1]
    strides = (a.strides[0],) + a.strides
    windows = np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
    return np.squeeze(windows)
Run Code Online (Sandbox Code Playgroud)

rwindows将采用1d或2d ndarray并构建等于指定窗口大小的滚动"块"(如下所示). 对象如何与下面.rollingndarray输出进行比较? 它是一个迭代器,是否为每个块存储了某些属性?或完全不同的东西?我已经尝试使用属性/方法(例如__dict__和)在对象上使用制表符完成,_get_index()并且它们并没有告诉我太多.我也在_create_blocks熊猫中看到过一种方法 - 它是否与该strided方法类似?

# as_strided version

a = np.arange(5)
print(rwindows(a, 3))           # 1d input
[[0 1 2]
 [1 2 3]
 [2 3 4]]

b = np.arange(10).reshape(5,2)
print(rwindows(b, 4))           # 2d input
[[[0 1]
  [2 3]
  [4 5]
  [6 7]]

 [[2 3]
  [4 5]
  [6 7]
  [8 9]]]
Run Code Online (Sandbox Code Playgroud)

第2部分,额外信贷

使用上面的NumPy方法(这里是OLS实现)是必要的,因为funcpandas.core.window.Rolling.apply中必须

从ndarray输入生成单个值*args和**kwargs传递给函数

所以论证不能是另一个滚动对象.即

def prod(a, b):
    return a * b
df.rolling(3).apply(prod, args=((df + 2).rolling(3),))
-----------------------------------------------------------------------
...
TypeError: unsupported operand type(s) for *: 'float' and 'Rolling'
Run Code Online (Sandbox Code Playgroud)

所以这真的是我上面提到的问题所在.为什么传递的函数必须使用NumPy数组并生成单个标量值,这与.rolling对象的布局有什么关系?

And*_*sen 37

我建议你看一下源代码,以便深入了解滚动的内容.特别建议你看一下generic.pywindow.py中的rolling函数.从那里,你可以看一下,如果你指定一个窗口类型或默认所使用的类.最后一个继承自最终和.WindowRolling_Rolling_and_Expanding_Rolling_Window

那就是说,我会给我两分钱:熊猫的整个滚动机制依赖于numpy功能apply_along_axis.特别是它在这里用于熊猫.它与windows.pyxcython模块一起使用.在你的系列中,out是聚合的滚动窗口.对于典型的聚合函数,它可以有效地处理它们,但对于自定义聚合函数(使用apply()),它使用roll_generic()in windows.pyx.

pandas中的滚动功能独立地在pandas数据帧列上运行.它不是python迭代器,并且是延迟加载的,这意味着在对其应用聚合函数之前不会计算任何内容.直到聚合完成之前,才使用实际应用数据滚动窗口的函数.

混淆的一个原因可能是您将滚动对象视为数据帧.(您已df在最后一个代码段中命名了滚动对象).它真的不是.它是一个可以通过在其所包含的窗口逻辑上应用聚合来生成数据帧的对象.

您提供的lambda将应用于新数据帧的每个单元格.它在旧数据帧中向后(沿着每列)需要一个窗口,并将其聚合到新数据帧中的一个单元格.聚合可以是一些东西,例如sum,mean你自己定制的东西等,在一些窗口大小上,比如说3.以下是一些例子:

a = np.arange(5)
df = pd.DataFrame(a, columns=['a'])
df.rolling(3).mean().dropna()
Run Code Online (Sandbox Code Playgroud)

......也可以通过以下方式完成:

df.rolling(3).apply(np.mean).dropna()
Run Code Online (Sandbox Code Playgroud)

......并生产:

     a
2  3.0
3  6.0
4  9.0
Run Code Online (Sandbox Code Playgroud)

(第一列是索引值,可以在此处忽略,以及下一个示例.)

请注意我们如何提供现有的numpy聚合函数.这就是主意.我们应该能够提供我们想要的任何东西,只要它符合聚合函数的功能,即从中获取值的向量并从中生成单个值.这是另一个创建自定义聚合函数的函数,在本例中是窗口的L2范数:

df.rolling(3).apply(lambda x: np.sqrt(x.dot(x))).dropna()
Run Code Online (Sandbox Code Playgroud)

如果您不熟悉lambda函数,则与以下内容相同:

def euclidean_dist(x):
    return np.sqrt(x.dot(x))

df.rolling(3).apply(euclidean_dist).dropna()
Run Code Online (Sandbox Code Playgroud)

...屈服:

          a
2  2.236068
3  3.741657
4  5.385165
Run Code Online (Sandbox Code Playgroud)

为了确保,我们可以手动检查np.sqrt(0**2 + 1**2 + 2**2)确实2.236068.

[在最初的编辑中,在最后一段代码片段中,您的代码可能会比您预期的更早失败.在调用之前失败df.apply(...)您试图df在传递给数字2之前添加一个名为数字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]
Run Code Online (Sandbox Code Playgroud)

这是一个人为的例子,但我正在展示它,你可以传递任何你想要的常量,甚至是你正在使用的滚动对象.动态部分是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
Run Code Online (Sandbox Code Playgroud)

它为每个滚动块添加数组/向量,从而产生:

   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]
Run Code Online (Sandbox Code Playgroud)

请注意,它仅在您一次对列执行时才有效.如果你想在窗口上进行一些数学运算之前将其存储起来keep就好了.

也就是说,如果没有更多关于您想要实现的内容的输入,很难构建一个适合您需求的示例.

如果您的最终目标是创建滞后变量的数据框,那么我将使用以下实际列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()
Run Code Online (Sandbox Code Playgroud)

......给:

   a  a-1  a-2
2  2  1.0  0.0
3  3  2.0  1.0
4  4  3.0  2.0
Run Code Online (Sandbox Code Playgroud)

(可能有一些更美妙的方式,但它完成了工作.)

关于您b的第一个代码段中的变量,请记住,pandas中的DataFrames通常不会被处理为任意维度/对象的张量.您可以将任何内容填充到其中,但最终字符串,时间对象,整数和浮点数是预期的.这可能是大熊猫的设计者没有考虑允许滚动聚合到非标量值的原因.它甚至看起来不像允许简单的字符串作为聚合函数的输出.

无论如何,我希望这回答你的一些问题.如果没有让我知道,我会尽力帮助你在评论或更新.


关于_create_blocks()滚动物体功能的最后说明.

_create_blocks()当您使用freq参数时,该函数处理重建索引和装箱rolling.

如果你使用freq,比如说周数,那么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)
Run Code Online (Sandbox Code Playgroud)

...然后我们逐周获得分档(不滚动)的原始数据:

               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
Run Code Online (Sandbox Code Playgroud)

请注意,这不是聚合滚动的输出.这只是它所使用的新块.在这之后.我们做了一个聚合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
Run Code Online (Sandbox Code Playgroud)

...用测试总和检验: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)
Run Code Online (Sandbox Code Playgroud)

......产生......

            a
a            
2016-01-01  0
2016-01-02  1
2016-01-03  2
2016-01-04  3
2016-01-05  4
Run Code Online (Sandbox Code Playgroud)

...用于滚动窗口聚合.