29

I am trying to make a 3-dimensional surface plot for the expression: z = y^2/x, for x in the interval [-2,2] and y in the interval [-1.4,1.4]. I also want the z-values to range from -4 to 4.

The problem is that when I'm viewing the finished surfaceplot, the z-axis values do not stop at [-4,4].

So my question is how I can "remove" the z-axis value that range outside the intervall [-4,4] from the finished plot?

My code is:

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

fig = plt.figure()
ax = fig.gca(projection="3d")

x = np.arange(-2.0,2.0,0.1,float)       # x in interval [-2,2]
y = np.arange(-1.4,1.4,0.1,float)       # y in interval [-1.4,1.4]
x,y = np.meshgrid(x,y)
z = (y**2/x)                            # z = y^2/x

ax.plot_surface(x, y, z,rstride=1, cstride=1, linewidth=0.25)

ax.set_zlim3d(-4, 4)                    # viewrange for z-axis should be [-4,4] 
ax.set_ylim3d(-2, 2)                    # viewrange for y-axis should be [-2,2] 
ax.set_xlim3d(-2, 2)                    # viewrange for x-axis should be [-2,2] 
plt.show()
Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
user605243
  • 291
  • 1
  • 3
  • 3

3 Answers3

22

I am having the same issue and still have not found anything better than clipping my data. Unfortunately in my case I am tied to matplotlib 1.2.1. But in case you can upgrade to version 1.3.0 you could have a solution: it seems there is a bunch of new API related to axes ranges. In particular, you may be interested by the "set_zlim".

Edit 1: Manage to migrate my environnement to use matplotlib 1.3.0; set_zlim worked like a charm :)

The follwing code worked for me (By the way I am running this on OSX, I am not sure this has an impact?):

# ----------------------------------------------------------------------------
# Make a 3d plot according to data passed as arguments  
def Plot3DMap( self, LabelX, XRange, LabelY, YRange, LabelZ, data3d ) :
    fig = plt.figure()
    ax = fig.add_subplot( 111, projection="3d" )
    xs, ys =  np.meshgrid( XRange, YRange )
    surf = ax.plot_surface( xs, ys, data3d )
    ax.set_xlabel( LabelX )
    ax.set_ylabel( LabelY )
    ax.set_zlabel( LabelZ )
    ax.set_zlim(0, 100)
    plt.show()
TocToc
  • 4,739
  • 4
  • 29
  • 34
  • 9
    Can you confirm that set_zlim works for you? I'm using 1.3.1 and when I `set_zlim` or `set_zlim3d` it just limits the axes but doesn't actually clip the data. – KobeJohn Nov 10 '13 at 16:57
  • 1
    Can you please explain how to use set_zlim? – Jonny Sep 06 '14 at 13:29
  • 1
    @Jonny: http://matplotlib.org/mpl_toolkits/mplot3d/api.html#mpl_toolkits.mplot3d.axes3d.Axes3D.set_zlim – TocToc Sep 28 '15 at 21:11
7

clipping your data will accomplish this, but it's not very pretty.

z[z>4]= np.nan
z[z<-4]= np.nan
Paul
  • 42,322
  • 15
  • 106
  • 123
  • Is there no better way to do this? To somehow "cut" away the z-values outside the range of [-4,4] when viewing the plot? – user605243 Feb 06 '11 at 15:28
  • @user605243: in principle, you should be able to use a masked_array, like you can with 2D plots. In practice it doesn't seem to work (although the necessary changes in axes3d.py to get it to don't seem _too_ daunting.) Unfortunately I couldn't actually get the np.nan trick to work either, with lots of "CGPathCloseSubpath: no current point." errors. – DSM Feb 06 '11 at 15:54
3

Rather than using ax.plot_surface I found ax.plot_trisurf to work well, since you don't need to give it a rectangular grid of values like ax.plot_surface. If you're using numpy arrays, you can then use the following trick to only select points within your z-bounds.

from matplotlib import cm

x, y, z = x.flatten(), y.flatten(), z.flatten()
usable_points = (-4 < z) & (z < 4)
x, y, z = x[usable_points], y[usable_points], z[usable_points]
ax.plot_trisurf(x, y, z, cmap=cm.jet)
Simply Beautiful Art
  • 1,284
  • 15
  • 16