c17_paired_slope 实验前后对比 (Thesis Special)
国标学位论文 Lancet Cell
实验前后测配对样本斜率连线图 (Before-After Paired Slope)
理学、医学与教育实验前后测对比图,展示每个独立样本的干预演变斜率与均值变化显著性。
🔬 矢量预览 (Vector Preview)
600 DPI Ready
格式支持: SVG, PDF, PNG (600 DPI), TIFF
📊 统计学特性 (Statistical Features)
- ✓ 配对样本 t 检验 (Paired t-test)
- ✓ 显著性水平标记 (*p<0.05)
- ✓ 个体演变轨迹线
- ✓ 组均值与标准误 (Mean ± SEM)
🛡️ QC 质检要点 (QC Highlights)
- ✓ 前后测横坐标清晰居中
- ✓ 显著性横括号高度自适应
- ✓ 个体连线透明度适中
- ✓ 附带均值误差棒显眼标注
🎨 顶刊色彩提取器 (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)。
| 字段名称 (Column) | 数据类型 (Type) | 约束性 | 语义说明与值域约束 |
|---|---|---|---|
| subject_id | categorical | 必需 (Required) | 被试/样本唯一编号 |
| group | categorical | 必需 (Required) | 实验分组(如对照组/干预组) |
| pre_score | continuous | 必需 (Required) | 前测/干预前测量值 |
| post_score | continuous | 必需 (Required) | 后测/干预后测量值 |
组内最小样本量: n ≥ 4
最大允许缺失率: 0%
🐍 独立可复现 Python 绘图源码 Stand-alone Script
完全可复现的 Python 脚本,支持 CLI 参数 `--data`, `--style`, `--output-dir` 与模块化 `render()` 调用。
#!/usr/bin/env python3
"""FigureCraft Chart Engine: c17_paired_slope (实验前后测配对样本斜率连线图)."""
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
from scipy import stats
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 = "c17_paired_slope"
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)
groups = df['group'].unique()
n_groups = len(groups)
fig, axes = plt.subplots(1, n_groups, figsize=(3.8 * n_groups, 4.0), sharey=True, dpi=dpi)
if n_groups == 1:
axes = [axes]
for idx, (grp, ax) in enumerate(zip(groups, axes)):
sub = df[df['group'] == grp]
col = palette[idx % len(palette)]
for _, row in sub.iterrows():
ax.plot([0, 1], [row['pre_score'], row['post_score']], color=col, alpha=0.35, linewidth=0.9)
ax.scatter([0, 1], [row['pre_score'], row['post_score']], color=col, alpha=0.5, s=18)
pre_m, pre_sem = sub['pre_score'].mean(), stats.sem(sub['pre_score'])
post_m, post_sem = sub['post_score'].mean(), stats.sem(sub['post_score'])
ax.errorbar([0, 1], [pre_m, post_m], yerr=[pre_sem, post_sem], color='#111111', lw=2.0,
capsize=3.5, capthick=1.2, zorder=5, label='组均值 ± SEM')
ax.scatter([0, 1], [pre_m, post_m], color='#111111', s=35, zorder=6)
t_stat, p_val = stats.ttest_rel(sub['pre_score'], sub['post_score'])
p_text = f"p < 0.001 (***)" if p_val < 0.001 else (f"p = {p_val:.3f} (**)" if p_val < 0.01 else f"p = {p_val:.3f}")
y_max = max(sub['pre_score'].max(), sub['post_score'].max()) + 4
ax.plot([0, 0, 1, 1], [y_max, y_max + 1.5, y_max + 1.5, y_max], color='#333333', lw=0.8)
ax.text(0.5, y_max + 2.2, p_text, ha='center', va='bottom', fontsize=7.5, fontweight='bold')
ax.set_xticks([0, 1])
ax.set_xticklabels(['前测 (Pre)', '后测 (Post)'], fontsize=8.5, fontweight='semibold')
ax.set_title(f"{grp} (N={len(sub)})", fontsize=9.0, fontweight='bold', pad=8)
if idx == 0:
ax.set_ylabel('测量指标评分 (Score)', fontsize=8.5, fontweight='semibold')
ax.legend(loc='lower left', frameon=False, fontsize=7.5)
fig.suptitle('实验干预前后配对样本变化轨迹与统计显著性', y=1.02, fontsize=10.5, 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 Before-After Paired Slope Chart")
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()