matplotlib (unidad de longitud igual): con una relación de aspecto ‘igual’ el eje z no es igual a x y y-

Cuando configuro la relación de aspecto igual para el gráfico 3d, el eje z no cambia a “igual”. Así que esto:

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 da lo siguiente: introduzca la descripción de la imagen aquí

donde, obviamente, la longitud de la unidad del eje z no es igual a las unidades x e y.

¿Cómo puedo hacer que la unidad de longitud de los tres ejes sea igual? Todas las soluciones que pude encontrar no funcionaron. Gracias.

    Creo que matplotlib aún no establece correctamente el eje igual en 3D … Pero hace tiempo que encontré un truco (no recuerdo dónde) que adapté para usarlo. El concepto es crear un cuadro delimitador cúbico falso alrededor de sus datos. Puedes probarlo con el siguiente código:

     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() 

    Los datos de z son aproximadamente un orden de magnitud mayor que x e y, pero incluso con la opción de eje igual, matplotlib autoscale z axis:

    malo

    Pero si agrega el cuadro delimitador, obtiene una escala correcta:

    introduzca la descripción de la imagen aquí

    Simplifiqué la solución de Remy F usando las set_x/y/zlim .

     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() 

    introduzca la descripción de la imagen aquí

    Me gustan las soluciones anteriores, pero tienen el inconveniente de que necesita realizar un seguimiento de los rangos y los medios de todos sus datos. Esto podría ser engorroso si tiene varios conjuntos de datos que se trazarán juntos. Para solucionar este problema, utilicé los métodos ax.get_ [xyz] lim3d () y coloqué todo en una función independiente que se puede llamar solo una vez antes de llamar a plt.show (). Aquí está la nueva versión:

     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, eg, 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() 

    Adaptado de la respuesta de @karlo para hacer las cosas aún más limpias:

     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, eg, 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) 

    Uso:

     fig = plt.figure() ax = fig.gca(projection='3d') ax.set_aspect('equal') # important! # ...draw here... set_axes_equal(ax) # important! plt.show() 

    EDITAR: el código del usuario 2525140 debería funcionar perfectamente bien, aunque esta respuesta supuestamente intentó corregir un error inexistente. La respuesta a continuación es solo una implementación duplicada (alternativa):

     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])