【问题标题】:How to convert a spline fit into a piecewise function?如何将样条拟合转换为分段函数?
【发布时间】:2020-04-28 19:51:22
【问题描述】:

假设我有

import numpy as np
from scipy.interpolate import UnivariateSpline

# "true" data; I don't know this function
x = np.linspace(0, 100, 1000)
d = np.sin(x * 0.5) + 2 + np.cos(x * 0.1)

# sample data; that's what I actually measured
x_sample = x[::20]
d_sample = d[::20]

# fit spline
s = UnivariateSpline(x_sample, d_sample, k=3, s=0.005)

plt.plot(x, d)
plt.plot(x_sample, d_sample, 'o')
plt.plot(x, s(x))
plt.show()

我明白了

我现在想要的是所有橙色点之间的函数,比如

knots = s.get_knots()
f0 = <some expression> for knots[0] <= x < knots[1]
f1 = <some expression> for knots[1] <= x < knots[2]
...

因此,fi 的选择方式应能再现样条拟合的形状。

我找到了here 的帖子,但在上面的示例中生成的样条线似乎不正确,而且它也不完全是我需要的,因为它不返回表达式。

如何将样条曲线变成分段函数?是否有一种(直截了当的)方式来表达每个间隔,例如作为多项式?

【问题讨论】:

  • 我也没有弄清楚,但我的理解是困难在于将B样条基础的系数转换为您想要的标准幂基础的系数,它不像@那么容易987654328@。一旦你有了系数,它就是一个简单的应用程序,例如np.polyevalnp.piecewise。看看this discussion 了解转换的技巧。
  • @stevemo:您能否详细说明PPoly.from_spline 部分?我不必使用UnivariateSpline,但任何给我fi 的样条都可以,然后我确实可以输入piecewise
  • 我最终弄清楚并给出了完整的答案......如果需要可以编辑一下。
  • @stevemo:太好了,谢谢!今天没有时间再看它了,但明天会这样做,然后再回复你;因此,如果我沉默了一会儿,请不要感到惊讶... :)

标签: python numpy scipy piecewise


【解决方案1】:

简短的回答是,如果您对基于标准幂的多项式系数感兴趣,最好使用CubicSpline(请参阅this discussion):

cu = scipy.interpolate.CubicSpline(x_sample, d_sample)

plt.plot(x_sample, y_sample, 'ko')
for i in range(len(cu.x)-1):
    xs = np.linspace(cu.x[i], cu.x[i+1], 100)
    plt.plot(xs, np.polyval(cu.c[:,i], xs - cu.x[i]))

为了回答您的问题,您可以改为使用numpy.piecewisecu.x 中的断点和cu.c 中的系数从此处创建一个分段函数,然后自己直接编码多项式表达式或使用numpy.polyval .例如,

cu.c[:,0]  # coeffs for 0th segment
# array([-0.01316353, -0.02680068,  0.51629024,  3.        ])

# equivalent ways to code polynomial for this segment
f0 = lambda x: cu.c[0,0]*(x-x[0])**3 + cu.c[1,0]*(x-x[0])**2 + cu.c[2,0]*(x-x[0]) + cu.c[3,0]
f0 = lambda x: [cu.c[i,0]*(x-x[0])**(3-i) for i in range(4)]

# ... or getting values directly from x's
y0 = np.polyval(cu.c[:,0], xs - cu.x[0])

更长的答案:

这里有几点潜在的混淆:

  • UnivariateSpline 适合 B-spline 基,因此系数与标准多项式幂基不同
  • 为了从 B 样条转换,我们可以使用 PPoly.from_spline,但不幸的是,UnivariateSpline 返回一个 截断 的结和系数列表,这些结和系数无法使用此函数。我们可以通过访问样条对象的内部数据来解决这个问题,这是一个小禁忌。
  • 此外,系数矩阵c(无论来自UnivariateSpline 还是CubicSpline)的度数顺序相反,并假设您自己“居中”,例如c[k,i] 处的系数属于c[k,i]*(x-x[i])^(3-k)

根据您的设置,请注意,如果我们不使用 UnivariateSpline 包装器,而是直接拟合 splrep 并且没有平滑 (s=0),我们可以获取 tck (knots-coefficients-degree) 元组并发送将它传递给PPoly.from_spline 函数并获得我们想要的系数:

