簡體   English   中英

設置 matplotlib 3d plot 縱橫比

[英]set matplotlib 3d plot aspect ratio

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

設置縱橫比適用於 2d 圖:

ax = plt.axes()
ax.plot([0,1],[0,10])
ax.set_aspect('equal','box')

但不適用於 3d:

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

3d 案例是否有不同的語法,或者它沒有實現?

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

我沒有嘗試所有這些答案,但是這個 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)

看起來這個功能已經被添加了,所以我想我會像我一樣為將來通過這個線程的人添加一個答案:

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

figaspect(0.5)使圖形寬是高的兩倍。 然后*1.5增加了圖形的大小。 標簽等不會增加,因此這是一種使圖表看起來不那么被標簽混亂的方法。

如果你知道界限,例如。 +-3 以 (0,0,0) 為中心,您可以像這樣添加不可見點:

import numpy as np
import pylab as pl
from mpl_toolkits.mplot3d import Axes3D
fig = pl.figure()
ax = fig.add_subplot(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')

我認為設置正確的“盒子方面”是一個很好的解決方案:

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

import matplotlib.pyplot as plt

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

ax.plot(dataX,dataY,dataZ)

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

如果您知道邊界,您還可以通過以下方式設置縱橫比:

ax.auto_scale_xyz([minbound, maxbound], [minbound, maxbound], [minbound, maxbound])

另一個有用的(希望是)解決方案,例如,當需要更新已經存在的圖形時:

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

get_w_lims()

set_box_aspect()

我的理解基本上是這還沒有實現(參見GitHub 中的這個錯誤)。 我也希望它盡快實施。 有關可能的解決方案,請參閱此鏈接(我自己沒有測試過)。

Matt Panzer 回答的后續行動。

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

這可能很快就會變得不必要,因為正在進行拉取請求以使ax.set_aspect('equal')工作。

從 matplotlib 3.6.0 開始,此功能通過快捷方式ax.set_aspect('equal')添加。 其他選項是'equalxy''equalxz''equalyz' ,僅將兩個方向設置為相等的縱橫比。 這會更改數據限制,如下例所示。

在即將發布的 3.7.0 中,您將能夠通過命令ax.set_aspect('equal', adjustable='box')更改 plot 盒子縱橫比而不是數據限制。 (感謝@tfpf 在這里實現了另一個答案,)要獲得原始行為,請使用adjustable='datalim'

在此處輸入圖像描述

Matt Panzer 的回答對我有用,但我花了一段時間才弄清楚我遇到的問題。 如果您將多個數據集繪制到同一張圖中,則必須計算整個數據點范圍的峰峰值。

我使用以下代碼來解決我的問題:

x1, y1, z1 = ..., ..., ...
x2, y2, z2 = ..., ..., ...   

ax.set_box_aspect((
    max(np.ptp(x1), np.ptp(x2)), 
    max(np.ptp(y1), np.ptp(y2)), 
    max(np.ptp(z1), np.ptp(y2))
))

ax.plot(x1, y1, z1)
ax.scatter(x2, y2, z2)

請注意,此解決方案並不完美。 如果 x1 包含最大的負數而 x2 包含最大的正數,它將不起作用。 僅當 x1 或 x2 包含最大峰峰值范圍時。

如果您比我更了解 numpy,請隨時編輯此答案,使其適用於更一般的情況。

我嘗試了幾種方法,例如ax.set_box_aspect(aspect = (1,1,1))並不起作用。 我希望一個球體顯示為一個球體——而不是橢球體 我編寫了這個函數並在各種數據上進行了嘗試。 這是一個 hack,它並不完美,但非常接近。

def set_aspect_equal(ax):
    """ 
    Fix the 3D graph to have similar scale on all the axes.
    Call this after you do all the plot3D, but before show
    """
    X = ax.get_xlim3d()
    Y = ax.get_ylim3d()
    Z = ax.get_zlim3d()
    a = [X[1]-X[0],Y[1]-Y[0],Z[1]-Z[0]]
    b = np.amax(a)
    ax.set_xlim3d(X[0]-(b-a[0])/2,X[1]+(b-a[0])/2)
    ax.set_ylim3d(Y[0]-(b-a[1])/2,Y[1]+(b-a[1])/2)
    ax.set_zlim3d(Z[0]-(b-a[2])/2,Z[1]+(b-a[2])/2)
    ax.set_box_aspect(aspect = (1,1,1))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM