I have a time series of 3D vectors in a Python numpy array similar to the following:
array([[-0.062, -0.024, 1. ],
[-0.071, -0.03 , 0.98 ],
[-0.08 , -0.035, 0.991],
[-0.083, -0.035, 0.98 ],
[-0.083, -0.035, 0.977],
[-0.082, -0.035, 0.993],
[-0.08 , -0.034, 1.006],
[-0.081, -0.032, 1.008],
.......
I want to rotate each vector around a specified axis through a specified angle theta. I have been using quaternions to achieve this for one vector as found here in henneray's answer.
v1 = np.array ([1, -2, 0])
axis = np.array([-4, -2, 3])
theta = 1.5
rot_axis = np.insert(axis, 0, 0, axis=0)
axis_angle = (theta*0.5) * rot_axis/np.linalg.norm(rot_axis)
vec = quat.quaternion(*v1)
qlog = quat.quaternion(*axis_angle)
q = np.exp(qlog)
v_prime = q * vec * np.conjugate(q)
v_prime_vec = v_prime.imag
My question is, what is the fastest way to apply the same rotation to each vector in v1?
You cannot create a quaternion from v1 if v1 contains a 2D array of vectors, so I could use a loop to rotate each array element in turn; however, in henneray's answer in the link above, it is mentioned that the quaternions could be applied to 'appropriately vectorised numpy arrays'. Does anyone has any suggestions on how this could be implemented?
(A side question: if my theta and axis variables were arrays of equal length to v1, could the same method also be used to rotate each vector in v1 through a corresponding rotation?)
It is necessary to first convert the [x,y,z] Cartesian vectors into 4-vectors with the first component equal to zero [0,x,y,z]. Then you can cast this to a quaternion array to do vectorised calculations.
This function below takes an array of Cartesian vectors and rotates them about a single rotation axis. You will need to make sure the norm of this axis is equal to your rotation angle theta.
def rotate_vectors(vecs, axis):
"""
Rotate a list of 3D [x,y,z] vectors about corresponding 3D axis
[x,y,z] with norm equal to the rotation angle in radians
Parameters
----------
vectors : numpy.ndarray with shape [n,3]
list of [x,y,z] cartesian vector coordinates
axis : numpy.ndarray with shape [3]
[x,y,z] axis to rotate corresponding vectors about
"""
# Make an 4 x n array of zeros
vecs4 = np.zeros([vecs.shape[0],vecs.shape[1]+1])
# Fill the imaginary i, j, k components with x, y, z values, leaving the real part w=0
vecs4[:,1:] = vecs
# Convert to quaternion array
vecsq = quat.as_quat_array(vecs4)
# Make a rotation quaternion
qrot = quat.from_rotation_vector(axis)
# Rotate vectors
vecsq_rotated = qrot * vecsq * qrot.conjugate()
# Cast quaternion array to float and return only imaginary components (ignore real part)
return quat.as_float_array(vecsq_rotated)[:,1:]
As a bonus, this function takes an array of rotation axes to rotate each vector by the corresponding axes.
def rotate_vectors_each(vecs, axes):
"""
Rotate a list of 3D [x,y,z] vectors about corresponding 3D axes
[x,y,z] with norm equal to the rotation angle in radians
Parameters
----------
vectors : numpy.ndarray with shape [n,3]
list of [x,y,z] cartesian vector coordinates
axes : numpy.ndarray with shape [n,3]
axes to rotate corresponding vectors about
n = pulse shape time domain
3 = [x,y,z]
"""
# Make an 4 x n array of zeros
vecs4 = np.zeros([vecs.shape[0],vecs.shape[1]+1])
# Fill the imaginary i, j, k components with x, y, z values, leaving the real part w=0
vecs4[:,1:] = vecs
# Convert to quaternion array
vecsq = quat.as_quat_array(vecs4)
# Make an 4 x n array of zeros
rots4 = np.zeros([rots.shape[0],rots.shape[1]+1])
# Fill the imaginary i, j, k components with x, y, z values, leaving the real part w=0
rots4[:,1:] = rots
# Convert to quaternion array and take exponential
qrots = np.exp(quat.as_quat_array(0.5 * rots4))
# Rotate vectors
vecsq_rotated = qrots * vecsq * qrots.conjugate()
return quat.as_float_array(vecsq_rotated)[:,1:]
Note that with so many conversions between axis angle and quaternion representation, this will give you little performance improvement over rotation matrix algebra. Quaternions really only benefit when you are rotating a vector through many sequential rotations, whereby you can stack the quaternion multiplication.
One "fast" way to do the rotation calculation itself would be to turn your quaternion into a 3x3 direction cosine matrix, have your vectors in a single 3xN contiguous matrix, and then call a BLAS library routine (e.g., dgemm) to do a standard matrix multiply. A good BLAS library with large N would do this calculation multi-threaded.
Related
I've got a function f(x,y) that takes two 1-d arrays and returns a scalar.
If I have a 2d matrix of shape (M,N), how do I efficiently apply the function pairwise across the 0 axis to end up with a square symmetric result of shape (M, M)?
Edit:
I'm trying to calculate pairwise correlation of an array of 1d arrays:
def f(x, y):
sigma_x_y = np.nanstd(x) * np.nanstd(y)
covariance = np.nanmean((x-np.nanmean(x))*(y-np.nanmean(y)))
return covariance/sigma_x_y
I think this is what you are looking for. The equations are similar to your function f(x, y):
x_m = x - np.nanmean(x,axis=1)[:,None]
y_m = y - np.nanmean(y,axis=1)[:,None]
X = np.nansum(x_m**2,axis=1)
Y = np.nansum(y_m**2,axis=1)
corr = np.dot(x_m,y_m.T)/np.sqrt(np.dot(X[:,None],Y[None]))
EDIT: If you wish to ignore NaN values in calculating correlation of two rows, simply replace last line with this:
corr = np.dot(np.nan_to_num(x_m), np.nan_to_num(y_m).T)/np.sqrt(np.dot(X[:,None],Y[None]))
I want to vectorise the dot product of several 3x3 matrices (rotation matrix around x-axis) with several 3x1 vectors. The application is the transformation of points (approx 500k per array) from one to another coordinate system.
Here in the example only four of each. Hence, the result should be again 4 times a 3x1 vector, respectively the single components x,y,z be a 4x0 vector. But I cannot get the dimensions figured out: Here the dot product with tensordot in results in a shape of (4,3,4), of which I need the diagonals again:
x,y,z = np.zeros((3,4,1))
rota = np.arange(4* 3 * 3).reshape((4,3, 3))
v= np.arange(4 * 3).reshape((4, 3))
result = np.zeros_like(v, dtype = np.float64)
vec_rotated = np.tensordot(rota,v, axes=([-1],[1]))
for i in range(result.shape[0]):
result[i,:] = vec_rotated[i,:,i]
x,y,z = result.T
How can i vectorise the complete thing?
Use np.einsum for an efficient solution -
x,y,z = np.einsum('ijk,ik->ji',rota,v)
Alternative with np.matmul/# operator in Python 3.x -
x,y,z = np.matmul(rota,v[:,:,None])[...,0].T
x,y,z = (rota#v[...,None])[...,0].T
works via transpose to obtain one component per diagonal:
vec_rotated = vec_rotated.transpose((1,0,2))
x,y,z = np.diag(vec_rotated[0,:,:]),np.diag(vec_rotated[1,:,:]),np.diag(vec_rotated[2,:,:])
I have a pair of 3d vectors u and v. I have another function f mapping 3d space onto real numbers (so, a scalar field). I want to draw a 2d plot f(xu + yv) using a colormap. So I need to end up with a matrix z filled with values of f, so I can go
pyplot.imshow(z)
But how can I do this? I tried
x = numpy.linspace(0, s2, 500)
y = numpy.linspace(0, 1, 500)
xs, ys = numpy.meshgrid(x, y)
z = f(u*xs + v*ys) # Not actually valid
Hoping that u*xs + v*ys would produce a matrix of 3d vectors, but that doesn't work. Also, even if I can get a matrix A of 3d vectors, what's the best way to get the matrix obtained by applying f to each element?
So I have a N-dimensional array of values, let's call it A. Now, I can plot this in a contour map, with coordinate axes X and Y, using
plt.contourf(X,Y,A)
Now, I have to carry out a mapping of these points to another plane, so, basically another set of coordinates. Let the transformation be
X - X1
Y - X1
Now, each point with magnitude "I" in matrix A at (X,Y) is at (X- X1, Y - Y1). I can plot this using
plt.contourf(X-X1, Y-Y1,A)
My question is, how do I index the array A such that I obtain an array B where the indexing corresponds to X-X1 and Y-Y1 instead of X and Y so that I can plot it directly using the following
plt.contourf(X,Y,B)
Thanks!
I would like to use Delaunay Triangulation in Python to interpolate the points in 3D.
What I have is
# my array of points
points = [[1,2,3], [2,3,4], ...]
# my array of values
values = [7, 8, ...]
# an object with triangulation
tri = Delaunay(points)
# a set of points at which I want to interpolate
p = [[1.5, 2.5, 3.5], ...]
# this gets simplexes that contain given points
s = tri.find_simplex(p)
# this gets vertices for the simplexes
v = tri.vertices[s]
I was only able to find one answer here that suggest to use transform method for the interpolation, but without being any more specific.
What I need to know is how to use the vertices of the containing simplex to get the weights for the linear interpolation. Let's assume a general n-dim case so that the answer does not depend on the dimension.
EDIT: I do not want to use LinearNDInterpolator or similar approach because I do not have a number at each point as a value but something more complex (array/function).
After some experimenting, the solution looks simple (this post was quite helpful):
# dimension of the problem (in this example I use 3D grid,
# but the method works for any dimension n>=2)
n = 3
# my array of grid points (array of n-dimensional coordinates)
points = [[1,2,3], [2,3,4], ...]
# each point has some assigned value that will be interpolated
# (e.g. a float, but it can be a function or anything else)
values = [7, 8, ...]
# a set of points at which I want to interpolate (it must be a NumPy array)
p = np.array([[1.5, 2.5, 3.5], [1.1, 2.2, 3.3], ...])
# create an object with triangulation
tri = Delaunay(points)
# find simplexes that contain interpolated points
s = tri.find_simplex(p)
# get the vertices for each simplex
v = tri.vertices[s]
# get transform matrices for each simplex (see explanation bellow)
m = tri.transform[s]
# for each interpolated point p, mutliply the transform matrix by
# vector p-r, where r=m[:,n,:] is one of the simplex vertices to which
# the matrix m is related to (again, see bellow)
b = np.einsum('ijk,ik->ij', m[:,:n,:n], p-m[:,n,:])
# get the weights for the vertices; `b` contains an n-dimensional vector
# with weights for all but the last vertices of the simplex
# (note that for n-D grid, each simplex consists of n+1 vertices);
# the remaining weight for the last vertex can be copmuted from
# the condition that sum of weights must be equal to 1
w = np.c_[b, 1-b.sum(axis=1)]
The key method to understand is transform, which is briefly documented, however the documentation says all it needs to be said. For each simplex, transform[:,:n,:n] contains the transformation matrix, and transform[:,n,:] contains the vector r to which the matrix is related to. It seems that r vector is chosen as the last vertex of the simplex.
Another tricky point is how to get b, because what I want to do is something like
for i in range(len(p)): b[i] = m[i,:n,:n].dot(p[i]-m[i,n,:])
Essentially, I need an array of dot products, while dot gives a product of two arrays. The loop over the individual simplexes like above would work, but a it can be done faster in one step, for which there is numpy.einsum:
b = np.einsum('ijk,ik->ij', m[:,:n,:n], p-m[:,n,:])
Now, v contains indices of vertex points for each simplex and w holds corresponding weights. To get the interpolated values p_values at set of points p, we do (note: values must be NumPy array for this):
values = np.array(values)
for i in range(len(p)): p_values[i] = np.inner(values[v[i]], w[i])
Or we may do this in a single step using `np.einsum' again:
p_values = np.einsum('ij,ij->i', values[v], w)
Some care must be taken in situations, when some of the interpolated points lie outside the grid. In such case, find_simplex(p) returns -1 for those points and then you will have to mask out them (using masked arrays perhaps).
You don't need to implement this from scratch, there is already built-in support in scipy for this feature:
scipy.interpolate.LinearNDInterpolator
You need an interval and a linear interpolation, i.e. the lenght of the edge and the distance of the interpolated points from the start vertex.