Extract plottable Matplotlib 2D axes from 3D plot? - python

So, this is my problem - I use a matplotlib-based library that accepts 2D Axes ax as input, and uses ax.imshow, which makes the assumption that the ax passed to it is a 2D one. I'd like to use this library, but to plot its result on the xy plane at z=0 on a 3D matplotlib plot.
As far as I can see from:
Plotting a imshow() image in 3d in matplotlib
Creating intersecting images in matplotlib with imshow or other function
... I have to basically use ax.plot_surface to have the equivalent of ax.imshow in 3D. However, that involves rewriting/hacking the library, so all corresponding calls are replaced.
So, I tried to come up with this simple example, to see what can be achieved by using imshow in a 3D context:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
fig = plt.figure()
ax3d = fig.add_subplot(projection='3d')
x = np.linspace(0, 1, 100)
y = np.sin(x * 2 * np.pi) / 2 + 0.5
#ax3d.plot(x, y, zs=0, zdir='z', label='curve in (x, y)') # works
# syntax as for 2d plot:
ax3d.plot(x, y, label='curve in (x, y)') # works
# https://matplotlib.org/stable/gallery/images_contours_and_fields/image_demo.html
delta = 0.025
xa = ya = np.arange(0.0, 1.0, delta)
Xa, Ya = np.meshgrid(xa, ya)
Z1a = np.exp(-Xa**2 - Ya**2)
Z2a = np.exp(-(Xa - 1)**2 - (Ya - 1)**2)
Za = (Z1a - Z2a) * 2
# imshow causes NotImplementedError: Axes3D currently only supports the aspect argument 'auto'. You passed in 'equal'.
ax3d.set_aspect('auto') # does not help
im = ax3d.imshow(Za, interpolation='bilinear', cmap=cm.RdYlGn,
origin='lower', extent=[0, 1, 0, 1],
vmax=abs(Za).max(), vmin=-abs(Za).max(),
aspect='auto' # makes imshow pass and draw - but the drawing is not connected to 3d rotation
)
ax3d.set_xlim(0, 1)
ax3d.set_ylim(0, 1)
ax3d.set_zlim(0, 1)
ax3d.view_init(elev=20., azim=-35)
plt.show()
... so, syntactically, it can be "coaxed" - unfortunately, the result is not a "part" of the 3D plot, in the sense that it is not on the xy plane at z=0, and it does not rotate with the 3D view as the rest of the plot:
So, I was thinking - is there a way/a "hack" of sorts, so that I could "extract" 2D Axes matplotlib object for the xy plane at z=0 of the 3D plot, - and then use that Axes object to pass as input to the library, which will proceed as usual (but the ultimate results of its plot will be a part of the 3D plot)? Basically, as in the following pseudocode:
...
ax2dxy = ax3d.get_2daxes('x', 'y', z=0) # PSEUDO
im = ax2dxy.imshow(Za, interpolation='bilinear', cmap=cm.RdYlGn,
origin='lower', extent=[0, 1, 0, 1],
vmax=abs(Za).max(), vmin=-abs(Za).max(),
)
...

