【问题标题】:Rolling window polynomial fit in pandas大熊猫中的滚动窗口多项式拟合
【发布时间】:2019-07-30 06:27:13
【问题描述】:

我正在尝试从 n 次多项式计算系数,该多项式应用于时间序列的 t 天窗口。但是,我收到了一个异常TypeError: only length-1 arrays can be converted to Python scalars

我的版本是:

  • Python 3.6
  • 熊猫版本 0.22.0
  • numpy 版本 1.13.3

代码:

import pandas as pd
import numpy as np
my_ts = pd.Series(data = np.random.normal(size = 365 * 2), index = pd.date_range(start = '2013-01-01', periods = 365 * 2))
coefs = pd.rolling_apply(my_ts, 21, lambda x: np.polyfit(range(len(x)), x, 3))

然而,当我包装 np.polyfit 使其只返回一个系数时,rolling_apply 没有问题。

def pf_wrapper(x):
    coef_lst = np.polyfit(range(len(x)), x, 3)
    return coef_lst[0]
coefs = pd.rolling_apply(my_ts, 21, pf_wrapper)

更新:

由于pd.rolling_apply() 无法返回非标量,我目前的解决方案如下:

def get_beta(ts, deg):
    coefs = polyfit(range(len(ts)), ts, deg = 3)[::-1]
    return coefs[deg]

b0 = pd.rolling_apply(my_ts, 21, lambda x: get_beta(x, 0))
...
b3 = pd.rolling_apply(my_ts, 21, lambda x: get_beta(x, 3))

【问题讨论】:

    标签: python numpy pandas


    【解决方案1】:

    我认为rolling_apply 不可能。 documentation 表示应用函数“必须从 ndarray 输入产生单个值”。它的实际意思似乎是“必须产生一个值是或可以转换为单个浮点数”。如果您跟进完整的异常回溯,它会将您带到algos.pyx 中的此代码:

    output = np.empty(n, dtype=float)
    counts = roll_sum(np.isfinite(input).astype(float), win, minp)
    
    bufarr = np.empty(win, dtype=float)
    oldbuf = <float64_t*> bufarr.data
    
    n = len(input)
    for i from 0 <= i < int_min(win, n):
        if counts[i] >= minp:
            output[i] = func(input[int_max(i - win + 1, 0) : i + 1], *args,
                             **kwargs)
        else:
            output[i] = NaN
    

    错误是在output[i] = func(...) 行引发的。您可以看到输出数组被硬编码为具有 dtype 浮点数。如果您尝试将 numpy 数组(长度大于 1)转换为浮点数,您收到的错误与您收到的错误相同:

    >>> float(np.array([1, 2, 3]))
    Traceback (most recent call last):
      File "<pyshell#14>", line 1, in <module>
        float(np.array([1, 2, 3]))
    TypeError: only length-1 arrays can be converted to Python scalars
    

    所以发生的事情是它试图将polyfit 的输出分配给 float ndarray 的单个元素,但由于 polyfit 的输出是一个无法转换为 float 的数组而失败。

    这可以通过使 output 具有 dtype 对象来“修复”,但这会减慢速度。

    我认为您必须将rolling_apply 视为仅可用于返回单个浮点数的函数。要支持非标量输出,您必须滚动(har har)您自己的rolling_apply 版本。

    【讨论】:

      【解决方案2】:

      我遇到了同样的问题,你可以在 lambda 函数中添加 [0]:

      coefs = pd.rolling_apply(my_ts, 21, lambda x: np.polyfit(range(len(x)), x, 3)[0])
      

      现在可以正常使用了。

      【讨论】:

        【解决方案3】:

        由于 rolling_apply 已被弃用,Nissar 的解决方案也适用于 pd.rolling.apply 方法:

        coefs = my_ts.rolling(21).apply(lambda x: np.polyfit(range(len(x)), x, 3)[0])    
        

        这里特别重要的是 Nissar 使用 range(len(x)) 来满足时间分量,这避免了无法将 rolling.apply 与两列或系列的 lambda 函数一起使用(因为 x (时间)组件通常位于数据框或另一个系列的另一列中)。

        【讨论】:

          【解决方案4】:

          我想创建一个 IIR 过滤器类型的时间序列扩展。 例如:[1,2,3,4,5] with window = 2 应该给出:[[1,2], [2,3], [3,4], [4,5]]

          这是我基于一些不良编码实践的解决方案,但可以完成工作。 从 rolling_apply() 返回到全局字典/数组的索引并丢弃返回值。当 rolling_apply 返回时,字典中的解决方案就准备好了。

          import pandas as pd
          import numpy as np
          
          dataDict = dict()
          INDEX = 0
          
          def windowFunc(w):
            global INDEX
            global dataDict
            dataDict[INDEX] = np.copy(w)
            INDEX = INDEX + 1
            return INDEX
          
          dd = pd.DataFrame([1,2,3,4,5,6,7,8,9,0])
          dd2 = pd.rolling_apply(dd, window=2, func = windowFunc)
          print(list(dataDict.values()))
          

          【讨论】:

            猜你喜欢
            • 2017-03-30
            • 2020-04-21
            • 2021-03-30
            • 1970-01-01
            • 2015-01-10
            • 2017-01-15
            • 2021-04-06
            • 1970-01-01
            • 2018-07-10
            相关资源
            最近更新 更多