Use same scale for 3D pyplot - python

I would like to set the same scale for the X and Y axis on a 3D plot. Here is a sample plot:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
X = np.array([-3, 5, 6])
Y = np.array([14, -2, -31])
Z = np.array([0.1, 0, -0.1])
ax = plt.axes(projection='3d')
ax.plot(X, Y, Z)
plt.show()
The scale for the X and Y axis is such that they take up the same amount of space even though the true scale of the Y axis is larger than that of the X axis.
How do I make it so that they have an equal scale?
Edit: ax.set_xlim(Y.min(), Y.max()) worked.

In addition to answer above you can use set_box_aspect.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
X = np.array([-3, 5, 6])
Y = np.array([14, -2, -31])
Z = np.array([0.1, 0, -0.1])
ax = plt.axes(projection='3d')
ax.set_box_aspect([1,1,1]) #aspect ratio x,y,z
ax.plot(X, Y, Z)
plt.show()

Related

How to update PolyCollection in matplotlib

I would like to know, if there is a way to redraw already created PolyCollection.
MWP
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(0.0, 2, 0.01)
y = np.sin(2*np.pi*x)
fig, (ax,ax2) = plt.subplots(1,1)
polycolelction = ax.fill_between(x, y)
for i in range(10):
y = 1.2*np.sin(i*np.pi*x)
# here should be data update, probably something like set_offset or set_verts?
polycolelction.set_verts([x,y])
fig.canvas.draw()
fig.canvas.flush_events()
It could be easier to create the polygon from scratch and then update its vertices. That way, we have precise control over the number of vertices and how the polygon is represented.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
x = np.arange(0.0, 2, 0.01)
y = np.sin(2 * np.pi * x)
fig, ax = plt.subplots()
points = np.array([x, y]).T
points = np.vstack([points, [points[-1, 0], 0], [points[0, 0], 0]])
polycollection = PatchCollection([Polygon(points, closed=True)])
pathcollection = ax.add_collection(polycollection)
ax.set_xlim(x[0], x[-1])
ax.set_ylim(-1.3, 1.3)
for i in range(10):
y = 1.2 * np.sin(i * np.pi * x)
points = np.array([x, y]).T
points = np.vstack([points, [points[-1, 0], 0], [points[0, 0], 0]])
pathcollection.get_paths()[0].vertices = points
fig.canvas.draw()
fig.canvas.flush_events()

Matplotlib plot contourf on 3d surface

I am trying to use the colormap feature of a 3d-surface plot in matplotlib to color the surface based on values from another array instead of the z-values.
The surface plot is created and displayed as follows:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def gauss(x, y, w_0):
r = np.sqrt(x**2 + y**2)
return np.exp(-2*r**2 / w_0**2)
x = np.linspace(-100, 100, 100)
y = np.linspace(-100, 100, 100)
X, Y = np.meshgrid(x, y)
Z = gauss(X, Y, 50)
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.plot_surface(X, Y, Z, cmap='jet')
Now instead of coloring based on elevation of the 3d-surface, I am looking to supply the color data for the surface in form of another array, here as an example a random one:
color_data = np.random.uniform(0, 1, size=(Z.shape))
However, I did not find a solution to colorize the 3d-surface based on those values. Ideally, it would look like a contourf plot in 3d, just on the 3d surface.
You can use matplotlib.colors.from_levels_and_colors to obtain a colormap and normalization, then apply those to the values to be colormapped.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.colors
x = np.linspace(-100, 100, 101)
y = np.linspace(-100, 100, 101)
X, Y = np.meshgrid(x, y)
Z = np.exp(-2*np.sqrt(X**2 + Y**2)**2 / 50**2)
c = X+50*np.cos(Y/20) # values to be colormapped
N = 11 # Number of level (edges)
levels = np.linspace(-150,150,N)
colors = plt.cm.get_cmap("RdYlGn", N-1)(np.arange(N-1))
cmap, norm = matplotlib.colors.from_levels_and_colors(levels, colors)
color_vals = cmap(norm(c))
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.plot_surface(X, Y, Z, facecolors=color_vals, rstride=1, cstride=1)
plt.show()

Boolean masking of arrays in 3D surface plot destroys colormap

