scipy: Interpolating trajectory - python

I have a trajectory formed by a sequence of (x,y) pairs. I would like to interpolate points on this trajectory using splines.
How do I do this? Using scipy.interpolate.UnivariateSpline doesn't work because neither x nor y are monotonic. I could introduce a parametrization (e.g. length d along the trajectory), but then I have two dependent variables x(d) and y(d).
Example:
import numpy as np
import matplotlib.pyplot as plt
import math
error = 0.1
x0 = 1
y0 = 1
r0 = 0.5
alpha = np.linspace(0, 2*math.pi, 40, endpoint=False)
r = r0 + error * np.random.random(len(alpha))
x = x0 + r * np.cos(alpha)
y = x0 + r * np.sin(alpha)
plt.scatter(x, y, color='blue', label='given')
# For this special case, the following code produces the
# desired results. However, I need something that depends
# only on x and y:
from scipy.interpolate import interp1d
alpha_i = np.linspace(alpha[0], alpha[-1], 100)
r_i = interp1d(alpha, r, kind=3)(alpha_i)
x_i = x0 + r_i * np.cos(alpha_i)
y_i = x0 + r_i * np.sin(alpha_i)
plt.plot(x_i, y_i, color='green', label='desired')
plt.legend()
plt.show()

Using splprep you can interpolate over curves of any geometry.
from scipy import interpolate
tck,u=interpolate.splprep([x,y],s=0.0)
x_i,y_i= interpolate.splev(np.linspace(0,1,100),tck)
Which produces a plot like the one given, but only using the x and y points and not the alpha and r paramters.
Sorry about my original answer, I misread the question.

Related

Plotting arrows perpendicular to coordinates

