【问题标题】:Apply function on a rolling slice of a pandas DataFrame在 Pandas DataFrame 的滚动切片上应用函数
【发布时间】:2017-10-14 11:40:42
【问题描述】:

我想计算 2x2 矩阵的行列式,这是通过在 Nx2 矩阵上滚动大小为 2 的窗口来获取的。我只是将行列式用作示例函数。一般来说,我想将一个函数应用于一个数据帧,该函数是通过窗口更大的数据帧来获取的。

例如,这是一个 2x2 矩阵,我这样计算行列式:

import pandas as pd
import numpy as np

d = pd.DataFrame({
   "X": [1,2],
   "Y": [3,4]
   })
np.linalg.det(d)

现在,我可以通过沿以下数据帧的 axis=0 滑动大小为 2 的窗口来形成 4 个 2x2 矩阵:

df = pd.DataFrame({
    "A": [1,2,3,4,5],
    "B": [6,7,8,9,10],
  })

看起来像:

    A   B
0   1   6
1   2   7
2   3   8
3   4   9
4   5   10

所以我会得到 [-5., -5., -5., -5.]

据我所知,pandas.DataFrame.rolling 和 rolling.apply 只能应用于一维向量,而不是数据帧?你会怎么做呢?

【问题讨论】:

    标签: python pandas numpy scipy


    【解决方案1】:

    从数据框中提取一个 numpy 数组:

    >>> array = df.values
    >>> array
    array([[ 1,  6],
           [ 2,  7],
           [ 3,  8],
           [ 4,  9],
           [ 5, 10]])
    

    使用 numpy 的 as_strided 函数来创建你的滑动窗口视图:

    >>> from numpy.lib.stride_tricks import as_strided
    
    >>> rows, cols = array.shape
    >>> row_stride, col_stride = array.strides
    >>> windowed_array = as_strided(
    ...     array,
    ...     shape=(rows - 2 + 1, 2, cols),
    ...     strides=(row_stride, row_stride, col_stride))
    >>> windowed_array
    array([[[ 1,  6],
            [ 2,  7]],
    
           [[ 2,  7],
            [ 3,  8]],
    
           [[ 3,  8],
            [ 4,  9]],
    
           [[ 4,  9],
            [ 5, 10]]])
    

    现在将你的函数应用到结果数组:

    >>> np.linalg.det(windowed_array)
    array([-5., -5., -5., -5.])
    

    【讨论】:

      【解决方案2】:
      #You can replace np.linalg.det with other functions as you like.
      #use apply to get 'A' and 'B' from current row and next row and feed them into the function.
      df.apply(lambda x: np.linalg.det(df.loc[x.name:x.name+1, 'A':'B']) if x.name <(len(df)-1) else None,axis=1)
      
      Out[157]: 
      0   -5.0
      1   -5.0
      2   -5.0
      3   -5.0
      4    NaN
      dtype: float64
      

      【讨论】:

        【解决方案3】:

        使用列表推导进行自己的滚动:

        s = pd.Series([np.linalg.det(df.iloc[i:i+2]) for i in range(df.shape[0]-1)])
        

        输出:

        0   -5.0
        1   -5.0
        2   -5.0
        3   -5.0
        dtype: float64
        

        【讨论】:

          【解决方案4】:

          以前有人问过这个问题。但是,在您的情况下,一个简单的解决方法是:

          df['A'] * df['B'].shift(-1) - df['A'].shift(-1) * df['B']
          

          输出:

          0   -5.0
          1   -5.0
          2   -5.0
          3   -5.0
          4    NaN
          

          【讨论】:

          • 这种方法肯定比使用 apply 快,但是 OP 已经指定他正在寻找一种方法来使 rolling().apply() 处理多列数据(并且行列式只是一个示例函数)。
          猜你喜欢
          • 2022-12-10
          • 2017-03-28
          • 2017-11-14
          • 2020-12-08
          • 2017-12-11
          • 2013-09-01
          • 2012-08-06
          • 2017-08-22
          • 1970-01-01
          相关资源
          最近更新 更多