I try to 3D-plot function fun and use colormap to show the level of function values. I'd like to plot this function on a non-sqaured area and hence I used boolean mask to set np.nan to certain values in meshgrid. But I got
RuntimeWarning: invalid value encountered in less
cbook._putmask(xa, xa < 0.0, -1)
whenever I added boolean mask. It seems the bug is due to that np.nan cannot be compared in colormap. But I can't find a way to fix this.
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
%matplotlib inline
fun = lambda x: np.sin(x[0])*np.exp(1-np.cos(x[1]))**2 + np.cos(x[1])*np.exp(1-np.sin(x[0]))**2 + (x[0]-x[1])**2
fig = plt.figure(figsize=(8, 5))
ax = fig.gca(projection='3d')
x = np.arange(-6, 6, 3e-2)
y = np.arange(-6, 6, 3e-2)
# A constraint on x and y
x, y = np.meshgrid(x, y)
r2 = (x+5)**2 + (y+5)**2
scope = r2 < 25
# Mask is the cause of the problem
x[scope] = np.nan
y[scope] = np.nan
z = fun(np.array([x, y]))
surf=ax.plot_surface(x, y, z, cmap=cm.jet)
ax.contourf(x, y, z, offset=-120, cmap=cm.jet)
fig.colorbar(surf)
ax.view_init(elev=30, azim=60)
You cannot fix the runtime warning. It's a warning based on the fact that there are nan values in the array.
In order to still get a colorcoded surface plot, you can however use a matplotlib.colors.Normalize instance to tell the surface plot which colors to use.
See full code below:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
import matplotlib.colors
fun = lambda x: np.sin(x[0])*np.exp(1-np.cos(x[1]))**2 + np.cos(x[1])*np.exp(1-np.sin(x[0]))**2 + (x[0]-x[1])**2
fig = plt.figure(figsize=(8, 5))
ax = fig.gca(projection='3d')
x = np.arange(-6, 6, 3e-2)
y = np.arange(-6, 6, 3e-2)
# A constraint on x and y
x, y = np.meshgrid(x, y)
r2 = (x+5)**2 + (y+5)**2
scope = r2 < 25
# Mask is the cause of the problem
x[scope] = np.nan
y[scope] = np.nan
z = fun(np.array([x, y]))
norm = matplotlib.colors.Normalize(vmin=-120, vmax=120)
cm.jet.set_under((0,0,0,0))
ax.contourf(x, y, z, offset=-120, cmap=cm.jet, norm=norm)
surf=ax.plot_surface(x, y, z, cmap=cm.jet, norm=norm)
fig.colorbar(surf)
#ax.view_init(elev=30, azim=60)
plt.show()

Streamflow plot not matching mgrid

I am plotting a streamplot of a 2D ODE system using the Python code:
import numpy as np
import matplotlib.pyplot as plt
Y, X = np.mgrid[-4:4:100j, -4:4:100j]
U = -0.5*X - Y
V = X - Y
plt.streamplot(X, Y, U, V, density=[1, 1])
plt.show()
However I am getting the following plot where it is extending the x and y axis by an additional unit, 5 instead of 4 even though the mgrid is -4 to 4 in both directions. I've tried different ranges but without success. Any ideas on how to confine the plot to -4:-4 and -4:4
You could use
ax.set(xlim=(-4,4), ylim=(-4,4))
to manually set the limits:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
Y, X = np.mgrid[-4:4:100j, -4:4:100j]
U = -0.5*X - Y
V = X - Y
ax.streamplot(X, Y, U, V, density=[1, 1])
ax.set(xlim=(-4,4), ylim=(-4,4))
plt.show()

3D plots using maplot3d from matplotlib-

I have to plot data which is in the following format :
x = range(6)
y = range(11)
and z depends on x, y
For each value of x, there should be a continuous curve that shows the variation of z w.r.t y and the curves for different values of x must be disconnected
I am using mplot3d and it is not very clear how to plot disconnected curves.
This is what it looks like using bar plots.
You could overlay multiple plots using Axes3D.plot:
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as axes3d
import numpy as np
x = np.arange(6)
y = np.linspace(0, 11, 50)
z = x[:, np.newaxis] + y**2
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection = '3d')
for xval, zrow in zip(x, z):
ax.plot(xval*np.ones_like(y), y, zrow, color = 'black')
plt.show()

Categories

Resources