当前位置:首页>python>Nature 期刊图复现 | Python 实现云雨图(组合分布图)

Nature 期刊图复现 | Python 实现云雨图(组合分布图)

  • 2026-10-11 07:13:55
Nature 期刊图复现 | Python 实现云雨图(组合分布图)

来源论文

论文地址:

https://www.nature.com/articles/s41586-023-05921-z

论文题目:

Flexible solar cells based on foldable silicon wafers with blunted edges

原图释(英):c, Jsc, Voc, FF and PCE of 65-μm and 55-μm SHJ solar cells. The top lines, bottom lines, lines in the box, circles and boxes represent maximum values, minimum values, median values, mean values and 25–75% distributions, respectively. 

原图释(中):分别为 65 微米与 55 微米本征层钝化异质结(SHJ)太阳能电池的短路电流密度Jsc、开路电压 Voc、填充因子(FF)及光电转换效率(PCE)。图中顶线、底线、箱内横线、圆形标记与箱体,依次代表最大值、最小值、中位数、平均值以及25%–75% 数据分布区间。

复现图片

  1. 左侧箱线图: 精炼提供中位数、均值和四分位数等核心统计指标,便于快速定量对比两组工艺的整体水平。

  2. 中间散点图: 呈现每一个真实实验样本和总样本量。

  3. 右侧分布曲线: 运用内核密度估计(KDE)平滑展现数据的几何分布形态,曲线越尖锐高耸,代表工艺的一致性和良率越高。

配色方案

