How to fill selected cells in 3D Matplotlib?

61 Views Asked by At

This should a straight forward question but I could not find the answer, sorry if the question is a duplicate one. Basically I want to fill Ncell cells on a 3D grid of Ngrid x Ngrid x Ngrid. Below I present a MWE where I just scatter plot the centre (or one corner, it does not matter, I can readjust) of each cell.

import numpy as np
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt

Ngrid,Ncell=100,150

xx=np.random.choice(Ngrid,Ncell)
yy=np.random.choice(Ngrid,Ncell)
zz=np.random.choice(Ngrid,Ncell)

%matplotlib widget

fig = plt.figure(figsize = (10, 7))
ax = plt.axes(projection ="3d")
ax.scatter3D(xx,yy,zz,marker='o',alpha=0.5)

Instead of the scatter plot, I need each cell to be filled with a color. Note that my grid and Ncell are much larger than the above values (grid = 1000^3 and Ncell= order 10^5), so an efficient code will be very important. Thank you in advacne.

1

There are 1 best solutions below

2
Davide_sd On BEST ANSWER

You can try with voxels but it is already slow with your test data:

import numpy as np
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt

Ngrid,Ncell=100,250

xx=np.random.choice(Ngrid,Ncell)
yy=np.random.choice(Ngrid,Ncell)
zz=np.random.choice(Ngrid,Ncell)
v = np.zeros((Ngrid, Ngrid, Ngrid), dtype=bool)
v[xx, yy, zz] = True

fig = plt.figure(figsize = (10, 7))
ax = plt.axes(projection ="3d")
ax.voxels(v)

I would suggest to use K3D Jupyter and its voxel. This appears to be much much faster. Note that, as the name suggests, it requires Jupyter Notebook.

import k3d
import numpy as np

Ngrid,Ncell=100,1000

xx=np.random.choice(Ngrid,Ncell)
yy=np.random.choice(Ngrid,Ncell)
zz=np.random.choice(Ngrid,Ncell)

v = np.zeros((Ngrid, Ngrid, Ngrid), dtype=bool)

# NOTE: K3D's voxels works with the the matrix indexing, ij, rather
# then with the cartesian indexing, xy. Read np.meshgrid and run
# some example to understand the differences.
# The bottom line is, the following is the correct indexing:
v[zz, yy, xx] = True

# Once the plot is shown on the screen we can play with options
# by clicking K3D panel -> Objects -> Voxels #1. If we find some
# good options, we can write the values in this command and
# execute it again.
plt_voxels = k3d.voxels(
    v.astype(np.uint8),
    color_map=(0xfdfe03),
    outlines=False, # better hide outlines when setting opacity < 1
    opacity=0.5
)
plot = k3d.plot(grid_visible=False)
plot += plt_voxels

# create scatter
positions = np.vstack([t.flatten() for t in [xx+0.5, yy+0.5, zz+0.5]]).T.astype(np.float32)
plt_points = k3d.points(positions=positions, point_size=0.4, color=0xff0000)
plot += plt_points

plot.display()

enter image description here