I have data in 2 numpy arrays: one a list of 3D positions, the other the values of a scalar AT each of those positions. The ordering of the position data is fairly 'weird' (see below).
The 3D position data is in an array:
pos = np.array([[1,1,1],[1,1,2],[1,1,3],[1,2,2],[1,2,1], ...])
pos.shape is (100000,3)
where the ordering is not intuitive (it is following a space filling curve).
and I also have the scalar values that I want to plot at each of those locations:
vel = np.array([1,2,1,3,4,...])
vel.shape = (1000000,1)
My question is, how do I plot an xy slice with pcolormesh from this data??? I can extract an xy plane with numpy:
xs = pos[:,:1][:,0]
ys = pos[:,1:2][:,0]
Now I have a bunch of essentially random x coords and y coords that no long map 1-1 to the vel
data... :/. I don't know how to first map those initial positions to my vel
data, so that I can generate a pcolormesh
:
plt.pcolormesh(X, Y, V)
Can someone help me slice this data up, so everything stays mapped to the right position in xy (and z) space?
If I understand correctly, the following approach would work on your data:
np.where()
)pos
and vel
by these indicespos
vel
, and reshape to a 2D arrayimshow()
; setting vmin
and vmax
will have the same colors for the same values for different subplotsimport matplotlib.pyplot as plt
import numpy as np
# first create some test data
# generate a test grid
N = 100 # size in one dimension
# create a grid of x y and z coordinates
xs, ys, zs = np.meshgrid(range(1, N + 1), range(1, N + 1), range(1, N + 1))
# rearrange them in the shape of the example
pos = np.vstack([xs.ravel(), ys.ravel(), zs.ravel()]).T
# randomly reorder the positions
np.random.shuffle(pos)
# generate a test function for `vel`, depending on pos
# calculate distance to the center of two spheres
center1 = [(N - 1) * 0.2, (N - 1) * 0.6, (N - 1) * 0.5]
radius1 = (N - 1) * 0.3
dist_to_center1 = np.sqrt((pos[:, 0] - center1[0]) ** 2 + (pos[:, 1] - center1[1]) ** 2 + (pos[:, 2] - center1[2]) ** 2)
center2 = [(N - 1) * 0.7, (N - 1) * 0.4, (N - 1) * 0.5]
radius2 = (N - 1) * 0.2
dist_to_center2 = np.sqrt((pos[:, 0] - center2[0]) ** 2 + (pos[:, 1] - center2[1]) ** 2 + (pos[:, 2] - center2[2]) ** 2)
# take union of the two spheres: vel is the minimum of signed distance to the spheres
vel = np.minimum(dist_to_center1 - radius1, dist_to_center2 - radius2)
# display the layers for some z-values
xmin = pos[:, 0].min()
xmax = pos[:, 0].max()
ymin = pos[:, 1].min()
ymax = pos[:, 1].max()
fig, axs = plt.subplots(nrows=2, ncols=4, figsize=(15, 7))
for z_special, ax in zip(range(2, N - 2, N // 8 + 1), axs.flat):
# indices where the z-value almost equals the desired z value
filter = np.argwhere((pos[:, 2] > z_special - 0.001) & (pos[:, 2] < z_special + 0.001))[:, 0]
xy_special = pos[:, :2][filter]
vel_special = vel[filter]
# find the order of xy_special on x and y
order = np.lexsort(([xy_special[:, 0], xy_special[:, 1]]))
vel_ordered = vel_special[order].reshape(N, -1)
ax.imshow(vel_ordered, origin='lower', extent=[xmin, xmax, ymin, ymax], aspect='auto',
cmap='turbo', vmin=-20, vmax=20)
ax.set_xticks([]) # remove ticks to simplify the plot
ax.set_yticks([])
ax.set_title(f"z = {z_special:.3g}")
plt.tight_layout()
plt.show()
Instead of images, you could also draw isolines for vel
values. tricontourf()
also works for unordered x, y
pairs, even when not all pairs of a grid are present. To make the coloring consistent between subplots, you can explicitly set a list of levels.
fig, axs = plt.subplots(nrows=2, ncols=4, figsize=(15, 7))
for z_special, ax in zip(range(2, N - 2, N // 8 + 1), axs.flat):
# indices where the z-value almost equals the desired z value
filter = np.argwhere((pos[:, 2] > z_special - 0.001) & (pos[:, 2] < z_special + 0.001))[:, 0]
xy_special = pos[:, :2][filter]
vel_special = vel[filter]
ax.tricontourf(xy_special[:, 0], xy_special[:, 1], vel_special, cmap='turbo', levels=range(-20, 21, 5))
ax.set_title(f"z = {z_special:.3g}")