当我为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()
给我以下内容:
显然,z轴的单位长度不等于x和y单位。
如何使所有三个轴的单位长度相等?我能找到的所有解决方案都不起作用。谢谢。
答案 0 :(得分:56)
我相信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大一个数量级,但即使使用等轴选项,matplotlib也可以自动调整z轴:
但是如果添加边界框,则可以获得正确的缩放:
答案 1 :(得分:41)
我使用set_x/y/zlim
functions简化了Remy F的解决方案。
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()
答案 2 :(得分:36)
我喜欢上述解决方案,但它们确实有缺点,您需要跟踪所有数据的范围和方法。如果您有多个数据集将一起绘制,这可能很麻烦。为了解决这个问题,我使用了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()
答案 3 :(得分:14)
简单修复!
我设法使它在3.3.1版中工作。
此问题似乎已在PR#17172中得到解决;您可以使用<ReactTable
getTrProps={(_, row) => {
return {
onClick: () => { /* select your row here */ }
};
}}
/>
函数来确保外观正确(请参见set_aspect函数的注释)。当与@karlo和/或@Matee Ulhaq提供的边界框功能结合使用时,这些图现在在3D模式下看起来是正确的!
最小工作示例
ax.set_box_aspect([1,1,1])
答案 4 :(得分:9)
改编自@ karlo的答案,使事情变得更加清洁:
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)
<强>用法:强>
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_aspect('equal') # important!
# ...draw here...
set_axes_equal(ax) # important!
plt.show()
答案 5 :(得分:5)
编辑:用户2525140的代码应该可以正常工作,尽管这个答案应该尝试修复一个不存在的错误。下面的答案只是一个重复(替代)实现:
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])
答案 6 :(得分:1)
从matplotlib 3.3.0开始,Axes3D.set_box_aspect似乎是推荐的方法。
import numpy as np
xs, ys, zs = <your data>
ax = <your axes>
# Option 1: aspect ratio is 1:1:1 in data space
ax.set_box_aspect((np.ptp(xs), np.ptp(ys), np.ptp(zs)))
# Option 2: aspect ratio 1:1:1 in view space
ax.set_box_aspect((1, 1, 1))
答案 7 :(得分:0)
我认为这个功能已经添加到 matplotlib 中,因为这些答案已经发布。如果有人仍在寻找解决方案,我就是这样做的:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=plt.figaspect(1)*2)
ax = plt.gca(projection='3d', proj_type = 'ortho')
X = np.random.rand(100)
Y = np.random.rand(100)
Z = np.random.rand(100)
ax.scatter(X, Y, Z, color='b')
代码的关键位是 figsize=plt.figaspect(1)
,它将图形的纵横比设置为 1 x 1。*2
之后的 figaspect(1)
将图形缩放为 2。您可以将此比例因子设置为您想要的任何值。
注意:这仅适用于具有一个图的图形。