PALETTES = {    1: {"65 µm": "#00bcd4", "55 µm": "#ffab60"},  # 方案1:青蓝与暖橙 (高对比度,Nature/Science常用)    2: {"65 µm": "#2f4b7c", "55 µm": "#ffa600"},  # 方案2:深海蓝与明亮金 (经典互补色)    3: {"65 µm": "#003f5c", "55 µm": "#ff7c43"},  # 方案3:暗夜蓝与珊瑚橙 (强调对比)    4: {"65 µm": "#1f77b4", "55 µm": "#ff7f0e"},  # 方案4:Matplotlib 经典蓝橙 (通用学术色)    5: {"65 µm": "#377eb8", "55 µm": "#e41a1c"},  # 方案5:学界经典红蓝 (医学与生物学期刊常用)    6: {"65 µm": "#415a77", "55 µm": "#e07a5f"},  # 方案6:莫兰迪低饱和度蓝红 (雅致质感)    7: {"65 µm": "#008080", "55 µm": "#cd5c5c"},  # 方案7:深碧绿与印第安红 (地学/环境科学常用)    8: {"65 µm": "#4a90e2", "55 µm": "#f5a623"},  # 方案8:现代扁平化蓝黄 (清晰度极高)    9: {"65 µm": "#0d47a1", "55 µm": "#ffb300"},  # 方案9:皇家蓝与琥珀金 (高贵典雅对比)    10: {"65 µm": "#264653", "55 µm": "#e76f51"},  # 方案10:双色调深青与砖红 (材料科学常用)    11: {"65 µm": "#457b9d", "55 µm": "#e63946"},  # 方案11:经典灰蓝与正红 (亮眼对比)    12: {"65 µm": "#2a9d8f", "55 µm": "#f4a261"},  # 方案12:浅海绿与沙滩橙 (自然学科常用)    13: {"65 µm": "#3d5a80", "55 µm": "#ee6c4d"},  # 方案13:群青蓝与熟卡其 (沉稳中带亮点)    14: {"65 µm": "#1d3557", "55 µm": "#f1faee"},  # 方案14:极深蓝与极浅绿 (高暗度对比)    15: {"65 µm": "#58508d", "55 µm": "#ff6361"},  # 方案15:绛紫色与西瓜红 (多组分对比首选)    16: {"65 µm": "#005f73", "55 µm": "#ca6702"},  # 方案16:深孔雀蓝与复古铜 (Cell级高质感)    17: {"65 µm": "#0077b6", "55 µm": "#00b4d8"},  # 方案17:渐变海洋蓝 (适合递进式分组)    18: {"65 µm": "#606c38", "55 µm": "#dda15e"},  # 方案18:橄榄绿与麦穗黄 (生态与植物学常用)    19: {"65 µm": "#4c5c68", "55 µm": "#1985a1"},  # 方案19:工业高级灰与科技蓝 (半导体与器件)    20: {"65 µm": "#14213d", "55 µm": "#fca311"}  # 方案20:午夜黑蓝与工业黄 (极致对比度)}

完整代码(模拟数据)

import osimport numpy as npimport pandas as pdimport matplotlib.pyplot as pltfrom matplotlib.gridspec import GridSpecfrom scipy.stats import gaussian_kde# ==============================================================================# 1. 20种符合主流期刊标准的专业科研配色方案# ==============================================================================PALETTES = {    1: {"65 µm": "#00bcd4", "55 µm": "#ffab60"},  # 方案1:青蓝与暖橙 (高对比度,Nature/Science常用)}# 依照要求选择方案 1colors = PALETTES[4]# ==============================================================================# 2. 自动读取同目录下的 data.xlsx# ==============================================================================excel_path = 'data.xlsx'if not os.path.exists(excel_path):    raise FileNotFoundError(f"未在当前目录下找到 '{excel_path}' 文件,请确保该 Excel 文件已存在。")print(f"正在从 {excel_path} 读取数据...")try:    df_read = pd.read_excel(excel_path, sheet_name='AllDataLong')except Exception:    print("未找到 'AllDataLong' 工作表,尝试读取默认第一张工作表...")    df_read = pd.read_excel(excel_path)# ==============================================================================# 3. 自动创建 图表 文件夹# ==============================================================================chart_dir = '图表'if not os.path.exists(chart_dir):    os.makedirs(chart_dir)    print(f"已创建 '{chart_dir}' 文件夹。")# ==============================================================================# 4. 基于 Excel 数据生成学术图表# ==============================================================================print("正在基于 Excel 数据生成高还原度学术图表...")plt.rcParams['font.family'] = 'sans-serif'plt.rcParams['font.sans-serif'] = ['Arial', 'Liberation Sans', 'DejaVu Sans']plt.rcParams['axes.unicode_minus'] = Falsefig = plt.figure(figsize=(15, 4.5), dpi=300)gs = GridSpec(1, 4, figure=fig, wspace=0.35)metrics_config = {    'Jsc': {'ylabel': r'$J_{sc}$ ($\mathrm{A\ cm^{-2}}$)', 'yticks': np.arange(37.2, 38.2, 0.1), 'fmt': '%.1f'},    'Voc': {'ylabel': r'$V_{oc}$ (V)', 'yticks': [0.747, 0.750, 0.753, 0.756], 'fmt': '%.3f'},    'FF': {'ylabel': 'FF (%)', 'yticks': np.arange(79, 86, 1), 'fmt': '%d'},    'PCE': {'ylabel': 'PCE (%)', 'yticks': np.arange(22.5, 24.5, 0.5), 'fmt': '%.1f'}}conditions = ['65 µm', '55 µm']for i, (metric, config) in enumerate(metrics_config.items()):    ax = fig.add_subplot(gs[i])    df_metric = df_read[df_read['Metric'] == metric]    for idx, cond in enumerate(conditions):        cond_data = df_metric[df_metric['Condition'] == cond]['Value'].dropna().values        if len(cond_data) == 0:            continue        x_base = idx + 1        # A. 绘制自定义箱线图        box_x = x_base - 0.2        q1, q3 = np.percentile(cond_data, [25, 75])        med = np.median(cond_data)        mean_val = np.mean(cond_data)        low_whisker = np.min(cond_data[cond_data >= q1 - 1.5 * (q3 - q1)])        high_whisker = np.max(cond_data[cond_data <= q3 + 1.5 * (q3 - q1)])        box_width = 0.25        ax.plot([box_x - box_width / 2, box_x + box_width / 2, box_x + box_width / 2, box_x - box_width / 2,                 box_x - box_width / 2],                [q1, q1, q3, q3, q1], color='black', linewidth=1)        ax.plot([box_x - box_width / 2, box_x + box_width / 2], [med, med], color='black', linewidth=1)        ax.plot([box_x, box_x], [q3, high_whisker], color='black', linewidth=1)        ax.plot([box_x, box_x], [q1, low_whisker], color='black', linewidth=1)        ax.plot([box_x - box_width / 4, box_x + box_width / 4], [high_whisker, high_whisker], color='black',                linewidth=1)        ax.plot([box_x - box_width / 4, box_x + box_width / 4], [low_whisker, low_whisker], color='black', linewidth=1)        ax.plot(box_x, mean_val, marker='o', markerfacecolor='white', markeredgecolor='black', markersize=7, zorder=5)        # B. 绘制数据散点         scatter_x_center = x_base + 0.05        np.random.seed(42)        jitter = np.random.uniform(-0.04, 0.04, size=len(cond_data))        ax.scatter(scatter_x_center + jitter, cond_data,                   color=colors[cond], s=35, edgecolor='none', alpha=0.9, zorder=4)        # C. 绘制半边分布曲线 (KDE)        curve_x_start = x_base + 0.18        if len(cond_data) > 1:            kde = gaussian_kde(cond_data)            y_span = np.max(cond_data) - np.min(cond_data)            y_grid = np.linspace(np.min(cond_data) - y_span * 0.1, np.max(cond_data) + y_span * 0.1, 200)            kde_vals = kde(y_grid)            kde_vals_scaled = (kde_vals / np.max(kde_vals)) * 0.18            ax.plot(curve_x_start + kde_vals_scaled, y_grid, color='black', linewidth=1, zorder=3)    # D. 坐标轴与边框精细化配置    ax.set_ylabel(config['ylabel'], fontsize=12, labelpad=8)    ax.set_yticks(config['yticks'])    ax.set_yticklabels([config['fmt'] % y for y in config['yticks']], fontsize=10)    ax.set_xticks([1, 2])    ax.set_xticklabels(conditions, fontsize=11)    ax.set_xlim(0.5, 2.6)    ax.spines['top'].set_visible(False)    ax.spines['right'].set_visible(False)    ax.spines['left'].set_linewidth(1.2)    ax.spines['bottom'].set_linewidth(1.2)    ax.tick_params(direction='out', length=5, width=1.2, labelsize=11)    if i == 0:        ax.text(-0.32, 1.05, 'c', transform=ax.transAxes, fontsize=16, fontweight='bold', va='top')# ==============================================================================# 5. 保存图像# ==============================================================================output_path = os.path.join(chart_dir, 'figure_c.png')fig.subplots_adjust(left=0.08, right=0.98, top=0.92, bottom=0.12)plt.savefig(output_path, bbox_inches='tight')plt.close()print(f"绘图成功,图片已保存至: {output_path}")

数据获取

评论+私信获取

最新文章

随机文章