我正在尝试对一些数据拟合平滑B样条,我发现这里的post非常有用。但是,我不仅需要样条,还需要它的派生,所以我尝试在示例中添加以下代码:
tck_der = interpolate.splder(tck, n=1)
x_der, y_der, z_der = interpolate.splev(u_fine, tck_der)
由于某些数据类型问题,由于某种原因,这似乎不起作用。我得到以下回溯:
Traceback (most recent call last):
File "interpolate_point_trace.py", line 31, in spline_example
tck_der = interpolate.splder(tck, n=1)
File "/home/user/anaconda3/lib/python3.7/site-packages/scipy/interpolate/fitpack.py", line 657, in splder
return _impl.splder(tck, n)
File "/home/user/anaconda3/lib/python3.7/site-packages/scipy/interpolate/_fitpack_impl.py", line 1206, in splder
sh = (slice(None),) + ((None,)*len(c.shape[1:]))
AttributeError: 'list' object has no attribute 'shape'
这样做的原因似乎是tck
元组的第二个参数包含numpy
数组的列表。我认为将输入数据也转换为numpy
数组会有所帮助,但不会改变tck
的数据类型。
此行为反映出scipy
中的错误,还是输入格式错误?
我尝试手动将列表变成数组:
tck[1] = np.array(tck[1])
但是这个(这并不令我惊讶)也给出了一个错误:
ValueError: operands could not be broadcast together with shapes (0,8) (7,1)
关于问题可能是什么的任何想法?我在1D样条线之前和之前都使用过scipy
,而splder
函数的功能还不错,所以我认为它与3D样条线有关。
答案 0 :(得分:2)
偶然发现相同的问题...
我使用interpolate.splder(tck, n=1)
来规避错误,而是使用interpolate.splev(spline_ev, tck, der=1)
来返回点spline_ev
处的导数(请参阅Scipy Doku)。
如果需要花键,我想您可以再次使用interpolate.splprep()
。
总共类似:
import numpy as np
from scipy import interpolate
import matplotlib.pyplot as plt
points = np.random.rand(10,2) * 10
(tck, u), fp, ier, msg = interpolate.splprep(points.T, s=0, k=3, full_output=True)
spline_ev = np.linspace(0.0, 1.0, 100, endpoint=True)
spline_points = interpolate.splev(spline_ev, tck)
# Calculate derivative
spline_der_points = interpolate.splev(spline_ev, tck, der=1)
spline_der = interpolate.splprep(spline_der_points.T, s=0, k=3, full_output=True)
# Plot the data and derivative
fig = plt.figure()
plt.plot(points[:,0], points[:,1], '.-', label="points")
plt.plot(spline_points[0], spline_points[1], '.-', label="tck")
plt.plot(spline_der_points[0], spline_der_points[1], '.-', label="tck_der")
# Show tangent
plt.arrow(spline_points[0][23]-spline_der_points[0][23], spline_points[1][23]-spline_der_points[1][23], 2.0*spline_der_points[0][23], 2.0*spline_der_points[1][23])
plt.legend()
plt.show()
我还在Github上发布了一个Issue,并且根据 ev-br ,interpolate.splprep
的使用已过时,应该使用make_interp_spline
/ {{1 }}。
答案 1 :(得分:0)
如其他答案所述,splprep
的输出与splder
不兼容,但与splev
兼容。后者可以评估导数。
但是,对于插值,有另一种方法可以完全避免使用splprep
。我基本上是在SciPy问题跟踪器(https://github.com/scipy/scipy/issues/10389)上复制答复:
这里是复制splprep
输出的示例。首先,让我们从splprep
输出中了解一下:
# start with the OP example
import numpy as np
from scipy import interpolate
points = np.random.rand(10,2) * 10
(tck, u), fp, ier, msg = interpolate.splprep(points.T, s=0, k=3, full_output=True)
# check the meaning of the `u` array: evaluation of the spline at `u`
# gives back the original points (up to a list/transpose)
xy = interpolate.splev(u, tck)
xy = np.asarray(xy)
np.allclose(xy.T, points)
接下来,让我们在不使用splprep
的情况下进行复制。首先,建立u
数组:用参数表示曲线,而u
本质上是弧长的近似值。其他参数化也是可能的,但是在这里让我们坚持splprep
所做的事情。翻译文档页面https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.splprep.html
vv = np.sum((points[1:, :] - points[:-1, :])**2, axis=1)
vv = np.sqrt(vv).cumsum()
vv/= vv[-1]
vv = np.r_[0, vv]
# check:
np.allclose(u, vv)
现在,沿着参数曲线插值:points
与vv
:
spl = interpolate.make_interp_spline(vv, points)
# check spl.t vs knots from splPrep
spl.t - tck[0]
结果spl
是一个BSpline
对象,您可以按照通常的方式对其进行评估,区分等:
np.allclose(points, spl(vv))
# differentiate
spl_derivative = spl.derivative(vv)