Matplotlib中如何为2D子图设置双倍宽度(结合3D投影图)?
调整2D折线图宽度为默认两倍(无需重构代码)
问题描述
已有可运行的Matplotlib代码,通过add_subplot生成3组(accel/angle/avelo)并排的2D折线图与3D投影图,希望将每组中的2D折线图宽度设置为默认值的两倍,询问是否可通过现有add_subplot方式实现,还是必须改用plt.subplots。
原代码如下:
from mpl_toolkits import mplot3d import numpy as np import pandas as pd import matplotlib.pyplot as plt # code here loads data into a dataframe df fig = plt.figure(figsize=(10,8)) fig.suptitle(filename, fontsize=12) i = 0 # 补充原代码缺失的i初始化 for p in ('accel','angle','avelo'): i += 1 ax = fig.add_subplot(3, 2, i) ax.plot(idx,df[p,'x'], label = "x") ax.plot(idx,df[p,'y'], label = "y") ax.plot(idx,df[p,'z'], label = "z") ax.set_ylabel(p) ax.legend(loc="best") i += 1 ax = fig.add_subplot(3,2,i,projection='3d') ax.plot3D(df[p,'x'],df[p,'y'],df[p,'z'],'black') ax.scatter(df[p]['x'][0],df[p]['y'][0],df[p]['z'][0], c='green', marker='o', s=50) ax.scatter(df[p]['x'].iloc[-1],df[p]['y'].iloc[-1],df[p]['z'].iloc[-1], c='red', marker='x', s=50) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_zlabel('z') plt.subplots_adjust(left=0.1, bottom=0.1, right=0.9, top=0.9, wspace=0.4, hspace=0.1) plt.show()
解决方案:用GridSpec实现自定义宽度比例
不需要重构为plt.subplots,通过matplotlib.gridspec.GridSpec可以直接在add_subplot中指定子图的宽度占比,实现2D图宽度为3D图的两倍。具体操作:
- 创建3行3列的GridSpec,设置列宽比例为
[2,2,1](前两列合并给2D图,第三列给3D图,这样每组2D图宽度是3D图的两倍) - 循环中为每个参数分配对应的GridSpec位置:2D图占每行的前两列,3D图占每行的第三列
修改后的代码:
from mpl_toolkits import mplot3d import numpy as np import pandas as pd import matplotlib.pyplot as plt from matplotlib.gridspec import GridSpec # 导入GridSpec # code here loads data into a dataframe df # 示例测试数据(无真实数据时可使用): # idx = np.arange(100) # df = pd.DataFrame({ # ('accel','x'): np.random.randn(100), # ('accel','y'): np.random.randn(100), # ('accel','z'): np.random.randn(100), # ('angle','x'): np.random.randn(100), # ('angle','y'): np.random.randn(100), # ('angle','z'): np.random.randn(100), # ('avelo','x'): np.random.randn(100), # ('avelo','y'): np.random.randn(100), # ('avelo','z'): np.random.randn(100), # }) # filename = "Accelerometer Data" fig = plt.figure(figsize=(12,8)) # 适当加宽画布适配宽子图 fig.suptitle(filename, fontsize=12) # 创建3行3列的GridSpec,列宽比例为2:2:1(前两列合并给2D图,第三列给3D图) gs = GridSpec(3, 3, width_ratios=[2, 2, 1]) for row, p in enumerate(('accel','angle','avelo')): # 2D折线图:占当前行的第0-1列(合并两列) ax_2d = fig.add_subplot(gs[row, :2]) ax_2d.plot(idx, df[p,'x'], label="x") ax_2d.plot(idx, df[p,'y'], label="y") ax_2d.plot(idx, df[p,'z'], label="z") ax_2d.set_ylabel(p) ax_2d.legend(loc="best") # 3D投影图:占当前行的第2列 ax_3d = fig.add_subplot(gs[row, 2], projection='3d') ax_3d.plot3D(df[p,'x'], df[p,'y'], df[p,'z'], 'black') ax_3d.scatter(df[p]['x'][0], df[p]['y'][0], df[p]['z'][0], c='green', marker='o', s=50) ax_3d.scatter(df[p]['x'].iloc[-1], df[p]['y'].iloc[-1], df[p]['z'].iloc[-1], c='red', marker='x', s=50) ax_3d.set_xlabel('x') ax_3d.set_ylabel('y') ax_3d.set_zlabel('z') plt.subplots_adjust(left=0.1, bottom=0.1, right=0.9, top=0.9, wspace=0.3, hspace=0.2) plt.show()
说明
- 这种方式完全保留了原代码的
add_subplot调用逻辑,仅通过GridSpec替换了原有的3x2网格布局,无需重构为plt.subplots width_ratios=[2,2,1]定义了三列的宽度比例,将前两列合并给2D图,其总宽度是第三列(3D图)的两倍,精准满足需求- 适当调整了画布
figsize和子图间距wspace,让整体布局更美观协调
内容的提问来源于stack exchange,提问作者Steve
相关产品推荐
相关产品推荐