I have a plot like this, plotting a semicircle with x and y
I want to add arrows at each point like so (ignore the horrible paint job):
Is there an easy way to add arrows perpendicular to the plot?
Current code:
import numpy as np
import matplotlib.pyplot as plt
r = 2
h = 0
k = 0
x0 = h-r
x1 = h+r
x = np.linspace(x0,x1,9)
y = k + np.sqrt(r**2 - (x-h)**2)
plt.scatter(x,y)
plt.xlim(-4,4)
plt.ylim(-4,4)
PERPENDICULAR TO THE TANGENT OF THE CURVE I'M SORRY I FORGOT TO ADD THIS
A point in space has no idea what "perpendicular" means, but assuming your y is some function of x that has a derivate, you can think of the derivate of the function at some point to be the tangent of the curve at that point, and to get a perpendicular vector you just need to rotate the vector counter-clockwise 90 degrees:
x1, y1 = -y0, x0
We know that these points come from a circle. So given three points we can easily find the center using basic geometry notions. If you need a refresher, take a look here.
For this particular case, the center is at the origin. Knowing the center coordinates, the normal at each point is just the vector from the center to the point itself. Since the center is the origin, the normals' components are just given by the coordinates of the points themselves.
import numpy as np
import matplotlib.pyplot as plt
r = 2
h = 0
k = 0
x0 = h-r
x1 = h+r
x = np.linspace(x0, x1, 9)
y = k + np.sqrt(r**2 - (x-h)**2)
center = np.array([0.0, 0.0])
plt.scatter(x, y)
plt.quiver(x, y, x, y, width=0.005)
plt.xlim(-4, 4)
plt.ylim(-4, 4)
plt.show()
If you are in a hurry and you do not have time to implement equations, you could use the scikit-spatial library in the following way:
from skspatial.objects import Circle, Vector, Points
import numpy as np
import matplotlib.pyplot as plt
r = 2
h = 0
k = 0
x0 = h-r
x1 = h+r
x = np.linspace(x0, x1, 9)
y = k + np.sqrt(r**2 - (x-h)**2)
points = Points(np.vstack((x, y)).T)
circle = Circle.best_fit(np.vstack((x, y)).T)
center = circle.point
normals = np.array([Vector.from_points(center, point) for point in points])
plt.scatter(x, y)
plt.quiver(x, y, normals[:, 0], normals[:, 1], width=0.005)
plt.xlim(-4, 4)
plt.ylim(-4, 4)
plt.show()
Postulate of blunova's and simon's answers is correct, generally speaking: points have no normal, but curve have; so you need to rely on what you know your curve is. Either, as blunova described it, by the knowledge that it is a circle, and computing those normal with ad-hoc computation from that knowledge.
Or, as I am about to describe, using the function f such as y=f(x). and using knowledge on what is the normal to such a (x,f(x)) chart.
Here is your code, written with such a function f
import numpy as np
import matplotlib.pyplot as plt
r = 2
h = 0
k = 0
x0 = h-r
x1 = h+r
x = np.linspace(x0,x1,9)
def f(x):
return k + np.sqrt(r**2 - (x-h)**2)
y=f(x)
plt.scatter(x,y)
plt.xlim(-4,4)
plt.ylim(-4,4)
So, all I did here is rewriting your line y=... in the form of a function.
From there, it is possible to compute the normal to each point of the chart (x,f(x)).
The tangent to a point (x,f(x)) is well known: it is vector (1,f'(x)), where f'(x) is the derivative of f. So, normal to that is (-f'(x), 1).
Divided by √(f'(x)²+1) to normalize this vector.
So, just use that as entry to quiver.
First compute a derivative of your function
dx=0.001
def fprime(x):
return (f(x+dx)-f(x-dx))/(2*dx)
Then, just
plt.quiver(x, f(x), -fprime(x), 1)
Or, to have all vector normalized
plt.quiver(x, f(x), -fprime(x)/np.sqrt(fprime(x)**2+1), 1/np.sqrt(fprime(x)**2+1))
(note that fprime and the normalization part are all vectorizable operation, so it works with x being a arange)
All together
import numpy as np
import matplotlib.pyplot as plt
r = 2
h = 0
k = 0
x0 = h-r
x1 = h+r
def f(x):
return k+ np.sqrt(r**2 - (x-h)**2)
dx=0.001
x = np.linspace(x0+dx,x1-dx,9)
y = f(x)
def fprime(x):
return (f(x+dx)-f(x-dx))/(2*dx)
plt.scatter(x,y)
plt.quiver(x,f(x), -fprime(x)/np.sqrt(fprime(x)**2+1), 1/np.sqrt(fprime(x)**2+1))
plt.xlim(-4,4)
plt.ylim(-4,4)
plt.show()
That is almost an exact copy of your code, but for the quiver line, and with the addition of fprime.
One other slight change, specific to your curve, is that I changed x range to ensure the computability of fprime (if first x is x0, then fprime need f(x0-dx) which does not exist because of sqrt. Likewise for x1. So, first x is x0+dx, and last is x1-dx, which is visually the same)
That is the main advantage of this solution over blunova's: it is your code, essentially. And would work if you change f, without assuming that f is a circle. All that is assume is that f is derivable (and if it were not, you could not define what those normal are anyway).
For example, if you want to do the same with a parabola instead, just change f
import numpy as np
import matplotlib.pyplot as plt
r = 2
h = 0
k = 0
x0 = h-r
x1 = h+r
def f(x):
return x**2
dx=0.001
x = np.linspace(x0+dx,x1-dx,9)
y = f(x)
def fprime(x):
return (f(x+dx)-f(x-dx))/(2*dx)
plt.scatter(x,y)
plt.quiver(x,f(x), -fprime(x)/np.sqrt(fprime(x)**2+1), 1/np.sqrt(fprime(x)**2+1))
plt.xlim(-4,4)
plt.ylim(-2,5)
plt.show()
All I changed here is the f formula. Not need for a new reasoning to compute the normal.
Last remark: an even more accurate version (not forcing the approximate computation of fprime with a dx) would be to use sympy to define f, and then compute the real, symbolic, derivative of f. But that doesn't seem necessary for your case.

Smooth circular data

I have an array of data Y such that Y is a function of an independent variable X (another array).
The values in X vary from 0 to 360, with wraparound.
The values in Y vary from -180 to 180, also with wraparound.
(That is, these values are angles in degrees around a circle.)
Does anyone know of any function in Python (in numpy, scipy, etc.) capable of low-pass filtering my Y values as a function of X?
In case this is at all confusing, here's a plot of example data:
Say you start with
import numpy as np
x = np.linspace(0, 360, 360)
y = 5 * np.sin(x / 90. * 3.14) + np.random.randn(360)
plot(x, y, '+');
To perform a circular convolution, you can do the following:
yy = np.concatenate((y, y))
smoothed = np.convolve(np.array([1] * 5), yy)[5: len(x) + 5]
This uses, at each point, the cyclic average with the previous 5 points (inclusive). Of course, there are other ways of doing so.
>>> plot(x, smoothed)
Here is a solution using pandas to do a moving average. First unwrap the data (need to convert to radians and back), so there are no discontinuities (e.g., jump from 180 to -179). Then compute the moving average and finally convert back to wrapped data if desired. Also, check out this numpy cookbook recipe using np.convolve().
import numpy as np
import pandas as pd
# generate random data
X = pd.Series([(x + 5*np.random.random())%360 for x in range(-100, 600, 15)])
Y = pd.Series([(y + 5*np.random.random())%360 - 180 for y in range(-200, 500, 15)])
# 'unwrap' the angles so there is no wrap around
X1 = pd.Series(np.rad2deg(np.unwrap(np.deg2rad(Y))))
Y1 = pd.Series(np.rad2deg(np.unwrap(np.deg2rad(Y))))
# smooth the data with a moving average
# note: this is pandas 17.1, the api changed for version 18
X2 = pd.rolling_mean(X1, window=3)
Y2 = pd.rolling_mean(Y1, window=3)
# convert back to wrapped data if desired
X3 = X2 % 360
Y3 = (Y2 + 180)%360 - 180
You can use convolve2D from scipy.signal. Here is a function, which applies smoothing to a numpy array a. If a has more than one dimension smoothing is applied to the innermost (fastest) dimension.
import numpy as np
from scipy import signal
def cyclic_moving_av( a, n= 3, win_type= 'boxcar' ):
window= signal.get_window( win_type, n, fftbins=False ).reshape( (1,n) )
shp_a= a.shape
b= signal.convolve2d( a.reshape( ( np.prod( shp_a[:-1], dtype=int ), shp_a[-1] ) ),
window, boundary='wrap', mode='same' )
return ( b / np.sum( window ) ).reshape( shp_a )
For instance it can be used like
import matplotlib.pyplot as plt
x = np.linspace(0, 360, 360)
y1 = 5 * np.sin(x / 90. * 3.14) + 0.5 * np.random.randn(360)
y2 = 5 * np.cos(0.8 * x / 90. * 3.14) + 0.5 * np.random.randn(360)
y_av= cyclic_moving_av( np.stack((y1,y2)), n=10 ) #1
plt.plot(x, y1, '+')
plt.plot(x, y2, '+')
plt.plot(x, y_av[0])
plt.plot(x, y_av[1])
plt.show()
This results in
Line #1 is equivalent to
y_av[0]= cyclic_moving_av( y1, n=10 )
y_av[1]= cyclic_moving_av( y2, n=10 )
win_type= 'boxcar' results in averaging over neighbors with equal weights. See signal.get_window for other options.

Create a 2D array that is the product of two functions

I am working on a visualization and trying to create a 2D array that is the product of a normalized Gaussian function on the X axis and a normalized exponential function on the Y axis (using Python).
I would use NumPy for this. You can use np.meshgrid to create the (X, Y) axes and use NumPy's vectorized functions to create the function on these coordinates. The array f below is your two-dimensional array, here containing the product of exp(-X/4) and exp(-((Y-2)/1.5)**2). (Substitute your own normalized functions here.)
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0,10,100)
y = np.linspace(0,5,100)
X, Y = np.meshgrid(x, y)
f = np.exp(-X/4.) * np.exp(-((Y-2)/1.5)**2)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(f)
plt.show()
If you can't or don't want to use NumPy, you'll have to loop by hand and use conventional math functions:
import math
dx, dy = 0.1, 0.05
nx, ny = 101, 101
f = [[None]*nx for i in range(ny)]
for ix in range(nx):
x = xmin + dx*ix
for iy in range(ny):
y = ymin + dy*iy
f[iy][ix] = math.exp(-x/4.) * math.exp(-((y-2)/1.5)**2)
I would use numpy for this, because numpy makes it very simple to do what you want. If you can't use it, then something like the following should work:
import math
def gauss(x, mu=0.0, sigma=1.0):
return 1.0 / math.sqrt(2.0*math.pi*sigma**2) * math.exp(-0.5*(x-mu)**2/sigma**2)
def exponential(x, lam=1.0):
return lam * math.exp(-lam * x)
# X values from -10 to 10 with 0.01 step size
xvals = [x * 0.01 for x in range(-1000, 1001)]
# Y values from 0 to 10 with 0.01 step size
yvals = [y * 0.01 for y in range(0, 1001)]
# Calculate your function at the grid points
f = [[gauss(x)*exponential(y) for x in xvals] for y in yvals]

Matplotlib like matlab's trisurf

To make a long story short, I'd like to plot a generic 3D triangle mesh in python. Matplotlib seems to be the ideal candidate, but I'd go with any 3D rendering that can do what I'm about to describe.
Suppose I have a triangle mesh defined by X, Y, and Z, the 3D coordinates of a point cloud, each a vector of length n, and UVW, a 2D m-x-3 matrix in which each row is a triplet of indices into the point cloud. This triplet represents an individual triangle. In other words, I have m triangles over n points. In Matlab, to generated a 3D plot, I just do:
trisurf(UVW, X, Y, Z)
Does anyone have any experience with this? In particular, can mplots trisurf be shoehorned to work?
Depending on your performance needs, mayavi is likely to be best suited for this - as per Davis comment.
However, matplotlib comes with plot_trisurf to which you can perfectly pass generic UVW, X, Y , Z as you describe.
Exemple with a torus mesh:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
R = 1.
r = 0.8
n = 50
m = 50
def torus_triangles(n, m):
""" Returns triangles to mesh a (n, m) torus """
tri = []
for i in range(n):
for j in range(m):
a = i + j*(n)
b = ((i+1) % n) + j*n
d = i + ((j+1) % m) * n
c = ((i+1) % n) + ((j+1) % m) * n
tri += [[a, b, d], [b, c, d]]
return np.array(tri, dtype=np.int32)
theta0 = np.linspace(0, (2*np.pi), n, endpoint=False)
phi0 = np.linspace(0, (2*np.pi), m, endpoint=False)
theta, phi = np.meshgrid(theta0, phi0)
x = (R + r * np.sin(phi)) * np.cos(theta)
y = (R + r * np.sin(phi)) * np.sin(theta)
z = r * np.cos(phi)
triangles = torus_triangles(n , m)
triang = mtri.Triangulation(x.ravel(), y.ravel(), triangles)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(triang, z.ravel(), lw=0.2, edgecolor="black", color="grey",
alpha=0.5)
plt.show()
I was also looking for a solution to this problem and this discussion helped me to succeed. Here is how it works:
A solution to very similar problem was already given as a link here in the comment from GBy (see above: Colouring the surface of a sphere with a set of scalar values in matplotlib)
Transfering the knowledge to the problem here it results in creating an additional array containing the amplitudes and assigning it to the "underlying ScalarMappable through set_array method". The corresponding python code looks like this:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib import pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
colors = np.mean(CorticalImage[Face], axis=1)
collec = ax.plot_trisurf(Xcoordinates, Ycoordinates, Zcoordinates, triangles=Face, cmap=cm.jet, linewidth=0.2)
collec.set_array(colors)
collec.autoscale()
ax.view_init(30, 0)
cbar = fig.colorbar(collec)
The arrays Xcoordinates, Ycoordinates, Zcoordinates contain the X, Y and Z coordinates of the mesh nodes. When checking their shape with e.g. Xcoordinates.shape it should look like this (750,), where 750 is the number of mesh nodes. The matrix Face is the same as the matrix UVW in the original question asked by Larry. It is "a 2D m-x-3 matrix in which each row is a triplet of indices into the point cloud". If you check the shape of the matrix Face it should be something like (1496, 3), where 1496 is the number of triangles in the mesh and 3 is the number of nodes in one triangle. Finally, the array CorticalImage contains the amplitudes for every node in the mesh and these are the values, which we want to use for the colors of the mesh (and not the Z values). The shape of that array should be like the shapes of the coordinates arrays, i.e. (750,).
IMPORTANT!!! You can see that the number of nodes and the number of triangles are not equal. This is almost always the case. Additionally, the amplitudes are usually given for the nodes and not for the triangles. Consequently, the amplitudes should be calculated for the triangles in order to get the right colors in the plot. This is done in the line colors = np.mean(CorticalImage[Face], axis=1).
Plotly has an open-source trisurf Python implementation that is closer to MATLAB's trisurf().
Python code and examples here:
https://plot.ly/python/tri-surf/
Thought for completeness sake I would add a mayavi example here, using the mesh from GBy's answer.
import numpy as np
from mayavi import mlab
from tvtk.api import tvtk
R = 1.
r = 0.8
n = 50
m = 50
def torus_triangles(n, m):
""" Returns triangles to mesh a (n, m) torus """
tri = []
for i in range(n):
for j in range(m):
a = i + j*(n)
b = ((i+1) % n) + j*n
d = i + ((j+1) % m) * n
c = ((i+1) % n) + ((j+1) % m) * n
tri += [[a, b, d], [b, c, d]]
return np.array(tri, dtype=np.int32)
theta0 = np.linspace(0, (2*np.pi), n, endpoint=False)
phi0 = np.linspace(0, (2*np.pi), m, endpoint=False)
theta, phi = np.meshgrid(theta0, phi0)
x = (R + r * np.sin(phi)) * np.cos(theta)
y = (R + r * np.sin(phi)) * np.sin(theta)
z = r * np.cos(phi)
triangles = torus_triangles(n , m)
mesh = mlab.triangular_mesh(x,y,z, triangles, representation='wireframe',color=(0,0,1) )
mlab.show()
Yielding:

Numerical ODE solving in Python

How do I numerically solve an ODE in Python?
Consider
\ddot{u}(\phi) = -u + \sqrt{u}
with the following conditions
u(0) = 1.49907
and
\dot{u}(0) = 0
with the constraint
0 <= \phi <= 7\pi.
Then finally, I want to produce a parametric plot where the x and y coordinates are generated as a function of u.
The problem is, I need to run odeint twice since this is a second order differential equation.
I tried having it run again after the first time but it comes back with a Jacobian error. There must be a way to run it twice all at once.
Here is the error:
odepack.error: The function and its Jacobian must be callable functions
which the code below generates. The line in question is the sol = odeint.
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from numpy import linspace
def f(u, t):
return -u + np.sqrt(u)
times = linspace(0.0001, 7 * np.pi, 1000)
y0 = 1.49907
yprime0 = 0
yvals = odeint(f, yprime0, times)
sol = odeint(yvals, y0, times)
x = 1 / sol * np.cos(times)
y = 1 / sol * np.sin(times)
plot(x,y)
plt.show()
Edit
I am trying to construct the plot on page 9
Classical Mechanics Taylor
Here is the plot with Mathematica
In[27]:= sol =
NDSolve[{y''[t] == -y[t] + Sqrt[y[t]], y[0] == 1/.66707928,
y'[0] == 0}, y, {t, 0, 10*\[Pi]}];
In[28]:= ysol = y[t] /. sol[[1]];
In[30]:= ParametricPlot[{1/ysol*Cos[t], 1/ysol*Sin[t]}, {t, 0,
7 \[Pi]}, PlotRange -> {{-2, 2}, {-2.5, 2.5}}]
import scipy.integrate as integrate
import matplotlib.pyplot as plt
import numpy as np
pi = np.pi
sqrt = np.sqrt
cos = np.cos
sin = np.sin
def deriv_z(z, phi):
u, udot = z
return [udot, -u + sqrt(u)]
phi = np.linspace(0, 7.0*pi, 2000)
zinit = [1.49907, 0]
z = integrate.odeint(deriv_z, zinit, phi)
u, udot = z.T
# plt.plot(phi, u)
fig, ax = plt.subplots()
ax.plot(1/u*cos(phi), 1/u*sin(phi))
ax.set_aspect('equal')
plt.grid(True)
plt.show()
The code from your other question is really close to what you want. Two changes are needed:
You were solving a different ODE (because you changed two signs inside function deriv)
The y component of your desired plot comes from the solution values, not from the values of the first derivative of the solution, so you need to replace u[:,0] (function values) for u[:, 1] (derivatives).
This is the end result:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
def deriv(u, t):
return np.array([u[1], -u[0] + np.sqrt(u[0])])
time = np.arange(0.01, 7 * np.pi, 0.0001)
uinit = np.array([1.49907, 0])
u = odeint(deriv, uinit, time)
x = 1 / u[:, 0] * np.cos(time)
y = 1 / u[:, 0] * np.sin(time)
plt.plot(x, y)
plt.show()
However, I suggest that you use the code from unutbu's answer because it's self documenting (u, udot = z) and uses np.linspace instead of np.arange. Then, run this to get your desired figure:
x = 1 / u * np.cos(phi)
y = 1 / u * np.sin(phi)
plt.plot(x, y)
plt.show()
You can use scipy.integrate.ode. To solve dy/dt = f(t,y), with initial condition y(t0)=y0, at time=t1 with 4th order Runge-Kutta you could do something like this:
from scipy.integrate import ode
solver = ode(f).set_integrator('dopri5')
solver.set_initial_value(y0, t0)
dt = 0.1
while t < t1:
y = solver.integrate(t+dt)
t += dt
Edit: You have to get your derivative to first order to use numerical integration. This you can achieve by setting e.g. z1=u and z2=du/dt, after which you have dz1/dt = z2 and dz2/dt = d^2u/dt^2. Substitute these into your original equation, and simply iterate over the vector dZ/dt, which is first order.
Edit 2: Here's an example code for the whole thing:
import numpy as np
import matplotlib.pyplot as plt
from numpy import sqrt, pi, sin, cos
from scipy.integrate import ode
# use z = [z1, z2] = [u, u']
# and then f = z' = [u', u''] = [z2, -z1+sqrt(z1)]
def f(phi, z):
return [z[1], -z[0]+sqrt(z[0])]
# initialize the 4th order Runge-Kutta solver
solver = ode(f).set_integrator('dopri5')
# initial value
z0 = [1.49907, 0.]
solver.set_initial_value(z0)
values = 1000
phi = np.linspace(0.0001, 7.*pi, values)
u = np.zeros(values)
for ii in range(values):
u[ii] = solver.integrate(phi[ii])[0] #z[0]=u
x = 1. / u * cos(phi)
y = 1. / u * sin(phi)
plt.figure()
plt.plot(x,y)
plt.grid()
plt.show()
scipy.integrate() does ODE integration. Is that what you are looking for?

Categories

Resources