← 返回提示詞庫
通用 #簡短 難度:入門

R语言基础

base-R

提供R语言编程指导,涵盖数据结构、数据处理、统计建模和可视化。

適用平台: ChatGPTClaudeGemini
---
name: base-r
description: 提供基础 R 编程指导,涵盖数据结构、数据整理、统计建模、可视化和 I/O,仅使用标准 R 安装中包含的包
---

# 基础 R 编程技能

基础 R 编程的综合参考——涵盖数据结构、控制流、函数、I/O、统计计算和绘图。

## 快速参考

### 数据结构

```r
# 向量(原子)
x <- c(1, 2, 3)              # 数值型
y <- c("a", "b", "c")        # 字符型
z <- c(TRUE, FALSE, TRUE)    # 逻辑型

# 因子
f <- factor(c("low", "med", "high"), levels = c("low", "med", "high"), ordered = TRUE)

# 矩阵
m <- matrix(1:6, nrow = 2, ncol = 3)
m[1, ]       # 第一行
m[, 2]       # 第二列

# 列表
lst <- list(name = "ali", scores = c(90, 85), passed = TRUE)
lst$name      # 按名称访问
lst[[2]]      # 按位置访问

# 数据框
df <- data.frame(
  id = 1:3,
  name = c("a", "b", "c"),
  value = c(10.5, 20.3, 30.1),
  stringsAsFactors = FALSE
)
df[df$value > 15, ]    # 筛选行
df$new_col <- df$value * 2  # 添加列
```

### 子集选择

```r
# 向量
x[1:3]             # 按位置
x[c(TRUE, FALSE)]  # 按逻辑值
x[x > 5]           # 按条件
x[-1]              # 排除第一个

# 数据框
df[1:5, ]                    # 前 5 行
df[, c("name", "value")]     # 选择列
df[df$value > 10, "name"]    # 筛选 + 选择
subset(df, value > 10, select = c(name, value))

# which() 用于索引位置
idx <- which(df$value == max(df$value))
```

### 控制流

```r
# if/else
if (x > 0) {
  "positive"
} else if (x == 0) {
  "zero"
} else {
  "negative"
}

# ifelse (向量化)
ifelse(x > 0, "pos", "neg")

# for 循环
for (i in seq_along(x)) {
  cat(i, x[i], "\n")
}

```

# while 循环
while (condition) {
  # 循环体
  if (stop_cond) break
}

# switch 语句
switch(type,
  "a" = do_a(),
  "b" = do_b(),
  stop("未知类型")
)
```

### 函数

```r
# 定义函数
my_func <- function(x, y = 1, ...) {
  result <- x + y
  return(result)  # 或者直接写:result
}

# 匿名函数
sapply(1:5, function(x) x^2)
# R 4.1+ 简写形式:
sapply(1:5, \(x) x^2)

# 有用函数:do.call 用于使用参数列表调用函数
do.call(paste, list("a", "b", sep = "-"))
```

### Apply 家族函数

```r
# sapply — 将结果简化为向量/矩阵
sapply(lst, length)

# lapply — 总是返回列表
lapply(lst, function(x) x[1])

# vapply — 类似于 sapply 但具有类型安全
vapply(lst, length, integer(1))

# apply — 对矩阵的边距(1=行,2=列)进行操作
apply(m, 2, sum)

# tapply — 按组应用函数
tapply(df$value, df$group, mean)

# mapply — 多元应用
mapply(function(x, y) x + y, 1:3, 4:6)

# aggregate — 类似于 tapply,用于数据框
aggregate(value ~ group, data = df, FUN = mean)
```

### 字符串操作

```r
paste("a", "b", sep = "-")    # "a-b"
paste0("x", 1:3)              # "x1" "x2" "x3"
sprintf("%.2f%%", 3.14159)    # "3.14%"
nchar("hello")                # 5
substr("hello", 1, 3)         # "hel"
gsub("old", "new", text)      # 替换所有匹配项
grep("pattern", x)            # 匹配项的索引
grepl("pattern", x)           # 逻辑向量
strsplit("a,b,c", ",")        # list("a","b","c")
trimws("  hi  ")              # "hi"
tolower("ABC")                # "abc"
```

### 数据 I/O

```r
# CSV
df <- read.csv("data.csv", stringsAsFactors = FALSE)
write.csv(df, "output.csv", row.names = FALSE)

# Tab 分隔文件
df <- read.delim("data.tsv")

# 通用读取
df <- read.table("data.txt", header = TRUE, sep = "\t")

# RDS(单个 R 对象,保留类型)
saveRDS(obj, "data.rds")
obj <- readRDS("data.rds")

# RData(多个对象)
save(df1, df2, file = "data.RData")
load("data.RData")

# 连接
con <- file("big.csv", "r")
chunk <- readLines(con, n = 100)
close(con)
```

### 基础绘图

```r
# 散点图
plot(x, y, main = "标题", xlab = "X轴", ylab = "Y轴",
     pch = 19, col = "steelblue", cex = 1.2)

# 折线图
plot(x, y, type = "l", lwd = 2, col = "red")
lines(x, y2, col = "blue", lty = 2)  # 添加折线

# 条形图
barplot(table(df$category), main = "计数",
        col = "lightblue", las = 2)

# 直方图
hist(x, breaks = 30, col = "grey80",
     main = "分布", xlab = "值")

# 箱线图
boxplot(value ~ group, data = df,
        col = "lightyellow", main = "按组别")

# 多图
par(mfrow = c(2, 2))  # 2x2 网格
# ... 四个图 ...
par(mfrow = c(1, 1))  # 重置

# 保存到文件
png("plot.png", width = 800, height = 600)
plot(x, y)
dev.off()

# 添加元素
legend("topright", legend = c("A", "B"),
       col = c("red", "blue"), lty = 1)
abline(h = 0, lty = 2, col = "grey")
text(x, y, labels = names, pos = 3, cex = 0.8)
```

### 统计

```r
# 描述性统计
mean(x); median(x); sd(x); var(x)
quantile(x, probs = c(0.25, 0.5, 0.75))
summary(df)
cor(x, y)
table(df$category)  # 频率表

# 线性模型
fit <- lm(y ~ x1 + x2, data = df)
summary(fit)
coef(fit)
predict(fit, newdata = new_df)
confint(fit)

# t检验
t.test(x, y)                    # 两样本
t.test(x, mu = 0)               # 单样本
t.test(before, after, paired = TRUE)

# 卡方检验
chisq.test(table(df$a, df$b))

# 方差分析
fit <- aov(value ~ group, data = df)
summary(fit)
TukeyHSD(fit)

# 相关性检验
cor.test(x, y, method = "pearson")
```

### 数据操作

```r
# 合并 (连接)
merged <- merge(df1, df2, by = "id")                  # 内连接
merged <- merge(df1, df2, by = "id", all = TRUE)      # 全外连接
merged <- merge(df1, df2, by = "id", all.x = TRUE)    # 左连接

# 重塑
wide <- reshape(long, direction = "wide",
                idvar = "id", timevar = "time", v.names = "value")
long <- reshape(wide, direction = "long",
                varying = list(c("v1", "v2")), v.names = "value")

# 排序
df[order(df$value), ]              # 升序
df[order(-df$value), ]             # 降序
df[order(df$group, -df$value), ]   # 多列排序

# 移除重复项
df[!duplicated(df), ]
df[!duplicated(df$id), ]

# 堆叠 / 组合
rbind(df1, df2)    # 堆叠行(列相同)
cbind(df1, df2)    # 绑定列(行相同)

# 转换列
df$log_val <- log(df$value)
df$category <- cut(df$value, breaks = c(0, 10, 20, Inf),
                   labels = c("low", "med", "high"))
