设置matplotlib 3d绘图长宽比?

hat*_*rix 36 python matplotlib

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
Run Code Online (Sandbox Code Playgroud)

设置宽高比适用于2d图:

ax = plt.axes()
ax.plot([0,1],[0,10])
ax.set_aspect('equal','box')
Run Code Online (Sandbox Code Playgroud)

但不适用于3D:

ax = plt.axes(projection='3d')
ax.plot([0,1],[0,1],[0,10])
ax.set_aspect('equal','box')
Run Code Online (Sandbox Code Playgroud)

是否有不同的3d案例语法,或者它没有实现?

小智 28

从 matplotlib 3.3.0 开始,Axes3D.set_box_aspect似乎是推荐的方法。

import numpy as np
import matplotlib.pyplot as plt

xs, ys, zs = ...
ax = plt.axes(projection='3d')

ax.set_box_aspect((np.ptp(xs), np.ptp(ys), np.ptp(zs)))  # aspect ratio is 1:1:1 in data space

ax.plot(xs, ys, zs)
Run Code Online (Sandbox Code Playgroud)

  • 这是唯一对我有用的解决方案。其他人都没有这样做。对于我的程序,我将其修改为自动选择参数。`limits = np.array([getattr(self.ax, f'get_{axis}lim')() for axis in 'xyz']); ax.set_box_aspect(np.ptp(限制,轴 = 1))` (5认同)
  • @p8me 添加了!但是,请参阅 https://github.com/matplotlib/matplotlib/pull/23409:该功能可能很快就会受到支持。 (3认同)

Ben*_*Ben 22

我没有尝试所有这些答案,但这个kludge为我做了:

def axisEqual3D(ax):
    extents = np.array([getattr(ax, 'get_{}lim'.format(dim))() for dim in 'xyz'])
    sz = extents[:,1] - extents[:,0]
    centers = np.mean(extents, axis=1)
    maxsize = max(abs(sz))
    r = maxsize/2
    for ctr, dim in zip(centers, 'xyz'):
        getattr(ax, 'set_{}lim'.format(dim))(ctr - r, ctr + r)
Run Code Online (Sandbox Code Playgroud)


Dan*_*Dan 18

看起来这个功能已被添加,所以我想我会为将来通过这个帖子的人添加一个答案,就像我做的那样:

fig = plt.figure(figsize=plt.figaspect(0.5)*1.5) #Adjusts the aspect ratio and enlarges the figure (text does not enlarge)
ax = fig.gca(projection='3d')
Run Code Online (Sandbox Code Playgroud)

figaspect(0.5)这个数字是它的两倍宽.然后*1.5增加图的大小.标签等不会增加,因此这是一种使图形看起来不那么混乱的方法.

  • 这不会设置实际绘图的纵横比。只是附上的图。 (3认同)

Jen*_*man 11

如果你知道边界,例如.+ -3以(0,0,0)为中心,你可以像这样添加不可见的点:

import numpy as np
import pylab as pl
from mpl_toolkits.mplot3d import Axes3D
fig = pl.figure()
ax = fig.gca(projection='3d')
ax.set_aspect('equal')
MAX = 3
for direction in (-1, 1):
    for point in np.diag(direction * MAX * np.array([1,1,1])):
        ax.plot([point[0]], [point[1]], [point[2]], 'w')
Run Code Online (Sandbox Code Playgroud)


Cra*_*min 8

如果你知道边界,你也可以这样设置宽高比:

ax.auto_scale_xyz([minbound, maxbound], [minbound, maxbound], [minbound, maxbound])
Run Code Online (Sandbox Code Playgroud)

  • 或者,自动执行:`scaling = np.array([getattr(ax,'get _ {} lim'.format(dim))()用于'xyz'中的dim]); ax.auto_scale_xyz(*[[np.min(scaling),np.max(scaling)]]*3)` (6认同)

小智 8

我认为设置正确的“盒子方面”是一个很好的解决方案:

ax.set_box_aspect(aspect = (1,1,1))

import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_box_aspect(aspect = (1,1,1))

ax.plot(dataX,dataY,dataZ)
Run Code Online (Sandbox Code Playgroud)

https://matplotlib.org/stable/api/_as_gen/mpl_toolkits.mplot3d.axes3d.Axes3D.html?highlight=3d%20set_box_aspect#mpl_toolkits.mplot3d.axes3d.Axes3D.set_box_aspect

  • 这个答案是错误的。`set_box_aspect` 只是改变*显示*中 x、y、z 轴的长度。它不会改变轴的比例。OP 询问如何将所有三个轴设置为在数据空间中具有相同的比例,就像“set_aspect('equal')”在二维图中的工作方式一样。 (5认同)

Sco*_*ott 7

从 matplotlib 3.6.0 开始,此功能已添加ax.set_aspect. 使用:

ax.set_aspect('equal')
Run Code Online (Sandbox Code Playgroud)

其他选项包括'equalxy''equalxz''equalyz',仅将两个方向设置为相等的长宽比。这会更改数据限制,如下例。

在此输入图像描述


在即将推出的 3.7.0 中,您将能够通过以下方式更改绘图框的纵横比而不是数据限制:

ax.set_aspect('equal', adjustable='box')
Run Code Online (Sandbox Code Playgroud)

(感谢@tfpf 在另一个答案中实现了这一点!)要获得原始行为,请使用adjustable='datalim'.


tfp*_*fpf 6

马特·潘泽 (Matt Panzer) 的回答的后续行动。(这最初是对上述答案的评论。)

limits = np.array([getattr(ax, f'get_{axis}lim')() for axis in 'xyz'])
ax.set_box_aspect(np.ptp(limits, axis=1))
Run Code Online (Sandbox Code Playgroud)

现在这个拉取请求已经合并,当 Matplotlib 的下一个版本发布时,您应该能够使用ax.set_aspect('equal'). 当这种情况发生时,我会尽力记住并更新这个答案。

更新:Matplotlib 3.6 已发布;ax.set_aspect('equal')现在将按预期工作。


小智 5

例如,当需要更新现有的图形时,另一个有用的(希望如此)解决方案:

world_limits = ax.get_w_lims()
ax.set_box_aspect((world_limits[1]-world_limits[0],world_limits[3]-world_limits[2],world_limits[5]-world_limits[4]))
Run Code Online (Sandbox Code Playgroud)

获取_w_lims()

设置框方面()


Sco*_*t B 4

我的理解基本上是,这还没有实现(请参阅GitHub 中的此错误)。我也希望它能够尽快实施。请参阅此链接以获取可能的解决方案(我自己尚未测试过)。

  • 该链接已损坏,但可以通过 [Wayback Machine](https://web.archive.org/web/20141011215154/http://comments.gmane.org/gmane.comp.python.matplotlib.general/ 27415)。但是,如果您在答案中包含相关代码,而不是要求将来的人搜索邮件列表存档,那就更好了。 (9认同)