Lorsque je configure un rapport d'aspect égal pour le graphique 3D, l'axe z ne change pas en "égal". Donc ça:
fig = pylab.figure()
mesFig = fig.gca(projection='3d', adjustable='box')
mesFig.axis('equal')
mesFig.plot(xC, yC, zC, 'r.')
mesFig.plot(xO, yO, zO, 'b.')
pyplot.show()
me donne ce qui suit:
où évidemment la longueur unitaire de l'axe z n'est pas égale aux unités x et y.
Comment puis-je égaliser la longueur unitaire des trois axes? Toutes les solutions que j'ai pu trouver n'ont pas fonctionné. Merci.
Je crois que matplotlib ne définit pas encore correctement un axe égal en 3D ... Mais j'ai trouvé il y a quelques temps (je ne me souviens plus où) que je me suis adapté en l'utilisant. Le concept est de créer une fausse boîte englobante cubique autour de vos données. Vous pouvez le tester avec le code suivant:
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')
ax.set_aspect('equal')
X = np.random.Rand(100)*10+5
Y = np.random.Rand(100)*5+2.5
Z = np.random.Rand(100)*50+25
scat = ax.scatter(X, Y, Z)
# Create cubic bounding box to simulate equal aspect ratio
max_range = np.array([X.max()-X.min(), Y.max()-Y.min(), Z.max()-Z.min()]).max()
Xb = 0.5*max_range*np.mgrid[-1:2:2,-1:2:2,-1:2:2][0].flatten() + 0.5*(X.max()+X.min())
Yb = 0.5*max_range*np.mgrid[-1:2:2,-1:2:2,-1:2:2][1].flatten() + 0.5*(Y.max()+Y.min())
Zb = 0.5*max_range*np.mgrid[-1:2:2,-1:2:2,-1:2:2][2].flatten() + 0.5*(Z.max()+Z.min())
# Comment or uncomment following both lines to test the fake bounding box:
for xb, yb, zb in Zip(Xb, Yb, Zb):
ax.plot([xb], [yb], [zb], 'w')
plt.grid()
plt.show()
les données z sont d'un ordre de grandeur supérieur à x et y, mais même avec une option d'axe égal, l'axe z de matplotlib met automatiquement à l'échelle:
Mais si vous ajoutez la boîte englobante, vous obtenez une mise à l'échelle correcte:
J'ai simplifié la solution de Remy F en utilisant le set_x/y/zlim
fonctions .
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')
ax.set_aspect('equal')
X = np.random.Rand(100)*10+5
Y = np.random.Rand(100)*5+2.5
Z = np.random.Rand(100)*50+25
scat = ax.scatter(X, Y, Z)
max_range = np.array([X.max()-X.min(), Y.max()-Y.min(), Z.max()-Z.min()]).max() / 2.0
mid_x = (X.max()+X.min()) * 0.5
mid_y = (Y.max()+Y.min()) * 0.5
mid_z = (Z.max()+Z.min()) * 0.5
ax.set_xlim(mid_x - max_range, mid_x + max_range)
ax.set_ylim(mid_y - max_range, mid_y + max_range)
ax.set_zlim(mid_z - max_range, mid_z + max_range)
plt.show()
J'aime les solutions ci-dessus, mais elles ont l'inconvénient dont vous avez besoin pour garder une trace des plages et des moyens sur toutes vos données. Cela peut être fastidieux si vous disposez de plusieurs ensembles de données qui seront tracés ensemble. Pour résoudre ce problème, j'ai utilisé les méthodes ax.get_ [xyz] lim3d () et mis le tout dans une fonction autonome qui peut être appelée une seule fois avant d'appeler plt.show (). Voici la nouvelle version:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
def set_axes_equal(ax):
'''Make axes of 3D plot have equal scale so that spheres appear as spheres,
cubes as cubes, etc.. This is one possible solution to Matplotlib's
ax.set_aspect('equal') and ax.axis('equal') not working for 3D.
Input
ax: a matplotlib axis, e.g., as output from plt.gca().
'''
x_limits = ax.get_xlim3d()
y_limits = ax.get_ylim3d()
z_limits = ax.get_zlim3d()
x_range = abs(x_limits[1] - x_limits[0])
x_middle = np.mean(x_limits)
y_range = abs(y_limits[1] - y_limits[0])
y_middle = np.mean(y_limits)
z_range = abs(z_limits[1] - z_limits[0])
z_middle = np.mean(z_limits)
# The plot bounding box is a sphere in the sense of the infinity
# norm, hence I call half the max range the plot radius.
plot_radius = 0.5*max([x_range, y_range, z_range])
ax.set_xlim3d([x_middle - plot_radius, x_middle + plot_radius])
ax.set_ylim3d([y_middle - plot_radius, y_middle + plot_radius])
ax.set_zlim3d([z_middle - plot_radius, z_middle + plot_radius])
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_aspect('equal')
X = np.random.Rand(100)*10+5
Y = np.random.Rand(100)*5+2.5
Z = np.random.Rand(100)*50+25
scat = ax.scatter(X, Y, Z)
set_axes_equal(ax)
plt.show()
Adapté de la réponse de @ karlo pour rendre les choses encore plus propres:
def set_axes_radius(ax, Origin, radius):
ax.set_xlim3d([Origin[0] - radius, Origin[0] + radius])
ax.set_ylim3d([Origin[1] - radius, Origin[1] + radius])
ax.set_zlim3d([Origin[2] - radius, Origin[2] + radius])
def set_axes_equal(ax):
'''Make axes of 3D plot have equal scale so that spheres appear as spheres,
cubes as cubes, etc.. This is one possible solution to Matplotlib's
ax.set_aspect('equal') and ax.axis('equal') not working for 3D.
Input
ax: a matplotlib axis, e.g., as output from plt.gca().
'''
limits = np.array([
ax.get_xlim3d(),
ax.get_ylim3d(),
ax.get_zlim3d(),
])
Origin = np.mean(limits, axis=1)
radius = 0.5 * np.max(np.abs(limits[:, 1] - limits[:, 0]))
set_axes_radius(ax, Origin, radius)
tilisation:
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_aspect('equal') # important!
# ...draw here...
set_axes_equal(ax) # important!
plt.show()
EDIT: le code de user2525140 devrait fonctionner parfaitement bien, bien que cette réponse soit censée tenter de corriger une erreur inexistante. La réponse ci-dessous n'est qu'une mise en œuvre (alternative) en double:
def set_aspect_equal_3d(ax):
"""Fix equal aspect bug for 3D plots."""
xlim = ax.get_xlim3d()
ylim = ax.get_ylim3d()
zlim = ax.get_zlim3d()
from numpy import mean
xmean = mean(xlim)
ymean = mean(ylim)
zmean = mean(zlim)
plot_radius = max([abs(lim - mean_)
for lims, mean_ in ((xlim, xmean),
(ylim, ymean),
(zlim, zmean))
for lim in lims])
ax.set_xlim3d([xmean - plot_radius, xmean + plot_radius])
ax.set_ylim3d([ymean - plot_radius, ymean + plot_radius])
ax.set_zlim3d([zmean - plot_radius, zmean + plot_radius])