创建一个Pandas滚动窗口数组系列

10

假设我有以下代码:

import numpy as np
import pandas as pd
x = np.array([1.0, 1.1, 1.2, 1.3, 1.4])
s = pd.Series(x, index=[1, 2, 3, 4, 5])

这将生成以下的s

1    1.0
2    1.1
3    1.2
4    1.3
5    1.4

现在我想创建一个大小为n的滚动窗口,但我不想取每个窗口的平均值或标准差,我只想要数组。所以,假设n = 3。我想要一种转换方法,给定输入s,输出以下系列:

1    array([1.0, nan, nan])
2    array([1.1, 1.0, nan])
3    array([1.2, 1.1, 1.0])
4    array([1.3, 1.2, 1.1])
5    array([1.4, 1.3, 1.2])

我该怎么做?

4个回答

16

以下是一种实现方式

In [294]: arr = [s.shift(x).values[::-1][:3] for x in range(len(s))[::-1]]

In [295]: arr
Out[295]:
[array([  1.,  nan,  nan]),
 array([ 1.1,  1. ,  nan]),
 array([ 1.2,  1.1,  1. ]),
 array([ 1.3,  1.2,  1.1]),
 array([ 1.4,  1.3,  1.2])]

In [296]: pd.Series(arr, index=s.index)
Out[296]:
1    [1.0, nan, nan]
2    [1.1, 1.0, nan]
3    [1.2, 1.1, 1.0]
4    [1.3, 1.2, 1.1]
5    [1.4, 1.3, 1.2]
dtype: object

这非常优雅。也适用于完整的数据框而不仅仅是系列。 - Kyle

9

这里提供了一种使用NumPy广播的向量化方法 -

n = 3 # window length
idx = np.arange(n)[::-1] + np.arange(len(s))[:,None] - n + 1
out = s.get_values()[idx]
out[idx<0] = np.nan

这将为您生成一个二维数组的输出。
要获取每个元素都持有窗口作为列表的系列 -
In [40]: pd.Series(out.tolist())
Out[40]: 
0    [1.0, nan, nan]
1    [1.1, 1.0, nan]
2    [1.2, 1.1, 1.0]
3    [1.3, 1.2, 1.1]
4    [1.4, 1.3, 1.2]
dtype: object

如果您希望将一维数组列表拆分为多个数组,可以使用np.split命令对输出进行操作,如下所示 -

out_split = np.split(out,out.shape[0],axis=0)

示例运行 -

In [100]: s
Out[100]: 
1    1.0
2    1.1
3    1.2
4    1.3
5    1.4
dtype: float64

In [101]: n = 3

In [102]: idx = np.arange(n)[::-1] + np.arange(len(s))[:,None] - n + 1
     ...: out = s.get_values()[idx]
     ...: out[idx<0] = np.nan
     ...: 

In [103]: out
Out[103]: 
array([[ 1. ,  nan,  nan],
       [ 1.1,  1. ,  nan],
       [ 1.2,  1.1,  1. ],
       [ 1.3,  1.2,  1.1],
       [ 1.4,  1.3,  1.2]])

In [104]: np.split(out,out.shape[0],axis=0)
Out[104]: 
[array([[  1.,  nan,  nan]]),
 array([[ 1.1,  1. ,  nan]]),
 array([[ 1.2,  1.1,  1. ]]),
 array([[ 1.3,  1.2,  1.1]]),
 array([[ 1.4,  1.3,  1.2]])]

通过使用 strides 实现内存效率

为了提高内存效率,我们可以使用步幅方式 - strided_axis0,类似于 @B. M.'s solution,但更加通用。

因此,要获取在第一个元素之前有NaN的2D数组值 -

In [35]: strided_axis0(s.values, fillval=np.nan, L=3)
Out[35]: 
array([[nan, nan, 1. ],
       [nan, 1. , 1.1],
       [1. , 1.1, 1.2],
       [1.1, 1.2, 1.3],
       [1.2, 1.3, 1.4]])

要按照问题所述的方式,在每行中将NaN作为填充物放在原始元素后面,并且翻转元素顺序,以获取带有2D值数组 -

In [36]: strided_axis0(s.values, fillval=np.nan, L=3)[:,::-1]
Out[36]: 
array([[1. , nan, nan],
       [1.1, 1. , nan],
       [1.2, 1.1, 1. ],
       [1.3, 1.2, 1.1],
       [1.4, 1.3, 1.2]])

为了获得一个系列,每个元素都包含一个窗口列表,只需将早期的方法用 pd.Series(out.tolist()) 包装起来,其中 out2D 数组输出 -
In [38]: pd.Series(strided_axis0(s.values, fillval=np.nan, L=3)[:,::-1].tolist())
Out[38]: 
0    [1.0, nan, nan]
1    [1.1, 1.0, nan]
2    [1.2, 1.1, 1.0]
3    [1.3, 1.2, 1.1]
4    [1.4, 1.3, 1.2]
dtype: object

2

您的数据看起来像是一个步进数组:

data=np.lib.stride_tricks.as_strided(np.concatenate(([NaN]*2,s))[2:],(5,3),(8,-8))
"""
array([[ 1. ,  nan,  nan],
       [ 1.1,  1. ,  nan],
       [ 1.2,  1.1,  1. ],
       [ 1.3,  1.2,  1.1],
       [ 1.4,  1.3,  1.2]])
"""

然后按系列进行转换:
pd.Series(map(list,data))
"""" 
0    [1.0, nan, nan]
1    [1.1, 1.0, nan]
2    [1.2, 1.1, 1.0]
3    [1.3, 1.2, 1.1]
4    [1.4, 1.3, 1.2]
dtype: object
""""

1

如果你在序列的开头和结尾添加缺失的nan,那么你可以使用一个简单的窗口

def wndw(s,size=3):

    stretched = np.hstack([
        np.array([np.nan]*(size-1)),
        s.values.T,
        np.array([np.nan]*size)
    ])

    for begin in range(len(stretched)-size):
        end = begin+size
        yield stretched[begin:end][::-1]

for arr in wndw(s, 3):
    print arr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接