如何缩放 3D 和 2D 子图以使相应的轴具有相同的长度?

问题描述 投票:0回答:2

我正在尝试缩放具有两个子图的图形(见下图),以便 3D 图的 z 轴与 2D 图的 y 轴大小相同。我该怎么做?

我已经尝试过gridspec和aspect,但这不起作用......

python matplotlib 3d 2d scale
2个回答
2
投票

确定 3D 轴的精确位置和长度并通过 2D 图形复制它是相当困难的(尽管并非不可能 - 我会投票支持任何完全做到这一点的解决方案)。但如果你做不到——那就假装吧。在这里,我们使用两个 3D 图并将其中一个转换为看起来像 2D 图:

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

def plot_3d_pseudo2d(arr):
    #one figure, two 3D subplots
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 10), subplot_kw={'projection': "3d"})

    #ax1 - normal isometric 3D projection
    ax1.view_init(elev=10, azim=-45) 
    ax1.set_xlabel("X")
    ax1.set_ylabel("Y")
    ax1.set_zlabel("Z")
    ax1.plot(arr[:, 0], arr[:, 1], arr[:, 2], marker="o")
    
    #pseudo-2D projection
    ax2.view_init(elev=0, azim=-90.1)
    #shrink y-axis projection length
    ax2.get_proj = lambda: np.dot(Axes3D.get_proj(ax2), np.diag([1, 0.01, 1, 1]))
    ax2.set_xlabel("X")
    ax2.set_zlabel("Z")
    #plot x-z pairs for y=0
    ax2.plot(arr[:, 0], np.zeros(arr[:, 1].size), arr[:, 2], marker="o")
    #remove y-ticks
    ax2.set_yticks([])
    #increase label distance 
    ax2.xaxis.labelpad = 10
    ax2.zaxis.labelpad = 10
        
    plt.subplots_adjust(wspace=0.1)     
    plt.show()
    

import numpy as np
plot_3d_pseudo2d(np.asarray([[1,   4,   5], 
                             [12,  23,  89], 
                             [123, 234, 789]]).T)

输出示例:

它并不完美 - 3D 投影在其周围创建了大量空白(以便有旋转空间),并且透视变形隐藏了 x 轴刻度并使 z 轴标签稍微偏离。

免责声明:在这个答案的帮助下,y轴投影被缩小了。


0
投票

抱歉拖了一个旧线程,但我试图解决同样的问题,最终使用 gridspec 让它工作(并且无法通过 T 先生的方法得到我喜欢的东西)并想分享。它不会自动执行解决方案,并且需要手动进行一些调整,但如果您的格式一致,则只需执行一次。我已将其设置为使用我发现对我有用的默认设置,里程可能会有所不同,因为它们使用我的默认格式设置。

from matplotlib import pyplot as plt
import numpy as np

def plot_3d_and_2d_in_same_figure(scale_to_match_3dz_with_2dy=True, 
twod_bottom_bound=None, twod_top_bound=None, x_offset=7):
    '''
    scale_to_match_3dz_with_2dy: bool. If True, scales the 2D subplot height to match the z-axis of the 3D
    subplot with the y-axis of the 2D subplot. If False, scales the 2D subplot height to align the titles of both 
    subplots and the lowest visible parts of the subplots.
    twod_bottom_bound: int/None. Integer between 0 and 99, should be less than the twod_top_bound value. 
    twod_top_bound: int/None. Integer between 0 and 99, should be greater than the twod_bottom_bound value. 
    x_offset: int, default 7. Integer between 0 and 49, how far to move the 2D subplot to the right to avoid axes overlapping.
    '''
    fig = plt.figure(figsize=(14,6))
    gs = fig.add_gridspec(100,100)
    if scale_to_match_3dz_with_2dy and twod_bottom_bound is None:
        twod_bottom_bound = 30
    elif twod_bottom_bound is None:
        twod_bottom_bound = 18

    if scale_to_match_3dz_with_2dy and twod_top_bound is None:
        twod_top_bound = 76
    elif twod_top_bound is None:
        twod_top_bound = 87
    x_offset = 7
    ax1 = fig.add_subplot(gs[:,:50], projection='3d')
    n_points = 50
    rand = np.random.default_rng(45)
    x = rand.normal(loc=0, scale=1, size=n_points)
    y = rand.exponential(scale=4, size=n_points)
    z = np.linspace(0,5,n_points)
    ax1.plot(x,y,z, alpha=0.5)
    ax1.set_title('3d subplot',y=0.95)
    ax1.set_xlabel('x label 1',labelpad=15)
    ax1.set_ylabel('y label 1',labelpad=15)
    ax1.set_zlabel('z label 1',labelpad=15)
    ax1.view_init(elev=13, azim=-142, )

    ax2 = fig.add_subplot(gs[100-twod_top_bound:100-twod_bottom_bound, 50+x_offset:])
    # ^ use 100-value so setting the values of twod_top_bound and twod_bottom_bound are more intuitive
    ax2.scatter(x,y)
    ax2.set(
        title='2d subplot',
        xlabel='x label 2',
        ylabel='y label 2'
    )

我有一个布尔值可以在两个格式选项之间进行更改,因为对于我的问题,我希望子图元素的整体高度匹配:

plot_3d_and_2d_in_same_figure(scale_to_match_3dz_with_2dy=True)
https://i.imgur.com/Foij7Vx.png
plot_3d_and_2d_in_same_figure(scale_to_match_3dz_with_2dy=False)
https://i.imgur.com/qcL4cYG.png

抱歉,我还没有发布图片的声誉。

© www.soinside.com 2019 - 2024. All rights reserved.