由于AttributeError,无法区分3D中的样条曲线

时间:2019-05-29 09:36:13

标签: python scipy spline derivative

我正在尝试对一些数据拟合平滑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样条线有关。

2 个答案:

答案 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)

现在,沿着参数曲线插值:pointsvv

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)