Not quite an answer to the question - and likely, it is not easily possible to "extract" "plottable" 2D axes from 3D ones - but in the below example I've attempted it, and I couldn't get it to do much.
However, I also tried plotting 2D imshow on "virtual" 2D axes - and reusing that data for 3D plot surface - and it seems to work:
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import inspect
fig = plt.figure()
ax3d = fig.add_subplot(projection='3d')
#~ ax3d = fig.add_subplot()
x = np.linspace(0, 1, 100)
y = np.sin(x * 2 * np.pi) / 2 + 0.5
#ax3d.plot(x, y, zs=0, zdir='z', label='curve in (x, y)') # works
# syntax as for 2d plot:
ax3d.plot(x, y, label='curve in (x, y)') # works
# https://matplotlib.org/stable/gallery/images_contours_and_fields/image_demo.html
delta = 0.025
xa = ya = np.arange(0.0, 1.0, delta)
Xa, Ya = np.meshgrid(xa, ya)
Z1a = np.exp(-Xa**2 - Ya**2)
Z2a = np.exp(-(Xa - 1)**2 - (Ya - 1)**2)
Za = (Z1a - Z2a) * 2
#print(inspect.getsourcefile(ax3d.plot3D)) # /mingw64/lib/python3.8/site-packages/mpl_toolkits/mplot3d/axes3d.py
#print(inspect.getsource(ax3d.plot3D)) # def plot! plot3D = plot
#print(inspect.getsourcefile(matplotlib.axes.Axes)) # /mingw64/lib/python3.8/site-packages/matplotlib/axes/_axes.py
# imshow in /mingw64/lib/python3.8/site-packages/matplotlib/axes/_axes.py
print( ax3d.xaxis, ax3d.yaxis , ax3d._position ) # ax3d._position is rect, Bbox(x0=0.125, y0=0.10999999999999999, x1=0.9, y1=0.88)
newax = matplotlib.axes.Axes(fig, (0,0,1,1) )
print(newax._position) # Bbox(x0=0.0, y0=0.0, x1=1.0, y1=1.0)
newax.xaxis = ax3d.xaxis
newax.yaxis = ax3d.yaxis
# imshow causes NotImplementedError: Axes3D currently only supports the aspect argument 'auto'. You passed in 'equal'.
ax3d.set_aspect('auto') # does not help
#im = ax3d.imshow(Za, interpolation='bilinear', cmap=cm.RdYlGn,
# origin='lower', extent=[0, 1, 0, 1],
# vmax=abs(Za).max(), vmin=-abs(Za).max(),
# aspect='auto' # makes imshow pass and draw - but the drawing is not connected to 3d rotation
# )
imB = newax.imshow(Za, interpolation='bilinear', cmap=cm.RdYlGn,
origin='lower', extent=[0, 1, 0, 1],
vmax=abs(Za).max(), vmin=-abs(Za).max(),
) # passes, but does not show anything
#imB.axes = ax3d # ValueError: Can not reset the axes. You are probably trying to re-use an artist in more than one Axes which is not supported
#~ print(newax.images) # [<matplotlib.image.AxesImage object at 0x000002505f06f670>]
#~ print(imB._A) # is there
fig.canvas.draw() # call to create fig.canvas.renderer
im, l, b, trans = imB.make_image(fig.canvas.renderer, unsampled=True)
#print(im)
ax3d.set_axisbelow(False)
ax3d.plot_surface(Xa, Ya, np.zeros(Xa.shape), rstride=1, cstride=1, facecolors=np.divide(im, 255.0), shade=False, zorder=-100)
# ax3d.grid(True, which='major')
ax3d.set_xlim(0, 1)
ax3d.set_ylim(0, 1)
ax3d.set_zlim(0, 1)
ax3d.view_init(elev=20., azim=-35)
plt.show()
The code above produces:
... which looks decent ...
Now I just wish I knew how I could control the z-order (plot_surface below, gridlines and sinusoid on top of it) - but I posted a separate Q for that ( How to draw Axes3D grid lines over plot_surface() in Matplotlib? )

Related

Filling in a grid in numpy

I want to achieve the following. I have a x-y plot with a function y dependent on x. The plot consists of a mesh of squares. When a function point is inside the function block, the block changes color. I have attached an example:
I want to make an simillar figure, but I want to be able to make the gridsize variable.
I know how to make a plot with plt.plot(), but I'm not familliar with making a grid and filling in that grid if the function point falls in a square. Can somebody refer to numpy or mathplotlib functions that can help?
Thanks
here is a piece of code that should work for graphs centered around (min(X)-max(X)) and (min(Y),max(Y)) :
import numpy as np
def grid_plot(X,Y,resx,resy) :
d_x=resx/(np.max(X)-np.min(X))
d_y=resy/(np.max(Y)-np.min(Y))
mat=np.zeros((resy,resx))
for i in range(len(X)) :
mat[int((Y[i]-np.min(Y))*d_y),resx-int((X[i]-np.min(X))*d_x)]=1
return mat
You can use np.histogram2d to create a 2D histogram and plot the locations with count greater than zero with imshow:
import numpy as np
import matplotlib.pyplot as plt
# Input data
x = np.linspace(-2, 2, 300)
y = np.sin(x)
# Plot limits
x_min, x_max = -3, 3
y_min, y_max = -2, 2
plt.figure(figsize=(8, 3))
# First plot
resolution = 25
xg = np.linspace(x_min, x_max, resolution)
yg = np.linspace(y_min, y_max, resolution)
h, _, _ = np.histogram2d(x, y, (xg, yg))
plt.subplot(121)
# Transpose because imshow swaps X and Y axes
plt.imshow(h.T > 0, origin='lower', extent=(xg[0], xg[-1], yg[0], yg[-1]))
# Show grid
plt.gca().set_xticks([], minor=False)
plt.gca().set_xticks(xg, minor=True)
plt.gca().set_yticks([], minor=False)
plt.gca().set_yticks(yg, minor=True)
plt.grid(True, 'minor')
# Second plot
resolution = 50
xg = np.linspace(x_min, x_max, resolution)
yg = np.linspace(y_min, y_max, resolution)
h, _, _ = np.histogram2d(x, y, (xg, yg))
plt.subplot(122)
plt.imshow(h.T > 0, origin='lower', extent=(xg[0], xg[-1], yg[0], yg[-1]))
plt.gca().set_xticks([], minor=False)
plt.gca().set_xticks(xg, minor=True)
plt.gca().set_yticks([], minor=False)
plt.gca().set_yticks(yg, minor=True)
plt.grid(True, 'minor')
# Show plot
plt.tight_layout()
plt.show()
Result:

