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)