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,全职搜索那些我本该知道的事情。*