Overlaying plots on a single graph

I have two heatmaps which are based on 2d histograms that I am trying to overlay on a single graph. The limits of their axes (extent_L and extent_H) do not necessarily coincide exactly. I can make the individual plots satisfactorily if needed, but when trying to show both heatmaps on a single graph nicely, only the most recent one is displayed.
import numpy as np
import numpy.random
import matplotlib.pyplot as plt
# Generate some test data
x_L = np.random.randn(8873)
y_L = np.random.randn(8873)
x_H = np.random.randn(1000)
y_H = np.random.randn(1000)
heatmap_L, xedges_L, yedges_L = np.histogram2d(x_L, y_L, bins=50)
extent_L = [xedges_L[0], xedges_L[-1], yedges_L[0], yedges_L[-1]]
heatmap_H, xedges_H, yedges_H = np.histogram2d(x_H, y_H, bins=50)
extent_H = [xedges_H[0], xedges_H[-1], yedges_H[0], yedges_H[-1]]
plt.clf()
im1 = plt.imshow(heatmap_L.T, extent=extent_L, origin='lower', cmap='Blues')
im2 = plt.imshow(heatmap_H.T, extent=extent_H, origin='lower', cmap='Greens')
plt.show()
Edit: If I'm not mistaken, all points are not in exactly the proper location
import numpy as np
import numpy.random
import matplotlib.pyplot as plt
# Generate some test data
x_L = np.random.randn(8873)
y_L = np.random.randn(8873)
x_H = np.random.randn(1000)
y_H = np.random.randn(1000)
heatmap_L, xedges_L, yedges_L = np.histogram2d(x_L, y_L, bins=50)
extent_L = np.array([xedges_L[0], xedges_L[-1], yedges_L[0], yedges_L[-1]])
heatmap_H, xedges_H, yedges_H = np.histogram2d(x_H, y_H, bins=50)
extent_H = np.array([xedges_H[0], xedges_H[-1], yedges_H[0], yedges_H[-1]])
plt.clf()
im1 = plt.imshow(heatmap_L.T, extent=extent_L, origin='lower', cmap='Blues')
im2 = plt.imshow(heatmap_H.T, extent=extent_H, origin='lower', cmap='Greens')
plt.autoscale()
plt.show()
flatHMH = np.reshape(heatmap_H, 2500) # flatten the 2D arrays
flatHML = np.reshape(heatmap_L, 2500)
maxHMH = flatHMH.max() # Find the maximum in each
maxHML = flatHML.max()
# Now for each value in the flat array build an RGBA tuple using
# 1 for the colour we want - either green or blue, and then scaling
# the value by the maximum, finally reshaping back to a 50x50 array
augHMH = np.array([(0, 1, 0, x/maxHMH) for x in flatHMH]).reshape((50, 50, 4))
augHML = np.array([(0, 0, 1, x/maxHML) for x in flatHML]).reshape((50, 50, 4))
plt.clf()
# Plot without cmap as colours are now part of the data array passed.
im1 = plt.imshow(augHML, extent=extent_L, origin='lower')
im2 = plt.imshow(augHMH, extent=extent_H, origin='lower')
plt.autoscale()
plt.show()
If you look closely at the points in the last plot, for example the clustering of points at the edge, you'll notice they are not the same as in the plot above.
You are displaying both plots, the problem is that you are drawing one on top of the other. To see this in action you can shift one of the plots as in:
import numpy as np
import numpy.random
import matplotlib.pyplot as plt
# Generate some test data
x_L = np.random.randn(8873)
y_L = np.random.randn(8873)
x_H = np.random.randn(1000)
y_H = np.random.randn(1000)
heatmap_L, xedges_L, yedges_L = np.histogram2d(x_L, y_L, bins=50)
extent_L = np.array([xedges_L[0], xedges_L[-1], yedges_L[0], yedges_L[-1]])
heatmap_H, xedges_H, yedges_H = np.histogram2d(x_H, y_H, bins=50)
extent_H = np.array([xedges_H[0], xedges_H[-1], yedges_H[0], yedges_H[-1]])
plt.clf()
im1 = plt.imshow(heatmap_L.T, extent=extent_L, origin='lower', cmap='Blues')
im2 = plt.imshow(heatmap_H.T+2, extent=extent_H+2, origin='lower', cmap='Greens')
plt.autoscale()
plt.show()
You also need the plt.autoscale() call in there as otherwise the limits are not adjusted correctly.
One way to show the two plots on top of each other is to use the argument alpha=X to the imshow call (where 0 < X < 1) in order to set transparency on the plot call. Another, possibly clearer way is to transform each value from the histogram2D to an RGBA value. See the imshow docs for both alternatives to displaying the plots on top of each other.
One way of transforming the values would be to flatten the data, and augment it with the colours you want.
# imports and test data generation as before, removed for clarity...
flatHMH = np.reshape(heatmap_H, 2500) # flatten the 2D arrays
flatHML = np.reshape(heatmap_L, 2500)
maxHMH = flatHMH.max() # Find the maximum in each
maxHML = flatHML.max()
# Now for each value in the flat array build an RGBA tuple using
# 1 for the colour we want - either green or blue, and then scaling
# the value by the maximum, finally reshaping back to a 50x50 array
augHMH = np.array([(0, 1, 0, x/maxHMH) for x in flatHMH]).reshape((50, 50, 4))
augHML = np.array([(0, 0, 1, x/maxHML) for x in flatHML]).reshape((50, 50, 4))
plt.clf()
# Plot without cmap as colours are now part of the data array passed.
im1 = plt.imshow(augHML, extent=extent_L, origin='lower')
im2 = plt.imshow(augHMH, extent=extent_H, origin='lower')
plt.autoscale()
plt.show()
You can call
plt.autoscale()
such that the limits are adjusted to the content of the axes.
Example:
import numpy as np
import matplotlib.pyplot as plt
def get(offs=0):
# Generate some test data
x = np.random.randn(8873)+offs
y = np.random.randn(8873)+offs
heatmap, xedges, yedges = np.histogram2d(x, y, bins=50)
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
heatmap, xedges, yedges = np.histogram2d(x, y, bins=50)
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
return heatmap, extent
h1,e1 = get(-3)
h2,e2 = get(+3)
plt.imshow(h1, extent=e1, origin='lower', cmap="RdBu")
plt.imshow(h2, extent=e2, origin='lower', cmap="YlGnBu")
plt.autoscale()
plt.show()