tck = scipy.interpolate.splrep(x_sample, d_sample, s=0)
tck
# (array([0.        , 0.        , 0.        , 0.        , 2.68456376,
#        4.02684564, 5.36912752, 6.7114094 , 9.39597315, 9.39597315,
#        9.39597315, 9.39597315]),
# array([3.        , 3.46200469, 4.05843704, 3.89649312, 3.33792889,
#        2.29435138, 1.65015175, 1.59021688, 0.        , 0.        ,
#        0.        , 0.        ]),
# 3)

p = scipy.interpolate.PPoly.from_spline(tck)
p.x.shape  # breakpoints in unexpected shape
# (12,)

p.c.shape  # polynomial coeffs in unexpected shape
# (4, 11)

注意tckp.x 中奇怪的重复断点:这是一个 FITPACK 的东西(运行所有这些的算法)。

如果我们尝试使用 (s.get_knots(), s.get_coeffs(), 3) 从 UnivariateSpline 发送 tck 元组,我们会丢失那些重复,所以 from_spline 不起作用。检查source,虽然看起来完整的向量存储在self._data,所以我们可以

s = scipy.interpolate.UnivariateSpline(x_sample, d_sample, s=0)
tck = (s._data[8], s._data[9], 3)
p = scipy.interpolate.PPoly.from_spline(tck)

和以前一样。要检查这些系数是否有效:

plt.plot(x_sample, d_sample, 'o')

for i in range(len(p.x)-1):
    xs = np.linspace(p.x[i], p.x[i+1], 10)
    plt.plot(xs, np.polyval(p.c[:,i], xs - p.x[i]))

注意 numpy.polyval 想要 coeff 的反向顺序,因此我们可以按原样传递 p.c

【讨论】:

  • 还是没时间真正测试一下,不过看起来还不错。只是一件小事,然后我接受并奖励赏金:“如果您对标准幂基础中的多项式系数感兴趣”,我认为那很好。您能否根据np.polyval 的输出写下前三个部分的解析方程,所以(现在只是随机表达式)f1 = x^2 + 2, f2 = x^3 + x^2 + 2,...?在我的实际应用中,我需要这些表达式来处理每一件作品,所以我不能使用numpy;如果这样做了,这似乎是一个完美的答案,谢谢:)
  • 我为第一段添加了一个示例,希望对您有所帮助
【解决方案2】:

应该能够使用您描述的分段函数,例如:

import numpy as np
from scipy.interpolate import UnivariateSpline

# "true" data; I don't know this function
x = np.linspace(0, 100, 1000)
d = np.sin(x * 0.5) + 2 + np.cos(x * 0.1)

# sample data; that's what I actually measured
x_sample = x[::20]
d_sample = d[::20]

# fit spline
s = UnivariateSpline(x_sample, d_sample, k=3, s=0.005)

plt.plot(x, d)
plt.plot(x_sample, d_sample, 'o')
plt.plot(x, s(x))

knots = s.get_knots()

conditions = [x < knots[0], (x >= knots[0]) * (x < knots[1]), (x >= knots[1]) * (x < knots[10]), x >= knots[10]]
# need one function for each condition
fns = [0, lambda x :-x, lambda x: 0.01*x**2, lambda x: 0.2*x**0.5]
y = np.piecewise(x, conditions, fns)

plt.plot(x, y)
plt.show()

我选择了一些随机条件和函数——我相信你能找到更合适的!

【讨论】:

  • 谢谢。几个问题:什么是红线?它不应该与绿色和蓝色的形状相同吗?也许我的帖子不清楚,但我想重现曲线的确切形状,就像一个分段函数而不是使用s(x)。你是如何构造fns的,例如0.01*x**2,这是从哪里来的?你不是也需要s.get_coeffs()吗?
  • 啊,是的,抱歉,我以为您可能想要重建绿色曲线,但不清楚。我制作的fns0.01*x**2 只是为了展示 piecewise 操作,但我现在意识到这并不是你想要的!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-11
  • 2018-11-22
  • 2012-09-11
  • 2012-06-23
  • 2014-08-07
相关资源
最近更新 更多