You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用ax.scatter绘制3D散点图时,如何绘制匹配数据范围的12条坐标轴?

问题描述

使用ax.scatter绘制3D散点图时,希望画出立方体的全部12条棱作为坐标轴框架,但通过ax.get_xlim()/ax.get_ylim()/ax.get_zlim()获取的轴范围与散点实际数据范围不匹配,导致绘制的线条和散点范围错位。

附相关代码、数据说明及生成的效果图:
数据为固定宽度格式的文本文件,处理后提取__net对应的数据点进行3D绘制。

import pandas as pd
import matplotlib.pyplot as plt

file_path = 'data.txt'

col_widths = [12, 13, 18, 13, 13, 16, 16, 15, 16, 15]

data = pd.read_fwf(file_path, widths=col_widths, header=0)

columns_to_format = ['Sum', 'Mean', 'Std', 'Min', 'Max']
for col in columns_to_format:
    data[col] = data[col].apply(lambda x: '{:14.8e}'.format(float(x)) if pd.notnull(x) else x)

net_data = data[data['Variable'] == '__net'].copy()

net_data['b'] = net_data['b'].replace({'10^11': 1e11, '10^13': 1e13})

c_floats = net_data['Mean'].astype(float).to_numpy()

fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')

x = net_data['x'].astype(float).to_numpy()
y = net_data['y'].astype(float).to_numpy()
z = net_data['b'].astype(float).to_numpy()

point_size = 50  
img = ax.scatter(x, y, z, c=c_floats, s=point_size, cmap='jet', vmin=c_floats.min(), vmax=c_floats.max() )
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('b')

x_min, x_max = ax.get_xlim()
y_min, y_max = ax.get_ylim()
z_min, z_max = ax.get_zlim()

# 绘制12条立方体棱线
ax.plot([x_min, x_max], [y_min, y_min], [z_min, z_min], color='black')
ax.plot([x_min, x_max], [y_max, y_max], [z_min, z_min], color='black')
ax.plot([x_min, x_max], [y_min, y_min], [z_max, z_max], color='black')
ax.plot([x_min, x_max], [y_max, y_max], [z_max, z_max], color='black')

ax.plot([x_min, x_min], [y_min, y_max], [z_min, z_min], color='black')
ax.plot([x_max, x_max], [y_min, y_max], [z_min, z_min], color='black')
ax.plot([x_min, x_min], [y_min, y_max], [z_max, z_max], color='black')
ax.plot([x_max, x_max], [y_min, y_max], [z_max, z_max], color='black')

ax.plot([x_min, x_min], [y_min, y_min], [z_min, z_max], color='black')
ax.plot([x_max, x_max], [y_min, y_min], [z_min, z_max], color='black')
ax.plot([x_min, x_min], [y_max, y_max], [z_min, z_max], color='black')
ax.plot([x_max, x_max], [y_max, y_max], [z_min, z_max], color='black')

offset = 0.01  
for (i, txt) in enumerate(c_floats):
    ax.text(x[i] + offset, y[i] + offset, z[i] + offset, '{:6.4f}'.format(txt), size=6, zorder=1, color='k')

elev = 45  
azim = 45  
ax.view_init(elev=elev, azim=azim)

cbar = fig.colorbar(img, ax=ax, shrink=0.5)
cbar.set_label('Mean')

plot_save_path = 'plot.png'
fig.savefig(plot_save_path, dpi=300, bbox_inches='tight')

生成的效果图:
生成的3D散点图

解决方法

问题根源是Matplotlib的3D轴默认会给数据范围添加额外边距(padding),ax.get_xlim()获取的是添加边距后的轴范围,而非原始数据的最小/最大值。因此直接从原始数据数组中计算范围即可匹配散点的实际分布。

修改要点

  1. 直接从x、y、z数组中提取最小/最大值,替代ax.get_xlim()等方法
  2. 可选:添加少量边距,让框架线条不紧贴散点,提升视觉效果

