paperconan 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. paperconan-0.5.0/LICENSE +21 -0
  2. paperconan-0.5.0/PKG-INFO +302 -0
  3. paperconan-0.5.0/README.md +270 -0
  4. paperconan-0.5.0/pyproject.toml +50 -0
  5. paperconan-0.5.0/setup.cfg +4 -0
  6. paperconan-0.5.0/src/paperconan/__init__.py +13 -0
  7. paperconan-0.5.0/src/paperconan/__main__.py +5 -0
  8. paperconan-0.5.0/src/paperconan/_audit.py +974 -0
  9. paperconan-0.5.0/src/paperconan/_extract.py +100 -0
  10. paperconan-0.5.0/src/paperconan/_html.py +532 -0
  11. paperconan-0.5.0/src/paperconan/fetch/__init__.py +43 -0
  12. paperconan-0.5.0/src/paperconan/fetch/_cli.py +105 -0
  13. paperconan-0.5.0/src/paperconan/fetch/_download.py +133 -0
  14. paperconan-0.5.0/src/paperconan/fetch/_files.py +19 -0
  15. paperconan-0.5.0/src/paperconan/fetch/_http.py +28 -0
  16. paperconan-0.5.0/src/paperconan/fetch/_resolve.py +130 -0
  17. paperconan-0.5.0/src/paperconan/fetch/_sources.py +129 -0
  18. paperconan-0.5.0/src/paperconan.egg-info/PKG-INFO +302 -0
  19. paperconan-0.5.0/src/paperconan.egg-info/SOURCES.txt +27 -0
  20. paperconan-0.5.0/src/paperconan.egg-info/dependency_links.txt +1 -0
  21. paperconan-0.5.0/src/paperconan.egg-info/entry_points.txt +2 -0
  22. paperconan-0.5.0/src/paperconan.egg-info/requires.txt +13 -0
  23. paperconan-0.5.0/src/paperconan.egg-info/top_level.txt +1 -0
  24. paperconan-0.5.0/tests/test_benign.py +83 -0
  25. paperconan-0.5.0/tests/test_collisions.py +128 -0
  26. paperconan-0.5.0/tests/test_extract.py +115 -0
  27. paperconan-0.5.0/tests/test_fdr.py +52 -0
  28. paperconan-0.5.0/tests/test_relations_flood.py +61 -0
  29. paperconan-0.5.0/tests/test_smoke.py +216 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Xiaotong
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,302 @@
1
+ Metadata-Version: 2.4
2
+ Name: paperconan
3
+ Version: 0.5.0
4
+ Summary: Numeric forensics for paper supplementary source-data: surfaces fabrication-style patterns (signal, not verdict).
5
+ Author: Xiaotong
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/zixixr/paperconan
8
+ Project-URL: Issues, https://github.com/zixixr/paperconan/issues
9
+ Keywords: research-integrity,data-forensics,pubpeer,scientific-fraud,xlsx,audit
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Scientific/Engineering
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: openpyxl>=3.1
22
+ Requires-Dist: numpy>=1.24
23
+ Requires-Dist: scipy>=1.10
24
+ Provides-Extra: pdf
25
+ Requires-Dist: pdfplumber>=0.11; extra == "pdf"
26
+ Provides-Extra: docx
27
+ Requires-Dist: python-docx>=1.1; extra == "docx"
28
+ Provides-Extra: all
29
+ Requires-Dist: pdfplumber>=0.11; extra == "all"
30
+ Requires-Dist: python-docx>=1.1; extra == "all"
31
+ Dynamic: license-file
32
+
33
+ # 论文柯南 / paperconan
34
+
35
+ > **真相只有一个!**
36
+ >
37
+ > 现在学术界弊病丛生,
38
+ > 大家要小心 paperconan 的推理哦!
39
+ > 唯一看透论文数据真相的,
40
+ > 是这个外表看似 Python 小工具、
41
+ > 智慧却过于常人的——
42
+ >
43
+ > 名侦探,**论文柯南**!
44
+
45
+ ---
46
+
47
+ ## 它是什么
48
+
49
+ `paperconan` 是一个 **论文数据 sanity check** 小工具。
50
+
51
+ 喂一份 supplementary source data(一个目录,里面装着 `.xlsx` / `.csv` / `.tsv`,**也可以是补充材料 `.pdf` / `.docx` 里的表格**)进去,它会跑十几组数值取证检测器,输出 **scan.json**(结构化的全部命中)+ **report.html**(自包含的可交互法证报告,每条 finding 直接嵌可疑表格片段并高亮可疑列/行),告诉你哪些位置值得人工再看一眼。
52
+
53
+ **适合谁**:
54
+
55
+ - 研究生 / 青椒:引用论文前 sanity check 一遍
56
+ - 实验室 / 课题组 / 院系:响应高校自查要求时的初筛工具
57
+ - 普通好奇心人士:跟着学术圈热点看看自己关心的论文
58
+
59
+ **不适合什么**:
60
+
61
+ - 不能给出 "是不是造假" 的结论 — 那是期刊编辑部和同行的事
62
+ - 不能扫图像 / 凝胶电泳 / Western blot 拼接 — 只看数值表格
63
+ - 不能替代专业的统计学审稿
64
+
65
+ ---
66
+
67
+ ## 它能找出什么
68
+
69
+ | 检测器 | 寻找的模式 | 典型证据形态 |
70
+ |--------|-----------|------------|
71
+ | `identical_column` / `constant_offset` / `constant_ratio` / `exact_linear` | 同一 block 内两列存在精确数值关系 | `col B = col A + 2.13` 出现在所有 10 行 |
72
+ | `arithmetic_progression` | 整列等差 / 等比 | "一组对照组 Y 是完美 1, 2, 3, 4… 整数" |
73
+ | `within_col_value_duplication` | 单列里同一个 6 位以上小数反复出现 | "0.208975 在'独立的'实验里出现 8 次" |
74
+ | `within_col_decimal_repetition` | 同一组的 N 个数字小数尾 2 位高度重复 | "全部以 .25 / .75 结尾,分布不正常" |
75
+ | `rounded_to_half_or_int` | 整列被舍入到固定刻度 (整数 / 0.5 / 0.25) | 自然测量不会全部精确落在某网格 |
76
+ | `identical_after_rounding` | 两列舍掉末位后完全相同 | 一列 = 另一列 × 小扰动 |
77
+ | `many_equal_pairs` | 两个本该独立的列里有 ≥40% 行 byte-identical | 9/10 一致 + 1 格手改的指纹 |
78
+ | `cross_sheet_position_identical` | 两张 sheet 在同行同列位置数值完全一样 | 同一份样本被分析了两次 |
79
+ | `last_digit_chi_square` | 整 sheet 末位数字偏离 χ² 均匀分布(BH-FDR 多重检验校正后 q ≤ 0.05) | 真实测量末位应近似均匀 |
80
+ | `repeated_two_decimal_endings` | 末两位高度集中 | 编造数字常见模式 |
81
+
82
+ 每条命中都带 severity (`high` / `medium` / `low`) + 涉及的文件 / sheet / block 行范围 / 规则字符串,方便人工复核时直接定位。
83
+
84
+ > 密集 / 相关矩阵(相关系数表、归一化重复样本面板)会按 O(列²) 刷出成千上万条 `identical_column` / `exact_linear`——这类列关系是数据形态的结构性产物,不是造假指纹。工具按 **(file, sheet) 整张表**的关系总数判定洪泛,超阈值时整体**自动降级为 `low`** 并打 `dense_block` 标记(保留可见、不丢弃),不让它淹没真正的跨 sheet 重复信号。
85
+
86
+ **跨 sheet 重复**还会额外给出上下文,降低误读:
87
+
88
+ - **按图号分级**:解析 sheet 名里的图号(`Figure 5o` → `main:5`、`exFig.6i` → `ext:6`)。同一图号两个 panel 共享数据(合并曲线 vs 个体曲线的预期重画)自动**降级为 `low`**;只有跨图/跨文件的重复才保持 `high`/`medium`——让真正值得查的那条不被同图噪音淹没。
89
+ - **`delta` 形态**:区分 `perfect_dup`(干净重画)/ `superset`(多一列重复样本,n=5 vs n=6)/ `value_tweaked`(就地改值,copy-then-tweak 指纹)/ `value_divergent`。
90
+ - **`likely_benign`**:常见良性解释直接写进 finding(如"day/dose 轴""主图/扩展图共享 cohort,核对 legend"),报告里随卡片展示,避免把 signal 当 verdict。
91
+
92
+ ---
93
+
94
+ ## 想先看看效果?
95
+
96
+ [`examples/`](examples/) 里有一份完整的可跑示例:一份**合成的**"假论文" source data(埋了各类造假模式)+ paperconan 跑出来的报告 + 每条 finding 的逐条走查。直接看 [examples/README.md](examples/README.md) 和报告截图 [examples/report-preview.png](examples/report-preview.png),不装也能感受输出长什么样。
97
+
98
+ ---
99
+
100
+ ## 安装 & 跑
101
+
102
+ 需要 Python ≥ 3.10。
103
+
104
+ ```bash
105
+ # 安装
106
+ git clone https://github.com/zixixr/paperconan.git
107
+ cd paperconan
108
+ pip install .
109
+
110
+ # 想顺带审补充材料里 .pdf / .docx 的表格,装上对应可选依赖(纯 Python、无 Java/系统依赖):
111
+ pip install '.[all]' # 或 .[pdf] / .[docx] 单装
112
+
113
+ # 跑一篇论文(指向其 source data 目录)
114
+ paperconan path/to/paper_dir/
115
+
116
+ # 也可以用 module 形式
117
+ python -m paperconan path/to/paper_dir/
118
+
119
+ # 输出(默认在 <in_dir>/audit/):
120
+ # scan.json — 完整结构化 findings
121
+ # report.html — 自包含的人类可读 HTML 报告(浏览器打开即可)
122
+ #
123
+ # 可选:
124
+ paperconan path/to/paper_dir/ --md # 额外生成 REPORT.md
125
+ paperconan path/to/paper_dir/ --no-html # 跳过 HTML(CI / 脚本场景)
126
+ paperconan path/to/paper_dir/ --out /tmp/audit-of-this-paper
127
+ ```
128
+
129
+ ### 自动抓取论文数据(v0.4)
130
+
131
+ 只有论文、没有本地数据时,可以让 paperconan 去开放数据源找:
132
+
133
+ ```bash
134
+ paperconan fetch "10.xxxx/your.doi" # 列出 Zenodo/Figshare/Dryad/Europe PMC 候选 + 匹配信号
135
+ paperconan fetch "10.xxxx/your.doi" --download zenodo:123456 --out data/
136
+ paperconan fetch "10.xxxx/your.doi" --auto --out data/ # 仅当有候选确属本文时才下载
137
+ paperconan data/ # 再照常分析
138
+ ```
139
+
140
+ 只覆盖开放数据源、**不绕付费墙、不抓取出版商页面**:
141
+
142
+ - **Zenodo / Figshare**:keyless 直接下载。
143
+ - **Europe PMC**:keyless。开放获取论文(很多 NIH/Wellcome 资助的 Nature 论文都在内)的补充材料以一个 zip 提供,`fetch` 会自动下载并解压出其中的 `.xlsx/.csv/.tsv`。整包档案按 250MB 上限下载(单张表仍限 50MB),避免"体积大但内含小表格"的档案被截断丢弃。
144
+ - **Dryad**:仅做检索(下载接口需鉴权),命中后请到 Dryad 数据集页面手动下载。
145
+ - **匹配可信度门槛**:仓库全文检索(尤其 figshare/zenodo)常返回**完全无关的他人数据集**,所以 `--auto` 只在候选 DOI 命中或标题高度重合时才下载,否则拒绝并转期刊指引;`--download` 一个不匹配的候选需加 `--force`;列表里这类候选会标 `⚠ no DOI/title match`。绝不让你误把别人的数据当本文来审。
146
+ - **都没命中**:`fetch` 会按 DOI 输出一段期刊指引(出版商 + `doi.org` 文章链接 + 该刊 source data 的常见位置,如 Nature 的 `...MOESM<N>_ESM.xlsx`),而不是简单告诉你"没找到"——绝不暗示"查过=干净"。
147
+
148
+ `fetch --download/--auto` 还会在下载目录写一个 `paperconan_source.json`(记录 DOI/标题/来源),随后 `paperconan <dir>` 会自动把它写进 scan.json 的 `paper` 字段做溯源;也可手动 `paperconan <dir> --doi <DOI> --title <T>` 标注。
149
+
150
+ ### 审补充材料 PDF / Word 里的表格(v0.5)
151
+
152
+ 很多数据造假根本不在可下载的 `.xlsx` source data 里,而是**就摆在论文/补充材料本身**——补充 PDF 的附表、Word 附录表。这类"不需要单独 source data、看论文就能查"的数值,paperconan 现在能直接吃进来:
153
+
154
+ ```bash
155
+ pip install '.[pdf]' # 或 .[docx] / .[all]
156
+ paperconan path/to/dir_with_si_pdf/ # 目录里有 .pdf / .docx 即可,和 xlsx 混放也行
157
+ ```
158
+
159
+ - 从 PDF / Word 里抽出的**每一张表**变成一个 sheet,命名带溯源:PDF 是 `<文件名>!p<页>_t<第几张表>`、Word 是 `<文件名>!t<第几张表>`——报告里一眼看出"这条命中来自补充 PDF 第 3 页第 1 张表"。
160
+ - 抽出来的表走的是**和 xlsx 完全相同的那套数值检测器**(等差数列、末位偏置、列复制、跨表重复……),不是新算法。
161
+ - **只抽真正的结构化表格**:不从 bar chart / 曲线的像素里数字化数据点(数字化误差本身会触发等差/重复检测器造成假阳性),也不做扫描件 OCR。图表像素数字化 / 图像取证仍是未做项(见路线图)。
162
+ - 可选依赖缺失时给出明确提示(`pip install 'paperconan[pdf]'`),绝不影响只跑 xlsx 的基础安装。
163
+
164
+ **report.html** 长这样(单文件、无外部依赖、可直接邮件/PubPeer 附件分享):
165
+
166
+ - 顶部摘要:n_files / n_sheets / high / medium / low 计数
167
+ - 左侧边栏:按 severity / detector 类型 / 文件 勾选过滤 + 关键字搜索
168
+ - 主区域:每条 finding 是一张可折叠卡片,里面直接渲染出**可疑表格片段**,高亮可疑列(黄底)和高亮行(红边)—— 用户不需要再打开 xlsx 翻位置;有常见良性解释的,卡片里直接给出 `likely_benign` 旁注
169
+ - 末位数字 χ² 异常(BH-FDR 校正后)自带 0-9 inline 直方图,并显示 q 值
170
+ - 跨 sheet bit-identical collisions 单独成段,最高优先级展示
171
+
172
+ ---
173
+
174
+ ## 作为 skill 使用(被 Claude Code / Codex / 其他 agent 调用)
175
+
176
+ [`skills/paperconan/SKILL.md`](skills/paperconan/SKILL.md) 是给 agent 看的入口,自带 YAML frontmatter(name + 触发关键词)和怎么解读 `scan.json` 的指引。同目录 `references/` 下有:
177
+
178
+ - [`detectors.md`](skills/paperconan/references/detectors.md) — 每个检测器的原理 / 典型命中 / **常见误报**
179
+ - [`interpretation.md`](skills/paperconan/references/interpretation.md) — severity 语义 + "signal not verdict" 红线 + 推荐回复话术
180
+
181
+ 整个 skill 收在 `skills/paperconan/` 一个目录里,安装只需软链一次。
182
+
183
+ ### 安装
184
+
185
+ ```bash
186
+ # 1. 装 CLI
187
+ pip install -e /path/to/paperconan # 本地开发;发到 PyPI 后改成 pip install paperconan
188
+
189
+ # 2a. Claude Code: 软链整个 skill 目录(一次搞定)
190
+ ln -s /path/to/paperconan/skills/paperconan ~/.claude/skills/paperconan
191
+
192
+ # 2b. Codex / 其他 agent: 在 AGENTS.md / CLAUDE.md / GEMINI.md 里引用
193
+ echo '@/path/to/paperconan/skills/paperconan/SKILL.md' >> AGENTS.md
194
+ ```
195
+
196
+ 之后在 agent 会话里说"我有一篇论文的 source data 在 /path/to/data,帮我做个 sanity check",agent 会自动调用 paperconan 并按 SKILL.md 的解读规则向你汇报。
197
+
198
+ ---
199
+
200
+ ## ⚠️ 重要声明
201
+
202
+ `paperconan` 输出的是 **算法标注的可疑模式 (statistical anomalies)**,**不是学术不端结论**。
203
+
204
+ 最终判定需由原作者澄清、期刊编辑部核实,或经独立同行复议。
205
+
206
+ **请走正规渠道**:
207
+
208
+ - 把可疑信号提交到 [PubPeer](https://pubpeer.com/)
209
+ - 联系期刊编辑部(每本期刊都有 ethics inquiry 渠道)
210
+ - 如果是你所在单位的论文,走单位 research integrity office
211
+
212
+ **请不要**:
213
+
214
+ - 在微博 / 微信 / 知乎 / 抖音直接喊话指控某个具体作者
215
+ - 用 paperconan 的输出截图作为 "实锤" 在社交媒体传播
216
+ - 跳过原作者澄清环节直接定性
217
+
218
+ 工具是中立的,使用方式不能。
219
+
220
+ ---
221
+
222
+ ## FAQ
223
+
224
+ **Q: 它会漏掉哪些造假?**
225
+
226
+ 会。`paperconan` 只看**数值表格**——无论它来自 `.xlsx/.csv/.tsv`,还是补充材料 `.pdf/.docx` 里的表格。下面这些它一概查不到:
227
+
228
+ - 图像 PS / Western blot 拼接 / 显微镜照片重复使用(图像取证是另一套,仍未做)
229
+ - **图表(bar chart / 曲线)里的数据**——只要数字没以表格形式给出,就读不到(不做像素数字化,详见路线图)
230
+ - 临床数据造假但 source data / 补充材料都没公开
231
+ - 实验完全没做但写进文章的部分
232
+ - 统计方法选择性使用 (p-hacking)
233
+ - 引用造假 / 同行评议造假
234
+
235
+ > 注:补充材料 PDF/Word 的**表格**现在能读了(v0.5,需 `pip install 'paperconan[all]'`)——这覆盖了"不需要单独 source data、数据就摆在论文里"的一大类。但"摆在图里"的数据(柱状图、折线图)和图像本身仍在覆盖之外。
236
+
237
+ **Q: 它会误报吗?**
238
+
239
+ 会。比如:
240
+
241
+ - 同一篇论文里 **合理共享的对照组数据** 会被标记为 "跨 sheet 复用"
242
+ - **量化产生的终位偏置**(细胞计数 / 4 视野平均 → 多 0.25 步长)会被标记为 "末位异常"
243
+ - **共享的剂量轴 / 时间轴** 会被标记为 "跨列复制"
244
+ - **密集 / 相关矩阵** 里成千上万对相同/线性列会被标记为 "列关系"
245
+
246
+ 不过这几类现在工具会主动帮你识别:同图重画的跨 sheet 复用会**自动降级**,密集矩阵的列关系洪泛会按整张表降级并打 `dense_block`,剂量/时间轴、量化偏置等会附 `likely_benign` 旁注,末位 χ² 也做了多重检验校正(看 q 值而非裸 p)。即便如此,报告里 high severity 的 anomaly **依然需要人工读 figure legend 和 Methods** 才能下判断。不要把 high = misconduct。
247
+
248
+ **Q: 我用它发现一篇看似有问题的论文,下一步该做什么?**
249
+
250
+ 1. 仔细读 paper 的 figure legends 和 Methods — 看作者是否已经在文字里说明了 "shared control" 等情况
251
+ 2. 自己用 Excel 复核 paperconan 标出的具体位置
252
+ 3. 提交到 PubPeer(最常见的做法),等原作者回应
253
+ 4. 如果是你所在单位,找 research integrity office
254
+
255
+ **Q: 这个工具会不会让普通硕博更难毕业?**
256
+
257
+ 不会。它检测的是 "编造数据" — 也就是 9 位小数一字不差、跨独立实验值池只用 17 个数字、9/10 完全一致只改 1 格这种 **直接编数字** 的模式。
258
+
259
+ 普通硕博正常做实验产生的 messy data(不规范、有 negative result、原始记录不齐),paperconan 一律不会标。
260
+
261
+ 如果你的数据 paperconan 也标出来了 — 那可能要回去看看自己的实验记录和分析流程了。
262
+
263
+ ---
264
+
265
+ ## 同款诞生背景
266
+
267
+ 这个工具最早是为做一期 YouTube / 抖音 / B 站视频造的 — 视频里我用它独立扫了 7 篇 Nature 及 Nature 子刊论文,全部找到了可疑模式。
268
+
269
+ 工具开源给所有人。希望它能帮老实做实验的人,减少作弊者占用版面、占用帽子、占用博士点的概率。
270
+
271
+ ---
272
+
273
+ ## 路线图 (好玩的可以一起做)
274
+
275
+ 短期:
276
+
277
+ - [x] CSV / TSV 输入 — v0.3 已实现(含跨文件数据复用检测)
278
+ - [x] HTML 报告(在 REPORT.md 之外)— v0.2 已实现,并取代 REPORT.md 成为默认人类可读输出
279
+ - [x] 把每条 finding 嵌入对应的表格片段 — v0.2 实现为可交互 HTML 表格(不是截图)
280
+ - [x] 作为 skill 给 agent 调用 — v0.2 已实现,见 skills/paperconan/SKILL.md
281
+ - [x] 补充材料 PDF / Word 表格输入 — v0.5 已实现(`pip install 'paperconan[all]'`,复用全部数值检测器)
282
+ - [ ] PubPeer 风格的 "为什么这值得复核" 旁注(LLM 生成的可选)
283
+
284
+ 中长期:
285
+
286
+ - [ ] 跨论文扫描(一个 lab 的多篇论文一起跑,看跨论文数据复用)
287
+ - [ ] 图表像素数字化(从 bar chart / 曲线里提取数据点)— 精度与假阳性风险高,需谨慎做
288
+ - [ ] 图像取证检测(Western blot / 显微镜照片重复)— 这块需要专门做
289
+ - [ ] 与 [PubPeer Public API](https://pubpeer.com/api) 联动
290
+
291
+ 欢迎 PR。给检测器加新模式,写 README 翻译,做 demo 都很欢迎。
292
+
293
+ ---
294
+
295
+ ## License
296
+
297
+ MIT.
298
+
299
+ ## Acknowledgments
300
+
301
+ - 名侦探柯南 / Detective Conan © 青山刚昌 / TMS Entertainment — 借了一下片头叙事结构,致敬不致敬都得感谢一下。
302
+ - [PubPeer](https://pubpeer.com/) — 学术界最重要的公开质疑平台,paperconan 的输出最终都应该走这里。
@@ -0,0 +1,270 @@
1
+ # 论文柯南 / paperconan
2
+
3
+ > **真相只有一个!**
4
+ >
5
+ > 现在学术界弊病丛生,
6
+ > 大家要小心 paperconan 的推理哦!
7
+ > 唯一看透论文数据真相的,
8
+ > 是这个外表看似 Python 小工具、
9
+ > 智慧却过于常人的——
10
+ >
11
+ > 名侦探,**论文柯南**!
12
+
13
+ ---
14
+
15
+ ## 它是什么
16
+
17
+ `paperconan` 是一个 **论文数据 sanity check** 小工具。
18
+
19
+ 喂一份 supplementary source data(一个目录,里面装着 `.xlsx` / `.csv` / `.tsv`,**也可以是补充材料 `.pdf` / `.docx` 里的表格**)进去,它会跑十几组数值取证检测器,输出 **scan.json**(结构化的全部命中)+ **report.html**(自包含的可交互法证报告,每条 finding 直接嵌可疑表格片段并高亮可疑列/行),告诉你哪些位置值得人工再看一眼。
20
+
21
+ **适合谁**:
22
+
23
+ - 研究生 / 青椒:引用论文前 sanity check 一遍
24
+ - 实验室 / 课题组 / 院系:响应高校自查要求时的初筛工具
25
+ - 普通好奇心人士:跟着学术圈热点看看自己关心的论文
26
+
27
+ **不适合什么**:
28
+
29
+ - 不能给出 "是不是造假" 的结论 — 那是期刊编辑部和同行的事
30
+ - 不能扫图像 / 凝胶电泳 / Western blot 拼接 — 只看数值表格
31
+ - 不能替代专业的统计学审稿
32
+
33
+ ---
34
+
35
+ ## 它能找出什么
36
+
37
+ | 检测器 | 寻找的模式 | 典型证据形态 |
38
+ |--------|-----------|------------|
39
+ | `identical_column` / `constant_offset` / `constant_ratio` / `exact_linear` | 同一 block 内两列存在精确数值关系 | `col B = col A + 2.13` 出现在所有 10 行 |
40
+ | `arithmetic_progression` | 整列等差 / 等比 | "一组对照组 Y 是完美 1, 2, 3, 4… 整数" |
41
+ | `within_col_value_duplication` | 单列里同一个 6 位以上小数反复出现 | "0.208975 在'独立的'实验里出现 8 次" |
42
+ | `within_col_decimal_repetition` | 同一组的 N 个数字小数尾 2 位高度重复 | "全部以 .25 / .75 结尾,分布不正常" |
43
+ | `rounded_to_half_or_int` | 整列被舍入到固定刻度 (整数 / 0.5 / 0.25) | 自然测量不会全部精确落在某网格 |
44
+ | `identical_after_rounding` | 两列舍掉末位后完全相同 | 一列 = 另一列 × 小扰动 |
45
+ | `many_equal_pairs` | 两个本该独立的列里有 ≥40% 行 byte-identical | 9/10 一致 + 1 格手改的指纹 |
46
+ | `cross_sheet_position_identical` | 两张 sheet 在同行同列位置数值完全一样 | 同一份样本被分析了两次 |
47
+ | `last_digit_chi_square` | 整 sheet 末位数字偏离 χ² 均匀分布(BH-FDR 多重检验校正后 q ≤ 0.05) | 真实测量末位应近似均匀 |
48
+ | `repeated_two_decimal_endings` | 末两位高度集中 | 编造数字常见模式 |
49
+
50
+ 每条命中都带 severity (`high` / `medium` / `low`) + 涉及的文件 / sheet / block 行范围 / 规则字符串,方便人工复核时直接定位。
51
+
52
+ > 密集 / 相关矩阵(相关系数表、归一化重复样本面板)会按 O(列²) 刷出成千上万条 `identical_column` / `exact_linear`——这类列关系是数据形态的结构性产物,不是造假指纹。工具按 **(file, sheet) 整张表**的关系总数判定洪泛,超阈值时整体**自动降级为 `low`** 并打 `dense_block` 标记(保留可见、不丢弃),不让它淹没真正的跨 sheet 重复信号。
53
+
54
+ **跨 sheet 重复**还会额外给出上下文,降低误读:
55
+
56
+ - **按图号分级**:解析 sheet 名里的图号(`Figure 5o` → `main:5`、`exFig.6i` → `ext:6`)。同一图号两个 panel 共享数据(合并曲线 vs 个体曲线的预期重画)自动**降级为 `low`**;只有跨图/跨文件的重复才保持 `high`/`medium`——让真正值得查的那条不被同图噪音淹没。
57
+ - **`delta` 形态**:区分 `perfect_dup`(干净重画)/ `superset`(多一列重复样本,n=5 vs n=6)/ `value_tweaked`(就地改值,copy-then-tweak 指纹)/ `value_divergent`。
58
+ - **`likely_benign`**:常见良性解释直接写进 finding(如"day/dose 轴""主图/扩展图共享 cohort,核对 legend"),报告里随卡片展示,避免把 signal 当 verdict。
59
+
60
+ ---
61
+
62
+ ## 想先看看效果?
63
+
64
+ [`examples/`](examples/) 里有一份完整的可跑示例:一份**合成的**"假论文" source data(埋了各类造假模式)+ paperconan 跑出来的报告 + 每条 finding 的逐条走查。直接看 [examples/README.md](examples/README.md) 和报告截图 [examples/report-preview.png](examples/report-preview.png),不装也能感受输出长什么样。
65
+
66
+ ---
67
+
68
+ ## 安装 & 跑
69
+
70
+ 需要 Python ≥ 3.10。
71
+
72
+ ```bash
73
+ # 安装
74
+ git clone https://github.com/zixixr/paperconan.git
75
+ cd paperconan
76
+ pip install .
77
+
78
+ # 想顺带审补充材料里 .pdf / .docx 的表格,装上对应可选依赖(纯 Python、无 Java/系统依赖):
79
+ pip install '.[all]' # 或 .[pdf] / .[docx] 单装
80
+
81
+ # 跑一篇论文(指向其 source data 目录)
82
+ paperconan path/to/paper_dir/
83
+
84
+ # 也可以用 module 形式
85
+ python -m paperconan path/to/paper_dir/
86
+
87
+ # 输出(默认在 <in_dir>/audit/):
88
+ # scan.json — 完整结构化 findings
89
+ # report.html — 自包含的人类可读 HTML 报告(浏览器打开即可)
90
+ #
91
+ # 可选:
92
+ paperconan path/to/paper_dir/ --md # 额外生成 REPORT.md
93
+ paperconan path/to/paper_dir/ --no-html # 跳过 HTML(CI / 脚本场景)
94
+ paperconan path/to/paper_dir/ --out /tmp/audit-of-this-paper
95
+ ```
96
+
97
+ ### 自动抓取论文数据(v0.4)
98
+
99
+ 只有论文、没有本地数据时,可以让 paperconan 去开放数据源找:
100
+
101
+ ```bash
102
+ paperconan fetch "10.xxxx/your.doi" # 列出 Zenodo/Figshare/Dryad/Europe PMC 候选 + 匹配信号
103
+ paperconan fetch "10.xxxx/your.doi" --download zenodo:123456 --out data/
104
+ paperconan fetch "10.xxxx/your.doi" --auto --out data/ # 仅当有候选确属本文时才下载
105
+ paperconan data/ # 再照常分析
106
+ ```
107
+
108
+ 只覆盖开放数据源、**不绕付费墙、不抓取出版商页面**:
109
+
110
+ - **Zenodo / Figshare**:keyless 直接下载。
111
+ - **Europe PMC**:keyless。开放获取论文(很多 NIH/Wellcome 资助的 Nature 论文都在内)的补充材料以一个 zip 提供,`fetch` 会自动下载并解压出其中的 `.xlsx/.csv/.tsv`。整包档案按 250MB 上限下载(单张表仍限 50MB),避免"体积大但内含小表格"的档案被截断丢弃。
112
+ - **Dryad**:仅做检索(下载接口需鉴权),命中后请到 Dryad 数据集页面手动下载。
113
+ - **匹配可信度门槛**:仓库全文检索(尤其 figshare/zenodo)常返回**完全无关的他人数据集**,所以 `--auto` 只在候选 DOI 命中或标题高度重合时才下载,否则拒绝并转期刊指引;`--download` 一个不匹配的候选需加 `--force`;列表里这类候选会标 `⚠ no DOI/title match`。绝不让你误把别人的数据当本文来审。
114
+ - **都没命中**:`fetch` 会按 DOI 输出一段期刊指引(出版商 + `doi.org` 文章链接 + 该刊 source data 的常见位置,如 Nature 的 `...MOESM<N>_ESM.xlsx`),而不是简单告诉你"没找到"——绝不暗示"查过=干净"。
115
+
116
+ `fetch --download/--auto` 还会在下载目录写一个 `paperconan_source.json`(记录 DOI/标题/来源),随后 `paperconan <dir>` 会自动把它写进 scan.json 的 `paper` 字段做溯源;也可手动 `paperconan <dir> --doi <DOI> --title <T>` 标注。
117
+
118
+ ### 审补充材料 PDF / Word 里的表格(v0.5)
119
+
120
+ 很多数据造假根本不在可下载的 `.xlsx` source data 里,而是**就摆在论文/补充材料本身**——补充 PDF 的附表、Word 附录表。这类"不需要单独 source data、看论文就能查"的数值,paperconan 现在能直接吃进来:
121
+
122
+ ```bash
123
+ pip install '.[pdf]' # 或 .[docx] / .[all]
124
+ paperconan path/to/dir_with_si_pdf/ # 目录里有 .pdf / .docx 即可,和 xlsx 混放也行
125
+ ```
126
+
127
+ - 从 PDF / Word 里抽出的**每一张表**变成一个 sheet,命名带溯源:PDF 是 `<文件名>!p<页>_t<第几张表>`、Word 是 `<文件名>!t<第几张表>`——报告里一眼看出"这条命中来自补充 PDF 第 3 页第 1 张表"。
128
+ - 抽出来的表走的是**和 xlsx 完全相同的那套数值检测器**(等差数列、末位偏置、列复制、跨表重复……),不是新算法。
129
+ - **只抽真正的结构化表格**:不从 bar chart / 曲线的像素里数字化数据点(数字化误差本身会触发等差/重复检测器造成假阳性),也不做扫描件 OCR。图表像素数字化 / 图像取证仍是未做项(见路线图)。
130
+ - 可选依赖缺失时给出明确提示(`pip install 'paperconan[pdf]'`),绝不影响只跑 xlsx 的基础安装。
131
+
132
+ **report.html** 长这样(单文件、无外部依赖、可直接邮件/PubPeer 附件分享):
133
+
134
+ - 顶部摘要:n_files / n_sheets / high / medium / low 计数
135
+ - 左侧边栏:按 severity / detector 类型 / 文件 勾选过滤 + 关键字搜索
136
+ - 主区域:每条 finding 是一张可折叠卡片,里面直接渲染出**可疑表格片段**,高亮可疑列(黄底)和高亮行(红边)—— 用户不需要再打开 xlsx 翻位置;有常见良性解释的,卡片里直接给出 `likely_benign` 旁注
137
+ - 末位数字 χ² 异常(BH-FDR 校正后)自带 0-9 inline 直方图,并显示 q 值
138
+ - 跨 sheet bit-identical collisions 单独成段,最高优先级展示
139
+
140
+ ---
141
+
142
+ ## 作为 skill 使用(被 Claude Code / Codex / 其他 agent 调用)
143
+
144
+ [`skills/paperconan/SKILL.md`](skills/paperconan/SKILL.md) 是给 agent 看的入口,自带 YAML frontmatter(name + 触发关键词)和怎么解读 `scan.json` 的指引。同目录 `references/` 下有:
145
+
146
+ - [`detectors.md`](skills/paperconan/references/detectors.md) — 每个检测器的原理 / 典型命中 / **常见误报**
147
+ - [`interpretation.md`](skills/paperconan/references/interpretation.md) — severity 语义 + "signal not verdict" 红线 + 推荐回复话术
148
+
149
+ 整个 skill 收在 `skills/paperconan/` 一个目录里,安装只需软链一次。
150
+
151
+ ### 安装
152
+
153
+ ```bash
154
+ # 1. 装 CLI
155
+ pip install -e /path/to/paperconan # 本地开发;发到 PyPI 后改成 pip install paperconan
156
+
157
+ # 2a. Claude Code: 软链整个 skill 目录(一次搞定)
158
+ ln -s /path/to/paperconan/skills/paperconan ~/.claude/skills/paperconan
159
+
160
+ # 2b. Codex / 其他 agent: 在 AGENTS.md / CLAUDE.md / GEMINI.md 里引用
161
+ echo '@/path/to/paperconan/skills/paperconan/SKILL.md' >> AGENTS.md
162
+ ```
163
+
164
+ 之后在 agent 会话里说"我有一篇论文的 source data 在 /path/to/data,帮我做个 sanity check",agent 会自动调用 paperconan 并按 SKILL.md 的解读规则向你汇报。
165
+
166
+ ---
167
+
168
+ ## ⚠️ 重要声明
169
+
170
+ `paperconan` 输出的是 **算法标注的可疑模式 (statistical anomalies)**,**不是学术不端结论**。
171
+
172
+ 最终判定需由原作者澄清、期刊编辑部核实,或经独立同行复议。
173
+
174
+ **请走正规渠道**:
175
+
176
+ - 把可疑信号提交到 [PubPeer](https://pubpeer.com/)
177
+ - 联系期刊编辑部(每本期刊都有 ethics inquiry 渠道)
178
+ - 如果是你所在单位的论文,走单位 research integrity office
179
+
180
+ **请不要**:
181
+
182
+ - 在微博 / 微信 / 知乎 / 抖音直接喊话指控某个具体作者
183
+ - 用 paperconan 的输出截图作为 "实锤" 在社交媒体传播
184
+ - 跳过原作者澄清环节直接定性
185
+
186
+ 工具是中立的,使用方式不能。
187
+
188
+ ---
189
+
190
+ ## FAQ
191
+
192
+ **Q: 它会漏掉哪些造假?**
193
+
194
+ 会。`paperconan` 只看**数值表格**——无论它来自 `.xlsx/.csv/.tsv`,还是补充材料 `.pdf/.docx` 里的表格。下面这些它一概查不到:
195
+
196
+ - 图像 PS / Western blot 拼接 / 显微镜照片重复使用(图像取证是另一套,仍未做)
197
+ - **图表(bar chart / 曲线)里的数据**——只要数字没以表格形式给出,就读不到(不做像素数字化,详见路线图)
198
+ - 临床数据造假但 source data / 补充材料都没公开
199
+ - 实验完全没做但写进文章的部分
200
+ - 统计方法选择性使用 (p-hacking)
201
+ - 引用造假 / 同行评议造假
202
+
203
+ > 注:补充材料 PDF/Word 的**表格**现在能读了(v0.5,需 `pip install 'paperconan[all]'`)——这覆盖了"不需要单独 source data、数据就摆在论文里"的一大类。但"摆在图里"的数据(柱状图、折线图)和图像本身仍在覆盖之外。
204
+
205
+ **Q: 它会误报吗?**
206
+
207
+ 会。比如:
208
+
209
+ - 同一篇论文里 **合理共享的对照组数据** 会被标记为 "跨 sheet 复用"
210
+ - **量化产生的终位偏置**(细胞计数 / 4 视野平均 → 多 0.25 步长)会被标记为 "末位异常"
211
+ - **共享的剂量轴 / 时间轴** 会被标记为 "跨列复制"
212
+ - **密集 / 相关矩阵** 里成千上万对相同/线性列会被标记为 "列关系"
213
+
214
+ 不过这几类现在工具会主动帮你识别:同图重画的跨 sheet 复用会**自动降级**,密集矩阵的列关系洪泛会按整张表降级并打 `dense_block`,剂量/时间轴、量化偏置等会附 `likely_benign` 旁注,末位 χ² 也做了多重检验校正(看 q 值而非裸 p)。即便如此,报告里 high severity 的 anomaly **依然需要人工读 figure legend 和 Methods** 才能下判断。不要把 high = misconduct。
215
+
216
+ **Q: 我用它发现一篇看似有问题的论文,下一步该做什么?**
217
+
218
+ 1. 仔细读 paper 的 figure legends 和 Methods — 看作者是否已经在文字里说明了 "shared control" 等情况
219
+ 2. 自己用 Excel 复核 paperconan 标出的具体位置
220
+ 3. 提交到 PubPeer(最常见的做法),等原作者回应
221
+ 4. 如果是你所在单位,找 research integrity office
222
+
223
+ **Q: 这个工具会不会让普通硕博更难毕业?**
224
+
225
+ 不会。它检测的是 "编造数据" — 也就是 9 位小数一字不差、跨独立实验值池只用 17 个数字、9/10 完全一致只改 1 格这种 **直接编数字** 的模式。
226
+
227
+ 普通硕博正常做实验产生的 messy data(不规范、有 negative result、原始记录不齐),paperconan 一律不会标。
228
+
229
+ 如果你的数据 paperconan 也标出来了 — 那可能要回去看看自己的实验记录和分析流程了。
230
+
231
+ ---
232
+
233
+ ## 同款诞生背景
234
+
235
+ 这个工具最早是为做一期 YouTube / 抖音 / B 站视频造的 — 视频里我用它独立扫了 7 篇 Nature 及 Nature 子刊论文,全部找到了可疑模式。
236
+
237
+ 工具开源给所有人。希望它能帮老实做实验的人,减少作弊者占用版面、占用帽子、占用博士点的概率。
238
+
239
+ ---
240
+
241
+ ## 路线图 (好玩的可以一起做)
242
+
243
+ 短期:
244
+
245
+ - [x] CSV / TSV 输入 — v0.3 已实现(含跨文件数据复用检测)
246
+ - [x] HTML 报告(在 REPORT.md 之外)— v0.2 已实现,并取代 REPORT.md 成为默认人类可读输出
247
+ - [x] 把每条 finding 嵌入对应的表格片段 — v0.2 实现为可交互 HTML 表格(不是截图)
248
+ - [x] 作为 skill 给 agent 调用 — v0.2 已实现,见 skills/paperconan/SKILL.md
249
+ - [x] 补充材料 PDF / Word 表格输入 — v0.5 已实现(`pip install 'paperconan[all]'`,复用全部数值检测器)
250
+ - [ ] PubPeer 风格的 "为什么这值得复核" 旁注(LLM 生成的可选)
251
+
252
+ 中长期:
253
+
254
+ - [ ] 跨论文扫描(一个 lab 的多篇论文一起跑,看跨论文数据复用)
255
+ - [ ] 图表像素数字化(从 bar chart / 曲线里提取数据点)— 精度与假阳性风险高,需谨慎做
256
+ - [ ] 图像取证检测(Western blot / 显微镜照片重复)— 这块需要专门做
257
+ - [ ] 与 [PubPeer Public API](https://pubpeer.com/api) 联动
258
+
259
+ 欢迎 PR。给检测器加新模式,写 README 翻译,做 demo 都很欢迎。
260
+
261
+ ---
262
+
263
+ ## License
264
+
265
+ MIT.
266
+
267
+ ## Acknowledgments
268
+
269
+ - 名侦探柯南 / Detective Conan © 青山刚昌 / TMS Entertainment — 借了一下片头叙事结构,致敬不致敬都得感谢一下。
270
+ - [PubPeer](https://pubpeer.com/) — 学术界最重要的公开质疑平台,paperconan 的输出最终都应该走这里。
@@ -0,0 +1,50 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "paperconan"
7
+ version = "0.5.0"
8
+ description = "Numeric forensics for paper supplementary source-data: surfaces fabrication-style patterns (signal, not verdict)."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "Xiaotong" },
14
+ ]
15
+ keywords = ["research-integrity", "data-forensics", "pubpeer", "scientific-fraud", "xlsx", "audit"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Science/Research",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Topic :: Scientific/Engineering",
25
+ ]
26
+ dependencies = [
27
+ "openpyxl>=3.1",
28
+ "numpy>=1.24",
29
+ "scipy>=1.10",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ # Extract tables out of supplementary PDF / Word files. Optional so the base
34
+ # install (xlsx/csv/tsv) stays dependency-light; the parsers are imported lazily.
35
+ pdf = ["pdfplumber>=0.11"]
36
+ docx = ["python-docx>=1.1"]
37
+ all = ["pdfplumber>=0.11", "python-docx>=1.1"]
38
+
39
+ [project.urls]
40
+ Homepage = "https://github.com/zixixr/paperconan"
41
+ Issues = "https://github.com/zixixr/paperconan/issues"
42
+
43
+ [project.scripts]
44
+ paperconan = "paperconan._audit:main"
45
+
46
+ [tool.setuptools.packages.find]
47
+ where = ["src"]
48
+
49
+ [tool.pytest.ini_options]
50
+ markers = ["network: live network test, skipped unless PAPERCONAN_LIVE=1"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+