Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I plot a numpy array of x, y, z in 3D surface plot?

I have reviewed both of these threads, but am still struggling to make a 3D surface plot from a numpy array of x, y, z coordinates.

My array looks like this:

>>> points
array([[ 322697.1875    , 3663966.5       ,  -30000.        ],
       [ 325054.34375   , 3663966.5       ,  -30000.        ],
       [ 325054.34375   , 3665679.5       ,  -30000.        ],
       [ 322697.1875    , 3665679.5       ,  -30000.        ],
       [ 322697.1875    , 3663966.5       ,  -27703.12304688],
       [ 325054.34375   , 3663966.5       ,  -27703.15429688],
       [ 325054.34375   , 3665679.5       ,  -27703.70703125],
       [ 322697.1875    , 3665679.5       ,  -27703.67382812]])

ax.plot_surface accepts x, y, z points so I convert the above array into separate pieces below:

x = points[:, 0]
y = points[:, 1]
z = points[:, 2]

I then put it into a meshgrid for passing into ax.plot_surface():

import numpy as np

X, Y, Z = np.meshgrid(x, y, z)

And then try to plot:

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(16,10))
ax = plt.axes(projection = '3d')
ax.plot_surface(X, Y, Z, alpha=0.5)
plt.show()

When I run this I receive an error: rows, cols = Z.shape ValueError: too many values to unpack (expected 2).

I'm not sure where to go with this now, I don't expect the answer but a push in the correct direction would be great.

I would like the output to be similar in appearance to this but with my data: enter image description here

UPDATE: If I do not include z in the meshgrid, but only x and y, I get this output when I run ax.plot_surface(X, Y, z, alpha=0.5): enter image description here

This is really close, but I want all the sides to be filled in. Only one is showing as filled in. I've added the point coordinates to show the boundaries. I feel like it has something to do with the meshgrid that I'm creating. Here is the output of X, Y:

>>> X, Y = np.meshgrid(x, y)
(array([[322697.1875 , 325054.34375, 325054.34375, 322697.1875 ,
        322697.1875 , 325054.34375, 325054.34375, 322697.1875 ],
       [322697.1875 , 325054.34375, 325054.34375, 322697.1875 ,
        322697.1875 , 325054.34375, 325054.34375, 322697.1875 ],
       [322697.1875 , 325054.34375, 325054.34375, 322697.1875 ,
        322697.1875 , 325054.34375, 325054.34375, 322697.1875 ],
       [322697.1875 , 325054.34375, 325054.34375, 322697.1875 ,
        322697.1875 , 325054.34375, 325054.34375, 322697.1875 ],
       [322697.1875 , 325054.34375, 325054.34375, 322697.1875 ,
        322697.1875 , 325054.34375, 325054.34375, 322697.1875 ],
       [322697.1875 , 325054.34375, 325054.34375, 322697.1875 ,
        322697.1875 , 325054.34375, 325054.34375, 322697.1875 ],
       [322697.1875 , 325054.34375, 325054.34375, 322697.1875 ,
        322697.1875 , 325054.34375, 325054.34375, 322697.1875 ],
       [322697.1875 , 325054.34375, 325054.34375, 322697.1875 ,
        322697.1875 , 325054.34375, 325054.34375, 322697.1875 ]]), array([[3663966.5, 3663966.5, 3663966.5, 3663966.5, 3663966.5, 3663966.5,
        3663966.5, 3663966.5],
       [3663966.5, 3663966.5, 3663966.5, 3663966.5, 3663966.5, 3663966.5,
        3663966.5, 3663966.5],
       [3665679.5, 3665679.5, 3665679.5, 3665679.5, 3665679.5, 3665679.5,
        3665679.5, 3665679.5],
       [3665679.5, 3665679.5, 3665679.5, 3665679.5, 3665679.5, 3665679.5,
        3665679.5, 3665679.5],
       [3663966.5, 3663966.5, 3663966.5, 3663966.5, 3663966.5, 3663966.5,
        3663966.5, 3663966.5],
       [3663966.5, 3663966.5, 3663966.5, 3663966.5, 3663966.5, 3663966.5,
        3663966.5, 3663966.5],
       [3665679.5, 3665679.5, 3665679.5, 3665679.5, 3665679.5, 3665679.5,
        3665679.5, 3665679.5],
       [3665679.5, 3665679.5, 3665679.5, 3665679.5, 3665679.5, 3665679.5,
        3665679.5, 3665679.5]]))

