c19_interactive_plotly 答辩交互展示 (Interactive Showcase) 答辩汇报PPT Web展示 IEEE

答辩汇报多系列交互动态图 (Interactive Presentation Chart)

面向毕业答辩、学术汇报与评优展示的交互图表,支持鼠标悬停数据显示、缩放与系列隐藏。

🔬 矢量预览 (Vector Preview) 600 DPI Ready
答辩汇报多系列交互动态图 (Interactive Presentation Chart)

📊 统计学特性 (Statistical Features)

  • 动态轮次损失与精度收敛
  • 多模型消融实验对照
  • 双Y轴多物理量映射
  • 交互悬停卡片Tooltip提示

🛡️ QC 质检要点 (QC Highlights)

  • 支持一键全屏交互演示
  • 高对比度深色/浅色配色
  • 导出高清位图用于答辩PPT
  • 响应式自适应容器宽度
🎨 顶刊色彩提取器 (Palette Extractor)Nature

Crisp sans-serif typography, clean borderless spines, high-contrast palette with soft muted secondary accents.

🛡️ 无障碍评分:AAA (Deuteranopia & Protanopia Compliant)

📋 Data Contract 数据契约规范 Strict Schema

输入数据必须完全符合下列列名与数据类型规范,方可通过自动化数据前检 (Pre-flight Validation)。

📥 下载规范示例 CSV 数据
字段名称 (Column) 数据类型 (Type) 约束性 语义说明与值域约束
epoch continuous 必需 (Required) 周期/轮次/时间刻度
model_a_acc continuous 必需 (Required) 方案/模型 A 准确率指标
model_b_acc continuous 必需 (Required) 方案/模型 B 准确率指标
baseline_acc continuous 必需 (Required) 基准对照指标
组内最小样本量: n ≥ 3
最大允许缺失率: 0%

🐍 独立可复现 Python 绘图源码 Stand-alone Script

完全可复现的 Python 脚本,支持 CLI 参数 `--data`, `--style`, `--output-dir` 与模块化 `render()` 调用。

#!/usr/bin/env python3
"""FigureCraft Chart Engine: c19_interactive_plotly (答辩汇报多系列交互动态图)."""

import os
import sys
import argparse
from pathlib import Path
from typing import List, Dict, Optional
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

try:
    from engines.styles import apply_style, get_palette
except ImportError:
    sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
    from engines.styles import apply_style, get_palette

CHART_ID = "c19_interactive_plotly"


def render(
    data_path: Optional[str] = None,
    style: str = "thesis_cn",
    output_dir: str = "output",
    formats: Optional[List[str]] = None,
    dpi: int = 600,
) -> Dict[str, str]:
    if formats is None:
        formats = ["svg", "pdf", "png", "tiff"]

    apply_style(style)
    palette = get_palette(style)

    if data_path is None:
        data_path = str(Path(__file__).resolve().parents[1] / "data" / f"{CHART_ID}.csv")

    df = pd.read_csv(data_path)
    os.makedirs(output_dir, exist_ok=True)

    fig, ax1 = plt.subplots(figsize=(7.0, 4.0), dpi=dpi)

    epochs = df['epoch']
    l1 = ax1.plot(epochs, df['model_a_acc'], color=palette[0], marker='o', lw=1.6, ms=4, label='改进方案 A (准确率)')
    l2 = ax1.plot(epochs, df['model_b_acc'], color=palette[1], marker='s', lw=1.6, ms=4, label='对比模型 B (准确率)')
    l3 = ax1.plot(epochs, df['baseline_acc'], color='#888888', linestyle='--', lw=1.2, label='基准对照 (Baseline)')

    ax1.set_xlabel('迭代训练轮次 (Epochs)', fontsize=8.5, fontweight='semibold')
    ax1.set_ylabel('模型准确率评估 (%)', fontsize=8.5, fontweight='semibold', color='#111111')
    ax1.set_ylim(40, 100)

    if 'model_a_loss' in df.columns:
        ax2 = ax1.twinx()
        l4 = ax2.plot(epochs, df['model_a_loss'], color=palette[2], linestyle=':', lw=1.2, label='方案 A 损失值 (Loss)')
        ax2.set_ylabel('交叉熵损失 (Loss)', fontsize=8.5, fontweight='semibold', color=palette[2])
        ax2.spines['top'].set_visible(False)
        ax2.spines['left'].set_visible(False)
        lines = l1 + l2 + l3 + l4
    else:
        lines = l1 + l2 + l3

    labels = [l.get_label() for l in lines]
    ax1.legend(lines, labels, loc='center right', frameon=False, fontsize=7.5)
    ax1.set_title('毕业答辩核心模型性能迭代演变与消融实验', pad=10, fontsize=10.0, fontweight='bold')
    plt.tight_layout()

    out_paths = {}
    base_name = f"{CHART_ID}_{style}"
    for fmt in formats:
        p = os.path.join(output_dir, f"{base_name}.{fmt}")
        fig.savefig(p, format=fmt, dpi=dpi, bbox_inches='tight')
        out_paths[fmt] = p

    plt.close(fig)
    return out_paths


def main():
    parser = argparse.ArgumentParser(description="Render Interactive & Static Presentation Plot")
    parser.add_argument("--data", type=str, default=None)
    parser.add_argument("--style", type=str, default="thesis_cn")
    parser.add_argument("--output-dir", type=str, default="output")
    parser.add_argument("--formats", type=str, default="svg,pdf,png,tiff")
    parser.add_argument("--dpi", type=int, default=600)
    args = parser.parse_args()

    fmts = [f.strip() for f in args.formats.split(",")]
    render(args.data, args.style, args.output_dir, fmts, args.dpi)


if __name__ == "__main__":
    main()