Prism Style Plot
概述
把 GraphPad Prism 的作图心智模型翻译成 Python(matplotlib + seaborn + scipy):先看数据是什么表,再决定画什么图和做什么统计,最终输出符合期刊规范的投稿级图表。目标是让不会写代码的科研人员,说出"我要一张 Prism 那种图",就能得到一张可以直接进论文的图。
你的工作方式
GraphPad 的精髓不是某个图形,而是四件事,这四件事贯穿本技能所有流程:
- 数据表驱动,而不是图形驱动。Prism 的第一步永远是选择数据表格式——表的类型决定了你能画什么图、能做什么统计。拿到数据后先识别它属于 8 种表里的哪一种,而不是先问用户"想要什么图"。
- 统计与绘图联动。图上的误差棒和显著性星号不是装饰,而是统计结果的视觉呈现。统计方法由数据结构决定(组数、因素数、是否配对),画图前必须先完成统计。
- 展示原始数据点。现代期刊(Nature 系列、Cell 系列)普遍要求图上叠加个体数据点,纯空心柱状图已不被推荐。本技能所有统计图默认叠加散点。
- 期刊规范优先。L 形坐标轴(去掉上边和右边框)、色盲友好配色、统一无衬线字体、明确标注误差线类型——这些是审稿人看得见的"专业感"。
环境准备(Phase 0 之前必做,防止反复重装依赖)
每次任务第一步:先运行 scripts/ensure_env.py 得到受管理 venv 解释器路径,之后所有 python xxx.py 都用这个解释器,不要再回到基础解释器。
PY=$(python scripts/ensure_env.py) # 打印 venv 解释器绝对路径
$PY your_plot.py # 之后一律用 $PY 运行绘图脚本
# 要跑 pytest / verify 套件时,加 --dev(额外装 pytest):
PY=$(python scripts/ensure_env.py --dev)
ensure_env.py 已做四件关键事(分享给他人才不会重蹈覆辙):
- 只用 venv 自己的解释器判定依赖是否齐全,装过就秒退(约 3 秒、不重新下载),并打印路径。
- venv 路径不硬编码用户名:从
sys.executable动态推导受管理结构<root>/binaries/python/versions/<ver>/python[.exe]→ venv 落在<root>/binaries/python/envs/default,跨机器/跨平台(WindowsScripts、POSIXbin)通用;推导不到时再依次尝试环境变量WORKBUDDY_HOME/CODEBUDDY_HOME,最后才兜底用户级目录。 - 当前解释器若已能 import 全部依赖则直接复用(含"已身处正确 venv"的情况),绝不重复建 venv;且绝不自我嵌套——若当前解释器本身就是 venv 解释器(父目录名
bin/Scripts),直接返回它自己。 - 畸形 venv 自愈:若目标目录存在却缺解释器(历史 bug 留下的双重嵌套产物),自动
rmtree重建;若 venv 内无 pip,先ensurepip引导,且吞掉 ensurepip/pip 的 stdout——因为本脚本 stdout 专门承载"解释器路径",被污染会让PY=$(...)拿到多行垃圾。
依赖清单分两档:
- REQUIRED(运行期必需,默认装):matplotlib / scipy / numpy / pandas<3.0 / seaborn / statsmodels / lifelines / openpyxl(读 .xlsx 与 read_excel_sheets 的硬依赖,缺了直接 ModuleNotFoundError)。
- REQUIRED_DEV(开发验证用,--dev 才装):pytest。
> ⚠️ 致命坑(务必转达给使用者):切勿用基础解释器(如 binaries/python/versions/<ver>/python.exe)去 import matplotlib 做"装没装"的判定——它看不到 venv 里的包,会误报 ModuleNotFoundError,从而每次任务都触发重装。依赖是否存在,一律以 venv 解释器(ensure_env.py 的输出)为准。
> ⚠️ 跑测试前必须先 --dev:pytest 不随 REQUIRED 安装。直接 $PY -m pytest tests/ 会报 No module named pytest;先跑 PY=$(python scripts/ensure_env.py --dev) 即可。
Phase 0:识别数据表类型
这是最关键的一步,决定了后续所有动作。读取 references/table-mapping.md,按其中的判别规则把用户数据归类到 8 种表之一:
- XY 表:每个点由 X 和 Y 两个值定义(如剂量-响应、标准曲线)
- Column 表:一个分组变量(Control vs 处理组)
- Grouped 表:两个分组变量(如 时间×药物)
- Contingency 表:计数/频数数据(如 存活 vs 死亡的例数)
- Survival 表:生存时间 + 事件/删失标记
- Parts of whole 表:各部分占整体的比例(饼图)
- Multiple variables 表:每行一个观测、每列一个变量的宽表
- Nested 表:层级嵌套设计(如 方法→教室→学生)
数据来源可能是 CSV/Excel 文件、粘贴的文本表格,或对话中描述的结构。不确定时,把判别结果和依据展示给用户确认,再继续——表类型认错,后面全错。
默认约定:读表自动设 y 轴标签 — 当数据来自 CSV/Excel 文件、且用户未显式指定 y 轴标签时,自动用数值列表头作为 ylabel 默认值:
- read_table(path) 读取文件后会返回 ylabel(long 格式的 value 列名,已做基础清洗:去换行、合并多余空格;wide 格式无单一数值列名时 ylabel=None)。
- 拿到 ylabel 后直接 ax.set_ylabel(res["ylabel"]) 即可,无需用户再给。
- 用户仍可随时覆盖此默认(显式给标签时优先用用户的)。
- 宽格式(每列一组)因没有"数值列名",ylabel 回退 None → 此时沿用该图型模板默认(如 Column→"Response (a.u.)")或请用户指定。
- 列名常非投稿级(缩写/缺单位),自动 ylabel 只是合理默认,最终投稿标签建议你确认/润色(如补 (fold change))。
- 注意:仅"来自文件"时自动套用;对话里粘贴的数值表不触发此默认(按既有规则处理)。
读表健壮性(坏输入不再直接崩) — read_table / read_excel_sheets 已内建以下容错,不要再手写临时补丁去预处理用户文件:
- 编码自动回退:
utf-8-sig → utf-8 → gbk → gb18030 → big5 → latin-1。Windows 版 Excel「另存为 CSV」默认 GBK,中文表头不再抛UnicodeDecodeError。 - 分隔符自动嗅探:逗号 → 制表符 → 分号 → 竖线。仪器/统计软件导出的 Tab 分隔
.txt可直接读,无需先转 CSV。 - 数值清洗:去首尾空格;千分位
"1,234"→1234(仅"数字,三位数字"形态,不误伤1,5这类欧式小数);ND/—/缺失/NA/null等占位符 → NaN 自动剔除。 - 友好报错(替代裸 traceback):文件不存在、空文件、CSV 改名成
.xlsx、列名/sheet 名写错 → 均抛中文可行动错误,并在提示里列出可用列 / 可用 sheet 名。 - 绝不静默产出错误数据(关键):分组列必须满足"去重取值数 ∈ [2, max(2, 行数//2)]"才算分组变量——否则像「备注」「编号」这类常量/自由文本列会被误判为分组列,静默丢掉真正的实验组(真实 bug:含备注列的宽表被误判为长表,Model 组整列消失且无任何报错)。
- 被忽略的列写进
notes:read_table返回值新增"notes": list[str](自动识别的分隔符、被忽略的列名等)。拿到notes后必须原样转述给用户——被忽略的列可能意味着数据没被完整使用,属学术严谨性范畴,严禁静默吞掉。典型写法:
python res = read_table(path) for n in res.get("notes", []): print("提示:", n) # 或拼进给用户的回复里
Phase 1:选择图形类型
按表类型查 references/table-mapping.md 的映射表,得到该表支持的图型列表。默认选择推荐的"Prism 默认图型",同时给用户 2-3 个备选让其确认:
| 表类型 | 默认图型 | 常见备选 |
|---|---|---|
| Column | 带散点的柱状图(Mean±SEM) | 箱线图+散点、小提琴图+散点、带散点的线图 |
| Grouped | 分组柱状图+散点 | 分组箱线图、意大利面条图(重复测量时) |
| XY | 散点+连线 | 仅散点、折线图、拟合曲线 |
| Survival | Kaplan-Meier 曲线 | — |
| Contingency | 分组柱状图(计数) | 堆叠柱状图 |
| Parts of whole | 饼图/环形图 | 堆叠柱状图 |
| Nested | 嵌套柱状图 | — |
| Multiple variables | 按用户意图选图 | 散点矩阵、箱线图分面 |
涉及"统计显著性展示"的图(Column/Grouped)默认展示个体散点 + 误差棒 + 星号;纯展示性的图(Parts of whole、Contingency)不标注显著性。
映射表里的三个"备选图型"均有实现:堆叠柱 / 百分比堆积 → prism_stacked_bars;水平柱状图(组名长、标签挤)→ prism_horizontal_bars;配对前后连线图 → prism_paired_lines。用法见 Phase 3 速查表与 references/recipes/。
两处常规科研绘图缺口已补齐:① 时间序列折线 + 误差带 → prism_series(个体轨迹用 prism_spaghetti,组均值折线 + mean±SEM 阴影带用 prism_series);② 森林图 / 效应量汇总 → prism_forest(把 cox_regression() 的多组 HR 一次画成点 + 水平 95% CI + 无效线)。另有:prism_survival 的 cumulative=(累积发生率 = 1−KM,同一数据的另一视角)、prism_pairplot 上三角默认镜像散点(upper="mirror")。详见 references/recipes/series.md。
原始记录类图型(钙成像 / 电生理):① 多通道平铺轨迹 → prism_traces(很多条波形逐条错位竖排、互不遮挡,适合 ΔF/F 多细胞轨迹、多通道/多 sweep 原始记录;offset 自动 = 最大峰-谷幅度 × 1.25,y 刻度默认隐藏,可加 scale_bar);② 事件栅格 + PSTH → prism_raster(spike raster / 钙瞬变事件,每个 trial 一行画短竖线,psth=True 自动在上 raster 下 PSTH 的两行画布出图)。与 prism_series/prism_spaghetti 的分工:series 画组均值+误差带(聚合视角)、spaghetti 画个体轨迹叠加误差棒(异质性视角)、traces 画逐条平铺的原始波形(形态视角)。详见 references/recipes/traces.md。
时间序列画布自动宽度:固定 wide(7×5 cm)在时间点多时显局促,故提供 series_figsize()——高度恒定 5 cm、宽度按时间点数自动分档(≤3→4 cm、4-6→7 cm、7-9→10 cm、≥10→13 cm 封顶;平均标签长度 >4 字符再 +2 cm)。prism_series 与 prism_spaghetti 均支持 ax=None 自建画布并自动套用该阶梯,返回值含 "ax" 键;显式优先级为 figsize > shape > 自动阶梯,传 ax 时完全不干预画布。见 Phase 1 画布规则与速查表。
Phase 2:统计分析
读取 references/statistics-guide.md,按决策树选择统计方法:
- 2 组比较 → t 检验(先判定配对/非配对;非正态或小样本考虑 Mann-Whitney / Wilcoxon 配对检验)
- ≥3 组、单因素 → One-way ANOVA + 事后多重比较(Tukey 默认,方差不齐时用 Welch ANOVA + Games-Howell)
- 两个因素 → Two-way ANOVA(含交互作用项,报告主效应与交互效应)
- 生存数据 → log-rank 检验(Kaplan-Meier 曲线;≥3 组自动含整体 χ² 检验 + 两两比较,逐对结果在
prism_survival返回值res["pairwise"]中——未做多重比较校正,正文/图注须注明) - 计数列联 → 卡方检验 / Fisher 精确检验
硬性规则:多组比较严禁两两 t 检验(成倍放大假阳性),必须 ANOVA + 事后检验一次完成。计算每组 n、均值、误差(SD 或 SEM,默认 SEM 用于柱状图、SD 用于箱线图外的分布展示),生成显著性标注(混合规则,见 Phase 3 标注细节):
- p < 0.05(显著):星号 *(p<0.05)、**(p<0.01)、***(p<0.001)、****(p<0.0001)
- 0.05 ≤ p < 0.1(边缘显著):直接标注精确 p 值(如 p=0.073、p=0.098)
- p ≥ 0.1:不标注(默认不标 ns;include_ns=True 可恢复 "ns" 文本)
统计函数返回值类型(调用时最容易踩的坑):
- ttest_two_groups(a, b)(2 组)返回 tuple (statistic, p_value, 方法名),如 stat, p, method = ttest_two_groups(a, b);paired=True 时同样返回 tuple
- mann_whitney_u(a, b)(非参数独立样本)返回 tuple (stat, p, "Mann-Whitney U")
- wilcoxon_signed_rank(a, b)(非参数配对)返回 tuple (stat, p, "Wilcoxon signed-rank")
- oneway_anova_tukey(groups, labels, var_equal="auto", posthoc="auto")(≥3 组)返回 dict {"method", "anova_p", "pairwise": [(label_i, label_j, p), ...], "f_stat", "df1", "df2", "welch": bool, "posthoc": str, "var_test"}。var_equal(方差不齐处理,对齐 GraphPad Prism 8+):
- "auto"(默认):先做 Levene(center='median',即 Brown-Forsythe,Prism 推荐)检验,p<0.05 自动切 Welch,否则回落标准 ANOVA
- "assumed":假设方差齐 → 标准 One-way ANOVA + Tukey HSD(保留以复现旧结果,或用户明确要按方差齐处理)
- "welch":不假设方差齐 → Welch ANOVA(Welch-Satterthwaite 校正 df),事后由 posthoc 决定
- 为什么把默认从 "assumed" 改成 "auto":Tukey HSD 的前提是方差齐性,方差不齐时它对 I 类错误的控制失效。实测三组、一组 SD=4 其余 SD=0.3 的设计下,两个分支的结论分歧率达 57.5%、p 值最大相差 19 亿倍——旧默认会把"有差异"静默报成 p=0.27 的"无差异"。默认改 auto 后这类数据自动走 Welch,无需用户记得手动指定。需要锁定旧行为的既有脚本请显式传 var_equal="assumed"。
- posthoc(仅 Welch 分支生效;assumed 分支永远是 Tukey):"auto"(默认,按 Prism 推荐:max(组内 n) > 50 → Games-Howell,≤50 → Dunnett T3)、"games_howell"(q 统计量 + 学生化极差分布)、"dunnett_t3"(Welch t + 未取整 df + 单步 Sidak 校正,等价 SMM 分布,Dunnett 1980)、"welch_t"(不校正,每对直接 Welch 校正 t 检验 = Prism "Don't correct for multiple comparisons")
- ⚠️ 小样本(如 n=3)下 Levene 检验力极低、几乎检不出方差不齐——auto 模式在 n 小时通常不切换;若实验设计或先验知识提示方差不齐(如处理组方差系统性更大),应显式用 var_equal="welch",不要依赖 auto 自动判定
- build_stats_report(..., var_equal=..., anova_posthoc=...) 同样支持该参数;方差不齐但未切 Welch 时报告会明确标注"下方为假设方差齐性的标准 ANOVA 结果",不再自相矛盾;Welch 分支的校正表(Dunnett T3 / Games-Howell)额外附一列 p (Welch t, uncorr)(未做多重比较校正的参考值,审稿人对照友好;anova_posthoc="welch_t" 本身即未校正,不重复加列)
- build_stats_report("ttest") 若检验本身切到非参数(Shapiro p<0.05 或 min(n)<30 → Mann-Whitney U / Wilcoxon 符号秩),报告与图注的"方法选择依据"会点名触发原因(哪一组 / 差值偏离正态);主检验已是非参数时不再重复输出一遍同值的补充检验;仅当主检验为参数但前提提示偏态(如 n≥30 未触发切换)才追加非参数作稳健性对照 + rank-biserial r 效应量,不替换主检验结论
- 方法选择依据:本技能的检验方法是自适应的(Shapiro 判正态、Levene/Brown-Forsythe 判方差齐),自动切换虽更稳健,但读者/审稿人无法从图注看出"为什么用了 Mann-Whitney 而不是 t 检验"——审稿人常见的质问就是 "Why was a non-parametric test used? Please justify."。因此 build_stats_report 的每一个 _report_* 现在都会:① 在报告正文输出 ### 方法选择依据 段落(点明前提检验结论 + 触发切换的具体原因);② 把同一句话写入 Figure Legend。format_legend(..., method_rationale="...") 是对外入口(新增可选参数,不传时输出与旧版逐字节一致)。配对 t 检验的正态性检验对象是配对差值 d = a − b(不是两组各自正态),报告与切换判定同一口径
- 一组返回值可直接喂给 add_pairwise_brackets(ax, res["pairwise"], labels=...)(后者接收 (label_i, label_j, p) 元组列表)
统计扩充(完整方案表见 references/statistics-guide.md):
- oneway_anova_tukey(groups, labels, posthoc=..., control=...) —— 方差齐分支也支持 7 种事后方案:tukey(默认,全两两)/ dunnett / sidak / bonferroni / holm / holm_sidak / none。Dunnett 是最常用的"处理组 vs 对照组"方案(比较次数 k−1 而非 k(k−1)/2,比 Tukey 敏感),对照组用 control="Ctrl"(标签名或下标,默认第 1 组)指定;posthoc="auto" 一旦同时给了 control= 就自动切 Dunnett,不会静默忽略。返回值新增效应量 eta_sq(η²)与 omega_sq(ω²);twoway_posthoc 新增 partial_eta_sq(偏 η²)
- 离群值识别(Prism "Identify outliers"):detect_outliers_grubbs(values, alpha=0.05)(迭代 ESD)、detect_outliers_robust_fdr(values, q=0.01)(ROUT 等价:稳健中位数 + MAD + BH-FDR,q 即 Prism 的 Q 值)。只报告不自动删除——剔不剔由实验者按专业判断决定,并在方法中声明
- RM one-way ANOVA(同一批受试者的单因素重复测量):rm_oneway_anova(df, subject, within, value)(长格式;GG 球形性校正 + Sidak 事后)。仅"一个组内因素、无组间因素"时用它;含组间因素(如 group × time)仍走 build_stats_report("twoway_rm", ...)
- 报告层传参:build_stats_report(..., anova_control="Ctrl", anova_posthoc="dunnett");error_type 同时接受代码("sem")与文本("mean ± SD")
若用户提供了自己的统计结果(如已发表的 p 值表),直接使用并跳过本阶段计算,但要在图注中注明统计方法来源。
Phase 3:Prism 风格美化
导入 scripts/prism_theme.py,使用其中封装的主题和辅助函数:
from prism_theme import (apply_prism_theme, recommend_figsize, prism_boxplot,
prism_violin, add_significance_brackets,
add_pairwise_brackets)
第一步:确定图形态(GraphPad 的 tall / square / wide 预设)
用 recommend_figsize(table_type, n_groups, shape) 确定画布,三级优先级(预设以 cm 为单位;函数内部已换算成 matplotlib 的 inch,调用方无需关心):
- 用户显式关键词最高优先——说"高/高瘦/tall"、"方/square"、"宽/wide"(中英文别名均可识别)→ 直接用该形态,覆盖一切默认建议;
- 数据表类型默认建议(用户没提形态时自动生效,不必询问)——
recommend_shape(table_type, n_groups):
- Column / Grouped:按总分组数分档——<5 组 → tall、5–8 组 → square、>8 组 → wide;
- XY → square、Survival → square、Contingency → wide;
- 补充默认:Parts of whole → square、Multiple variables → wide、Nested → tall;
- 时间序列 / 轨迹类(timeseries/series/timecourse/trajectory/repeatedmeasures)→ wide(横向更适合多时间点轨迹)。但wide本身只有 7×5 cm,时间点一多仍显局促,故prism_series/prism_spaghetti在ax=None时默认改走series_figsize()自动宽度阶梯(见下),不再用固定 wide; - 兜底 tall:表类型未识别、或 Column/Grouped 缺分组数时回退 tall(与原
get_figsize默认一致)。
| 形态 | 画布尺寸(cm,宽×高) | 适合场景 |
|---|---|---|
| tall | 3 × 5 | 单面板期刊图、柱状图/箱线图等统计图,高瘦构图让数据主体突出 |
| square | 5 × 5 | 单图均衡展示、散点相关图 |
| wide | 7 × 5 | 多组对比、需要并排的多面板图;时间序列请改走 series_figsize() 自动宽度(见下) |
series_figsize(n_x=None, x_labels=None, long_label_cm=2.0) —— 时间序列专用自动宽度
高度恒定 5 cm,宽度按时间点数分档伸缩;x_labels 平均字符数 >4 时再加宽 long_label_cm(默认 2 cm)以免长标签被裁:
| 时间点数 n | 画布(cm,宽×高) |
|---|---|
| ≤3 | 4 × 5 |
| 4–6 | 7 × 5 |
| 7–9 | 10 × 5 |
| ≥10 | 13 × 5(封顶) |
from prism_theme import prism_series, prism_spaghetti, series_figsize
# 推荐写法:不传 ax,函数自动按时间点数选画布并返回 ax
res = prism_series(df=df, x_col="time", group_col="group", value_col="y")
ax = res["ax"]
# 懒人查询:先看会画多大
series_figsize(n_x=5) # (7.0/2.54, 5.0/2.54) 英寸
series_figsize(x_labels=["0h","6h","12h","24h","48h"]) # 同上,按标签数推断
# 显式覆盖仍然最高优先:figsize > shape > 自动阶梯
prism_series(df=df, ..., figsize=(9, 5)) # 直接指定(inch)
prism_series(df=df, ..., shape="wide") # 强制 7×5 cm
from prism_theme import recommend_figsize
fig, ax = plt.subplots(figsize=recommend_figsize("column", n_groups=3)) # 3 组 → tall
fig, ax = plt.subplots(figsize=recommend_figsize("grouped", n_groups=6)) # 6 组 → square
fig, ax = plt.subplots(figsize=recommend_figsize("xy")) # XY → square
fig, ax = plt.subplots(figsize=recommend_figsize("contingency")) # → wide
fig, ax = plt.subplots(figsize=recommend_figsize("column", n_groups=3,
shape="wide")) # 关键词覆盖
统一执行以下美化(这是 Prism 用户手册和期刊规范的交集):
- L 形坐标轴:隐藏上边框和右边框,只保留左、下
- 样本点布局(镜像均衡抖动,投稿强制规则):默认
layout="jitter"+balanced=True,左右各取n//2点镜像分布,初值采样就避开中线带保证左右对称不偏侧,n 奇数时 1 点随机偏侧(左右差 ≤1)。去规律化:min_sep 微调改用随机方向 + 随机步长(rng.choice([-1,1]) × rng.uniform(0.6,1.1)·d_x),删除等距排开兜底,物理放不下时接受轻微重叠 + 透明度补偿(_apply_overlap_alpha按组内最大重叠密度压低填充透明度至 ≥0.30,无重叠时原样返回)。布局硬约束:max_j ≤ width/2(点绝不溢出 box/柱外);画点前必先设 xlim/ylim(prism_bars/boxplot/violin 内部已预设;手写多组循环务必set_xlim(-0.5, n-0.5)+set_ylim(...)置于画点前,否则点径换算d_x/d_y漂移导致前几组挤在一起)。点密集或审稿敏感可切layout="beeswarm"多列展开。实现级细节(mid_b/rng.uniform 公式、_apply_overlap_alpha判据与公式、jitter_ratio取值、接口签名):见references/scatter-layout.md。已覆盖所有叠加散点的图型:prism_bars/prism_boxplot/prism_violin/add_individual_dots - 散点大小与透明度(同图所有样本点同大,期刊规范):
common_point_size(总数, 组数)自适应——公式pt = clip(5·√(4/avg_n)·(3/组数)^0.25, 2.0, 5.0)pt(窗口 2.0–5.0;组数指数 0.25 < 样本量指数 0.5,样本量主导)。关键档位:
| avg_n | 组数 | pt | 场景 |
|---|---|---|---|
| 4 | 3 | 5.0 | 小样本满点(上限) |
| 8 | 3 | 3.54 | 敏感段(明显缩小) |
| 16 | 3 | 2.5 | 常规中等样本 |
| ≥32 | — | 2.0 | 大样本(下限) |
窗口为何是 [2.0, 5.0]:点更克制,多组图不易糊成一片;上限 5.0pt 与柱体宽度(默认 0.62)比例协调,下限 2.0pt 在 300 dpi 下直径约 8 px、缩到单栏图(~8.5 cm)仍可辨,并非噪点。本口径与 neuroresearch 随包副本一致(同一批数据在技能内与 neuroresearch 内出图点径相同);若某期刊要求散点更醒目,显式传 max_size=6.0 放宽上限即可,无需改默认。
透明度:填充与描边统一;auto_dot_alpha 按 n 自适应(n 大→越透明防重叠掩盖),含 _apply_overlap_alpha 同步压低。
- 配色:色盲安全色板(来源:图片中收录的标准 Colorblind-safe 方案)——默认 Okabe-Ito(8 色,分类图首选);印刷场景可切 Paul Tol Bright(7 色);多组/复杂图可选 Paul Tol Muted(9 色);高对比屏幕图可选 IBM Design Library(5 色);单组时优先灰度系。用 get_palette("okabe_ito") / get_palette("paul_tol_bright") / get_palette("paul_tol_muted") / get_palette("ibm_design") 获取,palette_sequence() 取分组配色序列。用户指定配色方案时按需切换。接口提示:get_palette(name) 返回 dict(颜色名→hex,如 {"blue": "#0072B2", ...}),不能直接 [:n] 切片;要按期刊常用顺序取颜色序列,用 palette_sequence(name)(返回 list,如 palette_sequence("okabe_ito")[:2] 取前两组色)。给 prism_bars/prism_boxplot/prism_survival/prism_xy_fit 的 palette= 参数传色板名字符串或颜色列表均可
- 字体:Arial/Helvetica 类无衬线,坐标轴标题 8pt,刻度数字 6pt(整体 −4)
- 误差线:柱状图默认 Mean±SEM(error bar 单独绘制且 zorder=6 > 散点 5,防散点遮盖);箱线图展示中位数/四分位距,须注明。"误差线永远最顶层"全局规则(覆盖所有图型):柱状图误差棒 zorder=6 > 散点 5;箱线图 median/whisker/cap zorder=4 > 散点 3 > 箱体填充 2;箱体边框也重绘到 zorder=4(seaborn boxprops 同时控制填充与边框无法单独提层,方案是填充留 2 + 边框独立线框重绘到 4)——任何图型的误差范围线/中心线/箱体边框都不被样本点覆盖
- 误差线类型(5 种 Prism 口径):error_type= 支持 "sem"(mean ± SEM,默认)、"sd"(mean ± SD)、"ci95"(95% CI)、"range"(min–max)、"iqr"(四分位距)。error_bars(values, error_type) 单独取误差量(对称类返回标量;range/iqr 返回 [下须, 上须] 不对称数组),error_type_label(error_type) 取规范图注文字(如 mean with 95% CI)——误差线口径与图注必须一致。prism_bars / prism_grouped_bars / prism_horizontal_bars / prism_paired_lines 均支持
- 对数坐标轴:prism_bars / prism_grouped_bars 传 logy=True、prism_horizontal_bars 传 logx=True 即按 log 轴绘制。set_nice_ylim / finish_axes 遇 log 轴自动跳过线性"取整收尾",否则会把 log 轴顶端改坏
- 零基准线:跨零柱状图自动补 y=0 灰色虚线(zero_baseline=True,默认)。仅在 0 严格落在轴内时画;全正(下限=0)与全负(上限=0)不画——0 即轴边界、脊柱已标示,再画属冗余
- 柱内样本量:show_n=True 标注各组 n(柱状图走刻度标签换行;分组柱/水平柱标在柱内)
- CLD 字母标注:add_cld_letters(ax, comparisons=res["pairwise"], labels=labels)——按"差异不显著"关系求极大团,给每组分配共享字母(a / ab / b),多组两两结果的最紧凑标注法;也接受 p_value= 传完整 p 值方阵
- 断轴:add_axis_break(ax1, ax2=None, which="y") 在两段轴交界画断口斜线
- 长标签折行:wrap_xticklabels(ax, width=12);rotate= 可显式指定旋转角(默认按重叠自动判断 45°)
- 显著性标注(混合规则):星号/精确 p 值 + 连接线(bracket)标注组间比较。规则:显著 p<0.05 → 星号;边缘显著 [0.05,0.1) → 精确 p 值(p=0.073);p≥0.1 → 不标。bracket 自动分层防重叠(层高 LH 0.055),标注底部分类型偏移(星号 -0.03 span 骑线、p 值 0 在横线),ylim 顶部收尾 ≤ ~1.3×数据最大值(_nice_ylim_top max_ticks=9 防刻度拥挤)。ylim<1 值域:刻度按量级分级(≥1 保持 0.5 倍数;<1 用 0.05/0.005/10^k 网格),0.1/0.2/0.25 等细刻度可用、不再被粗化成 0.5 顶高轴顶;bracket 数据最大值收集优先 get_bbox()(Rectangle 单位矩形坑,勿用 get_path().get_extents() 对柱子取值)
- 输出:PNG(300 dpi)+ PDF(矢量)双格式,bbox_inches='tight'
图型模板速查(按表型列出,完整模板在 references/recipes/)
画图前先读对应模板文件(含完整代码、参数细节、统计写法);下表为速查:
| 表型 → 图型 | 主函数(关键参数) | 统计 | 模板 |
|---|---|---|---|
| Column → 带散点柱状图 | prism_bars(ax, groups, labels)(error_type= 5 种:sem/sd/ci95/range/iqr) |
≥3 组 oneway_anova_tukey;2 组 ttest_two_groups |
recipes/bar.md |
| Column/Grouped → 箱线图+散点 | prism_boxplot(ax, groups, labels);小提琴 prism_violin(...) |
同上 | recipes/box.md |
| Column → 水平柱状图(组名长/标签挤时) | prism_horizontal_bars(ax, groups, labels)(logx= / show_n=) |
同柱状图 | recipes/bar.md |
| Grouped → 分组柱状图 | prism_grouped_bars(ax, df, x_col, group_col, value_col) |
twoway_posthoc(交互显著→简单效应;否则主效应) |
recipes/grouped.md |
| Grouped(重复测量)→ 意大利面条图 | prism_spaghetti(ax=None, df, x_col, group_col, value_col, subject_col)(个体轨迹+均值±SEM;ax=None 自建画布,宽度按时间点数自动伸缩、高 5cm) |
build_stats_report("twoway_rm", df=..., rm_subject=..., rm_within=..., rm_between=..., value=...)(混合设计 RM two-way,GG 球形性校正+Subjects(matching)+简单效应 Sidak) |
recipes/repeated_measures.md |
| 配对前后(Before/After)→ 配对连线图 | prism_paired_lines(ax, before, after, group=None)(个体连线 + 均值±SEM) |
配对 t / Wilcoxon 符号秩 | recipes/repeated_measures.md |
| Grouped/XY(长表)→ 时间序列折线 + 误差带 | prism_series(ax=None, df, x_col, group_col, value_col, error_type="sem")(fill_between 阴影带 + 组均值折线 + 可选 show_points/show_n;ax=None 自建画布 → series_figsize() 按时间点数自动选宽,返回 res["ax"]) |
逐时间点组间比较:各点 ttest_two_groups / mann_whitney_u |
recipes/series.md |
| Survival → Kaplan-Meier / 累积发生率 | prism_survival(ax, time, event, group, cumulative=False)(cumulative=:True → y = 1−KM,ylabel 自动切 "Cumulative incidence",中位生存线自动不画) |
log-rank + Cox HR(可选) | recipes/survival.md |
| 效应量汇总 → 森林图 | prism_forest(ax, cox=cr) 或 prism_forest(ax, labels=, estimates=, ci_low=, ci_high=)(点 + 水平 95% CI + 无效线,CI 不跨无效线即显著) |
直接消费 cox_regression() 的 comparisons |
recipes/series.md §森林图 |
| 钙成像/电生理 → 多通道平铺轨迹 | prism_traces(ax=None, series=..., x=..., groups=..., scale_bar="auto")(逐条纵向错位平铺、互不遮挡;offset 自动 = 最大峰-谷 ×1.25、y 刻度默认隐藏、比例尺可选;ax=None 自建画布并返回 res["ax"]) |
原始记录图通常不做统计(要聚合成组趋势改走 prism_series/prism_spaghetti) |
recipes/traces.md |
| 电生理/事件 → 栅格图(+ PSTH) | prism_raster(ax=None, spike_times=..., trial_ids=..., t_range=..., bin_size=..., psth=True)(每 trial 一行短竖线;psth=True 必须 ax=None,自动出上 raster 下 PSTH 两行画布) |
PSTH 计数闭合(counts.sum() = 范围内事件数);对发放率做统计需另调相应检验 |
recipes/traces.md |
| XY → 剂量-响应/标准曲线 | prism_xy_fit(ax, x, y, model="4pl")(linear/4pl/3pl/5pl/one_phase_decay/one_phase_assoc/exponential/michaelis_menten/power/deming) |
拟合 R²、IC50 | recipes/xy_fit.md |
| Contingency → 分组计数柱状图 | prism_bars(计数)+ χ² 检验 |
Fisher/χ² | recipes/contingency.md |
| Nested → 嵌套柱状图 | prism_nested_bars(ax, df, outer, inner, value) |
nested_anova(单元为分析单元防假重复) |
recipes/nested.md |
| Parts of whole → 饼图/环形图 | prism_pie / prism_donut |
— | recipes/parts_of_whole.md |
| Contingency / Parts of whole → 堆叠柱(含百分比堆积) | prism_stacked_bars(ax, df, x_col, group_col, value_col, percent=True)(可 show_totals=True) |
计数表用 χ²/Fisher | recipes/parts_of_whole.md |
| Multiple variables → 散点矩阵/分面箱线 | prism_pairplot(df, upper="mirror") / prism_facet_boxplot(...)(upper="mirror" 上三角镜像散点、"off" 保留旧空白) |
按需 | recipes/multiple_variables.md |
| 时间序列宽度查询(不画图) | series_figsize(n_x=5) / series_figsize(x_labels=[...])(高恒 5cm,宽按时间点数分档 ≤3→4cm、4-6→7cm、7-9→10cm、≥10→13cm,长标签再 +2cm) |
— | recipes/series.md |
通用骨架(所有图型一致,差异只在主函数与统计调用):
from prism_theme import (apply_prism_theme, recommend_figsize, prism_bars,
oneway_anova_tukey, add_pairwise_brackets,
build_stats_report, write_report, save_figure)
apply_prism_theme()
fig, ax = plt.subplots(figsize=recommend_figsize("column", n_groups=len(groups)))
prism_bars(ax, groups, labels) # 主函数按图型替换
res = oneway_anova_tukey(groups, labels) # 统计按图型替换
add_pairwise_brackets(ax, res["pairwise"], labels=labels)
save_figure(fig, name, out_dir)
write_report(name, build_stats_report("anova_tukey", description=...,
groups=groups, labels=labels), out_dir)
save_figure 默认 pad_inches=0.04(在 tight bbox 外加安全边距,
防止 clip_on=False 的轴外显著性 bracket 在极端场景被裁);format_p(precision=...)
对 precision<1 显式抛 ValueError(不再 f"{p:.{-1}e}" 崩溃)。
跨图型硬规则(模板里不再重复,此处统一):
- 点大小全图统一:dot_size=common_point_size(total_n, n_groups)**2——柱/箱/小提琴函数默认已统一,手画散点才需显式传
- box/violin 必须显式 palette= 才上色(函数已内置,勿后置 set_facecolor 会全蓝)
- 显著性 bracket:add_pairwise_brackets(ax, res["pairwise"], labels=labels) 自动分层防重叠
- 统计选择:≥3 组 oneway_anova_tukey;2 组 ttest_two_groups(自动 shapiro→非参数/Welch)
- 退化数据防御:组内零方差(归一化全同值)→ 确定性 p 值不崩;n=1 组 / 配对长度不等 → 明确报错提示
Phase 4:输出交付
完整细节见 references/output-guide.md(输出目录与命名口径、图注 p 值规则、build_stats_report 的 test 参数表、HTML 总览、合并面板图、投稿合规自检、五件套配对模板——交付时按需读取)。核心要点:
- 输出:PNG(300 dpi 预览)+ PDF(矢量投稿)双格式。落到
output/(out_dir不传时由_default_out_dir()决定;Windows 上切勿显式传/sandbox/...,否则会真的生成C:\sandbox意外目录)。同一个{name}驱动图/报告/脚本,五件套同前缀。 - 图注:用
format_legend(),必须含每组比较的精确 p 值(禁止<0.05阈值写法)。2 组图必须传test="ttest"——否则默认回退 "One-way ANOVA + Tukey",把 t 检验误写成 ANOVA(学术误导);更稳妥是直接复用build_stats_report自动生成的图注。 - 统计报告:
build_stats_report(test, ...)+write_report(name, rep, out_dir)→{name}_report.md;write_report默认另生成自包含 HTML 总览{name}.html(内嵌 300dpi 图 + 描述统计/前提检验/事后比较/图注表格),这是交付给用户查看的首选物,与 PNG 一起用 present_files 呈现。 - 多数据集合并面板图(用户说"把多张图画在一起"/"像 Nature 那样 A/B/C")→
compose_panel_figure(...)+build_master_report(...),完整契约见references/panel-figures.md。 - 可复现:脚本末尾加
emit_run_script(NAME, out_dir=OUT),自动落盘与产物同前缀的{NAME}.py(REPL 无__file__时要求显式传source=,不会静默失败)。 - 投稿合规自检:
check_submission(fig, journal="nature")把字号下限 / 线宽下限 / 位图分辨率 / 版面宽度一次查完,返回{"ok", "problems", "warnings", "stats", "report"}。这类"印刷才暴露"的硬指标在屏幕上永远看不出来(如 0.2pt 的线印刷时直接丢失)。自测可用min_font_pt=/min_lw_pt=/min_dpi=调阈值。注意:屏幕预览 dpi ≠ 投稿 dpi,函数按导出分辨率口径判定;linewidth=0(不描边)与不可见 artist 不计入违规 - 用户要求改图型/误差线/配色/画布形态 → 回到对应 Phase 调整后重新出图。
8 种表型模板在 templates/(column / grouped / xy / survival / contingency / parts_of_whole / multiple_variables / nested _template.py),复制改 3 处即可;模板自动从自身位置定位技能根目录(可移植),默认输出到 output/。
Phase 5:自我优化复盘(每次任务后自动执行)
本技能具备自我优化能力:每次完成 Phase 4 交付后,无条件进入本阶段,复盘本轮使用过程、
把踩过的坑/被纠正点/能力缺口固化回技能自身,形成"越用越好"的闭环。完整规则见
references/self-optimization.md(智能体每次执行本阶段前应读它),要点如下:
复盘信号源(逐项回顾本轮):① 脚本是否报错/Warning ② 用户是否纠正过 ③ 是否手写临时补丁绕过坑
④ 用户想要但脚本没有的能力 ⑤ 是否因文档不清而误解。
分类与处置(本模式 = 安全修复自动改):
- bug/convention/doc:满足"安全修复闸门"(可复现、局部、可验证、非破坏性、可回退)则自动改并记日志;
- gap:清晰低风险的加法(新函数)自动加,否则记日志待确认;
- ambiguous:不确定是否为真问题/怎么修 → 只写日志、不动文件、向用户展示待确认。
闭环动作:
1. 用 scripts/log_learning.py 把每条发现记入技能根目录 learnings.jsonl(append-only,含 type/status/files_changed/verified_by);
2. 决定自动改的条目 → 改 SKILL.md/scripts/references → 立刻回归验证(先 python -c "import prism_theme" 确保不崩,再跑一个 verify 脚本确认图形/统计正确);
3. 回归通过记 status=applied;回归失败立即还原改动并记 status=reverted,绝不带病交付;
4. 改动后务必 Read 复查确实落盘(trust-but-verify);用户级与项目副本保持同步。
验证分级策略(用户约定,按改动类型选档,不必次次全套):
| 改动类型 | 验证级别 |
|---|---|
| 小改动(文案/参数默认值/注释) | python -c "import prism_theme" 冒烟 + pytest tests/ --fast(跳过 slow,≈32s)或只跑改动相关的主题文件(≈8–14s,最快) |
| 中改动(加函数/改统计逻辑) | pytest 全套(最划算的统计正确性保险,117 例,≈40s) |
| 大改动(新 API/重构/绘图封装) | pytest + scripts/verify/run_all.py 全套(18 个脚本) |
> ⚠️ 跑验证前先装 pytest:pytest 不在绘图运行期依赖里,基础 venv 默认没有它。
> 直接 $PY -m pytest 会报 No module named pytest。正确做法是先跑一次
> PY=$(python scripts/ensure_env.py --dev)(--dev 会额外装 pytest)。
- 两层验证的分工:pytest 管断言、verify 管产物——新增能力先在
tests/建断言;只有需要人工目视产物(图型外观/布局/标注位置)或要跑大设计矩阵时,才加 verify 脚本,且不要在 verify 里重复 pytest 已断言的内容(verify15/verify16与 pytest 的既有重叠属历史遗留、勿再扩大;详见scripts/verify/README.md); - 日常修改默认只跑 pytest 全套;仅动到绘图封装或公共 API 时才跑 verify 全套;
- verify 输出已精简:清理失败只打一行汇总(Windows 回收站不可用时不再逐文件噪声);
- verify 全套默认并行执行 18 个脚本(worker = min(CPU核心数, 脚本数));含墙钟耗时断言的
verify11强制最后串行独占 CPU(见run_all.py顶部说明); - 按改动类型选档(档位定义以
run_all.py的PRESETS为唯一准绳,不要另立口径):
| 档位 | 含脚本 | 何时用 |
|---|---|---|
--quick |
verify 1/2/3a/3b/3c/4/5/6/7/8/15/16(12 个;跳过 9/10/11/12/13/14) | 只改文案/参数默认值/注释 |
--only stats |
verify 1/2/4/9/10/11/13/14/15(9 个) | 改统计逻辑(ttest/ANOVA/twoway/事后方案/效应量/离群值/RM one-way/深度矩阵/边界/多 sheet) |
--only plot |
verify 3a/3b/3c/5/6/7/8/12/14/15/16(11 个) | 改绘图封装/布局/点径/ylim/合并面板图/新图型 |
--only edge |
verify 11(1 个) | 深度边界/性能 |
| 默认(无参数) | 全部 18 个 | 改公共 API / 大重构 |
> ⚠️ --quick 会跳过 verify12/13/14——其中 verify14 正是"堆叠柱 / 水平柱 / 配对连线 / 误差线 5 口径 / CLD 字母 / 断轴 / 离群值 / RM one-way / XY 模型族"的回归,verify12/13 是合并面板图与多 sheet 读取。改动这些绘图能力时必须走 plot 或 stats 档,不要图省事用 --quick。(新增图型的回归 verify15/verify16 已并入 --quick。)
- pytest 实测 117 例(11 个主题文件 +
conftest.py);全量 ≈40s、--fast≈32s、只跑相关主题文件 ≈8–14s(日常最快路径);时间成本主要在 verify,token 成本可忽略; - 耗时以本机实测为准:不同机器、CPU 核心数、是否含 verify13 的 openpyxl 首次导入都会有差异,不必当作硬指标。
护栏:公开 API 稳定优先——新能力用"加法"(新函数)而非改旧签名;日志只追加、可还原;
超过约 50 条时按 self-optimization.md 瘦身协议归档:旧条目移入 references/learnings-archive-<日期>.jsonl,
learnings.jsonl 保留最近 20 条,并同步更新历史案例区。
工作流示例
示例 1:三组药物剂量实验(Column 表)
用户说:"我有个 Control、低剂量、高剂量三组的细胞活力数据,想画 Prism 那种带散点的柱状图,标显著性。"
- 识别:一个分组变量、三组独立样本 → Column 表
- 选图:默认带散点柱状图(Mean±SEM),确认箱线图/小提琴图备选
- 统计:3 组独立样本 → One-way ANOVA + Tukey 事后检验;生成 Control vs 低剂量、Control vs 高剂量、低 vs 高的星号
- 美化:L 形坐标轴 + 色盲友好配色 + 散点叠加 + 星号 bracket
- 交付:PNG + PDF + 图注 "Data are mean ± SEM, n=6 per group. One-way ANOVA with Tukey's post hoc test."
示例 2:时间×药物双因素实验(Grouped 表)
用户说:"有 0h/24h/48h 三个时间点和 Control/Drug 两组,要做分组柱状图。"
- 识别:两个分组变量(时间、药物)→ Grouped 表
- 选图:分组柱状图(X 轴=时间,分组=药物),叠加散点
- 统计:Two-way ANOVA,报告时间主效应、药物主效应、交互项;图内标注药物组间显著性
- 美化:同示例 1,分组用色盲友好两色
- 交付:图注 "Data are mean ± SEM, n=4. Two-way ANOVA; p<0.05, *p<0.01 vs Control at same time point."
示例 3:小鼠生存实验(Survival 表)
用户说:"两组小鼠的生存天数,Conventional vs Experimental,画生存曲线。"
- 识别:时间 + 事件/删失 → Survival 表
- 选图:Kaplan-Meier 曲线,阶梯状,删失标记为短竖线
- 统计:log-rank 检验,P 值标在图上
- 美化:两组对比色、风险表(number at risk)可选
- 交付:图注 "Kaplan-Meier survival curves; log-rank test, p=0.03, n=10 per group."
资源目录
scripts/prism_theme.py— 可执行的主题脚本:Prism 风格 matplotlib 主题、显著性标注函数、统计封装、完整统计报告生成(build_stats_report)和与图同名的报告写入(write_report,自动附带同名自包含 HTML 总览write_html_report,用户双击即看)、y 轴收尾(set_nice_ylim/_nice_ylim_top,max_ticks=9 防刻度拥挤)、分组柱状图整图封装(prism_bars,点大小全图统一)、画布推荐(recommend_shape/recommend_figsize,数据表类型→tall/square/wide 默认建议、用户关键词覆盖)、样本点自适应(common_point_size[2.0,5.0]pt /auto_dot_alpha透明度 /auto_dot_size/auto_point_size兼容保留)、8 种表型整图封装(prism_bars/prism_boxplot/prism_violin/prism_survival/prism_xy_fit/prism_nested_bars/prism_pie/prism_donut/prism_pairplot/prism_facet_boxplot,补齐 Nested/Parts of whole/Multiple variables 三个表型)、非参数检验(mann_whitney_u/wilcoxon_signed_rank,接口对齐ttest_two_groups)、读表辅助(健壮化:编码/分隔符自动回退、数值清洗、中文可行动报错、分组列合理性校验(杜绝静默错判)、notes字段;自动拆组 + 返回数值列表头作为 ylabel 默认;read_excel_sheets支持单 Excel 多 sheet 一键读取,"sheet"字段可作面板标题)、多数据集合并面板图 + 总报告(compose_panel_figure自动网格/A/B/C 标注/同时保留独立原图与独立统计报告,build_master_report合并图置顶 + 各面板独立统计依次列出的总 HTML 报告)、__all__71 项公开 API 锁定(agent/IDE 可静态校验)。新增 14 个公开 API:prism_stacked_bars/prism_horizontal_bars/prism_paired_lines(堆叠柱·水平柱·配对连线三个此前缺失的图型)、error_bars/error_type_label(误差线 5 口径)、add_cld_letters(CLD 字母)、add_axis_break(断轴)、wrap_xticklabels(标签折行)、detect_outliers_grubbs/detect_outliers_robust_fdr(离群值)、rm_oneway_anova(RM one-way)、check_submission(投稿合规自检)、finish_axes_axis(指定轴收尾)、clear_layout_cache(布局缓存清理)。新增 2 个公开 API:prism_series(时间序列折线 + mean±误差阴影带,5 种误差口径)、prism_forest(森林图:点 + 水平 95% CI + 无效线,可直接消费cox_regression()结果);并给prism_survival加cumulative=(累积发生率 = 1−KM)、给prism_pairplot加upper=(上三角镜像/关闭)。新增 1 个公开 API:series_figsize(n_x, x_labels, long_label_cm)(时间序列专用自动画布:高恒 5cm,宽按时间点数分档 ≤3→4 / 4-6→7 / 7-9→10 / ≥10→13 cm,长标签再 +2cm);prism_series与prism_spaghetti均改为ax=None可自建画布并按时间点数自动选宽,返回值新增"ax"键;TABLE_SHAPE_DEFAULTS补入timeseries/series/timecourse/trajectory/repeatedmeasures→ wide。新增 2 个公开 API:prism_traces(多通道/多细胞逐条纵向错位平铺轨迹,面向钙成像 ΔF/F 与电生理原始记录;offset自动 = 最大峰-谷幅度 ×1.25、y 刻度默认隐藏、可选scale_bar,ax=None自建画布并返回res["ax"])、prism_raster(事件栅格 raster + 可选 PSTH;每 trial 一行短竖线,trial 按首次出现顺序,psth=True自动出上 raster 下 PSTH 两行画布且要求ax=None)。绘图时直接导入使用,无需加载全文。references/table-mapping.md— 8 种数据表 ↔ 数据结构特征 ↔ 图型列表 ↔ 统计方法的完整映射表(Phase 0/1 必读)。references/statistics-guide.md— 统计方法选择决策树、配对判定、显著性星号规范、误差线规范(Phase 2 必读)。references/scatter-layout.md— 散点布局实现级细节(镜像均衡抖动公式、去规律化随机步长、_apply_overlap_alpha判据与公式、max_j ≤ width/2硬约束、画点前 xlim/ylim 硬约束、接口签名);Phase 3"样本点布局"段引用本文件,避免主入口冗长references/output-guide.md— 输出交付完整细节(自 Phase 4 下沉):输出目录与命名口径统一约定、图注format_legend的 p 值/精度/n/test规则、build_stats_report的 7 种 test 参数表、HTML 总览、合并面板图、y 轴收尾、五件套配对模板。交付时按需读取。references/user-guide.md— 面向终端用户的使用指南:数据上传格式(8 种表各自的 CSV 示例)、可选参数、FAQ,以及读表notes(被忽略列)的处置说明。用户询问"怎么用/传什么数据"时,把此文档内容提供给用户(或告知完整文档路径)。references/self-optimization.md— 自我优化协议:每次任务后如何复盘、问题分类与处置规则、安全修复闸门、闭环工作流、护栏、学习日志格式与历史案例。Phase 5 必读。references/recipes/— 11 类图型完整模板(自 SKILL.md Phase 3 迁移,原文零丢失;另含repeated_measures.md与traces.md):bar/box/grouped/survival/xy_fit/contingency/nested/parts_of_whole/multiple_variables/repeated_measures/traces.md,每文件含完整代码、参数细节、统计写法。画图前按速查表读取对应文件;SKILL.md 正文只留速查表 + 跨图型硬规则,避免每次全量加载。scripts/verify/— 回归验证套件(随技能包分发):verify1~6 覆盖 Column 三大图型 / 其他表型(含 ≥3 组生存pairwise逐对 p 与「单独只跑该两组」同源校验)/ 边界场景 / Two-way 事后比较 / Grouped 简单效应标注 / 画布推荐;verify7 覆盖 ylim<1 值域(细粒度步长 + bracket 数据最大值不被 Rectangle 单位矩形污染);verify8 覆盖 ylim<1 修复全图型推广(下限分级 + boxplot 边框数据坐标 + violin/grouped <1 收尾 + 负值下限不被粗化);verify9/10 覆盖析因矩阵深度回归与其他表型深度回归;verify11 覆盖深度边界/性能(NaN/inf 过滤、空组、n=1、pie/xy_fit 预检、palette 空列表、_conflict/overlap 向量化、3×1000 点 best-of-3 <8s);verify12 覆盖多数据集合并面板图与总报告(compose_panel_figure / build_master_report,自动网格 1×3/2×2/自定义、A/B/C 标注、独立原图与报告、空 panels 报错);verify13 覆盖单 Excel 多 sheet 读取 → 合并面板图(read_excel_sheets);verify14 覆盖本轮 14 个新 API(误差线 5 口径与 n=0/n=1 防御、log 轴守卫、零基线只在跨零时画、堆叠柱/水平柱/配对连线、CLD 字母两入口一致、断轴、标签折行、离群值 Grubbs/ROUT、RM one-way 的 SS 分解闭合与 F 自洽、投稿合规自检不误报、XY 模型族含power真实参数恢复、7 种事后方案+效应量单调性、risk 表计数单调不增、布局缓存容量护栏),每个 check 用独立固定种子(可单独重跑任一检查,23 项);verify15 覆盖本轮新增/修坑(prism_series阴影带 + 5 误差口径 + n=1 退化 + 选项,prism_forest显式数组/cox=端到端/互斥与长度校验/对数轴非正值守卫/sort,prism_survivalcumulative 严格 = 1−KM + ylabel 哨兵 + median 抑制 + log-rank 同源,prism_pairplotupper mirror/off/非法值,点径窗口 [2.0,5.0]pt 回归,画布自动宽度:series_figsize阶梯单调/长标签加成/退化输入/按时间点数选中/显式覆盖优先级/ax返回键/缺参报错 + spaghetti 同步,23 项);verify16 覆盖本轮新图型(prism_traces三种输入归一化 /offset=最大峰-谷×1.25 与spacing=offset×gap /groups同组同色 / 自建 wide 画布与 y 刻度隐藏与显式比例尺 / 5 项边界防御,prism_rastertrial 首次出现顺序 / PSTH 计数闭合(counts.sum()=范围内事件数)/n_bins分箱与单行 /t_range裁剪 / 4 项边界防御,10 项);run_all.py一键回归(18 项,含 verify13/14/15/16、默认并行、plot 档含 verify14/15/16、quick 档含 verify15/16、--only按需子集、含耗时断言的 verify11 自动串行隔离,用法见scripts/verify/README.md)。改动prism_theme.py后必跑。tests/— pytest 正式单测(与test_read_table.py合计 117 例);v2.9.3 由单文件按主题拆为 11 个(conftest.py:路径注入 +slow标记注册 +--fast开关):test_stats_ttest6 ·test_stats_anova14 ·test_stats_twoway8 ·test_stats_survival_xy9 ·test_stats_defenses_api8 ·test_stats_method_rationale8 ·test_plot_bars11 ·test_plot_series15 ·test_plot_forest_survival11 ·test_plot_pairplot3 ·test_plot_traces_raster9 ·test_read_table15。覆盖 ttest / 非参数(Mann-Whitney、Wilcoxon)/ ANOVA / Two-way / 重复测量 two-way / log-rank(含 ≥3 组pairwise逐对与单独两组同源 2 例)/ contingency / nested bars / 意大利面条图冒烟 /read_table长·宽·脏表头·xlsx / 坏输入健壮性 7 例(文件缺失·GBK 编码·Tab 分隔·宽表混入文本列静默错判回归·空文件·列名写错·占位符与千分位) /format_legend自动推导方法文字 /add_pairwise_bracketsytick_step/prism_series7 例 ·prism_forest7 例 ·prism_survivalcumulative 4 例 ·prism_pairplot upper3 例 / 画布自动宽度 8 例(series_figsize阶梯单调·长标签加成·退化输入·按时间点数选中·覆盖优先级·ax返回键·缺参报错·recommend_shape时间序列→wide) /prism_traces5 例 ·prism_raster4 例,每类含阳性+阴性双对照 + scipy 交叉验证;回归 bug 场景(p0 数据驱动 R²>0.9、x≤0 自动过滤、退化输入防御 6 例、Tukey-Kramer 逐对公式 + include_ns 生效、twoway 与 statsmodels 等价 + format_p precision 守卫、Welch/Dunnett T3/rm_twoway 固定参考值、prism_series误差带 y 轴上界双向断言防截断)。改动统计逻辑后跑:
bash PY=$(python scripts/ensure_env.py --dev) # --dev 装 pytest(基础 venv 默认没有) $PY -m pytest tests/ -v # 全量:期望 117 passed $PY -m pytest tests/ --fast # 快跑:跳过 slow 用例,期望 110 passed, 7 skipped $PY -m pytest tests/test_plot_series.py # 只跑改动相关的主题文件(最省时)
> 测试已可从任意目录运行(路径由__file__推导)。默认跑满全部用例,不静默跳过slow——统计类技能的失败模式是"算错了却没人发现",故要快必须显式加--fast。并行(pytest-xdist)对本套件无效:耗时以一次性 import 为主,实测 4 worker 反而更慢(66s vs 串行 40s),未采用。scripts/log_learning.py— 学习日志辅助脚本:把每条复盘发现以结构化 JSON 行追加进技能根目录learnings.jsonl,支持--list N查看最近条目、--check全文件体检(报告坏行)。scripts/ensure_env.py— 环境引导器(每次任务第一步必跑):可移植、幂等地确保受管理 venv 中装有 matplotlib/scipy/numpy/pandas/seaborn/statsmodels/lifelines/openpyxl,并打印 venv 解释器路径。关键:依赖判定只用 venv 解释器,绝不依赖基础解释器(基础解释器看不到 venv 包,误判未安装会导致每次任务重复装包)。venv 路径从sys.executable动态推导,跨机器/跨平台通用;且绝不自我嵌套(当前解释器已是 venv 解释器时直接返回自己)。用法:PY=$(python scripts/ensure_env.py)后一律用$PY运行绘图脚本;跑测试先用PY=$(python scripts/ensure_env.py --dev)装 pytest。修了三处真实缺陷:① 通用结构(pyenv / 受管理 venv 内 / Windows 混血路径)下 venv 被双重嵌套、bin/python不存在导致后续命令集体失败——现有 4 条防线(已在 venv 内直接复用 →binaries/python/versions结构 →WORKBUDDY_HOME/CODEBUDDY_HOME环境变量 → 用户级兜底);② 畸形 venv 无自愈(现在检测到缺解释器就rmtree重建);③ensurepip/pip的 stdout 污染PY=$(...)捕获(现已吞掉)。scripts/pack_skill.py— 打包上传 skillhub:生成 zip 到<技能根>/dist/(含PACK_MANIFEST.md清单)。不要用 skill-creator 自带的package_skill.py——那份会把__pycache__/.pytest_cache/learnings.jsonl/_user_meta.json一并打进包;本脚本显式定义排除清单,并补了官方没有的前置校验:frontmatter 严格 YAML 解析(单行description含冒号必须加引号,否则 YAML 报"mapping values are not allowed")、包内文件数上限(默认 100)、硬编码用户主目录绝对路径、疑似密钥、SKILL.md 内部引用是否指向真实文件。--check严格只读(只校验、不写任何文件——此前校验通过也会写PACK_MANIFEST.md,与"只校验"语义不符;现在仅校验失败时才写清单供排查);--exclude-dev去掉tests/与scripts/verify/出精简版。注意:本脚本需要 PyYAML 才能做严格 frontmatter 校验;缺 PyYAML 时会降级为宽松正则并出[WARN](此前会把完全合法的 frontmatter 误报成[FAIL] 缺少 name/description——因为缺 PyYAML 时fm被降级成空 dict 却仍继续跑严格校验)。CHANGELOG.md— 版本变更史(随包分发):按版本倒序记录引擎与文档的每次变更(口径调整、新增 API、修的真实 bug、修订理由);SKILL.md 正文只描述当前规则,历史叙事一律在此。使用者可据此判断"手上这份是什么版本、相对上一版改了什么"。- 学习日志(
learnings.jsonl)由scripts/log_learning.py按需生成并自动重建;逐条复盘记录(信号源、问题分类、验证结论、files_changed)留在开发副本,不随技能包分发。
变更日志
历次版本变更史见随包分发的 CHANGELOG.md。正文只描述当前规则,不保留历史版本号。