The Data Scientician
The Data Scientician

Reputation: 491

Equally spaced spline evaluation in scipy

I'm using scipy for building a bivariate spline of a curve (similar to an ellipse), with splprep and splev. The purpose is to smooth the points.

The problem is that the points I'm trying to smooth are not evenly distributed along the path, and when I try to evaluate the spline I will get uneven distribution, but I would like to have uniformly distributed points on the spline.

Here's an example, showing what my data looks like and a similar result (in my case this effect is, in fact, much more evident):

t = np.r_[0:2*np.pi:100.j, 0.142:np.pi+0.1:100j, 0.07+np.pi/2:0.23+np.pi:200j]
t = np.random.normal(t, 0.01)
t = np.unique(t)
# plt.plot(t)
r = np.asarray([1.0, 1.01] * (len(t) // 2)) # np.random.normal() # 1, 0.005, size=len(t))
xy = np.asarray([np.cos(t) * r, np.sin(t) * r]).T
# plt.plot(*xy.T, '.')
# plt.axis('equal')

tck, _ = splprep(xy.T, s=0, per=True)
xi, yi = splev(np.linspace(0, 1, 200), tck)
plt.subplots(figsize=(10, 10))
plt.plot(xi, yi, '.')
plt.axis('equal')

As you can see from the plot below, there is one area which is more dense of points: I would like to avoid this effect and have evenly spaced points (even better if they are spaced with a fixed angle relative to the centroid, e.g. 1 point every 0.5 degrees).

Non uniform distribution of points after spline eval

I think the reason for this is that points result in a "jagged" pattern in the dense area: see for example this plot showing how the points change in frequency at the top of the circle.

Jagged edges

I think this is related to how u is computed in splprep (see doc) and I think I could fix the problem by tweaking the u parameter, but I don't know how: the way it is calculated is apparently fine right now, and I can't come up with a better strategy:

v = [0]
for i in range(1, len(xy)):
    vi = v[i - 1] + sum((xy[i] - xy[i - 1]) ** 2) ** 0.5
    v.append(vi)
u = [v[i] / v[-1] for i in range(1, len(xy))]

Considering that using the spline is the method I'm trying to use to remove extra points from the dataset (xy), the only idea I had is to recompute u in some way to get the desired effect, but I don't know how.

How can I smooth my data making sure that evaluated points on spline are roughly at the same distance one from the other?

Edit

I realized that I basically have to set u to be the angle of each point (divided by 2pi, to normalize within 0 and 1). I tried and points look like evenly spaced, but for some reason I get some outliers

uu = t / (2 * np.pi) # u1# 2
tck, _ = splprep(xy.T, u=uu, s=0, per=True)
xi, yi = splev(np.linspace(0, 1, 200), tck)
plt.subplots(figsize=(10, 10))
plt.plot(xi, yi)#, '.')
plt.axis('equal')

Outliers after changing U

Problem is, I can't understand where these come from. I suspect it depends on how the spline is calculated, but can't figure out how to solve this issue. The only solution I can use right now is to use smoothing, but it's a very trial-and-error method that I'd rather not adopt.

Upvotes: 1

Views: 1697

Answers (1)

user6655984
user6655984

Reputation:

Forcing u=t makes life too hard for the interpolator, because some of the t values are very close to each other while the corresponding points are not so close due to the varying r. This results in large deviations of the interpolating curve from the data, i.e., outliers on your second plot.

Instead, compute the spline with the default u, and then reparametrize proportional to polar angle. To this end, I first evaluate the spline at equally spaced values in the parameter domain (as in your first attempt), find the polar angle of each resulting point with unwrap(arctan2), and then find the inverse of the u->angle function with linear interpolation. This inverse function, inserted in the spline, results in uniformly spaced points according to their polar angle.

xx, yy = splev(np.linspace(0, 1, 200), tck)
s = np.unwrap(np.arctan2(yy, xx))
s_inv = np.interp(np.linspace(s[0], s[-1], len(s)), s, np.linspace(0, 1, len(s))) 
xi, yi = splev(s_inv, tck)

better

Upvotes: 3

Related Questions