【问题标题】:Continuous Piecewise-Linear Fit in PythonPython中的连续分段线性拟合
【发布时间】:2014-03-28 12:31:46
【问题描述】:

我有许多短时间序列(可能是 30 到 100 个时间点),它们具有一般形状:它们从高位开始,迅速下降,可能会或可能不会在零附近稳定,然后回升。如果它们不平稳,它们看起来就像一个简单的二次曲线,如果它们确实是平稳的,那么您可能会有一长串零。

我正在尝试使用lmfit 模块来拟合连续的分段线性曲线。 我想推断线条在哪里改变梯度,也就是说,我想知道曲线在哪里“定性地”改变梯度。我想知道梯度什么时候停止下降,什么时候又开始增加,一般来说。我遇到了一些问题:

  • lmfit 似乎至少需要两个参数,所以我必须通过 _
  • 我不确定如何将一个参数限制为大于另一个参数。
  • 我收到could not broadcast input array from shape (something) into shape (something) 错误

这里有一些代码。首先,我的目标函数,要最小化。

def piecewiselinear(params, data, _) :

    t1 = params["t1"].value
    t2 = params["t2"].value
    m1 = params["m1"].value
    m2 = params["m2"].value
    m3 = params["m3"].value
    c = params["c"].value

    # Construct continuous, piecewise-linear fit
    model = np.zeros_like(data)
    model[:t1] = c + m1 * np.arange(t1)
    model[t1:t2] = model[t1-1] + m2 * np.arange(t2 - t1)
    model[t2:] = model[t2-1] + m3 * np.arange(len(data) - t2)

    return model - data

然后我打电话,

p = lmfit.Parameters()
p.add("t1", value = len(data)/4, min = 1, max = len(data))
p.add("t2", value = len(data)/4*3, min = 2, max = len(data))
p.add("m1", value = -100., max=0)
p.add("m2", value = 0.)
p.add("m3", value = 20., min = 1.)
p.add("c", min=0, value = 800.)

result = lmfit.minimize(piecewiselinear, p, args = (data, _) )

该模型是,在某个时间 t1,线的梯度发生变化,并且在 t2 发生同样的情况。这两个参数,以及线段的梯度(和一个截距)都需要推断。

我可以使用 MCMC 方法做到这一点,但我有太多这些系列,而且需要太长时间。

部分回溯:

     15     model = np.zeros_like(data)
     16     model[:t1] = c + m1 * np.arange(t1)
---> 17     model[t1:t2] = model[t1-1] + m2 * np.arange(t2-t1)
     18     model[t2:] = model[t2-1] + m3 * np.arange(len(data) - t2)
     19 

ValueError: could not broadcast input array from shape (151) into shape (28)

时间序列的几个例子:

欢迎提出任何建议。非常感谢。

【问题讨论】:

    标签: python time-series mathematical-optimization minimization inference


    【解决方案1】:

    这是一个相当蛮力的 3-pwlin 装配工的情节; 将用粗略的代码换取测试用例。

    另外,还有几个链接:
    Fit-piecewise-linear-data 关于 dsp.stack 可能会给你一些想法;加了一点 Dynamic programming.
    github.com/NickFoubert/simple-segment 有用于分割的python,例如具有 max_error 的心电图(不是件数), 来自 Keogh 等人的一篇好论文, An online algorithm for segmenting time series, 2001, 8p.

    还有一个可能的选择:你能不能把p 的力量放在y ~ x^plog y ~ p log x^2 (在将x 转换为 [-1 .. 1] 和y > 1e-6 左右之后)?
    这将是健壮快速并且易于绘制和理解。
    一个人可能应该权衡两端 使错误大致平坦且正常。
    也可以将pp' 分别放在左右两半。

    【讨论】:

    • 非常感谢您提供指向 DSP 帖子的链接。里面有一些有趣的选择!抱歉,我意识到我最初的问题不是很清楚,所以我已经解决了。我真的在寻找结。等效地,无论采用何种措施(PWL 与否),我都想要曲线真正“改变方向”的两个位置——我认为 PWL 近似是捕捉它的最佳方式。抱歉之前没说清楚。
    • 这是一个 pickle 文件,其中包含一个带有一些数据的 dict。 x1, x2, x3 是我的第一个图中的平滑曲线,代表数据平滑度的一个极端。 y1, y2, y3 代表另一个极端:非常尖锐和不连续的数据。阅读data = pickle.load("so.p")。 www.quentincaudron.com/so.p。感谢您在这方面的坚持。我很想去蛮力,测试所有可能的结位置。丑陋但可能有效!
    • 不,“ValueError:不安全的字符串泡菜”?? np.savetxt 一些(或者算了吧——收益递减)
    • 呃,pickle 文件在这里似乎可以正常打开。对于那个很抱歉。如果您仍然感兴趣,我在这里有六个 np.savetxt 数组:quentincaudron.com/so。否则,不用担心。感谢您迄今为止的帮助,非常感谢。
    【解决方案2】:

    走蛮力路线似乎可以解决问题。我只是在测试开关点的所有组合并选择最合适的。它非常快并且可以相当健壮。这是一个特别适合的结果。

    我强制第二行的梯度为零。这确保了我们不会得到两条线的良好拟合和一条完美的拟合,这可能会获得更高的分数(我在这里使用 R^2 值的总和)。绿色标记了切换点,它们应该非常适合我的应用程序。

    我很想学一个更优雅的方法来做这件事,但与此同时,这是一个选择......

    【讨论】:

      猜你喜欢
      • 2020-07-27
      • 2015-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-18
      • 1970-01-01
      • 2016-03-09
      • 1970-01-01
      相关资源
      最近更新 更多