修改后的代码

import pandas as pd
import matplotlib.pyplot as plt

file_path = 'data.txt'

col_widths = [12, 13, 18, 13, 13, 16, 16, 15, 16, 15]

data = pd.read_fwf(file_path, widths=col_widths, header=0)

columns_to_format = ['Sum', 'Mean', 'Std', 'Min', 'Max']
for col in columns_to_format:
    data[col] = data[col].apply(lambda x: '{:14.8e}'.format(float(x)) if pd.notnull(x) else x)

net_data = data[data['Variable'] == '__net'].copy()

net_data['b'] = net_data['b'].replace({'10^11': 1e11, '10^13': 1e13})

c_floats = net_data['Mean'].astype(float).to_numpy()

fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')

x = net_data['x'].astype(float).to_numpy()
y = net_data['y'].astype(float).to_numpy()
z = net_data['b'].astype(float).to_numpy()

point_size = 50  
img = ax.scatter(x, y, z, c=c_floats, s=point_size, cmap='jet', vmin=c_floats.min(), vmax=c_floats.max() )
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('b')

# 直接从数据中获取范围,可选添加5%的边距
padding_ratio = 0.05
x_min = x.min() - padding_ratio * (x.max() - x.min())
x_max = x.max() + padding_ratio * (x.max() - x.min())
y_min = y.min() - padding_ratio * (y.max() - y.min())
y_max = y.max() + padding_ratio * (y.max() - y.min())
z_min = z.min() - padding_ratio * (z.max() - z.min())
z_max = z.max() + padding_ratio * (z.max() - z.min())

# 绘制12条立方体棱线
ax.plot([x_min, x_max], [y_min, y_min], [z_min, z_min], color='black')
ax.plot([x_min, x_max], [y_max, y_max], [z_min, z_min], color='black')
ax.plot([x_min, x_max], [y_min, y_min], [z_max, z_max], color='black')
ax.plot([x_min, x_max], [y_max, y_max], [z_max, z_max], color='black')

ax.plot([x_min, x_min], [y_min, y_max], [z_min, z_min], color='black')
ax.plot([x_max, x_max], [y_min, y_max], [z_min, z_min], color='black')
ax.plot([x_min, x_min], [y_min, y_max], [z_max, z_max], color='black')
ax.plot([x_max, x_max], [y_min, y_max], [z_max, z_max], color='black')

ax.plot([x_min, x_min], [y_min, y_min], [z_min, z_max], color='black')
ax.plot([x_max, x_max], [y_min, y_min], [z_min, z_max], color='black')
ax.plot([x_min, x_min], [y_max, y_max], [z_min, z_max], color='black')
ax.plot([x_max, x_max], [y_max, y_max], [z_min, z_max], color='black')

offset = 0.01  
for (i, txt) in enumerate(c_floats):
    ax.text(x[i] + offset, y[i] + offset, z[i] + offset, '{:6.4f}'.format(txt), size=6, zorder=1, color='k')

elev = 45  
azim = 45  
ax.view_init(elev=elev, azim=azim)

# 同步轴范围到计算出的范围(可选,确保轴显示范围和框架一致)
ax.set_xlim(x_min, x_max)
ax.set_ylim(y_min, y_max)
ax.set_zlim(z_min, z_max)

cbar = fig.colorbar(img, ax=ax, shrink=0.5)
cbar.set_label('Mean')

plot_save_path = 'plot.png'
fig.savefig(plot_save_path, dpi=300, bbox_inches='tight')

说明

  • 直接从数据数组获取min/max,确保框架线条完全匹配散点的分布范围
  • 添加的padding_ratio可以根据需要调整,若不需要边距,设为0即可
  • 最后调用ax.set_xlim()等方法同步轴显示范围,确保整个图表的轴范围和绘制的框架一致

内容的提问来源于stack exchange,提问作者len

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 07:29:51