【问题标题】:Creating a Pandas rolling-window series of arrays创建 Pandas 滚动窗口系列数组
【发布时间】:2016-08-10 14:40:13
【问题描述】:

假设我有以下代码:

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

我该怎么做?

【问题讨论】:

    标签: python numpy pandas


    【解决方案1】:

    这是使用NumPy broadcasting 的矢量化方法 -

    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 的二维值数组 -

    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 作为填充符的二维值数组 -

    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
      """"
      

      【讨论】:

        【解决方案3】:

        这是一种方法

        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
        

        【讨论】:

        • 这很优雅。也适用于完整的数据帧而不是系列。
        【解决方案4】:

        如果在系列的开头和结尾附上缺少的nans,则使用简单的窗口

        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
        

        【讨论】:

          猜你喜欢
          • 2018-05-11
          • 2018-05-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-03-13
          • 2018-01-28
          相关资源
          最近更新 更多