Подтвердить что ты не робот

Matplotlib (равная единичная длина): с "равным" отношением размеров z-ось не равна x- и y-

Когда я устанавливаю равное соотношение сторон для 3d-графика, ось z не изменяется на "равно". Итак, это:

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

дает мне следующее: enter image description here

где, очевидно, единичная длина оси z не равна x- и y-единицам.

Как я могу сделать единицу длины всех трех осей равными? Все решения, которые я мог найти, не сработали. Спасибо.

4b9b3361

Ответ 1

Я считаю, что matplotlib еще не установил правильную равную ось в 3D... Но я нашел трюк несколько раз назад (я не помню, где), что я адаптировал его. Концепция заключается в создании фальшивой кубической ограничивающей рамки вокруг ваших данных. Вы можете протестировать его с помощью следующего кода:

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()
Данные

z примерно на порядок больше, чем x и y, но даже с опцией равной оси, автомасштабирование оси z matplotlib:

bad

Но если вы добавите ограничивающий прямоугольник, вы получите правильное масштабирование:

enter image description here

Ответ 2

Я упростил решение Remy F с помощью 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()

enter image description here

Ответ 3

Мне нравятся вышеупомянутые решения, но у них есть недостаток, который вам нужно отслеживать диапазоны и средства по всем вашим данным. Это может быть громоздким, если у вас есть несколько наборов данных, которые будут отображаться вместе. Чтобы исправить это, я использовал методы ax.get_ [xyz] lim3d() и поместил все это в отдельную функцию, которую можно вызвать только один раз, прежде чем вы вызовете plt.show(). Вот новая версия:

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

Ответ 4

EDIT: user2525140 код должен работать отлично, хотя этот ответ предположительно попытался исправить несуществующую ошибку. Ответ ниже - это всего лишь дубликат (альтернативная) реализация:

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