3D histograms and Contour plots Python

I have a problem with contourf function of matplotlib. I have a txt data file from which I am importing my data. I have columns of data (pm1 and pm2) and I am performing a 2D histogram. I want to plot this data as a 3D histogram and as a contour plot to see where is located the maximum values.
This is my code:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
rows = np.arange(200,1300,10)
hist, xedges, yedges = np.histogram2d (pm1_n, pm2_n, bins = (rows, rows) )
elements = (len(xedges) - 1) * (len(yedges) - 1)
xpos, ypos = np.meshgrid(xedges[:-1], yedges[:-1])
xpos = xpos.flatten()
ypos = ypos.flatten()
zpos = np.zeros(elements)
dx = 0.1 * np.ones_like(zpos)
dy = dx.copy()
dz = hist.flatten()
#####The problem is here#####
#ax.contourf(xpos,ypos,hist)
#ax.bar3d(xpos, ypos, zpos, dx, dy, dz, zsort='average')
plt.show()
I can plot the 3d bar graph but I am not able to plot the contour one, If I place hist in the contourf function I get the error: Length of x must be number of columns in z and if I place dz I get Input z must be a 2D array
I also have tried using xedges and yexges but this doesn't solve the problem.
I think that the problem is related with the shape of the return of the function histogram2D. But I don't know how to solve it.
I would also like to perform a 3D bar plot with a colorcode changing form the minimum to the maximum value. Is there anyway to make this?
Thank you
Perhaps I don't understand what exactly you are trying to do since I don't know what your data looks like, but it seems wrong to have your contourf plot sharing the same axis as your bar3d plot. If you add an axis without the 3D projection to a new figure, you should be able to make a contourf plot just fine using hist. An example using data from a random, normal distribution:
import numpy as np
import matplotlib.pyplot as plt
n_points = 1000
x = np.random.normal(0, 2, n_points)
y = np.random.normal(0, 2, n_points)
hist, xedges, yedges = np.histogram2d(x, y, bins=np.sqrt(n_points))
fig2D = plt.figure()
ax2D = fig2D.add_subplot(111)
ax2D.contourf(hist, interpolation='nearest',
extent=(xedges[0], xedges[-1], yedges[0], yedges[-1]))
plt.show()
returns an image like this.
As for your second question, regarding a color-coded 3D bar plot, how about this (using the same data as above but with 1/10 the size):
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.colors as colors
n_points = 100
x = np.random.normal(0, 2, n_points)
y = np.random.normal(0, 2, n_points)
hist, xedges, yedges = np.histogram2d(x, y, bins=np.sqrt(n_points))
# Following your data reduction process
xpos, ypos = np.meshgrid(xedges[:-1], yedges[:-1])
length, width = 0.4, 0.4
xpos = xpos.flatten()
ypos = ypos.flatten()
zpos = np.zeros(n_points)
dx = np.ones(n_points) * length
dy = np.ones(n_points) * width
dz = hist.flatten()
# This is where the colorbar customization comes in
dz_normed = dz / dz.max()
normed_cbar = colors.Normalize(dz_normed.min(), dz_normed.max())
# Using jet, but should work with any colorbar
color = cm.jet(normed_cbar(dz_normed))
fig3D = plt.figure()
ax3D = fig3D.add_subplot(111, projection='3d')
ax3D.bar3d(xpos, ypos, zpos, dx, dy, dz, color=color)
plt.show()
I get this image.

