当前位置:首页>python>用Python做科研级画图——雨云图

用Python做科研级画图——雨云图

  • 2026-10-11 08:40:48
用Python做科研级画图——雨云图

大家好,我是你们的小帅学长。

总结一下,前面我们做的图形的作用有:

直方图 → 看分布区间

KDE → 看平滑形态

箱线图 → 看统计结构

小提琴图 → 看分布形状

每一种图都有“缺点”:

KDE 看不到具体数据点

箱线图看不到形状

小提琴图看不到真实样本分布

所以问题来了,有没有一种图,可以把这些信息一次性整合?

答案就是这篇的主角:雨云图(Raincloud Plot)。

01.什么是雨云图?

雨云图可以理解为:KDE 密度(云)+ 箱线图(结构)+ 散点图(雨)

三层信息叠加在一起:

组件
表达内容
云(Density)
分布形状
箱(Box)
统计结构
雨(Points)
原始数据

所以它的本质是:最完整的分布可视化表达。

02.为什么雨云图越来越流行?

因为它解决了一个核心问题:不要让读者/审稿人“猜数据”。

传统图的问题是:

KDE:你不知道样本在哪

箱线图:你不知道分布形状

小提琴图:你看不到真实点

而雨云图:全部展示。

03.雨云图适合什么场景?

1.小样本 + 想展示真实数据

例如:实验数据、医学数据、行为研究

读者/审稿人会关心:每一个点长什么样。

2.多组分布对比

例如:模型误差分布

多区域数据

多实验条件

雨云图可以同时表达:分布形状、离散程度、个体差异。

3.需要“说服力”的图

在论文或做PPT汇报中,你不仅要展示结果,还要让人信服。

雨云图比单一图更有说服力。

04.雨云图的核心结构

1.上层:密度(云):表示数据整体分布。

2.中层:箱线图表示:中位数、四分位、离散程度。

3.下层:散点(雨)表示:每个真实数据点。

05.论文级雨云图示例代码

雨云图让“统计结果”和“原始数据”同时出现。下面是一段可以直接使用的示例代码:

import osimport numpy as npimport matplotlib as mplimport matplotlib.pyplot as pltfrom matplotlib import font_manager as fmfrom scipy.stats import gaussian_kdewin_fonts = r"C:\Windows\Fonts"for p in [          os.path.join(win_fonts, "times.ttf"),          os.path.join(win_fonts, "timesbd.ttf"),          os.path.join(win_fonts, "timesi.ttf"),          os.path.join(win_fonts, "simsun.ttc"),]:          if os.path.exists(p):                    try:                            fm.fontManager.addfont(p)                    except Exception:                            passmpl.rcParams["font.family"] = ["Times New Roman", "SimSun"]mpl.rcParams["axes.unicode_minus"] = FalseOUT_DIR = r"D:\py_figs"os.makedirs(OUT_DIR, exist_ok=True)# =========================# 示例数据# =========================np.random.seed(42)data_A = np.random.normal(0.50, 0.08, 80)data_B = np.random.normal(0.60, 0.10, 80)data_C = np.random.normal(0.65, 0.07, 80)data_list = [data_A, data_B, data_C]labels = ["Model A / 模型A", "Model B / 模型B", "Model C / 模型C"]colors = ["#4C78A8", "#F58518", "#54A24B"]fig, ax = plt.subplots(figsize=(8, 4.8))positions = np.array([1, 2, 3], dtype=float)# =========================# 1) 画“云”——半边 KDE# =========================for pos, data, color in zip(positions, data_list, colors):         # KDE         kde = gaussian_kde(data)         y_grid  = np.linspace(data.min() - 0.05, data.max() + 0.05, 300)         density = kde(y_grid)         density = density / density.max() * 0.32         ax.fill_betweenx(                   y_grid,                   pos - density,   # 左边界                   pos,             # 右边界贴着类别中心                   facecolor=color,                   alpha=0.35,                   linewidth=0    ) ax.plot(pos - density, y_grid, color=color, linewidth=1.8)# =========================# 2) 画箱线图# =========================box = ax.boxplot(         data_list,         positions=positions,         widths=0.16,         vert=True,         patch_artist=True,         showfliers=True,         medianprops=dict(color="black", linewidth=1.5),         whiskerprops=dict(color="black", linewidth=1.2),         capprops=dict(color="black", linewidth=1.2),         boxprops=dict(facecolor="white",      edgecolor="black", linewidth=1.2),         flierprops=dict(marker='o', markerfacecolor='white', markeredgecolor='black',                                             markersize=5, linestyle='none'))# =========================# 3) 画“雨”——抖动散点(放在类别右侧)# =========================rng = np.random.default_rng(123)for pos, data, color in zip(positions, data_list, colors):         jitter = rng.uniform(0.02, 0.14, size=len(data))         x_scatter = np.full(len(data), pos) + jitter          ax.scatter(                  x_scatter,                  data,                  s=26,                  color=color,                  alpha=0.9    )ax.set_xticks(positions)ax.set_xticklabels(labels, fontsize=12)ax.set_ylabel("Accuracy / 准确率", fontsize=13)ax.set_title("Raincloud Plot / 雨云图", fontsize=16)# ax.grid(False)ax.set_xlim(0.5, 3.55)for spine in ax.spines.values():          spine.set_linewidth(1.2)ax.tick_params(axis='both', labelsize=11, width=1.2, length=5)out_path = os.path.join(OUT_DIR, "raincloud_plot_fixed.jpg")fig.savefig(          out_path,          dpi=300,         bbox_inches="tight",         pad_inches=0.05)plt.close(fig)print("Saved:", out_path)

06.什么时候不建议用雨云图?

数据太多(>1000)→ 散点会过密

只是简单对比均值→ 用柱状图或点图更简单

读者不熟悉复杂图形→ 会增加理解成本

雨云图不是为了“复杂”,而是为了“完整”——让数据的形状、统计和个体同时被看见。

前面我们讲了这么多图,其实有一个问题一直存在:遇到异常值,你是直接删掉,还是先展示?很多人习惯“清洗数据”,但忽略了一件重要的事情:异常值本身也是信息。

下一篇我们讲:《异常值可视化》。我会告诉你:异常值该怎么画?怎么区分“错误数据”和“极端数据”?如何在论文中合理表达异常值?

——期待你的关注——

往期内容:

用Python做科研级画图——箱线图(解释统计含义)

用Python做科研级画图——KDE密度

用Python做科研级画图——小提琴图

用Python做科研级画图——哑铃图

用Python做科研级画图——分组对比

最新文章

随机文章