分析项目变乱,通常不是因为文件太多,而是名字没有表达用途、多个副本争当“最终版”,以及代码历史、数据批次和软件版本被混成了同一件事。
final.R
final_v2.R
final_v2_new.R
final_v2_new_last.R
解决方法不是设计更复杂的文件后缀,而是把职责分开:名称说明内容,目录说明角色,Git 保存代码历史,日期标识数据或结果快照,版本号标识正式发布。
文件名说明内容
文件名应当稳定、可预测,并能在脱离原目录后仍提供基本信息。R 项目中可以采用小写 snake_case:
sample_metadata.csv
clean_expression_data.R
fit_logistic_models.R
同一项目内保持一种分隔风格,比争论下划线还是连字符更重要。实用规则包括:
- 使用能说明内容或动作的词,避免
new、temp2、final; - 不依赖空格、大小写差异或特殊符号区分文件;
- 扩展名保持准确,不把格式信息重复写进主体名称;
- 批处理脚本需要固定顺序时,使用
01_、02_等零填充前缀。
01_import_data.R
02_clean_data.R
03_fit_models.R
04_make_figures.R
数字前缀表达执行顺序,不是版本号。如果脚本之间已经由工作流工具显式管理依赖,就不必再靠编号维持顺序。
日期只标识真正的时间快照
日期适合区分来自不同时间点、之后不会原地修改的数据或结果:
sample_manifest_2025-04-30.csv
database_snapshot_2025-04-30.parquet
report_2025-04-30.pdf
推荐使用 YYYY-MM-DD,这样按文件名排序就是时间顺序。日期不适合代替代码版本管理;每天修改同一脚本时,不应不断复制出 analysis_2025-04-29.R、analysis_2025-04-30.R。
文件名中的日期还应说明它代表什么:是数据采集日、下载日、分析运行日还是报告发布日期。若这个含义并不明显,应写进 README、数据字典或元数据中。
目录说明文件角色
一个简单的分析项目可以按职责分层:
project/
├─ data/
│ ├─ raw/
│ └─ processed/
├─ R/
├─ results/
│ ├─ figures/
│ └─ tables/
├─ README.md
└─ project.Rproj
data/raw/保存原始输入,通常不原地修改;data/processed/保存可由代码重新生成的中间数据;R/保存函数或分析脚本;results/保存表格、图形和模型输出;README.md说明项目入口、运行顺序与数据来源。
目录不必套很多层。只有当一组文件具有明确且长期稳定的共同职责时,才值得增加一层目录。
R 对象使用一致的命名风格
R 与 tidyverse 代码常使用 snake_case:
normalized_counts <- normalize_counts(raw_counts)
sample_metadata <- read.csv("data/raw/sample_metadata.csv")
函数名宜使用动词或动词短语,数据对象宜使用名词:
filter_significant_genes <- function(results, alpha = 0.05) {
results[results$p_value < alpha, ]
}
significant_genes <- filter_significant_genes(de_results)
逻辑值可以使用能直接读成判断的问题:
is_valid <- TRUE
has_missing_values <- anyNA(sample_metadata)
比起把类型缩写塞进每个名称,更重要的是表达对象在分析中的意义。patient_metadata 通常比 char_df 更有用,因为前者说明内容,后者只说明暂时的存储类型。
避免含糊和冲突
# 含义不清
df1 <- read.csv("data.csv")
result2 <- lm(y ~ x, data = df1)
# 表达角色
patient_data <- read.csv("data/raw/patients.csv")
age_model <- lm(outcome ~ age, data = patient_data)
避免把对象命名为常用函数或特殊缩写,例如 data、table、mean、T 和 F。单字符名称适合很短的数学表达或局部循环,不适合贯穿整个分析流程。
R 允许同一个名字被重新赋值,甚至改变类型:
x <- 1:5
x <- "hello"
代码不会因此报错,但一个名字在同一段流程中不断改变含义会增加阅读和调试成本。可以让对象经历清楚的阶段,例如 raw_counts、filtered_counts 和 normalized_counts;也可以在短而明确的管道中复用同一个语义稳定的名字。
Git 管理代码历史
Git 已经记录每次代码修改,因此受版本控制的脚本应保持稳定文件名:
fit_models.R
修改后提交历史,而不是复制出:
fit_models_v2.R
fit_models_v2_fixed.R
fit_models_final.R
一次提交应表达一个清楚的意图:
feat: add subgroup analysis
fix: correct age-group boundary
docs: explain input data source
Git 特别适合文本代码和配置。大型原始数据、隐私数据、凭据和可以重新生成的临时输出,通常不应直接提交;具体边界应由项目的数据治理要求和 .gitignore 共同决定。
Semantic Versioning 管理正式发布
vMAJOR.MINOR.PATCH 用于对外发布的软件、R 包、命令行工具或稳定接口:
MAJOR:引入不兼容变化;MINOR:向后兼容地增加功能;PATCH:向后兼容地修复问题。
例如,一个已发布 R 包可以从 1.2.0 更新到 1.2.1,并在 Git 中建立对应 tag:
git tag v1.2.1
普通分析脚本并不天然需要 Semantic Versioning。若没有使用者依赖它的公开接口,给每个 .R 文件附加 v2.0.0 往往只会重新制造副本混乱。此时提交哈希已经能够准确定位历史状态。
三种版本信息不要混用
| 要追踪的东西 | 推荐机制 | 示例 |
|---|---|---|
| 代码怎样变化 | Git commit | a1b2c3d |
| 哪次数据或结果快照 | ISO 日期或数据版本字段 | 2025-04-30 |
| 发布的软件接口 | Semantic Versioning + Git tag | v1.2.1 |
需要复现一次分析时,可以同时记录三者:输入数据的版本或日期、运行代码的 commit,以及关键软件环境。它们回答的是不同问题,不能用一个文件名后缀代替。
最小实践
一个项目不必先制定庞大的规范。先保持以下几条即可:
- 文件和 R 对象统一使用有语义的 snake_case;
- 原始数据、处理数据、代码和结果分开存放;
- 日期只用于真正的时间快照,并统一写成
YYYY-MM-DD; - 代码变化交给 Git,不创建
final_final副本; - 只有发布软件或稳定接口时才使用 Semantic Versioning;
- 在 README 中写清项目入口、数据来源和运行方式。
规范的目的不是让每个名字都变长,而是让未来的自己能够判断:这个文件是什么、从哪里来、是否可以重建,以及怎样找到它的历史。