Plotting a 2D heatmap

Using Matplotlib, I want to plot a 2D heat map. My data is an n-by-n Numpy array, each with a value between 0 and 1. So for the (i, j) element of this array, I want to plot a square at the (i, j) coordinate in my heat map, whose color is proportional to the element's value in the array.
How can I do this?
The imshow() function with parameters interpolation='nearest' and cmap='hot' should do what you want.
Please review the interpolation parameter details, and see Interpolations for imshow and Image antialiasing.
import matplotlib.pyplot as plt
import numpy as np
a = np.random.random((16, 16))
plt.imshow(a, cmap='hot', interpolation='nearest')
plt.show()
Seaborn is a high-level API for matplotlib, which takes care of a lot of the manual work.
seaborn.heatmap automatically plots a gradient at the side of the chart etc.
import numpy as np
import seaborn as sns
import matplotlib.pylab as plt
uniform_data = np.random.rand(10, 12)
ax = sns.heatmap(uniform_data, linewidth=0.5)
plt.show()
You can even plot upper / lower left / right triangles of square matrices. For example, a correlation matrix, which is square and is symmetric, so plotting all values would be redundant.
corr = np.corrcoef(np.random.randn(10, 200))
mask = np.zeros_like(corr)
mask[np.triu_indices_from(mask)] = True
with sns.axes_style("white"):
ax = sns.heatmap(corr, mask=mask, vmax=.3, square=True, cmap="YlGnBu")
plt.show()
I would use matplotlib's pcolor/pcolormesh function since it allows nonuniform spacing of the data.
Example taken from matplotlib:
import matplotlib.pyplot as plt
import numpy as np
# generate 2 2d grids for the x & y bounds
y, x = np.meshgrid(np.linspace(-3, 3, 100), np.linspace(-3, 3, 100))
z = (1 - x / 2. + x ** 5 + y ** 3) * np.exp(-x ** 2 - y ** 2)
# x and y are bounds, so z should be the value *inside* those bounds.
# Therefore, remove the last value from the z array.
z = z[:-1, :-1]
z_min, z_max = -np.abs(z).max(), np.abs(z).max()
fig, ax = plt.subplots()
c = ax.pcolormesh(x, y, z, cmap='RdBu', vmin=z_min, vmax=z_max)
ax.set_title('pcolormesh')
# set the limits of the plot to the limits of the data
ax.axis([x.min(), x.max(), y.min(), y.max()])
fig.colorbar(c, ax=ax)
plt.show()
For a 2d numpy array, simply use imshow() may help you:
import matplotlib.pyplot as plt
import numpy as np
def heatmap2d(arr: np.ndarray):
plt.imshow(arr, cmap='viridis')
plt.colorbar()
plt.show()
test_array = np.arange(100 * 100).reshape(100, 100)
heatmap2d(test_array)
This code produces a continuous heatmap.
You can choose another built-in colormap from here.
Here's how to do it from a csv:
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import griddata
# Load data from CSV
dat = np.genfromtxt('dat.xyz', delimiter=' ',skip_header=0)
X_dat = dat[:,0]
Y_dat = dat[:,1]
Z_dat = dat[:,2]
# Convert from pandas dataframes to numpy arrays
X, Y, Z, = np.array([]), np.array([]), np.array([])
for i in range(len(X_dat)):
X = np.append(X, X_dat[i])
Y = np.append(Y, Y_dat[i])
Z = np.append(Z, Z_dat[i])
# create x-y points to be used in heatmap
xi = np.linspace(X.min(), X.max(), 1000)
yi = np.linspace(Y.min(), Y.max(), 1000)
# Interpolate for plotting
zi = griddata((X, Y), Z, (xi[None,:], yi[:,None]), method='cubic')
# I control the range of my colorbar by removing data
# outside of my range of interest
zmin = 3
zmax = 12
zi[(zi<zmin) | (zi>zmax)] = None
# Create the contour plot
CS = plt.contourf(xi, yi, zi, 15, cmap=plt.cm.rainbow,
vmax=zmax, vmin=zmin)
plt.colorbar()
plt.show()
where dat.xyz is in the form
x1 y1 z1
x2 y2 z2
...
Use matshow() which is a wrapper around imshow to set useful defaults for displaying a matrix.
a = np.diag(range(15))
plt.matshow(a)
https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.matshow.html
This is just a convenience function wrapping imshow to set useful defaults for displaying a matrix. In particular:
Set origin='upper'.
Set interpolation='nearest'.
Set aspect='equal'.
Ticks are placed to the left and above.
Ticks are formatted to show integer indices.
Here is a new python package to plot complex heatmaps with different kinds of row/columns annotations in Python: https://github.com/DingWB/PyComplexHeatmap

