How do pandas Rolling objects work?

Viewed 28259

Edit: I condensed this question given that it was probably too involved to begin with. The meat of the question is in bold below.

I'd like to know more about the object that is actually created when using DataFrame.rolling or Series.rolling:

print(type(df.rolling))
<class 'pandas.core.window.Rolling'>

Some background: consider the oft-used alternative with np.as_strided. This code snippet itself isn't important, but its result is my reference point in asking this question.

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)

Here rwindows will take a 1d or 2d ndarray and build rolling "blocks" equal to the specified window size (as below). How does a .rolling object compare to the ndarray output below? Is it an iterator, with certain attributes stored for each block? Or something else entirely? I've tried playing around with tab completion on the object with attributes/methods such as __dict__ and _get_index() and they're not telling me much. I've also seen a _create_blocks method in pandas--does it at all resemble the strided method?

# 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]]]

Part 2, extra credit

Using the NumPy approach above (OLS implementation here) is necessitated by the fact that func within pandas.core.window.Rolling.apply must

produce a single value from an ndarray input *args and **kwargs are passed to the function

So the argument can't be another rolling object. I.e.

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'

So this is really from where my question above stems. Why is it that the passed function must use a NumPy array and produce a single scalar value, and what does this have to do with the layout of a .rolling object?

1 Answers
Related