```

### 环境与调试

```r
ls()                  # 列出对象
rm(x)                 # 移除对象
rm(list = ls())       # 清空所有对象
str(obj)              # 结构
class(obj)            # 类
typeof(obj)           # 内部类型
is.na(x)              # 检查NA
complete.cases(df)    # 没有NA的行
traceback()           # 错误后追踪
debug(my_func)        # 单步调试函数
browser()             # 代码中的断点
system.time(expr)     # 计时
Sys.time()            # 当前时间
```

## 参考文件

如需更深入的了解,请阅读 `references/` 中的参考文件:

### 函数陷阱与速查(摘自 R 4.5.3 参考手册)
非显而易见的行为、令人惊讶的默认值和棘手的交互——仅限 Claude 尚不了解的内容:
- **data-wrangling.md** — 阅读时机:子集返回错误类型,对数据框应用函数导致意外强制转换,merge/split/cbind 行为异常,过滤后因子水平仍然存在,table/duplicated 边缘情况。
- **modeling.md** — 阅读时机:公式语法令人困惑(`I()`、`*` 与 `:`、`/`),aov 给出错误的 SS 类型,glm 静默拟合 OLS,nls 不收敛,predict 返回错误尺度,optim/optimize 需要调优。
- **statistics.md** — 阅读时机:假设检验给出令人惊讶的结果,需要选择正确的 p.adjust 方法,聚类参数似乎错误,分布函数命名令人困惑(`d`/`p`/`q`/`r` 前缀)。
- **visualization.md** — 阅读时机:par 设置意外重置,layout/mfrow 交互令人困惑,轴标签被裁剪,颜色看起来不对,需要特殊图表(contour, persp, mosaic, pairs)。
- **io-and-text.md** — 阅读时机:read.table 静默丢弃数据或错误解析列,正则表达式行为与预期不同,sprintf 格式化很棘手,write.table 输出包含不需要的行名。
- **dates-and-system.md** — 阅读时机:Date/POSIXct 转换给出错误的日期,时区导致差一错误,difftime 单位出乎意料,需要以编程方式查找/列出/测试文件。
- **misc-utilities.md** — 阅读时机:do.call 行为与直接调用不同,需要 Reduce/Filter/Map,tryCatch 处理器未触发,all.equal 返回字符串而非逻辑值,时间序列函数需要设置。

## 编写良好 R 代码的技巧

- 在生产代码中,使用 `vapply()` 而非 `sapply()` — 它强制执行返回类型
- 优先使用 `seq_along(x)` 而非 `1:length(x)` — 后者在 `x` 为空时会出错
- 在 `read.csv()` / `data.frame()` 中使用 `stringsAsFactors = FALSE`(R 4.0 中默认值已更改)
- 尽可能向量化操作,而不是编写循环
- 使用 `stop()`、`warning()`、`message()` 进行错误处理 — 而不是 `print()`
- `<<-` 赋值到父环境 — 谨慎且有目的地使用
- `with(df, expr)` 避免到处重复 `df$`
- `Sys.setenv()` 和 `.Renviron` 用于环境变量
 文件:references/misc-utilities.md
# 杂项实用工具 — 快速参考

> R 函数中不明显的行为、陷阱和棘手的默认值。
> 仅限 Claude 尚不知道的内容。

---

## do.call

- `do.call(fun, args_list)` — `args` 必须是**列表**,即使只有一个参数。
- `quote = TRUE` 阻止在调用前评估参数 — 在传递表达式/符号时需要。
- `do.call` 内部 `substitute` 的行为与直接调用不同。此情况的语义未完全定义。
- 有用的模式:`do.call(rbind, list_of_dfs)` 用于组合数据帧列表。

---

## Reduce / Filter / Map / Find / Position

R 中来自基础包的函数式编程辅助函数 — 确实不明显。

- `Reduce(f, x)` 累积地应用二元函数 `f`:`Reduce("+", 1:4)` = `((1+2)+3)+4)`。对于非交换操作,方向很重要。
- `Reduce(f, x, accumulate = TRUE)` 返回所有中间结果——等同于 Python 的 `itertools.accumulate`。
- `Reduce(f, x, right = TRUE)` 从右侧折叠:`f(x1, f(x2, f(x3, x4)))`。
- `Reduce` 与 `init` 添加一个起始值:`Reduce(f, x, init = v)` = `f(f(f(v, x1), x2), x3)`。
- `Filter(f, x)` 保留 `f(elem)` 为 `TRUE` 的元素。与 `x[sapply(x, f)]` 不同,它能正确处理 `NULL`/空值。
- `Map(f, ...)` 是 `mapply(f, ..., SIMPLIFY = FALSE)` 的一个简单封装——总是返回一个列表。
- `Find(f, x)` 返回第一个 `f(elem)` 为 `TRUE` 的元素。`Find(f, x, right = TRUE)` 用于查找最后一个。
- `Position(f, x)` 返回第一个匹配的**索引**(类似于 `Find`,但返回位置而非值)。

---

## lengths

- `lengths(x)` 返回列表**每个元素**的长度。等同于 `sapply(x, length)` 但更快(用 C 实现)。
- 适用于任何类列表对象。返回整数向量。

---

## conditions (tryCatch / withCallingHandlers)

- `tryCatch` **展开**调用栈 — 处理器在调用环境中运行,而不是在错误发生的地方。无法恢复执行。
- `withCallingHandlers` 不会展开 — 处理器在条件被发出信号的地方运行。可以检查/记录,然后让条件传播。
- `tryCatch(expr, error = function(e) e)` 返回错误条件对象。
- `tryCatch(expr, warning = function(w) {...})` 捕获**第一个**警告并退出。使用 `withCallingHandlers` + `invokeRestart("muffleWarning")` 来抑制警告但继续执行。
- `tryCatch` 的 `finally` 子句总是运行(类似于 Java 的 try/finally)。
- `globalCallingHandlers()` 注册在会话期间持续存在的处理器(对日志记录很有用)。
- 自定义条件:`stop(errorCondition("msg", class = "myError"))` 然后用 `tryCatch(..., myError = function(e) ...)` 捕获。

---

## all.equal

- 使用容差(默认 `1.5e-8`,即 `sqrt(.Machine$double.eps)`)测试**近似相等**。
- 返回 `TRUE` 或一个描述差异的**字符串** — 而不是 `FALSE`。在条件语句中使用 `isTRUE(all.equal(x, y))`。
- `tolerance` 参数控制数值容差。`scale` 用于绝对与相对比较。
- 检查属性、名称、维度 — 比 `==` 更彻底。

---

## combn

- `combn(n, m)` 或 `combn(x, m)`:生成从 `x` 中选择 `m` 个项目的所有组合。
- 返回一个有 `m` 行的**矩阵**;每列是一个组合。
- `FUN` 参数对每个组合应用一个函数:`combn(5, 3, sum)` 返回所有 3 元素子集的和。
- `simplify = FALSE` 返回一个列表而不是矩阵。

---

## modifyList

- `modifyList(x, val)` 通过**名称**替换列表 `x` 中的元素。
- 将值设置为 `NULL` 会**移除**该元素。
- **会**添加 `x` 中不存在的新名称——它内部使用 `x[names(val)] <- val`,因此 `val` 中的任何名称都会被添加或替换。

---

## relist

- `unlist` 的逆操作:给定一个扁平向量和一个骨架列表,重建嵌套结构。
- `relist(flesh, skeleton)` — `flesh` 是扁平数据,`skeleton` 提供形状。
- 适用于因子、矩阵和嵌套列表。

---

## txtProgressBar

- `txtProgressBar(min, max, style = 3)` — style 3 显示百分比 + 进度条(最有用)。
- 使用 `setTxtProgressBar(pb, value)` 更新。使用 `close(pb)` 关闭。
- Style 1: 旋转 `|/-\`,style 2: 简单进度。只有 style 3 显示百分比。

---

## object.size

- 返回对象所用内存的**估计值**。对于共享引用不总是精确的。
- `format(object.size(x), units = "MB")` 用于人类可读的输出。
- 不计算环境或外部指针的大小。

---

## installed.packages / update.packages

- `installed.packages()` 可能很慢(扫描所有包)。使用 `find.package()` 或 `requireNamespace()` 检查特定包。
- `update.packages(ask = FALSE)` 在不提示的情况下更新所有包。
- `lib.loc` 指定要检查/更新的库。

---

## vignette / demo

- `vignette()` 列出所有小插图;`vignette("name", package = "pkg")` 打开特定小插图。
- `demo()` 列出所有演示;`demo("topic")` 交互式运行一个。
- `browseVignettes()` 在 HTML 中打开小插图浏览器。

---

## 时间序列: acf / arima / ts / stl / decompose

- `ts(data, start, frequency)`: `frequency` 是每单位时间的观测值(月度数据为12,季度数据为4)。
- `acf` 默认 `type = "correlation"`。使用 `type = "partial"` 获取 PACF。`plot = FALSE` 抑制自动绘图。
- `arima(x, order = c(p,d,q))` 用于 ARIMA 模型。`seasonal = list(order = c(P,D,Q), period = S)` 用于季节性分量。
- `arima` 通过卡尔曼滤波器处理时间序列中的 `NA` 值。
- `stl` 需要 `s.window`(季节性窗口)——必须指定,无默认值。`s.window = "periodic"` 假定固定季节性。
- `decompose`: 比 `stl` 简单,使用移动平均。`type = "additive"` 或 `"multiplicative"`。
- `stl` 结果分量:`$time.series` 矩阵,包含 `seasonal`、`trend`、`remainder` 列。
 FILE:references/data-wrangling.md 
# 数据整理 — 快速参考

> R 函数中不明显的行为、陷阱和棘手的默认设置。
> 仅限 Claude 尚不知道的内容。

---

## Extract / Extract.data.frame

R 基础包中的索引陷阱。

- `m[j = 2, i = 1]` 是 `m[2, 1]` 而不是 `m[1, 2]` — `[` 中的参数名称被 **忽略**,仅按位置匹配。切勿命名索引参数。
- 因子索引:`x[f]` 使用因子 `f` 的整数编码,而不是其字符标签。对于基于标签的索引,请使用 `x[as.character(f)]`。
- `x[[]]` 没有索引时总是错误。`x$name` 默认进行部分匹配;`x[["name"]]` 不进行(默认精确匹配)。
- 通过 `x[[i]] <- NULL` 或 `x$name <- NULL` 赋值 `NULL` 会 **删除** 该列表元素。
- 数据框 `[` 只有一个列时:`df[, 1]` 返回一个 **向量**(列的 drop=TRUE 默认),但 `df[1, ]` 返回一个 **数据框**(行的 drop=FALSE)。明确使用 `drop = FALSE`。
- 矩阵索引数据框 (`df[cbind(i,j)]`) 会先强制转换为矩阵 — 避免使用。

---

## subset

仅限交互式使用;编程不安全。

- `subset` 参数使用**非标准评估**——列名在数据框中解析,这在编程使用中可能悄无声息地选取错误的变量。在函数中使用 `[` 并明确逻辑。
- 逻辑条件中的 `NA` 被视为 `FALSE`(行被悄无声息地丢弃)。
- 子集化后因子可能保留未使用的水平;调用 `droplevels()`。

---

## match / %in%

- `%in%` **从不返回 NA**——这使得它与 `==` 不同,可以安全地用于 `if()` 条件。
- `match()` 仅返回**第一个**匹配项的位置;`table` 中的重复项被忽略。
- 因子、原始向量和列表在匹配前都转换为字符。
- `NaN` 匹配 `NaN` 但不匹配 `NA`;`NA` 仅匹配 `NA`。

---

## apply

- 在**数据框**上,`apply` 首先通过 `as.matrix` 强制转换为矩阵——混合类型变为字符。
- 返回值方向是转置的:如果 FUN 返回长度为 n 的向量,结果的维度为 `c(n, dim(X)[MARGIN])`。行结果变为**列**。
- 因子结果在输出数组中被强制转换为字符。
- `...` 参数不能与 `X`、`MARGIN` 或 `FUN` 共享名称(部分匹配风险)。

---

## lapply / sapply / vapply

- `sapply` 可以不可预测地返回向量、矩阵或列表——在非交互式代码中使用 `vapply` 并带有明确的 `FUN.VALUE` 模板。
- 直接在 `lapply` 中调用原语可能导致调度问题;应包装在 `function(x) is.numeric(x)` 中,而不是裸露的 `is.numeric`。
- `sapply` 与 `simplify = "array"` 可以生成更高维度的数组(不只是矩阵)。

---

## tapply

- 返回一个**数组**(不是数据框)。返回值的类信息**被丢弃**(例如,Date对象变为数值)。
- 传递给FUN的`...`参数**不会**被分割到各个单元格中——它们是全局应用的,因此FUN不应期望额外的参数与X的长度相同。
- `default = NA`填充空单元格;对于求和类操作,设置为`default = 0`。在R 3.4.0之前,这是硬编码为`NA`的。
- 使用`array2DF()`将结果转换为数据框。

---

## mapply

- 参数名是`SIMPLIFY`(全大写),而不是`simplify`——与`sapply`不一致。
- `MoreArgs`必须是一个**列表**,包含不进行向量化的参数。
- 将较短的参数循环利用到共同长度;零长度参数会产生零长度结果。

---

## merge

- 默认的`by`是`intersect(names(x), names(y))`——如果数据框共享列名,可能会在不经意间合并到不期望的列上。
- `by = 0`或`by = "row.names"`根据行名合并,并添加一个“Row.names”列。
- `by = NULL`(或`by.x`/`by.y`的长度都为0)会产生**笛卡尔积**。
- 结果默认按`by`列排序(`sort = TRUE`)。如需未排序的输出,请使用`sort = FALSE`。
- 重复的键匹配会产生**所有组合**(每对匹配一行)。

---

## split

- 如果`f`是因子列表,则使用交互作用;包含`"."`的级别可能会导致意外分割,除非更改`sep`。
- `drop = FALSE`(默认)保留空的因子级别作为空的列表元素。
- 支持公式语法:`split(df, ~ Month)`。

---

## cbind / rbind

- 数据框上的`cbind`调用`data.frame(...)`,而不是`cbind.matrix`。混合矩阵和数据框可能会产生意外结果。
- 数据框上的`rbind`按**名称**而不是位置匹配列。缺失的列会得到`NA`。
- `cbind(NULL)`返回`NULL`(不是矩阵)。为保持一致性,`rbind(NULL)`也返回`NULL`。

---

## table

- 默认**排除 NA** (`useNA = "no"`)。使用 `useNA = "ifany"` 或 `exclude = NULL` 来计算 NA。
- 将 `exclude` 设置为非空且非默认值意味着 `useNA = "ifany"`。
- 结果始终是一个**数组**(即使是一维),类为 "table"。使用 `as.data.frame(tbl)` 转换为数据框。
- 两种 NA(因子水平 NA 与实际 NA)的处理方式因 `useNA`/`exclude` 而异。

---

## duplicated / unique

- `duplicated` 将**第二次及以后**的出现标记为 TRUE,而不是第一次。使用 `fromLast = TRUE` 来反转。
- 对于数据框,作用于整行。对于列表,递归比较。
- `unique` 保留每个值的**第一次**出现。

---

## data.frame (陷阱)

- `stringsAsFactors = FALSE` 自 R 4.0.0 起为默认值(之前为 TRUE)。
- 原子向量会循环以匹配最长列,但仅当是精确倍数时。使用 `I()` 保护以防止转换。
- 只有在 `check.names = FALSE` 时才允许重复列名,但许多操作会静默地去重。
- 矩阵参数会扩展为多列,除非由 `I()` 保护。

---

## factor (陷阱)

- `as.numeric(f)` 返回**整数代码**,而不是原始值。使用 `as.numeric(levels(f))[f]` 或 `as.numeric(as.character(f))`。
- 只有 `==` 和 `!=` 适用于因子之间;因子必须具有相同的水平集。有序因子支持 `<`, `>`。
- 对因子使用 `c()` 会合并水平集(自 R 4.1.0 起),但早期版本会转换为整数。
- 水平默认排序,但排序顺序在创建时**取决于区域设置**。

---

## aggregate

- 公式接口 (`aggregate(y ~ x, data, FUN)`) 默认会丢弃 `NA` 分组。
- 数据框方法要求 `by` 是一个**列表**(而不是向量)。
- 返回的列以分组变量命名,结果列保留原始名称。
- 如果 FUN 返回多个值,结果列是数据框内部的**矩阵列**。

---

## complete.cases

- 返回一个逻辑向量:对于所有列/参数中**没有**NA的行,返回TRUE。
- 适用于多个参数(例如,`complete.cases(x, y)` 会同时检查两者)。

---

## order

- 返回一个索引的**排列向量**,而不是排序后的值。使用 `x[order(x)]` 进行排序。
- 默认是升序;对于数值降序,使用 `-x`,或者 `decreasing = TRUE`。
- 对于字符排序,取决于区域设置。使用 `method = "radix"` 进行与区域设置无关的快速排序。
- 对于大型整数/字符向量,`sort.int()` 配合 `method = "radix"` 速度快得多。
 文件:references/dates-and-system.md
# 日期和系统 — 快速参考

> R 函数中不明显的行为、陷阱和棘手的默认设置。
> 仅限 Claude 尚不知道的内容。

---

## 日期 (Date 类)

- `Date` 对象存储为**自 1970-01-01 以来的整数天数**。算术运算以天为单位。
- `Sys.Date()` 返回当前日期作为 Date 对象。
- `seq.Date(from, to, by = "month")` — "month" 增量可能产生长度不一的间隔。1 月 31 日加 1 个月得到 3 月 3 日(而不是 2 月 28 日)。
- `diff(dates)` 返回一个以天为单位的 `difftime` 对象。
- `format(date, "%Y")` 表示年份,`"%m"` 表示月份,`"%d"` 表示日期,`"%A"` 表示星期几名称(取决于区域设置)。
- 公元 1 年之前的年份可能无法正确处理。
- `length(date_vector) <- n` 如果延长,会用 `NA` 填充。

---

## 日期时间类 (POSIXct / POSIXlt)

- `POSIXct`: 自 1970-01-01 UTC 以来的秒数(紧凑型,一个数值向量)。
- `POSIXlt`: 包含 `$sec`、`$min`、`$hour`、`$mday`、`$mon`(0-11!)、`$year`(自 1900 年起!)、`$wday`(0-6,周日=0)、`$yday`(0-365)等组件的列表。
- `POSIXct` 和 `Date` 之间的转换:`as.Date(posixct_obj)` 默认使用 `tz = "UTC"` — 如果原始对象在另一个时区,可能会给出与预期不同的日期。
- `Sys.time()` 返回当前时区的 `POSIXct` 对象。
- `strptime` 返回 `POSIXlt` 对象;`as.POSIXct(strptime(...))` 用于获取 `POSIXct` 对象。
- `difftime` 算术:减去 `POSIXct` 对象会得到 `difftime` 对象。单位自动选择("secs", "mins", "hours", "days", "weeks")。

---

## difftime

- `difftime(time1, time2, units = "auto")` — 自动选择最小的合理单位。
- 显式单位:`"secs"`、`"mins"`、`"hours"`、`"days"`、`"weeks"`。没有 "months" 或 "years"(长度可变)。
- `as.numeric(diff, units = "hours")` 用于以特定单位提取数值。
- `units(diff_obj) <- "hours"` 会原地改变单位。

---

## system.time / proc.time

- `system.time(expr)` 返回 `user`、`system` 和 `elapsed` 时间。
- `gcFirst = TRUE`(默认):在计时前运行垃圾回收,以获得更一致的结果。
- `proc.time()` 返回 R 启动以来的累计时间 — 通过计算差值来获取时间间隔。
- `elapsed`(挂钟时间)可能小于 `user`(多线程 BLAS)或大于 `user`(I/O 等待)。

---

## Sys.sleep

- `Sys.sleep(seconds)` — 允许小数秒。实际睡眠时间可能更长(操作系统调度)。
- 进程在睡眠期间会**让出**给操作系统(不会忙等待)。

---

## options (关键选项)

选定的不明显选项:

- `options(scipen = n)`: 正值偏向固定记数法,负值偏向科学记数法。默认值为 0。适用于 `print`/`format`/`cat`,但不适用于 `sprintf`。
- `options(digits = n)`: 打印时使用的有效数字位数(1-22,默认 7)。仅为建议值。
- `options(digits.secs = n)`: 时间格式化中秒的小数部分最大位数(0-6,默认 0)。
- `options(warn = n)`: -1 = 忽略警告,0 = 收集警告(默认),1 = 立即显示警告,2 = 将警告转换为错误。
- `options(error = recover)`: 发生错误时进入调试器。`options(error = NULL)` 重置为默认值。
- `options(OutDec = ",")`: 更改输出中的小数点分隔符(影响 `format`、`print`,不影响 `sprintf`)。
- `options(stringsAsFactors = FALSE)`: `data.frame` 的全局默认值(自 R 4.0.0 起已默认为 FALSE,因此此选项已无关紧要)。
- `options(expressions = 5000)`: 最大嵌套评估次数。对于深度递归,请增加此值。
- `options(max.print = 99999)`: 控制 `print` 输出中的截断。
- `options(na.action = "na.omit")`: 模型函数中默认的 NA 处理方式。
- `options(contrasts = c("contr.treatment", "contr.poly"))`: 无序/有序因子的默认对比度。

---

## file.path / basename / dirname

- `file.path("a", "b", "c.txt")` → `"a/b/c.txt"`(平台适用的分隔符)。
- `basename("/a/b/c.txt")` → `"c.txt"`。`dirname("/a/b/c.txt")` → `"/a/b"`。
- `file.path` 不会规范化路径(不解析 `..`);为此请使用 `normalizePath()`。

---

## list.files

- `list.files(pattern = "*.csv")` — `pattern` 是一个**正则表达式**,而不是通配符!请使用 `glob2rx("*.csv")` 或 `"\\.csv$"`。
- `full.names = FALSE`(默认)仅返回基本名称。使用 `full.names = TRUE` 返回完整路径。
- `recursive = TRUE` 用于搜索子目录。
- `all.files = TRUE` 用于包含隐藏文件(以 `.` 开头的文件)。

---

## file.info

- 返回包含 `size`、`isdir`、`mode`、`mtime`、`ctime`、`atime`、`uid`、`gid` 的数据框。
- `mtime`: 修改时间 (POSIXct)。对 `file.info(f)$mtime` 有用。
- 在某些文件系统上,`ctime` 是状态更改时间,而非创建时间。

---

## file_test

- `file_test("-f", path)`: 如果常规文件存在则为 TRUE。
- `file_test("-d", path)`: 如果目录存在则为 TRUE。
- `file_test("-nt", f1, f2)`: 如果 f1 比 f2 新则为 TRUE。
- 比 `file.exists()` 在区分文件和目录方面更可靠。
 FILE:references/io-and-text.md 
# I/O 和文本处理 — 快速参考

> R 函数中不明显的行为、陷阱和棘手的默认设置。
> 仅限 Claude 尚不知道的内容。

---

## read.table (陷阱)

- `sep = ""` (默认) 表示**任意空白字符** (空格、制表符、换行符) — 而非字面上的空字符串。
- `comment.char = "#"` 默认 — 带有 `#` 的行会被截断。使用 `comment.char = ""` 禁用 (也更快)。
- `header` 自动检测: 如果第一行比后续行**少一个字段** (缺失的字段被假定为行名),则设置为 TRUE。
- `colClasses = "NULL"` 会完全**跳过**该列 — 对速度非常有用。
- `read.csv` 的默认值与 `read.table` 不同: `header = TRUE`、`sep = ","`、`fill = TRUE`、`comment.char = ""`。
- 对于大文件: 指定 `colClasses` 和 `nrows` 可显著减少内存使用。`read.table` 对于宽数据框 (数百列) 较慢;对于矩阵请使用 `scan` 或 `data.table::fread`。
- `stringsAsFactors = FALSE` 自 R 4.0.0 起 (之前为 TRUE)。

---

## write.table (陷阱)

- `row.names = TRUE` 是默认值——会生成一个未命名的第一列,导致重新读取时混淆。使用 `row.names = FALSE` 或 `col.names = NA` 以生成 Excel 兼容的 CSV。
- `write.csv` 固定了 `sep = ","`、`dec = "."`,并使用 `qmethod = "double"`——无法通过 `...` 覆盖这些设置。
- `quote = TRUE`(默认值)会引用字符/因子列。数值列从不被引用。
- 数据框中类似矩阵的列会静默地扩展为多列。
- 对于具有许多列(数百列以上)的数据框来说速度较慢;每列按其类别单独处理。

---

## read.fwf

- 读取固定宽度格式文件。`widths` 是一个字段宽度向量。
- **负宽度会跳过**相应数量的字符(用于忽略字段)。
- `buffersize` 控制一次读取的行数;对于大文件请增加此值。
- 内部使用 `read.table` 在分割字段后进行处理。

---

## count.fields

- 计算文件中每行的字段数——有助于诊断读取错误。
- `sep` 和 `quote` 参数与 `read.table` 的参数匹配。

---

## grep / grepl / sub / gsub (陷阱)

- 三种正则表达式模式:POSIX 扩展(默认)、`perl = TRUE`、`fixed = TRUE`。它们在边缘情况下的行为不同。
- **显式命名参数** — `x`/`pattern` 之后未命名的参数会按位置匹配到 `ignore.case`、`perl` 等。这是静默 bug 的常见来源。
- `sub` 只替换**第一个**匹配项;`gsub` 替换**所有**匹配项。
- 反向引用:替换字符串中的`"\\1"`(R 字符串中是双反斜杠)。当 `perl = TRUE` 时:`"\\U\\1"` 用于转换为大写。
- `grep(value = TRUE)` 返回匹配的**元素**;`grep(value = FALSE)`(默认)返回**索引**。
- `grepl` 返回逻辑向量 — 过滤时首选。
- `regexpr` 返回第一个匹配项的位置 + 长度(作为属性);`gregexpr` 以列表形式返回所有匹配项。
- `regexec` 返回匹配项 + 捕获组位置;`gregexec` 对所有匹配项执行此操作。
- 字符类如 `[:alpha:]` 在 POSIX 模式下必须放在 `[[:alpha:]]`(双括号)内。

---

## strsplit

- 返回一个**列表**(每个输入字符串一个元素),即使只有一个字符串。
- `split = ""` 或 `split = character(0)` 将字符串拆分为单个字符。
- 字符串开头的匹配:结果的第一个元素是 `""`。字符串末尾的匹配:没有尾随的 `""`。
- `fixed = TRUE` 更快,并避免正则表达式解释。
- 常见错误:未命名参数静默匹配 `fixed`、`perl` 等。

---

## substr / substring

- `substr(x, start, stop)`:提取/替换子字符串。1-索引,两端都包含。
- `substring(x, first, last)`:相同,但 `last` 默认为 `1000000L`(实际上是“到末尾”)。对 `first`/`last` 进行向量化。
- 赋值形式:`substr(x, 1, 3) <- "abc"` 进行原地替换(替换字符串必须与被替换部分长度相同)。

---

## trimws

- `which = "both"` (默认), `"left"`, 或 `"right"`。
- `whitespace = "[ \\t\\r\\n]"` — 可自定义的正则表达式,用于定义什么是空白字符。

---

## nchar

- `type = "bytes"` 计算字节数;`type = "chars"` (默认) 计算字符数;`type = "width"` 计算显示宽度。
- `nchar(NA)` 返回 `NA` (不是 2)。`nchar(factor)` 作用于因子水平标签。
- `keepNA = TRUE` (R 3.3.0 以来的默认值);设置为 `FALSE` 会将 `"NA"` 计为 2 个字符。

---

## format / formatC

- `format(x, digits, nsmall)`: `nsmall` 强制最小小数位数。`big.mark = ","` 添加千位分隔符。
- `formatC(x, format = "f", digits = 2)`: C 风格格式化。`format = "e"` 用于科学计数法,`"g"` 用于通用格式。
- `format` 返回字符向量;默认总是右对齐 (`justify = "right"`)。

---

## type.convert

- 将字符向量转换为适当的类型 (逻辑型、整型、双精度型、复数型、字符型)。
- `as.is = TRUE` (推荐): 保持字符型为字符型,而不是因子型。
- 对数据框按列应用。`tryLogical = TRUE` (R 4.3+) 转换 "TRUE"/"FALSE" 列。

---

## Rscript

- `commandArgs(trailingOnly = TRUE)` 获取脚本参数 (不包括 R/Rscript 标志)。
- Unix 上的 `#!` 行: `/usr/bin/env Rscript` 或完整路径。
- `--vanilla` 或 `--no-init-file` 跳过 `.Rprofile` 加载。
- 退出码: `quit(status = 1)` 表示错误退出。

---

## capture.output

- 捕获 `cat`、`print` 或任何写入标准输出的表达式的输出。
- `file = NULL` (默认) 返回字符向量。`file = "out.txt"` 直接写入文件。
- `type = "message"` 捕获标准错误而不是标准输出。

---

## URLencode / URLdecode

- `URLencode(url, reserved = FALSE)` 默认不编码保留字符(`/`、`?`、`&` 等)。
- 设置 `reserved = TRUE` 以编码 URL **组件**(查询参数值)。

---

## glob2rx

- 将 shell glob 模式转换为正则表达式:`glob2rx("*.csv")` → `"^.*\\.csv$"`。
- 与 `list.files(pattern = glob2rx("data_*.RDS"))` 结合使用时很有用。
 文件:references/modeling.md
# 建模 — 快速参考

> R 函数中不明显的行为、陷阱和棘手的默认值。
> 仅限 Claude 尚不知道的内容。

---

## formula

符号模型规范的陷阱。

- `I()` 是使用算术运算符字面意义的必需项:`y ~ x + I(x^2)`。没有 `I()`,`^` 表示交互交叉。
- `*` = 主效应 + 交互作用:`a*b` 扩展为 `a + b + a:b`。
- `(a+b+c)^2` = 所有主效应 + 所有两两交互作用(不是平方)。
- `-` 移除项:`(a+b+c)^2 - a:b` 只移除 `a:b` 交互作用。
- `/` 表示嵌套:`a/b` = `a + b %in% a` = `a + a:b`。
- 公式中的 `.` 表示“数据中的所有其他列”(在 `terms.formula` 上下文中)或“先前内容”(在 `update.formula` 中)。
- 公式对象带有一个用于变量查找的**环境**;`as.formula("y ~ x")` 使用 `parent.frame()`。

---

## terms / model.matrix

- `model.matrix` 创建设计矩阵,包括虚拟编码。默认对比:无序因子使用 `contr.treatment`,有序因子使用 `contr.poly`。
- `terms` 对象属性:`order`(每个项的交互顺序)、`intercept`、`factors` 矩阵。
- `model.matrix` 的列名可能令人惊讶:例如,`factorLevelName` 串联。

---

## glm

- 默认 `family = gaussian(link = "identity")` — `glm()` 在没有指定 `family` 时会静默地拟合 OLS(与 `lm` 相同,但速度较慢且输出基于离差)。
- 常用族:`binomial(link = "logit")`、`poisson(link = "log")`、`Gamma(link = "inverse")`、`inverse.gaussian()`。
- `binomial` 接受以下形式的响应:0/1 向量、逻辑值、因子(第二层级为成功)、或两列矩阵 `cbind(success, failure)`。
- `glm` 中的 `weights` 表示**先验权重**(而非频率权重)— 对于频率权重,请使用 cbind 技巧或偏移量。
- `predict.glm(type = "response")` 用于预测概率;默认 `type = "link"` 返回对数几率(对于逻辑回归)或对数率(对于泊松回归)。
- `anova(glm_obj, test = "Chisq")` 用于基于离差的检验;`"F"` 对于非高斯族是无效的。
- 拟似族(`quasibinomial`、`quasipoisson`)允许过离散 — 不计算 AIC。
- 收敛:如果默认的 25 次迭代不足,可使用 `control = glm.control(maxit = 100)`。

---

## aov

- `aov` 是 `lm` 的一个封装,用于存储平衡 ANOVA 的额外信息。对于非平衡设计,计算的是 Type I SS(顺序平方和)— 项的顺序很重要。
- 对于 Type III SS,请使用 `car::Anova()` 或将对比设置为 `contr.sum`/`contr.helmert`。
- 重复测量误差分层:`aov(y ~ A*B + Error(Subject/B))`。
- `summary.aov` 提供 ANOVA 表;`summary.lm(aov_obj)` 提供回归风格的摘要。

---

## nls

- 需要在 `start = list(...)` 中提供**良好的初始值**,否则收敛会失败。
- 自启动模型(`SSlogis`、`SSasymp` 等)会自动计算初始值。
- 算法 `"port"` 允许对参数设置边界(`lower`/`upper`)。
- 如果数据拟合得过于精确(没有残余噪声),收敛检查会失败 — 使用 `control = list(scaleOffset = 1)` 或对数据进行抖动。
- `weights` 参数用于加权 NLS;`na.action` 用于处理缺失值。

---

## step / add1

- `step` 通过 AIC(默认)进行**逐步**模型选择。使用 `k = log(n)` 进行 BIC。
- 方向:`direction = "both"`(默认)、`"forward"` 或 `"backward"`。
- `add1`/`drop1` 评估单项添加/删除;`step` 迭代调用这些函数。
- `scope` 参数定义搜索的上限/下限模型边界。
- `step` 会就地修改模型对象 — 对于包含许多候选项的大型模型可能会很慢。

---

## predict.lm / predict.glm

- `predict.lm` 与 `interval = "confidence"` 一起使用时,提供**平均响应**的置信区间 (CI);`interval = "prediction"` 提供**新观测值**的预测区间 (PI)(更宽)。
- `newdata` 必须具有与原始公式变量匹配的列 — 因子必须具有相同的水平。
- `predict.glm` 与 `type = "response"` 一起使用时,在响应尺度上给出预测(例如,逻辑回归的概率);`type = "link"`(默认)在链接尺度上给出。
- `se.fit = TRUE` 返回标准误差;对于 `predict.glm`,这些标准误差无论 `type` 如何都在**链接**尺度上。
- `predict.lm` 与 `type = "terms"` 一起使用时,返回每个项的贡献。

---

## loess

- `span` 控制平滑度(默认为 0.75)。`span < 1` 使用该比例的点;`span > 1` 使用所有点并调整距离。
- 最多 **4 个预测变量**。内存使用量大致与 n 的**平方**成正比(1000 个点约 10MB)。
- 允许 `degree = 0`(局部常数),但测试不充分 — 请谨慎使用。
- 与 S 的 `loess` 不完全相同;未实现条件化。
- `normalize = TRUE`(默认)将预测变量标准化到共同尺度;对于空间坐标,请设置为 `FALSE`。

---

## lowess vs loess

- `lowess` 是较旧的函数;返回 `list(x, y)` — 无法在新点进行预测。
- `loess` 是较新的公式接口,带有 `predict` 方法。
- `lowess` 参数是 `f`(span,默认 2/3);`loess` 参数是 `span`(默认 0.75)。
- `lowess` `iter` 默认为 3(稳健性迭代);`loess` 默认 `family = "gaussian"`(无稳健性)。

---

## smooth.spline

- 默认平滑参数由 **GCV**(广义交叉验证)选择。
- `cv = TRUE` 改用普通留一法交叉验证 — 请勿与重复的 x 值一起使用。
- `spar` 和 `lambda` 控制平滑度;`df` 可以指定等效的自由度。
- 返回带有 `predict`、`print`、`plot` 方法的对象。`fit` 组件包含节点和系数。

---

## optim

- 默认**最小化**。要最大化:设置 `control = list(fnscale = -1)`。
- 默认方法是 Nelder-Mead(无梯度,鲁棒但慢)。不适用于一维问题——请使用 `"Brent"` 或 `optimize()`。
- `"L-BFGS-B"` 是唯一支持箱型约束(`lower`/`upper`)的方法。设置边界会自动选择此方法并发出警告。
- `"SANN"`(模拟退火):收敛代码**始终为 0**——它从不“失败”。`maxit` = 总函数评估次数(默认 10000),没有其他停止准则。
- `parscale`:缩放参数,使每个参数的单位变化产生可比较的目标变化。对于混合尺度问题至关重要。
- `hessian = TRUE`:返回**无约束**问题的数值 Hessian,即使箱型约束处于活动状态。
- `fn` 可以返回 `NA`/`Inf`(除了 `"L-BFGS-B"` 始终要求有限值)。初始值必须是有限的。

---

## optimize / uniroot

- `optimize`:在有界区间上进行一维最小化。返回 `minimum` 和 `objective`。
- `uniroot`:在 `[lower, upper]` 中查找 `f` 的根。**要求** `f(lower)` 和 `f(upper)` 符号相反。
- `uniroot` 与 `extendInt = "yes"` 可以自动扩展区间以查找符号变化——但对于实际不穿过零的函数,可能会找到虚假根。
- `nlm`:牛顿型最小化器。梯度/Hessian 作为 `fn` 返回值的**属性**(不寻常的接口)。

---

## TukeyHSD

- 需要一个拟合的 `aov` 对象(不是 `lm`)。
- 默认 `conf.level = 0.95`。返回所有成对比较的调整 p 值和置信区间。
- 仅对**平衡**或接近平衡的设计有意义;对于非常不平衡的数据可能会过于宽松。

---

## anova (for lm)

- `anova(model)`: 序贯(I 型)SS — **项的顺序很重要**。
- `anova(model1, model2)`: 比较嵌套模型的 F 检验。
- 对于 II 型或 III 型 SS,请使用 `car::Anova()`。
 FILE:references/statistics.md 
# 统计学 — 快速参考

> R 函数中不明显的行为、陷阱和棘手的默认设置。
> 仅限 Claude 尚不知道的内容。

---

## chisq.test

- `correct = TRUE`(默认)仅对 **2x2 表**应用 Yates 连续性校正。
- `simulate.p.value = TRUE`: 蒙特卡洛,`B = 2000` 次重复(最小 p 值约为 0.0005)。模拟假设**固定的边际**(Fisher 式抽样,而非卡方假设)。
- 对于拟合优度检验:传入一个向量,而不是矩阵。`p` 必须总和为 1(或设置 `rescale.p = TRUE`)。
- 返回对象包括 `$expected`、`$residuals`(Pearson)和 `$stdres`(标准化)。

---

## wilcox.test

- 默认情况下,对于没有结的小样本,`exact = TRUE`。有结时,使用正态近似。
- `correct = TRUE` 对正态近似应用连续性校正。
- `conf.int = TRUE` 计算 Hodges-Lehmann 估计量和置信区间(不仅仅是 p 值)。
- 配对检验:`paired = TRUE` 使用符号秩检验(Wilcoxon),而不是秩和检验(Mann-Whitney)。

---

## fisher.test

- 对于大于 2x2 的表,使用模拟(`simulate.p.value = TRUE`)或网络算法。
- `workspace` 控制网络算法的内存;如果在大表上出现错误,请增加此值。
- `or` 参数检验特定的优势比(默认 1)— 仅适用于 2x2 表。

---

## ks.test

- 双样本检验或单样本对参考分布的检验。
- **不**能很好地处理平局——会发出警告并使用渐近近似。
- 对于复合假设(参数从数据中估计),p 值是**保守的**(过大)。对于离散分布,请使用 `dgof` 或 `ks.test` 并设置 `exact = NULL`。

---

## p.adjust

- 方法:`"holm"`(默认)、`"BH"`(Benjamini-Hochberg FDR)、`"bonferroni"`、`"BY"`、`"hochberg"`、`"hommel"`、`"fdr"`(BH 的别名)、`"none"`。
- `n` 参数:假设的总数(如果某些 p 值被排除,可以大于 `length(p)`)。
- 处理 `NA` 值:如果输入是 `NA`,则调整后的 p 值也是 `NA`。

---

## pairwise.t.test / pairwise.wilcox.test

- `p.adjust.method` 默认为 `"holm"`。更改为 `"BH"` 以控制 FDR。
- `pool.sd = TRUE`(t 检验的默认值):使用所有组的合并标准差(假设方差相等)。
- 返回 p 值矩阵,而不是检验统计量。

---

## shapiro.test

- 样本量必须在 3 到 5000 之间。
- 检验正态性;p 值低 = 反对正态性的证据。

---

## kmeans

- 建议 `nstart > 1`(例如,`nstart = 25`):从多个随机起点运行算法,返回最佳结果。
- 默认 `iter.max = 10`——对于收敛可能太低。对于大型/复杂数据,请增加此值。
- 默认算法是 "Hartigan-Wong"(通常是最好的)。非常接近的点可能导致不收敛(`ifault = 4` 时发出警告)。
- 簇编号是任意的;不同平台上的顺序可能不同。
- 当指定 k 时,总是返回 k 个簇(Lloyd-Forgy 除外,它可能返回更少的簇)。

---

## hclust

- `method = "ward.D2"` 正确实现了 Ward 准则(使用平方距离)。旧的 `"ward.D"` 没有平方距离(保留用于向后兼容)。
- 输入必须是 `dist` 对象。使用 `as.dist()` 转换对称矩阵。
- `plot()` 中的 `hang = -1` 将所有标签对齐到底部。

---

## dist

- `method = "euclidean"` (默认)。其他选项:`"manhattan"`、`"maximum"`、`"canberra"`、`"binary"`、`"minkowski"`。
- 返回一个 `dist` 对象(仅下三角)。使用 `as.matrix()` 获取完整矩阵。
- `"canberra"`:分子和分母都为零的项从总和中**省略**(不视为 0/0)。
- `Inf` 值:涉及 `Inf` 的欧几里得距离为 `Inf`。同一观测值中存在多个 `Inf` 会导致某些方法返回 `NaN`。

---

## prcomp vs princomp

- `prcomp` 使用 **SVD**(数值上更优);`princomp` 对协方差使用 `eigen`(稳定性较差,N-1 vs N 缩放)。
- `prcomp` 中的 `scale. = TRUE` 对变量进行标准化;当变量尺度差异很大时很重要。
- `princomp` 的标准差与 `prcomp` 的标准差相差 `sqrt((n-1)/n)` 倍。
- 两者都返回 `$rotation`(载荷)和 `$x`(得分);组件的符号在不同运行之间可能不同。

---

## density

- 默认带宽:`bw = "nrd0"` (Silverman 经验法则)。对于多峰数据,考虑 `"SJ"` 或 `"bcv"`。
- `adjust`:带宽的乘法因子。`adjust = 0.5` 将带宽减半(平滑度降低)。
- 默认核函数:`"gaussian"`。密度范围超出数据范围(由 `cut` 控制,默认为 3 个带宽)。
- `n = 512`:评估点的数量。增加以获得更平滑的绘图。
- `from`/`to`:明确限定评估范围。

---

## quantile

- **九种** `type` 选项(1-9)。默认 `type = 7`(R 默认,线性插值)。Type 1 = 经验 CDF 的逆(SAS 默认)。Type 4-9 是连续的;1-3 是不连续的。
- `na.rm = FALSE` 默认 — 如果存在任何 NA,则返回 NA。
- `names = TRUE` 默认,添加“0%”、“25%”等作为名称。

---

## 分布(所有分布的注意事项)

所有分布函数都遵循 `d/p/q/r` 模式。常见的非显而易见的点:

- **`r*()` 函数中的 `n` 参数**:如果 `length(n) > 1`,则使用 `length(n)` 作为计数,而不是 `n` 本身。因此 `rnorm(c(1,2,3))` 生成 3 个值,而不是 1+2+3。
- `log = TRUE` / `log.p = TRUE`:在对数尺度上计算,以提高尾部的数值稳定性。
- `lower.tail = FALSE` 直接给出生存函数 P(X > x)(在尾部比 1 - pnorm() 更准确)。
- **Gamma**:由 `shape` 和 `rate` (= 1/scale) 参数化。默认 `rate = 1`。同时指定 `rate` 和 `scale` 会报错。
- **Beta**:`shape1` (alpha),`shape2` (beta) — 没有 `mean`/`sd` 参数化。
- **Poisson `dpois`**:`x` 可以是非整数(如果 `log = FALSE`,则对非整数值返回 0 并发出警告)。
- **Weibull**:`shape` 和 `scale`(没有 `rate`)。R 的参数化:`f(x) = (shape/scale)(x/scale)^(shape-1) exp(-(x/scale)^shape)`。
- **Lognormal**:`meanlog` 和 `sdlog` 是**对数**的均值/标准差,而不是分布本身的均值/标准差。

---

## cor.test

- 默认方法:`"pearson"`。还有 `"kendall"` 和 `"spearman"`。
- 返回 `$estimate`、`$p.value`、`$conf.int`(置信区间仅适用于 Pearson)。
- 公式接口:`cor.test(~ x + y, data = df)` — 注意没有 LHS 的 `~`。

---

## ecdf

- 返回一个**函数**(阶梯函数)。在新值上调用它:`Fn <- ecdf(x); Fn(3.5)`。
- `plot(ecdf(x))` 绘制经验 CDF 图。
- 返回的函数是右连续且有左极限的(cadlag)。

---

## weighted.mean

- 处理权重中的 `NA`:如果权重为 `NA`,则丢弃该观测值。
- 权重不需要总和为 1;它们在内部进行归一化。
文件:references/visualization.md
# 可视化 — 快速参考

> R 函数中不明显的行为、陷阱和棘手的默认设置。
> 仅限 Claude 尚不知道的内容。

---

## par (陷阱)

- `par()` 设置是按设备进行的。打开新设备会重置所有内容。
- 设置 `mfrow`/`mfcol` 会将 `cex` 重置为 1,`mex` 重置为 1。对于 2x2 布局,基本 `cex` 乘以 0.83;对于 3 行/列以上,乘以 0.66。
- `mai`(英寸)、`mar`(行)、`pin`、`plt`、`pty` 都会相互作用。设备调整大小后恢复所有保存的参数可能会产生不一致的结果——按字母顺序排在最后的参数获胜。
- 通过 `par()` 设置 `bg` 也会设置 `new = FALSE`。通过 `par()` 设置 `fg` 也会设置 `col`。
- `xpd = NA` 裁剪到设备区域(允许在外部边距中绘图);`xpd = TRUE` 裁剪到图形区域;`xpd = FALSE`(默认)裁剪到绘图区域。
- `mgp = c(3, 1, 0)`:控制标题行 (`mgp[1]`)、标签行 (`mgp[2]`)、轴线 (`mgp[3]`)。所有单位均为 `mex`。
- `las`:0 = 平行于轴,1 = 水平,2 = 垂直于轴,3 = 垂直。**不**响应 `srt`。
- `tck = 1` 在绘图区域内绘制网格线。`tcl = -0.5`(默认)绘制向外的刻度线。
- `usr` 带对数刻度:包含坐标限制的 **log10** 值,而不是原始值。
- 只读参数:`cin`、`cra`、`csi`、`cxy`、`din`、`page`。

---

## layout

- `layout(mat)`,其中 `mat` 是一个整数矩阵,用于指定图形排列。
- `widths`/`heights` 接受 `lcm()` 用于混合绝对尺寸和相对尺寸。
- 比 `mfrow`/`mfcol` 更灵活,但一旦设置就无法查询(不像 `par("mfrow")`)。
- `layout.show(n)` 可视化布局以进行调试。

---

## axis / mtext

- `axis(side, at, labels)`:`side` 1=下,2=左,3=上,4=右。
- 轴标签之间的默认间距由 `par("mgp")` 控制。如果管理不当,标签可能会重叠。
- `mtext`:`line` 参数将文本定位在边距行中(0 = 紧邻绘图,正值 = 向外)。`adj` 控制水平位置(0-1)。
- `mtext` 与 `outer = TRUE` 一起使用时,会将文本写入**外部**边距(由 `par(oma = ...)` 设置)。

---

## curve

- 第一个参数可以是 `x` 中的**表达式**或函数:`curve(sin, 0, 2*pi)` 或 `curve(x^2 + 1, 0, 10)`。
- `add = TRUE` 用于叠加到现有绘图上。默认 `n = 101` 个评估点。
- `xname = "x"` 默认;如果你的表达式使用不同的变量名,请更改。

---

## pairs

- `panel` 函数接收 `(x, y, ...)` 用于每对。`lower.panel`、`upper.panel`、`diag.panel` 用于不同区域。
- `gap` 控制面板之间的间距(默认 1)。
- 公式接口:`pairs(~ var1 + var2 + var3, data = df)`。

---

## coplot

- 条件图:`coplot(y ~ x | a)` 或 `coplot(y ~ x | a * b)` 用于两个条件变量。
- `panel` 函数可以自定义;`rows`/`columns` 控制布局。
- 默认面板绘制点;使用 `panel = panel.smooth` 进行 loess 叠加。

---

## matplot / matlines / matpoints

- 将一个矩阵的列与另一个矩阵的列进行绘图。在列之间循环使用 `col`、`lty`、`pch`。
- 默认 `type = "l"`(与 `plot` 不同,`plot` 默认 `"p"`)。
- 适用于同时绘制多个时间序列或拟合曲线。

---

## contour / filled.contour / image

- `contour(x, y, z)`: `z` 必须是一个矩阵,其 `dim = c(length(x), length(y))`。
- `filled.contour` 具有非标准布局 — 它为颜色键创建了自己的绘图区域。**不能与 `par(mfrow)` 一起使用**。添加元素需要 `plot.axes` 参数。
- `image`: 将 z 值绘制为彩色矩形。默认颜色方案可能具有误导性;请明确设置 `col`。
- 对于 `image`,`x` 和 `y` 根据上下文指定**单元格边界**或**中点**。

---

## persp

- `persp(x, y, z, theta, phi)`: `theta` = 方位角,`phi` = 余纬。
- 返回一个**变换矩阵**(不可见),用于将 3D 投影到 2D — 使用 `trans3d()` 向透视图添加点/线。
- `shade` 和 `col` 控制曲面着色。`border = NA` 移除网格线。

---

## segments / arrows / rect / polygon

- 都接受向量化坐标;根据需要循环使用。
- `arrows`: `code = 1`(箭头在起点),`code = 2`(箭头在终点,默认),`code = 3`(两端都有)。
- `polygon`: 最后一个点自动连接到第一个点。用 `col` 填充;`border` 控制轮廓。
- `rect(xleft, ybottom, xright, ytop)` — 请注意参数顺序与其他系统不同。

---

## dev / dev.off / dev.copy

- `dev.new()` 打开一个新设备。`dev.off()` 关闭当前设备(并刷新文件设备(如 `pdf`)的输出)。
- 在**最后**一个打开的设备上调用 `dev.off()` 会恢复到空设备。
- `dev.copy(pdf, file = "plot.pdf")` 后跟 `dev.off()` 用于保存当前绘图。
- `dev.list()` 返回所有打开的设备;`dev.cur()` 返回活动设备。

---

## pdf

- 必须调用 `dev.off()` 来完成文件。否则,文件可能为空/损坏。
- `onefile = TRUE`(默认):一个 PDF 中包含多页。`onefile = FALSE`:每页一个文件(文件名中使用 `%d` 进行编号)。
- 建议使用 `useDingbats = FALSE` 以避免某些 PDF 查看器和 pch 符号的问题。
- 默认大小:7x7 英寸。`family` 控制字体族。

---

## png / 位图设备

- `res` 控制 DPI(默认 72)。用于出版:`res = 300`,并使用适当的 `width`/`height`(像素或英寸,配合 `units = "in"`)。
- `type = "cairo"`(在支持 cairo 的系统上)提供比默认更好的抗锯齿效果。
- `bg = "transparent"` 用于透明背景(PNG 支持 alpha 通道)。

---

## 颜色 / rgb / hcl / col2rgb

- `colors()` 返回所有 657 种命名颜色。`col2rgb("color")` 返回 RGB 矩阵。
- `rgb(r, g, b, alpha, maxColorValue = 255)` — 注意 `maxColorValue` 默认值为 1,而不是 255。
- `hcl(h, c, l)`:感知均匀的色彩空间。推荐用于颜色标度。
- `adjustcolor(col, alpha.f = 0.5)`:添加透明度的简便方法。

---

## colorRamp / colorRampPalette

- `colorRamp` 返回一个将 [0,1] 映射到 RGB 矩阵的**函数**。
- `colorRampPalette` 返回一个接受 `n` 并返回 `n` 个插值颜色的**函数**。
- `space = "Lab"` 提供比 `"rgb"` 更感知均匀的插值。

---

## palette / recordPlot

- `palette()` 返回当前调色板(默认 8 种颜色)。`palette("Set1")` 设置一个内置调色板。
- 绘图中的整数颜色是调色板的索引(带循环)。索引 0 = 背景色。
- `recordPlot()` / `replayPlot()`:保存和恢复完整的绘图——依赖设备且在不同会话间不稳定。
 FILE:assets/analysis_template.R 
# ============================================================
# 分析模板 — Base R
# 复制此文件,重命名,并填写您的详细信息。
# ============================================================
# 作者  : 
# 日期    : 
# 数据    : 
# 目的    : 
# ============================================================

# ── 0. 设置 ─────────────────────────────────────────────────
# 清空环境(可选 — 如果加载到现有会话中则注释掉)
rm(list = ls())

# 如有需要,设置工作目录
# setwd("/path/to/your/project")

# 可复现性
set.seed(42)

# 库 — 取消注释您需要的
# library(haven)        # 读取 .dta / .sav / .sas
# library(readxl)       # 读取 Excel 文件
# library(openxlsx)     # 写入 Excel 文件
# library(foreign)      # 较旧的 Stata / SPSS 格式
# library(survey)       # 调查加权分析
# library(lmtest)       # Breusch-Pagan, Durbin-Watson 等
# library(sandwich)     # 稳健标准误
# library(car)          # Type II/III ANOVA, VIF

# ── 1. 加载数据 ─────────────────────────────────────────────
df <- read.csv("your_data.csv", stringsAsFactors = FALSE)
# df <- readRDS("your_data.rds")
# df <- haven::read_dta("your_data.dta")

# 初步查看 — 务必运行这些
dim(df)
str(df)
head(df, 10)
summary(df)

# ── 2. 数据质量检查 ────────────────────────────────────
# 缺失值
na_report <- data.frame(
  column   = names(df),
  n_miss   = colSums(is.na(df)),
  pct_miss = round(colMeans(is.na(df)) * 100, 1),
  row.names = NULL
)
print(na_report[na_report$n_miss > 0, ])

# 重复值
n_dup <- sum(duplicated(df))
cat(sprintf("重复行数: %d\n", n_dup))

# 分类列的唯一值
cat_cols <- names(df)[sapply(df, function(x) is.character(x) | is.factor(x))]
for (col in cat_cols) {
  cat(sprintf("\n%s (%d 个唯一值):\n", col, length(unique(df[[col]]))))
  print(table(df[[col]], useNA = "ifany"))
}

# ── 3. 清理与转换 ─────────────────────────────────────
# 重命名列(示例)
# names(df)[names(df) == "old_name"] <- "new_name"

# 转换类型
# df$group <- as.factor(df$group)
# df$date  <- as.Date(df$date, format = "%Y-%m-%d")

# 重新编码值(示例)
# df$gender <- ifelse(df$gender == 1, "Male", "Female")

# 创建新变量(示例)
# df$log_income <- log(df$income + 1)
# df$age_group  <- cut(df$age,
#                      breaks = c(0, 25, 45, 65, Inf),
#                      labels = c("18-25", "26-45", "46-65", "65+"))

# 筛选行(示例)
# df <- df[df$year >= 2010, ]
# df <- df[complete.cases(df[, c("outcome", "predictor")]), ]

# 删除未使用的因子水平
# df <- droplevels(df)

# ── 4. 描述性统计 ────────────────────────────────
# 数值型汇总
num_cols <- names(df)[sapply(df, is.numeric)]
round(sapply(df[num_cols], function(x) c(
  n      = sum(!is.na(x)),
  mean   = mean(x, na.rm = TRUE),
  sd     = sd(x, na.rm = TRUE),
  median = median(x, na.rm = TRUE),
  min    = min(x, na.rm = TRUE),
  max    = max(x, na.rm = TRUE)
)), 3)

# 交叉制表
# table(df$group, df$category, useNA = "ifany")
# prop.table(table(df$group, df$category), margin = 1)  # 行比例

# ── 5. 可视化 (EDA) ───────────────────────────────────
par(mfrow = c(2, 2))

# 主要结果变量的直方图
hist(df$outcome_var,
     main   = "结果变量分布",
     xlab   = "结果变量",
     col    = "steelblue",
     border = "white",
     breaks = 30)

# 按组划分的箱线图
boxplot(outcome_var ~ group_var,
        data = df,
        main = "按组划分的结果变量",
        col  = "lightyellow",
        las  = 2)

# 散点图
plot(df$predictor, df$outcome_var,
     main = "预测变量 vs 结果变量",
     xlab = "预测变量",
     ylab = "结果变量",
     pch  = 19,
     col  = adjustcolor("steelblue", alpha.f = 0.5),
     cex  = 0.8)
abline(lm(outcome_var ~ predictor, data = df),
       col = "red", lwd = 2)

# 相关矩阵(仅限数值列)
cor_mat <- cor(df[num_cols], use = "complete.obs")
image(cor_mat,
      main = "相关矩阵",
      col  = hcl.colors(20, "RdBu", rev = TRUE))

par(mfrow = c(1, 1))

# ── 6. 分析 ───────────────────────────────────────────────

# ·· 6a. 均值比较 ··
t.test(outcome_var ~ group_var, data = df)

# ·· 6b. 线性回归 ··
fit <- lm(outcome_var ~ predictor1 + predictor2 + group_var,
          data = df)
summary(fit)
confint(fit)

# 检查多重共线性的VIF(需要car包)
# car::vif(fit)

# 稳健标准误(需要lmtest + sandwich包)
# lmtest::coeftest(fit, vcov = sandwich::vcovHC(fit, type = "HC3"))

# ·· 6c. 方差分析 (ANOVA) ··
# fit_aov <- aov(outcome_var ~ group_var, data = df)
# summary(fit_aov)
# TukeyHSD(fit_aov)

# ·· 6d. 逻辑回归 (二元结果) ··
# fit_logit <- glm(binary_outcome ~ x1 + x2,
#                  data   = df,
#                  family = binomial(link = "logit"))
# summary(fit_logit)
# exp(coef(fit_logit))         # 优势比
# exp(confint(fit_logit))      # 优势比置信区间

# ── 7. 模型诊断 ─────────────────────────────────────
par(mfrow = c(2, 2))
plot(fit)
par(mfrow = c(1, 1))

# 残差正态性
shapiro.test(residuals(fit))

# 同方差性 (需要 lmtest 包)
# lmtest::bptest(fit)

# ── 8. 保存输出 ────────────────────────────────────────────
# 清理后的数据
# write.csv(df, "data_clean.csv", row.names = FALSE)
# saveRDS(df, "data_clean.rds")

# 模型结果到文本文件
# sink("results.txt")
# cat("=== 线性模型 ===\n")
# print(summary(fit))
# cat("\n=== 置信区间 ===\n")
# print(confint(fit))
# sink()

# 图形到文件
# png("figure1_distributions.png", width = 1200, height = 900, res = 150)
# par(mfrow = c(2, 2))
# # ... 你的图 ...
# par(mfrow = c(1, 1))
# dev.off()

# ============================================================
# 模板结束
# ============================================================
 文件:scripts/check_data.R 
# check_data.R — 针对任何 R 数据框的快速数据质量报告
# 用法: source("check_data.R") 然后调用 check_data(df)
# 或者: source("check_data.R"); check_data(read.csv("yourfile.csv"))

check_data <- function(df, top_n_levels = 8) {
  
  if (!is.data.frame(df)) stop("输入必须是一个数据框。")
  
  n_row <- nrow(df)
  n_col <- ncol(df)
  
  cat("══════════════════════════════════════════\n")
  cat("  数据质量报告\n")
  cat("══════════════════════════════════════════\n")
  cat(sprintf("  行数: %d    列数: %d\n", n_row, n_col))
  cat("══════════════════════════════════════════\n\n")
  
  # ── 1. 列概览 ──────────────────────
  cat("── 列概览 ────────────────────────\n")
  
  for (col in names(df)) {
    x     <- df[[col]]
    cls   <- class(x)[1]
    n_na  <- sum(is.na(x))
    pct   <- round(n_na / n_row * 100, 1)
    n_uniq <- length(unique(x[!is.na(x)]))
    
    na_flag <- if (n_na == 0) "" else sprintf("  *** %d 个缺失值 (%.1f%%)", n_na, pct)
    cat(sprintf("  %-20s  %-12s  %d 个唯一值%s\n",
                col, cls, n_uniq, na_flag))
  }
  
  # ── 2. 缺失值总结 ────────────────────────────
  cat("\n── 缺失值总结 ─────────────────────────────\n")
  
  na_counts <- sapply(df, function(x) sum(is.na(x)))
  cols_with_na <- na_counts[na_counts > 0]
  
  if (length(cols_with_na) == 0) {
    cat("  无缺失值。\n")
  } else {
    cat(sprintf("  存在缺失值的列: %d / %d\n\n", length(cols_with_na), n_col))
    for (col in names(cols_with_na)) {
      bar_len  <- round(cols_with_na[col] / n_row * 20)
      bar      <- paste0(rep("█", bar_len), collapse = "")
      pct_na   <- round(cols_with_na[col] / n_row * 100, 1)
      cat(sprintf("  %-20s  [%-20s]  %d (%.1f%%)\n",
                  col, bar, cols_with_na[col], pct_na))
    }
  }
  
  # ── 3. 数值列 ───────────────────────
  num_cols <- names(df)[sapply(df, is.numeric)]
  
  if (length(num_cols) > 0) {
    cat("\n── 数值列 ────────────────────────\n")

cat(sprintf("  %-20s  %8s  %8s  %8s  %8s  %8s\n",
                "Column", "Min", "Mean", "Median", "Max", "SD"))
    cat(sprintf("  %-20s  %8s  %8s  %8s  %8s  %8s\n",
                "──────", "───", "────", "──────", "───", "──"))
    
    for (col in num_cols) {
      x  <- df[[col]][!is.na(df[[col]])]
      if (length(x) == 0) next
      cat(sprintf("  %-20s  %8.3g  %8.3g  %8.3g  %8.3g  %8.3g\n",
                  col,
                  min(x), mean(x), median(x), max(x), sd(x)))
    }
  }
  
  # ── 4. Factor / character columns ───────────
  cat_cols <- names(df)[sapply(df, function(x) is.factor(x) | is.character(x))]
  
  if (length(cat_cols) > 0) {
    cat("\n── CATEGORICAL COLUMNS ────────────────────\n")
    
    for (col in cat_cols) {
      x    <- df[[col]]
      tbl  <- sort(table(x, useNA = "no"), decreasing = TRUE)
      n_lv <- length(tbl)
      cat(sprintf("\n  %s  (%d unique values)\n", col, n_lv))
      
      show <- min(top_n_levels, n_lv)
      for (i in seq_len(show)) {
        lbl <- names(tbl)[i]
        cnt <- tbl[i]
        pct <- round(cnt / n_row * 100, 1)
        cat(sprintf("    %-25s  %5d  (%.1f%%)\n", lbl, cnt, pct))
      }
      if (n_lv > top_n_levels) {
        cat(sprintf("    ... and %d more levels\n", n_lv - top_n_levels))
      }
    }
  }
  
  # 5. Duplicate rows ────────────────────────
  cat("\n── DUPLICATES ─────────────────────────────\n")
  n_dup <- sum(duplicated(df))
  if (n_dup == 0) {
    cat("  No duplicate rows.\n")
  } else {
    cat(sprintf("  %d duplicate row(s) found (%.1f%% of data)\n",
                n_dup, n_dup / n_row * 100))
  }
  
  cat("\n══════════════════════════════════════════\n")
  cat("  END OF REPORT\n")
  cat("══════════════════════════════════════════\n")

# 以不可见方式返回,用于程序化使用
  invisible(list(
    dims       = c(rows = n_row, cols = n_col),
    na_counts  = na_counts,
    n_dupes    = n_dup
  ))
}
 文件:scripts/scaffold_analysis.R
#!/usr/bin/env Rscript
# scaffold_analysis.R — 生成一个入门分析脚本
#
# 用法(从终端):
#   Rscript scaffold_analysis.R myproject
#   Rscript scaffold_analysis.R myproject outcome_var group_var
#
# 用法(从 R 控制台):
#   source("scaffold_analysis.R")
#   scaffold_analysis("myproject", outcome = "score", group = "treatment")
#
# 输出:myproject_analysis.R (可编辑)

scaffold_analysis <- function(project_name,
                               outcome   = "outcome",
                               group     = "group",
                               data_file = NULL) {
  
  if (is.null(data_file)) data_file <- paste0(project_name, ".csv")
  out_file <- paste0(project_name, "_analysis.R")
  
  template <- sprintf(
'# ============================================================
# 项目 : %s
# 创建日期 : %s
# ============================================================

# ── 0. 库 ─────────────────────────────────────────────
# 在此处添加您需要的包
# library(ggplot2)
# library(haven)     # 用于 .dta 文件
# library(openxlsx)  # 用于 Excel 输出

# ── 1. 加载数据 ─────────────────────────────────────────────
df <- read.csv("%s", stringsAsFactors = FALSE)

# 快速检查 — 始终首先执行此操作
cat("维度:", dim(df), "\\n")
str(df)
head(df)

# ── 2. 探索 / EDA ─────────────────────────────────────────
summary(df)

# NA 检查
na_counts <- colSums(is.na(df))
na_counts[na_counts > 0]

# 关键变量分布
hist(df$%s, main = "%%s 的分布", xlab = "%s")

if ("%s" %%in%% names(df)) {
  table(df$%s)
  barplot(table(df$%s),
          main = "按 %s 分组的计数",
          col  = "steelblue",
          las  = 2)
}

# ── 3. 清理 / 转换 ──────────────────────────────────────
# df <- df[complete.cases(df), ]        # 删除含有任何 NA 的行
# df$%s <- as.factor(df$%s)            # 转换为因子

# ── 4. 分析 ───────────────────────────────────────────────

# 按组描述性统计
tapply(df$%s, df$%s, mean, na.rm = TRUE)
tapply(df$%s, df$%s, sd,   na.rm = TRUE)

# t 检验(两组)
# t.test(%s ~ %s, data = df)

# 线性模型
fit <- lm(%s ~ %s, data = df)
summary(fit)
confint(fit)

# 方差分析(多组)
# fit_aov <- aov(%s ~ %s, data = df)
# summary(fit_aov)
# TukeyHSD(fit_aov)

# ── 5. 可视化结果 ──────────────────────────────────────
par(mfrow = c(1, 2))

# 按组箱线图
boxplot(%s ~ %s,
        data = df,
        main = "按 %s 分组的 %s",
        xlab = "%s",
        ylab = "%s",
        col  = "lightyellow")

# 模型诊断
plot(fit, which = 1)  # 残差 vs 拟合值

par(mfrow = c(1, 1))

# ── 6. 保存输出 ────────────────────────────────────────────
# 保存清理后的数据
# write.csv(df, "%s_clean.csv", row.names = FALSE)

# 将模型摘要保存到文本
# sink("%s_results.txt")
# summary(fit)
# sink()

# 将图保存到文件
# png("%s_boxplot.png", width = 800, height = 600, res = 150)
# boxplot(%s ~ %s, data = df, col = "lightyellow")
# dev.off()
',
    project_name,
    format(Sys.Date(), "%%Y-%%m-%%d"),
    data_file,
    # 第2节 — EDA
    outcome, outcome, outcome,
    group, group, group, group,
    # 第3节
    group, group,
    # 第4节
    outcome, group,
    outcome, group,
    outcome, group,
    outcome, group,
    outcome, group,
    outcome, group,
    # 第5节
    outcome, group,
    outcome, group,
    group, outcome,
    # 第6节
    project_name, project_name, project_name,
    outcome, group
  )
  
  writeLines(template, out_file)
  cat(sprintf("已创建: %s\n", out_file))
  invisible(out_file)
}

# ── 从命令行运行 ─────────────────────────────────────
if (!interactive()) {
  args <- commandArgs(trailingOnly = TRUE)
  
  if (length(args) == 0) {
    cat("用法: Rscript scaffold_analysis.R <项目名称> [结果变量] [分组变量]\n")
    cat("示例: Rscript scaffold_analysis.R myproject score treatment\n")
    quit(status = 1)
  }
  
  project <- args[1]
  outcome <- if (length(args) >= 2) args[2] else "outcome"
  group   <- if (length(args) >= 3) args[3] else "group"
  
  scaffold_analysis(project, outcome = outcome, group = group)
}
 文件:README.md 
# base-r-skill 

GitHub: https://github.com/iremaydas/base-r-skill

一个用于基础R编程的Claude Code技能。

---

## 故事

我是一名政治学博士研究生,经常使用R,但绝不会称自己为“R专家”。我需要一个用于基础R的Claude Code技能——不带tidyverse,不带ggplot2,只有纯R——但我到处都找不到。

所以我自己动手做了一个。晚上11点。请Claude帮我为Claude构建一个技能。

如果你也每次都会谷歌 `how to drop NA rows in R`,那么这个就是为你准备的。🫶

---

## 包含内容

```
base-r/
├── SKILL.md                    # 主技能文件
├── references/                 # 陷阱和非显而易见的行为
│   ├── data-wrangling.md       # 子集陷阱、apply 家族、merge、因子怪癖
│   ├── modeling.md             # 公式语法、lm/glm/aov/nls、optim
│   ├── statistics.md           # 假设检验、分布、聚类
│   ├── visualization.md        # par、layout、devices、colors
│   ├── io-and-text.md          # read.table、grep、regex、format
│   ├── dates-and-system.md     # Date/POSIXct 陷阱、options()、文件操作
│   └── misc-utilities.md       # tryCatch、do.call、时间序列、实用工具
├── scripts/
│   ├── check_data.R            # 任何数据框的快速数据质量报告
│   └── scaffold_analysis.R     # 生成一个入门分析脚本
└── assets/
    └── analysis_template.R     # 复制粘贴分析模板
```

参考文件是从 R 4.5.3 官方手册中精简而来——**19,518 行 → 945 行**(减少 95%)。只保留了那些非显而易见的内容:陷阱、令人惊讶的默认值、棘手的交互。Claude 已经很了解的内容都被删除了。

---

## 如何使用

将此技能添加到你的 Claude Code 设置中,指向此仓库即可。然后,当你处理 R 任务时,Claude 将自动加载相关的参考文件。

最适用于:
- 基础 R 数据操作(无 tidyverse)
- 使用 `lm`、`glm`、`aov` 进行统计建模
- 使用 `plot`、`par`、`barplot` 进行基础图形绘制
- 理解你的 R 代码为何会做出奇怪的事情

不适用于:tidyverse、ggplot2、Shiny 或 R 包开发。

---

## `check_data.R` 脚本

这可能是这里最有用的独立功能。引用它并在任何数据帧上运行 `check_data(df)`,以获取包含维度、NA 计数、数值摘要和分类细分的格式化报告。

```r
source("scripts/check_data.R")
check_data(your_df)
```

---

## 鸣谢

- Claude (显然)
- R 官方手册 (共 19,518 行)
- 轻微的挫败感和几杯咖啡

---

## 贡献

如果你发现遗漏的注意事项、错误的默认设置或应该在参考文献中的内容——非常欢迎提交 PR。我也在学习。

---

*由 [@iremaydas](https://github.com/iremaydas) 制作 — 博士生,偶尔使用 R,全职搜索那些我本该知道的事情。*