How to do a 3D revolution plot in matplotlib?

Suppose you have a 2D curve, given by e.g.:
from matplotlib import pylab
t = numpy.linspace(-1, 1, 21)
z = -t**2
pylab.plot(t, z)
which produces
I would like to perform a revolution to achieve a 3d plot (see http://reference.wolfram.com/mathematica/ref/RevolutionPlot3D.html). Plotting a 3d surface is not the problem, but it does not produce the result I'm expecting:
How can I perform a rotation of this blue curve in the 3d plot ?
Your plot on your figure seems to use cartesian grid. There is some examples on the matplotlib website of 3D cylindrical functions like Z = f(R) (here: http://matplotlib.org/examples/mplot3d/surface3d_radial_demo.html).
Is that what you looking for ?
Below is what I get with your function Z = -R**2 :
And to add cut off to your function, use the following example:
(matplotlib 1.2.0 required)
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
Z = -(abs(X) + abs(Y))
## 1) Initial surface
# Flatten mesh arrays, necessary for plot_trisurf function
X = X.flatten()
Y = Y.flatten()
Z = Z.flatten()
# Plot initial 3D surface with triangles (more flexible than quad)
#surfi = ax.plot_trisurf(X, Y, Z, cmap=cm.jet, linewidth=0.2)
## 2) Cut off
# Get desired values indexes
cut_idx = np.where(Z > -5)
# Apply the "cut off"
Xc = X[cut_idx]
Yc = Y[cut_idx]
Zc = Z[cut_idx]
# Plot the new surface (it would be impossible with quad grid)
surfc = ax.plot_trisurf(Xc, Yc, Zc, cmap=cm.jet, linewidth=0.2)
# You can force limit if you want to compare both graphs...
ax.set_xlim(-5,5)
ax.set_ylim(-5,5)
ax.set_zlim(-10,0)
plt.show()
Result for surfi:
and surfc:

Categories

Resources