Matplotlib:3D 数据的正交投影(在 2D 图中)

Matplotlib: orthographic projection of 3D data (in 2D plot)

我正在尝试使用正交投影在 2D 中绘制 3D 数据。以下是我正在寻找的部分内容:

import numpy as np
import matplotlib.pyplot as plt

%matplotlib inline
fig = plt.figure(figsize=(10,10),facecolor='white')
axs = [fig.add_subplot(223)]
axs.append(fig.add_subplot(224))#,sharey=axs[0]))
axs.append(fig.add_subplot(221))#,sharex=axs[0]))
rng = np.random.default_rng(12345)
values = rng.random((100,3))-.5
values[:,1] = 1.6*values[:,1]
values[:,2] = .5*values[:,2]

for ax,axis in zip(axs,['y','x','z']):
    axis1,axis2={'x':(1,2),'y':(0,2),'z':(0,1)}[axis]
    ax.add_patch(plt.Circle([0,0], radius=.2, color='pink',zorder=-20))
    ax.scatter(values[:,axis1],values[:,axis2])
axs[0].set_xlabel('x')
axs[2].set_ylabel('y')
axs[1].set_xlabel('y')
axs[0].set_ylabel('z')
fig.subplots_adjust(.08,.06,.99,.99,0,0)
plt.show()

此图和我尝试的修复存在一些问题:我需要 'equal' 方面,以便圆圈实际上是圆圈。我还需要每个子图中的圆圈大小相同。最后,我希望 space 得到优化(即在子图内部和子图之间尽可能少的白色 space)。

我试过在子图之间共享轴,然后对每个轴执行 .axis('scaled').set_aspect('equal','box',share=True),但轴最终没有正确共享,每个子图中的圆圈结束了不同大小的。虽然它将子图裁剪成数据,但它在子图之间留下了很多 space。 .axis('equal').set_aspect('equal','datalim',share=True) 没有共享轴在子图中留下白色 space,而有共享轴,它遗漏了一些数据。

有什么办法让它起作用吗?如果它能在 matplotlib 3.4.3 上工作就完美了。

您可以为子图使用常见的 xlimylim,并设置与 ax.set_aspect(aspect='equal', adjustable='datalim') 相等的比例: 请参阅下面的完整代码:

import numpy as np
import matplotlib.pyplot as plt

%matplotlib inline
fig = plt.figure(figsize=(10,10),facecolor='white')

axs = [fig.add_subplot(223)]
axs.append(fig.add_subplot(224))#,sharey=axs[0]))
axs.append(fig.add_subplot(221))#,sharex=axs[0]))
rng = np.random.default_rng(12345)
values = rng.random((100,3))-.5
values[:,1] = 1.6*values[:,1]
values[:,2] = .5*values[:,2]


for ax,axis in zip(axs,['y','x','z']):
    axis1,axis2={'x':(1,2),'y':(0,2),'z':(0,1)}[axis]
    ax.add_patch(plt.Circle([0,0], radius=.2, color='pink',zorder=-20))
    ax.scatter(values[:,axis1],values[:,axis2])
    ax.set_xlim([np.amin(values),np.amax(values)])
    ax.set_ylim([np.amin(values),np.amax(values)])
    ax.set_aspect('equal', adjustable='datalim')

axs[0].set_xlabel('x')
axs[2].set_ylabel('y')
axs[1].set_xlabel('y')
axs[0].set_ylabel('z')
fig.subplots_adjust(.08,.06,.99,.99,0,0)
plt.show()

输出给出:

我使用 gridspec 使其工作(我更改了绘图的散点图以直观地确保没有遗漏任何数据)。它需要对 figsize 进行一些调整以真正最小化轴内的白色 space。感谢 @jylls 提供中间解决方案。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

%matplotlib inline
rng = np.random.default_rng(12345)
values = rng.random((100,3))-.5
values[:,1] = 1.6*values[:,1]
values[:,2] = .5*values[:,2]

fig = plt.figure(figsize=(10,8),facecolor='white')
ranges = np.ptp(values,axis=0)
gs = GridSpec(2, 2, None,.08,.06,.99,.99,0,0, width_ratios=[ranges[0], ranges[1]], height_ratios=[ranges[1], ranges[2]])
axs = [fig.add_subplot(gs[2])]
axs.append(fig.add_subplot(gs[3]))#,sharey=axs[0]))
axs.append(fig.add_subplot(gs[0]))#,sharex=axs[0]))

for ax,axis in zip(axs,['y','x','z']):
    axis1,axis2={'x':(1,2),'y':(0,2),'z':(0,1)}[axis]
    ax.add_patch(plt.Circle([0,0], radius=.2, color='pink',zorder=-20))
    ax.plot(values[:,axis1],values[:,axis2])
    ax.set_aspect('equal', adjustable='datalim')

axs[0].set_xlabel('x')
axs[2].set_ylabel('y')
axs[1].set_xlabel('y')
axs[0].set_ylabel('z')
plt.show()