1

I am trying to create a 3D surface energy diagram where an x,y position on a grid contains an associated z level. The issue is that the grid is not uniform (ie, there is not a z component for every x,y position). Is there a way to refrain from plotting those values by calling them NaN in the corresponding position in the array?

Here is what I have tried so far:

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

#Z levels 
energ = np.array([0,3.5,1,-0.3,-1.5,-2,-3.4,-4.8])
#function for getting x,y associated z values?
def fun(x,y,array):

    return array[x]

#arrays for grid
x = np.arange(0,7,0.5)
y = np.arange(0,7,0.5) 

#create grid
X, Y = np.meshgrid(x,y)
zs = np.array([fun(x,y,energ) for x in zip(np.ravel(X))])
Z = zs.reshape(X.shape)

plt3d = plt.figure().gca(projection='3d')

#gradients now with respect to x and y, but ideally with respect to z only
Gx, Gz = np.gradient(X * Y)  
G = (Gx ** 2 + Gz ** 2) ** .5  # gradient magnitude
N = G / G.max()  # normalize 0..1

plt3d.plot_surface(X, Y, Z, rstride=1, cstride=1,
               facecolors=cm.jet(N), edgecolor='k', linewidth=0, antialiased=False, shade=False)
plt.show()

I cannot post image here of this plot but if you run the code you will see it But I would like to not plot certain x,y pairs, so the figure should triangle downward to the minimum. Can this be accomplished by using nan values? Also would like spacing between each level, to be connected by lines.

n = np.NAN
#energ represents the z levels, so the overall figure should look like a triangle.
energ = np.array([[0,0,0,0,0,0,0,0,0,0,0,0,0],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,2.6,n,2.97,n,2.6,n,2.97,n,2.6,n,3.58,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,1.09,n,1.23,n,1.09,n,1.23,n,1.7,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,-0.65,n,-0.28,n,-0.65,n,0.33,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,-2.16,n,-2.02,n,-1.55,n,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,-3.9,n,-2.92,n,n,n,n,n,],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,n,-4.8,n,n,n,n,n,n,]])

plt3d = plt.figure().gca(projection='3d')

Gx, Gz = np.gradient(X * energ)  # gradients with respect to x and z
G = (Gx ** 2 + Gz ** 2) ** .5  # gradient magnitude
N = G / G.max()  # normalize 0..1

x = np.arange(0,13,1)
y = np.arange(0,13,1) 
X, Y = np.meshgrid(x,y)
#but the shapes don't seem to match up

plt3d.plot_surface(X, Y, energ, rstride=1, cstride=1,
               facecolors=cm.jet(N), edgecolor='k',
               linewidth=0, antialiased=False, shade=False
) 

Using masked arrays generates the following error: local Python[7155] : void CGPathCloseSubpath(CGMutablePathRef): no current point.

n = np.NAN
energ = np.array([[0,0,0,0,0,0,0,0,0,0,0,0,0],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,2.6,n,2.97,n,2.6,n,2.97,n,2.6,n,3.58,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,1.09,n,1.23,n,1.09,n,1.23,n,1.7,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,-0.65,n,-0.28,n,-0.65,n,0.33,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,-2.16,n,-2.02,n,-1.55,n,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,-3.9,n,-2.92,n,n,n,n,n,],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,n,-4.8,n,n,n,n,n,n,]])
x = np.arange(0,13,1)
y = np.arange(0,13,1) 
X, Y = np.meshgrid(x,y)

#create masked arrays
mX = ma.masked_array(X, mask=[[0,0,0,0,0,0,0,0,0,0,0,0,0],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,0,1,0,1,0,1,0,1,0,1,0,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,0,1,0,1,0,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,0,1,0,1,0,1,0,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,0,1,0,1,0,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,0,1,0,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,1,1,1,1]])
mY = ma.masked_array(Y, mask=[[0,0,0,0,0,0,0,0,0,0,0,0,0],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,0,1,0,1,0,1,0,1,0,1,0,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,0,1,0,1,0,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,0,1,0,1,0,1,0,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,0,1,0,1,0,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,0,1,0,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,1,1,1,1]])
m_energ = ma.masked_array(energ, mask=[[0,0,0,0,0,0,0,0,0,0,0,0,0],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,0,1,0,1,0,1,0,1,0,1,0,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,0,1,0,1,0,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,0,1,0,1,0,1,0,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,0,1,0,1,0,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,0,1,0,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,1,1,1,1]])

plt3d = plt.figure().gca(projection='3d')

plt3d.plot_surface(mX, mY, m_energ, rstride=1, cstride=1, edgecolor='k', linewidth=0, antialiased=False, shade=False)
plt.show()
XtremeJake
  • 116
  • 7
  • 3
    In most situations (not *all* I don't think, but most), you can plot with missing data by using [masked arrays](http://docs.scipy.org/doc/numpy/reference/maskedarray.html). Just make a masked array and plot it just like you would a normal array, matplotlib will handle it fine for the most part. – Ajean May 01 '15 at 00:17
  • @Ajean It doesn't look like the masked array will work in this case. Any other suggestions? – XtremeJake May 01 '15 at 15:16
  • 1
    A couple of points - your original code is already broken, so you should fix those things before you try anything new. 1) the original X and Y are of length 14 on a side, but your new energ array is only 13 on a side, and 2) you can't index from the module cm like `cm(N)`, you have to pick a colorscale (e.g. `cm.jet(N)`). For future reference, you should fix things like that *before* you post the question. – Ajean May 01 '15 at 17:20
  • 1
    As to the question, if you take your `energ` and just look at it with `imshow`, whether using masked arrays or nans you get a nice triangle like you wanted. However, you can't plot a 3D surface of that because there are only individual points - to get something 3D you need at least a couple of valid points to connect. Therefore I would recommend just setting everything to or some baseline negative number instead of using nans. – Ajean May 01 '15 at 17:22

1 Answers1

0

I was playing around with the code from this forum post, and I was able to make the graph have missing values. You can try the code yourself! I got it to work using float("nan") for the missing values.

import plotly.graph_objects as go
import numpy as np

x = np.arange(0.1,1.1,0.1)
y = np.linspace(-np.pi,np.pi,10)
#print(x)
#print(y)

X,Y = np.meshgrid(x,y)
#print(X)
#print(Y)
result = []

for i,j in zip(X,Y):
    result.append(np.log(i)+np.sin(j)) 

result[0][0] = float("nan")

upper_bound = np.array(result)+1
lower_bound = np.array(result)-1

fig = go.Figure(data=[
    go.Surface(z=result),
    go.Surface(z=upper_bound, showscale=False, opacity=0.3,colorscale='purp'),
    go.Surface(z=lower_bound, showscale=False, opacity=0.3,colorscale='purp')])
fig.show()