I have generated a numpy array of (x, y) values as a N x N grid.
grid = np.meshgrid(np.linspace(0, 1, 50), np.linspace(0, 1, 50))[0]
grid.shape // (50, 50, 1)
I have a function that takes two parameters and returns 3 values.
i.e. (x, y) -> (a, b, c)
How to I apply the function over the 2d numpy array to get a 3d numpy array?
If your function really takes two parameters you probably want to map not 2d to 3d, but rather 2xMxN to 3xMxN. For this change your first line to something like
gridx, gridy = np.meshgrid(np.linspace(0, 1, 50), np.linspace(0, 1, 50))
or even use the more economical ix_ which has the advantage of not swapping axes
gridy, gridx = np.ix_(np.linspace(0, 1, 50), np.linspace(0, 1, 50))
If your function f does not handle array arguments then as #Jacques Gaudin points out np.vectorize is probably what you want. Be warned that vectorize is primarily a convenience function it doesn't make things faster. It does useful things like broadcasting which is why using ix_ actually works
f_wrapped = np.vectorize(f)
result = f_wrapped(gridy, gridx)
Note that result in your case is a 3-tuple of 50 x 50 arrays, i.e. is grouped by output. This is convenient if you want to chain vectorized functions. If you want all in one big array just convert result to array and optionally use transpose to rearrange axes, e.g.
triplets_last = np.array(result).transpose((1, 2, 0))
If I understand correctly, you are after the np.vectorize decorator. By using it you can apply a function over a meshgrid. Your function should take only one parameter though as you do not pass the coordinates but the value at the coordinates (unless the values are tulpes with two elements).
import numpy as np
grid = np.meshgrid(np.linspace(0, 1, 5), np.linspace(0, 1, 5))[0]
#np.vectorize
def func(a):
return (a, a**.5, a**2)
res = np.array(list(func(grid)))
print(res.shape)
print(res)
Related
I would like to make a 2d array of even distribution of complex numbers, a part of complex plane, for example (-1, 1i), (-1, -1i), (1, 1i), (1, -1i) with 20 numbers in each dimension.
I know I can do this for complex numbers in 1 d with np.linspace like this:
import numpy as np
complex_array = np.linspace(0, complex(1, 1), num = 11)
print(complex_array)
[0. +0.j, 0.1+0.1j, 0.2+0.2j, 0.3+0.3j, 0.4+0.4j,
0.5+0.5j, 0.6+0.6j, 0.7+0.7j, 0.8+0.8j, 0.9+0.9j, 1. +1.j ]
But I can't get my head around how to produce this in two dimensions to get a part of a complex plane?
Some somewhat similar questions mention np.mgrid, but the examples are with reals and I would like the array to contain dtype=complex so my math keeps simple.
Maybe I am just missing something, and perhaps just a simple example would explain a lot..
There is no magic about complex numbers - they are simply a way to express a two dimensional space. You could use np.meshgrid (see here) to define a two dimensional Cartesian grid and then combine the coordinates into complex numbers.
Create vectors which will span the two dimensional grid (or complex plane)
real_points = np.linspace(0,1,num=11)
imag_points = np.linspace(0,1,num=11)
Create 2-D coordinate arrays
real_grid, imag_grid = np.meshgrid(real_points, imag_points)
Combine into complex array:
complex_array = real_grid + imag_grid * 1j
This produces a 11x11 complex128 array.
You can use broadcasting to do that. For example:
result = np.linspace(0, 1j, num = 11).reshape(-1, 1) + np.linspace(0, 1, num = 11)
Using meshgrid also works but it is likely slower:
a, b = np.meshgrid(np.linspace(0, 1, num = 11), np.linspace(0, 1j, num = 11))
result = a + b
I have a function D(x,y,z) in which I want to evaluate (via interpolation) planes within the z, y, and z axis. i.e. I want the output of my interpolations to be a 2D plane holding one of the values fixed, D(x,y,0) for example.
I have created an interpolating function via scipy using some given values of D, D_values, for my input values of x,y,z.
from scipy.interpolate import RegularGridInterpolator as rgi
D_interp=rgi((x_positions,y_positions,z_positions), D_values)
Now I can get any point interpolated by just calling
D_interpolated=D_interp(xi,yi,zi)
I understand how I can evaluate individual points from this, but how would I interpolate a plane? For example, in my case, D_values is of size 345x155x303 and I want to interpolate 345x155 planes all along the z axis corresponding to the x and y input values, at z=0, z=1, z=2, etc.
My attempt at a solution is to feed in the x_positions, y_positions vectors individually into D_interp keeping z fixed, but this just gets me a set of D values evaluated at specific positions, rather than organized into a grid like the planar output I'd actually like. Syntax doesn't allow me to call something like
Plane=D_interp(x_positions,y_positions,0)
so I was not quite sure about the syntax of calling this function to have planar output.
any help appreciated
Thanks,
The typical approach to combining multiple arrays with different sizes corresponding to different dimensions in numpy and scipy is to use broadcasting. Here is a sample problem to illustrate the application:
x_positions = np.linspace(0, 10, 101)
y_positions = np.linspace(-10, 10, 201)
z_positions = np.linspace(-5, 5, 101)
D_values = np.sin(2 * np.pi * x_positions[:, None, None] * y_positions[:, None] / 100) + np.cos(2 * np.pi * y_positions[:, None] * z_positions / 50)
This is similar to the D_values array you describe in your problem, where each of the bins in the different directions correspond to the *_positions arrays. I used broadcasting to turn x_positions into a (101, 1, 1)-shaped array, y_positions into a (201, 1)-shaped array and left z_positions as a (101,)-shaped array. The result is that D_values is a (101, 201, 101)-shaped array. The reshaped versions of the input arrays did not copy any data!
You can call your interpolator using the same idea that I used to create a sample D_values.
D_interp = rgi((x_positions, y_positions, z_positions), D_values)
Let's say you want to fix z = 0. All that scipy requires is that the inputs broadcast together. Scalars broadcast with everything, so you can just do
x_interp = np.linspace(0.05, 0.95, 200)
y_interp = np.linspace(-9.95, 9.95, 400)
z_interp = 0
D_xy_interp = D_interp((x_interp[:, None], y_interp, z_interp))
The advantage to doing this over creating a mesh is that you don't have to copy any data around and create extra 200x400 input arrays. Another advantage is that you have better control over the output. In this case, D_xy_interp has shape (len(x_interp), len(y_interp)). That's because in general, the shape of the output will be the broadcasted shape of the input. You can see that when we created D_values, and you can see it here. Since 0 is a scalar, it does not contribute to the shape. But I could also make a (400, 200) shaped array instead:
D_interp((x_interp, y_interp[:, None], z_interp))
Or even a (100, 4, 100, 2) shaped array:
D_interp((x_interp.reshape(-1, 2), y_interp.reshape(-1, 4, 1, 1), z_interp))
In either case, let's verify that the interpolator did it's job. We can compare the interpolated values to a much finer sampling of the function that created D_values:
D_xy_values = np.sin(2 * np.pi * x_interp[:, None] * y_interp / 100) + np.cos(2 * np.pi * y_interp * z_interp / 50)
fig, ax = plt.subplots(subplot_kw={'projection': '3d'})
ax.plot_surface(x_interp[:, None], y_interp, D_xy_interp, label='Interp')
ax.plot_surface(x_interp[:, None], y_interp, D_xy_values, label='Values')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
At the moment it doesn't look like you can add legends to 3D plots:
.
The two plots are virtually indistinguishable. With the default color cycler, you will see the surface chance from blue to orange as you rotate it. Here is an analytical verification:
>>> np.sqrt(np.mean((D_xy_values - D_xy_interp)**2))
4.707625623185639e-05
I need a general function f(array, axis, indices) to specify arbitrary axis in a numpy array. Here
array is a numpy array of arbitrary number of dimensions
axis is a tuple that specifies the dimensions of the array
indices is a tuple that specifies the indices of the above axis
For example, if I have a 6 dimensional array A, the value of the function f(A, (0,3,4), (20, 70, 3)) would be
A[20, :, :, 70, 3, :]
I suspect that one can use np.take to achieve this the following way
def f_take(A, axis, indices):
A1 = A.copy()
# Make sure we iterate over axis in descending order
descAxIdx = np.flip(np.argsort(axis))
descAxis = np.array(axis)[descAxIdx]
descIndices = np.array(indices)[descAxIdx]
for ax, ind in zip(descAxis, descIndices):
A1 = np.take(A1, ind, ax)
return A1
Does this function already exist in numpy? I could use f_take I wrote, but speed is an issue for me, so if there is something purely compiled (no python loop), that would be great
That can be implemented simply as this:
import numpy as np
def f(a, axes, indices):
a = np.asarray(a)
slices = tuple(indices[axes.index(i)] if i in axes else slice(None)
for i in range(a.ndim))
return a[slices]
I have a numpy array "data" with dimensions [t, z, x, y]. The
dimensions represent time (t) and three spatial dimensions (x, y, z).
I have a separate array "idx" of indices with dimensions [t, x, y]
describing vertical coordinates in data: each value in idx describes a
single vertical level in data.
I want to pull out the values from data indexed by idx. I've done it
successfully using loops (below). I've read several SO threads and numpy's indexing docs but I haven't been able to make it more pythonic/vectorized.
Is there an easy way I'm just not getting quite right? Or maybe loops
are a clearer way to do this anyway...
import numpy as np
dim = (4, 4, 4, 4) # dimensions time, Z, X, Y
data = np.random.randint(0, 10, dim)
idx = np.random.randint(0, 3, dim[0:3])
# extract vertical indices in idx from data using loops
foo = np.zeros(dim[0:3])
for this_t in range(dim[0]):
for this_x in range(dim[2]):
for this_y in range(dim[3]):
foo[this_t, this_x, this_y] = data[this_t,
idx[this_t, this_x, this_y],
this_x,
this_y]
# surely there's a better way to do this with fancy indexing
# data[idx] gives me an array with dimensions (4, 4, 4, 4, 4, 4)
# data[idx[:, np.newaxis, ...]] is a little closer
# data[tuple(idx[:, np.newaxis, ...])] doesn't quite get it either
# I tried lots of variations on those ideas but no luck yet
In [7]: I,J,K = np.ogrid[:4,:4,:4]
In [8]: data[I,idx,J,K].shape
Out[8]: (4, 4, 4)
In [9]: np.allclose(foo, data[I,idx,J,K])
Out[9]: True
I,J,K broadcast together to the same shape as idx (4,4,4).
More detail on this kind of indexing at
How to take elements along a given axis, given by their indices?
I think I missed something somewhere. I filled a numpy array using two for loops (x and y) and a function based on the x,y position. The only problem is that the value of the array always ends in zero irregardless of the size of the array.
thetamap = numpy.zeros(36, dtype=float)
thetamap.shape = (6, 6)
for y in range(0,5):
for x in range(0,5):
thetamap[x][y] = x+y
print thetamap
range(0, 5) produces 0, 1, 2, 3, 4. The endpoint is always omitted. You want simply range(6).
Better yet, use the awesome power of NumPy to make the array in one line:
thetamap = np.arange(6) + np.arange(6)[:,None]
This makes a row vector and a column vector, then adds them together using NumPy broadcasting to make a matrix.