0

I have a 3D numpy array of size (75, 150, 150) of numeric values which represents 75 layers, 150 grid cells in the x and 150 grid cells in the y directions. Is there a way to 3D plot this 3D array in x y, and z 3D map where the z dimension represents the layer of my model and x and y be the x and y dimensions? I have the following code but its giving me error: ValueError: shape mismatch: objects cannot be broadcast to a single shape.

array.shape = (75, 150, 150)

from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
n = 1687500

z=np.arange(76)
x=np.arange(151)
y=np.arange(151)

for i, j, k in x, y, z:
  y=array[k,i, :]
  x=array[k,:, j]
  colors = np.random.randint(0, 10, size=n)
  ax.scatter(z, x, y, c=colors, marker='o')
  plt.show()
5
  • I modified the code now after your comment, but I am not sure how to incorporate the z (each layer) to be the z axis in the 3d scatter plot and the corresponding x and y values from the array to be in the 3d scatter plot as well
    – sure_sky
    Commented Feb 2, 2022 at 19:32
  • 1
    What are the values in array and how do you want them plotted? Do you want a point plotted for each non-zero value, or a different colour for different values? Some more details of the values and how you want to see them plotted would be helpful. Commented Feb 2, 2022 at 20:15
  • Plotting them for each non-zero value would be helpful. Values are all between 0 and 1. Color plotting them based on i.e. 10 bins [[color1=less than 0.01 %, ,0.01<color2<0.1%, ,0.1<color3<1%, 1<color3<2%, 2<color4<3, 3%<color5<4, 4%<color6<5, 6%<color7<7, 7%<color8<8, 8%<color9<9, 9%<color10<10, and color11 = > %10]] would also be very helpful, for example, values
    – sure_sky
    Commented Feb 2, 2022 at 20:24
  • The Z values are layer numbers ranging from 1 to 75, and the x and y values which range from 0 to 1 for each 150 * 150 grid cells in x & y plane.
    – sure_sky
    Commented Feb 2, 2022 at 20:58
  • 1
    I agree with @JohanC, and putting colours into bins might not even be necessary with the right colour map. If array is sparse, you can probably get something by filtering out the 0-values. If it is dense, you may need to take a different approach (multiple graphs using the 10 bins, for example). Commented Feb 2, 2022 at 21:28

1 Answer 1

3

You have an array of 75*150*150 (1687500) elements. In your first version of the scatter plot, you were using one column of 75 elements for Z and 2 columns of 150 values for X and Y. You are ignoring almost all information from the 3D array.

In the edited version, x,y and z each contain the full 3D array. All values will be the same.

Supposing the array contains 75 layers of grids of 150x150, you could plot it as follows. Note that it will be quite slow as the number of points is huge. Also, it will be hard to make sense of. np.meshgrid creates arrays of position values.

To filter out the zero values, you can replace them by np.nan. To color according to bins, you can use a BoundaryNorm.

from matplotlib import pyplot as plt
from matplotlib.colors import BoundaryNorm
import numpy as np

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

num_layers = 75
num_pnt = 150
z, x, y = np.meshgrid(np.arange(1, num_layers + 1), np.arange(num_pnt), np.arange(num_pnt), indexing='ij')

# create some random test data, suppose all values outside a cone are zero
array = np.random.rand(num_layers, num_pnt, num_pnt) ** 2
array[(x - num_pnt / 2) ** 2 + (y - num_pnt / 2) ** 2 > (num_layers - z) ** 2] = 0

array[array == 0] = np.nan  # replace zeros by NaN to make them invisible

bounds = [0, 0.0001, 0.001, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 1.00]
norm = BoundaryNorm(bounds, len(bounds) - 1)
cmap = plt.get_cmap('turbo', len(bounds) - 1)
scat = ax.scatter(x, y, z, c=array, marker='o', cmap=cmap, norm=norm)
cbar = plt.colorbar(scat, ax=ax, ticks=bounds, format='%.4f')
plt.show()

3d scatter plot from 3D array

When there are many zeros, it helps to completely filter them away. You'll need to convert all arrays to 1D:

from matplotlib import pyplot as plt
from matplotlib.colors import BoundaryNorm
from matplotlib.ticker import FuncFormatter
import numpy as np

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

num_layers = 75
num_pnt = 150
z, x, y = np.meshgrid(np.arange(1, num_layers + 1), np.arange(num_pnt), np.arange(num_pnt), indexing='ij')

# create some random test data, suppose all values outside a cone are zero
array = np.random.rand(num_layers, num_pnt, num_pnt) ** 2
array[np.abs((x - num_pnt / 2) ** 2 + (y - num_pnt / 2) ** 2 - (num_layers - z) ** 2) > 5] = 0

# make the arrays 1D, so they are easier to filter
array = array.ravel()
filter = array != 0
x = x.ravel()[filter]
y = y.ravel()[filter]
z = z.ravel()[filter]
array = array[filter]

bounds = [0, 0.0001, 0.001, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 1.00]
norm = BoundaryNorm(bounds, len(bounds) - 1)
cmap = plt.get_cmap('turbo', len(bounds) - 1)
scat = ax.scatter(x, y, z, c=array, marker='o', cmap=cmap, norm=norm)
cbar = plt.colorbar(scat, ax=ax, ticks=bounds, format=FuncFormatter(lambda x, pos: f'{x * 100:3g} %'))
plt.show()

3d scatter plot from 3D array with zeros filtered away

4
  • Thanks. I would like to have something without the meshes just the points representing the values inside the array. Something like this: media.geeksforgeeks.org/wp-content/cdn-uploads/20200616153345/…
    – sure_sky
    Commented Feb 2, 2022 at 19:47
  • I modifed the code again after your comments but still not generating meaningful outout. Something like the last picture here in this page: studytonight.com/matplotlib/…
    – sure_sky
    Commented Feb 2, 2022 at 19:53
  • Thanks! It worked and actually when I removed the 0 values, it can make more sense. However, is there any way to be interactively able to rotate it with a mouse or to be able to zoom in to get closer at it?
    – sure_sky
    Commented Feb 2, 2022 at 21:58
  • Normally it is interactive, but extremely slow due to the many points. You could filter and only show part of the layers and parts of the points. If there are many zeros, it helps to create copies of the arrays will all those zeros filtered out. I added a new version which completely filters out the zeros. Instead of filter = array != 0, you could also try e.g. filter = array > 1e-5 to filter out more.
    – JohanC
    Commented Feb 2, 2022 at 22:17

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.