Embedding the plane into 3d space for a matplotlib plot - python

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?

Related

Applying quaternion rotation to a vector time series

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.

How to efficiently interpolate a 3D array on a finer, uniformed-spacing grid?

I have a 3D array and want to interpolate it into a finer resolution grid. The 3D array is defined on a regular grid with even spacing. The interpolation is also on a regular grid with even spacing. The step size is 2^(-n), n=0,1,2,3 .... Interpolation should be either linear or higher order Bspline. For example, a 3D array a has sizes of (3,3,5) and the step size of the interpolation grid is (0.5, 0.5, 0.5)(n=1). Then the interpolation grid is defined as
x=np.linspace(0,2,num=5) # 3/0.5-1 = 5
y=np.linspace(0,2,num=5) # 3/0.5-1 = 5
z=np.linspace(0,4,num=9) # 5/0.5-1 = 9
xx, yy, zz = np.meshgrid(x, y, z, sparse=True)
Then the output array should have sizes of (5,5,9). Which interpolation function in scipy or numpy can do the job in a most efficient way?
I have found zoom is the most efficient function to do it. Check it out: https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.zoom.html

Creating a 2D array using values of coordinate points on a grid

Suppose I have a coordinate grid with a few points(masses) sprinkled in the grid. I can create this using:
import numpy as np
import matplotlib.pyplot as plt
points = np.array([[0,1],[2,1],[3,5]]) # array containing the coordinates of masses(points) in the form [x,y]
x1, y1 = zip(*points)
Now, I can plot using :
plt.plot(x1,y1,'.')
Now, say I create a 2D meshgrid using:
x = np.linspace(-10,10,10)
y = np.linspace(-10,10,10)
X,Y = np.meshgrid(x,y)
Now, what I want to do is to create a 2D array 'Z',(a map of the masses)that contains masses at the locations that are in the array points. When I mean masses, I just mean a scalar at those points. So I could do something like plt.contourf(X,Y,Z). The problem I'm having is that the indices for Z cannot be the same as the coordinates in points. There has to be some sort of conversion which I'm not able to figure out. Another way to look at it is I want:
Z[X,Y] = 1
I want Z to have 1's at locations which are specified by the array points. So the essence of the problem is how do I calculate the X and Y indices such that they correspond to x1, y1 in real coordinates.
For example, if I simply do Z[x1(i),y1(i)] = 1, contourf gives this:
Instead I want the spikes to be at (0,1),(2,1),(3,5).
To have 1 at the coordinates specified by x1, y1 and zeros everywhere else, I would write it like this:
x = np.linspace(-10, 10, 21)
y = np.linspace(-10, 10, 21)
Z = np.zeros((len(y), len(x)))
for i in range(len(x1)):
Z[10 + y1[i], 10 + x1[i]] = 1
Then you should be able to write plt.contourf(x, y, Z).
Tell me if that gives you the desired result.

Split numpy array into similar array based on its content

I have a 2D numpy array that represents the coordinates (x, y) of a curve, and I want to split that curve into parts of the same length, obtaining the coordinates of the division points.
The most easy example is a line defined for two points, for example [[0,0],[1,1]], and if I want to split it in two parts the result would be [0.5,0.5], and for three parts [[0.33,0.33],[0.67,0.67]] and so on.
How can I do that in a large array where the data is less simple? I'm trying to split the array by its length but the results aren't good.
If I understand well, what you want is a simple interpolation. For that, you can use scipy.interpolate (http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html):
from scipy.interpolate import interp1d
f = interp1d(x, y) ## for linear interpolation
f2 = interp1d(x, y, kind='cubic') ## for cubic interpolation
xnew = np.linspace(x.min(), x.max(), num=41, endpoint=False)
ynew = f(xnew) ## or f2(xnew) for cubic interpolation
You can create a function that returns the coordinates of the split points, given x, y and the number of desired points:
def split_curve(x, y, npts):
from scipy.interpolate import interp1d
f = interp1d(x, y)
xnew = np.linspace(x.min(), x.max(), num=npts, endpoint=False)
ynew = f(xnew)
return zip(xnew[1:], ynew[1:])
For example,
split_curve(np.array([0, 1]), np.array([0, 1]), 2) ## returns [(0.5, 0.5)]
split_curve(np.array([0, 1]), np.array([0, 1]), 3) ## [(0.33333333333333331, 0.33333333333333331), (0.66666666666666663, 0.66666666666666663)]
Note that x and y are numpy arrays and not lists.
take the length of the line on every axes, the split as you want.
example:
point 1: [0,0]
point 2: [1,1]
then:
length of the line on X axes: 1-0 = 1
also in the Y axes.
now, if you want to split it in two, just divide these lengths, and create a new array.
[0,0],[.5,.5],[1,1]

Python numpy grid transformation using universal functions

Here is my problem : I manipulate 432*46*136*136 grids representing time*(space) encompassed in numpy arrays with numpy and python. I have one array alt, which encompasses the altitudes of the grid points, and another array temp which stores the temperature of the grid points.
It is problematic for a comparison : if T1 and T2 are two results, T1[t0,z0,x0,y0] and T2[t0,z0,x0,y0] represent the temperature at H1[t0,z0,x0,y0] and H2[t0,z0,x0,y0] meters, respectively. But I want to compare the temperature of points at the same altitude, not at the same grid point.
Hence I want to modify the z-axis of my matrices to represent the altitude and not the grid point. I create a function conv(alt[t,z,x,y]) which attributes a number between -20 and 200 to each altitude. Here is my code :
def interpolation_extended(self,temp,alt):
[t,z,x,y]=temp.shape
new=np.zeros([t,220,x,y])
for l in range(0,t):
for j in range(0,z):
for lat in range(0,x):
for lon in range(0,y):
new[l,conv(alt[l,j,lat,lon]),lat,lon]=temp[l,j,lat,lon]
return new
But this takes definitely too much time, I can't work this it. I tried to write it using universal functions with numpy :
def interpolation_extended(self,temp,alt):
[t,z,x,y]=temp.shape
new=np.zeros([t,220,x,y])
for j in range(0,z):
new[:,conv(alt[:,j,:,:]),:,:]=temp[:,j,:,:]
return new
But that does not work. Do you have any idea of doing this in python/numpy without using 4 nested loops ?
Thank you
I can't really try the code since I don't have your matrices, but something like this should do the job.
First, instead of declaring conv as a function, get the whole altitude projection for all your data:
conv = np.round(alt / 500.).astype(int)
Using np.round, the numpys version of round, it rounds all the elements of the matrix by vectorizing operations in C, and thus, you get a new array very quickly (at C speed). The following line aligns the altitudes to start in 0, by shifting all the array by its minimum value (in your case, -20):
conv -= conv.min()
the line above would transform your altitude matrix from [-20, 200] to [0, 220] (better for indexing).
With that, interpolation can be done easily by getting multidimensional indices:
t, z, y, x = np.indices(temp.shape)
the vectors above contain all the indices needed to index your original matrix. You can then create the new matrix by doing:
new_matrix[t, conv[t, z, y, x], y, x] = temp[t, z, y, x]
without any loop at all.
Let me know if it works. It might give you some erros since is hard for me to test it without data, but it should do the job.
The following toy example works fine:
A = np.random.randn(3,4,5) # Random 3x4x5 matrix -- your temp matrix
B = np.random.randint(0, 10, 3*4*5).reshape(3,4,5) # your conv matrix with altitudes from 0 to 9
C = np.zeros((3,10,5)) # your new matrix
z, y, x = np.indices(A.shape)
C[z, B[z, y, x], x] = A[z, y, x]
C contains your results by altitude.

Categories