If I just take x, y unique values I get an error thrown:

x = np.unique(x)
y = np.unique(y)

>>> x
array([322697.1875 , 325054.34375])
>>> y
array([3663966.5, 3665679.5])

X, Y = np.meshgrid(x, y)
>>> X, Y
(array([[322697.1875 , 325054.34375],
       [322697.1875 , 325054.34375]]), array([[3663966.5, 3663966.5],
       [3665679.5, 3665679.5]]))

>>> ax.plot_surface(X, Y, z, alpha=0.5)
Traceback (most recent call last):
  File "<pyshell#61>", line 1, in <module>
    ax.plot_surface(X, Y, z, alpha=0.5)
  File "/Users/NaN/anaconda/envs/py36/lib/python3.6/site-packages/mpl_toolkits/mplot3d/axes3d.py", line 1586, in plot_surface
    X, Y, Z = np.broadcast_arrays(X, Y, Z)
  File "/Users/NaN/anaconda/envs/py36/lib/python3.6/site-packages/numpy/lib/stride_tricks.py", line 259, in broadcast_arrays
    shape = _broadcast_shape(*args)
  File "/Users/NaN/anaconda/envs/py36/lib/python3.6/site-packages/numpy/lib/stride_tricks.py", line 193, in _broadcast_shape
    b = np.broadcast(*args[:32])
ValueError: shape mismatch: objects cannot be broadcast to a single shape
like image 353
NaN Avatar asked Jun 05 '19 18:06

NaN


People also ask

What function would we use to plot a 3D surface in Matplotlib?

To create the 3-dimensional surface plot the ax. plot_surface() function is used in matplotlib. In the above syntax, the X and Y mainly indicate a 2D array of points x and y while Z is used to indicate the 2D array of heights.

Is 3D graph possible in Matplotlib?

Matplotlib was introduced keeping in mind, only two-dimensional plotting. But at the time when the release of 1.0 occurred, the 3d utilities were developed upon the 2d and thus, we have 3d implementation of data available today! The 3d plots are enabled by importing the mplot3d toolkit.


1 Answers

The arrays x, y, z need to be parametrized in two dimensions. One way of doing this is to use spherical coordinates as e.g. in Plot surfaces on a cube.

The remaining task is to distill the unique coordinates from the input data. I'm assuming here that there are only 2 distinct values per dimension.

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def get_cube():   
    phi = np.arange(1,10,2)*np.pi/4
    Phi, Theta = np.meshgrid(phi, phi)

    x = np.cos(Phi)*np.sin(Theta)
    y = np.sin(Phi)*np.sin(Theta)
    z = np.cos(Theta)/np.sqrt(2)
    return x,y,z


points = np.array([[ 322697.1875    , 3663966.5       ,  -30000. ],
                   [ 325054.34375   , 3663966.5       ,  -30000. ],
                   [ 325054.34375   , 3665679.5       ,  -30000. ],
                   [ 322697.1875    , 3665679.5       ,  -30000. ],
                   [ 322697.1875    , 3663966.5       ,  -27703.12],
                   [ 325054.34375   , 3663966.5       ,  -27703.12],
                   [ 325054.34375   , 3665679.5       ,  -27703.12],
                   [ 322697.1875    , 3665679.5       ,  -27703.12]])

ux = np.unique(points[:,0])
uy = np.unique(points[:,1])
uz = np.unique(points[:,2])

x,y,z = get_cube()
offset = lambda X, o: o[0] + (X+.5)*np.diff(o)[0]


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

ax.plot_surface(offset(x, ux), offset(y, uy), offset(z, uz))

plt.show()

enter image description here

like image 91
ImportanceOfBeingErnest Avatar answered Oct 21 '22 07:10

ImportanceOfBeingErnest