本节介绍:一种论文中常见的数据集变量区间分布图。该图以 4 行 2 列的多面板形式,同时展示水灰比、橡胶含量、最大橡胶粒径、水泥含量、细骨料含量、粗骨料含量、硅灰掺量比和减水剂含量等变量的样本数与概率密度。本文根据原图结构和用户提供的 Python 复刻代码,对绘图流程、核心参数和图形解读进行整理说明。
需要说明的是,当前代码主要根据论文原图进行视觉复刻,图中区间、样本数和百分比来自代码中整理好的复刻数据。正式用于自己的论文时,应替换为真实数据统计结果,并结合数据来源、实验设计和建模任务进行解释。
这类图主要用于论文中的数据描述或数据集分析部分。相比只给出一张统计表,它能够把每个输入变量的分布范围、样本集中区间和长尾区间同时展示出来,读者不需要逐行读取表格,就能快速判断数据集是否存在明显偏态、样本稀疏区间或变量取值不均衡。
原图采用 8 个子图分别展示不同变量。每个子图中,左侧 y 轴表示样本数量 Number,蓝色柱形表示该区间内的样本个数;右侧 y 轴表示 Probability density,绿色折线和散点表示该区间样本占总样本的百分比。子图下方的横轴为变量区间,例如水灰比(w/c)、橡胶含量(C_R)、最大橡胶粒径(S_R)、水泥含量(C_C)等。
从图形表达上看,它不是模型解释图,而是建模前的数据集结构说明图。对于材料、土木、环境或工程类机器学习论文,这类图适合放在“Dataset description”“Data preprocessing”或“Descriptive statistics”附近,用于说明样本覆盖范围和变量分布特征。
下面代码使用整理后的区间统计数据进行流程演示,主要用于说明图形结构和绘图逻辑。若要迁移到自己的数据中,可以先用 pd.cut 或自定义分箱规则统计每个变量在不同区间内的样本数,再计算对应百分比。
import matplotlib.pyplot as pltimport numpy as npimport osimport warningswarnings.filterwarnings("ignore")plt.rcParams["font.family"] = ["Times New Roman", "SimHei", "DejaVu Sans"]plt.rcParams["mathtext.fontset"] = "stix"plt.rcParams["axes.unicode_minus"] = Falseplt.rcParams["savefig.bbox"] = "tight"plt.rcParams["savefig.dpi"] = 600
这部分代码主要完成全局绘图设置,包括字体、数学公式字体、负号显示和保存分辨率。复刻论文图时,字体和线宽很容易影响最终观感,因此建议先统一设置全局参数,再进入具体绘图步骤。
CUSTOM_FONT_SIZE = 14CUSTOM_BAR_WIDTH = 0.35CUSTOM_LINE_WIDTH = 3.5CUSTOM_SCATTER_SIZE = 70CUSTOM_BAR_COLOR = "#8FB2D9"CUSTOM_LINE_COLOR = "#A2D289"Y1_MAX = 1200Y2_MAX = 100
这组参数用于控制图形风格。蓝色柱形用于表示样本数量,浅绿色折线用于表示概率密度。左侧 y 轴固定到 1200,右侧 y 轴固定到 100%,这样 8 个子图可以保持统一尺度,便于横向比较。
plots_data = [ { "x": ["[0.3,0.4]", "(0.4,0.5]", "(0.5,0.6]", "(0.6,0.7]"], "y": [327, 555, 179, 148], "xlabel": "Water-to-cement ratio, $\\mathbf{w/c}$ (/)" }, { "x": ["[0,123]", "(123,245]", "(245,368]", "(368,490]"], "y": [1028, 152, 18, 11], "xlabel": "Rubber content, $\\mathbf{C_R}$ (kg/m$^3$)" }]
fig, axes = plt.subplots(4, 2, figsize=(14, 18))plt.subplots_adjust(wspace=0.35, hspace=0.45)for idx, ax1 in enumerate(axes.flatten()): data = plots_data[idx] x_labels = data["x"] y_vals = data["y"] x_idx = np.arange(len(x_labels)) total_count = sum(y_vals) y_pcts = [(v / total_count) * 100for v in y_vals] bars = ax1.bar( x_idx, y_vals, width=CUSTOM_BAR_WIDTH, color=CUSTOM_BAR_COLOR, edgecolor="white", linewidth=1.5, hatch="....", zorder=2 ) ax1.set_ylim(0, Y1_MAX) ax1.set_ylabel("Number", fontsize=CUSTOM_FONT_SIZE, fontweight="bold") ax1.set_xticks(x_idx) ax1.set_xticklabels(x_labels, fontsize=CUSTOM_FONT_SIZE - 2) ax1.set_xlabel(data["xlabel"], fontsize=CUSTOM_FONT_SIZE, fontweight="bold") ax1.yaxis.grid(True, linestyle="--", alpha=0.4) for i, val in enumerate(y_vals): y_pos = val / 2if val > 150else val + 40 ax1.text(i, y_pos, str(val), ha="center", va="center", fontsize=CUSTOM_FONT_SIZE - 2, color="black") ax2 = ax1.twinx() ax2.plot(x_idx, y_pcts, color=CUSTOM_LINE_COLOR, linewidth=CUSTOM_LINE_WIDTH, zorder=3, alpha=0.9) ax2.scatter(x_idx, y_pcts, color=CUSTOM_LINE_COLOR, s=CUSTOM_SCATTER_SIZE, edgecolor="white", zorder=4) ax2.set_ylim(0, Y2_MAX) ax2.set_ylabel("Probability density", fontsize=CUSTOM_FONT_SIZE, fontweight="bold") ax2.set_yticklabels([f"{int(y)}%"for y in np.arange(0, Y2_MAX + 1, 20)]) for i, pct in enumerate(y_pcts): y_offset = pct + 5if pct < 85else pct - 8 ax2.text(i, y_offset, f"{pct:.2f}%", ha="center", va="bottom"if pct < 85else"top", fontsize=CUSTOM_FONT_SIZE - 2, color="black")
这段代码是整张图的核心。plt.subplots(4, 2) 先创建 8 个子图,随后对每个变量进行循环绘制。每个子图中先绘制柱形图,用于表示不同区间的样本数;再通过 twinx() 创建右侧 y 轴,叠加概率密度折线。
需要注意的是,柱形图和折线图使用的是同一组横轴区间,但对应不同的 y 轴含义。柱形高度是样本数量,折线位置是百分比。如果自己的数据中不同变量的总样本量不一致,百分比计算应分别基于每个变量的有效样本数。
save_dir = os.path.join(os.getcwd(), "figure")os.makedirs(save_dir, exist_ok=True)save_path = os.path.join(save_dir, "橡胶混凝土配合比变量区间分布多面板图.png")# plt.savefig(save_path, dpi=600, format="png")plt.show()
第一,这张图展示的是变量分布,不是变量重要性,也不是因果分析。它只能说明样本在不同取值区间内的数量和占比,不能直接说明某个变量对抗压强度或其他性能指标的影响方向。
第二,分箱方式会影响图形结论。若区间划分过粗,细节可能被遮盖;若划分过细,图形会显得零散。正式论文中应结合变量物理意义、实验设计范围和样本量选择合适的分箱规则。
第三,双 y 轴图需要避免误读。左侧 y 轴是数量,右侧 y 轴是百分比,二者虽然共享横轴,但不应直接比较数值大小。绘图时建议保留清晰的轴标题和百分比标注。
第四,如果将该图用于自己的项目,建议先检查缺失值、异常值和单位是否统一。尤其是材料配合比变量,不同论文或数据来源可能存在 kg/m3、百分比、质量比等不同表达方式。
👇关注公众号【嗡嗡的Python日常】
✅ 支持代码定制|承接各类定制化科研绘图|质量放心
📌 关于源码:本文核心代码为原创定制,默认不免费公开。
✅ 获取源码方式:
1、直接添加号主微信,付费购买完整源码 + 数据
2、全部完整合集158元,后续将会持续更新,决定购买请联系作者,仅分享代码文件及模拟数据,不提供答疑服务,购买后不退不换!!!
👉请直接添加号主微信联系☕️:Wjtaiztt0406