R 语言中的一些神奇操作
2022年4月13日
编程
众所周知,R语言的优点是“一门统计学家开发的语言”,缺点也是“一门统计学家开发的语言”。相比于Python等比较偏计算机的语言,R语言中奇奇怪怪的操作实在是太多了。这篇博客就简单记录一下R语言中我所遇到的、感到叹为观止的操作,以备日后使用。
# 枚举字符串匹配
学C语言都知道枚举的重要性,用于将一系列固定的选项代码进行语义化。比如我现在有一个函数 `area()` ,其中有一个参数是 `type` ,可选值只有 `circle` 和 `square` 两种。如何在R语言中用枚举去表示呢?目前个人比较推荐的做法是,使用 `match.arg()` 函数,例如:
```R
area <- function(a, type = c("circle", "square")) {
switch(match.arg(type),
"circle" = pi * a * a,
"square" = a * a)
}
```
这样一来,函数 `match.arg()` 会自动根据函数定义中 `type` 的可选值,为我们匹配 `type` 的具体值。那么这个函数使用起来就可以用下面的方式
```R
area(1) ## 3.14159265358979
area(1, "circle") ## 3.14159265358979
area(1, "square") ## 1
area(1, "rectangle") ## Error in match.arg(type): 'arg' should be one of “circle”, “square”
```
甚至枚举字符串不用写全
```R
area(1, "cir") ## 3.14159265358979
area(1, "s") ## 1
```
这样就解决了枚举字符串的问题。
# 模型数据提取
在 **GWmodel** 和 **lm** 等做回归分析的包的代码中,往往会看到一段令人莫名其妙的代码,类似于
```R
function(formula, data) {
mf <- match.call(expand.dots = FALSE) ### 将函数的调用进行分析
m <- match(c("formula", "data"), names(mf), 0L) ### 找到 formula 和 data 两个参数在调用中的位置
### 以下四行用于构造类似于下面代码的调用
### model.frame(frame = frame, data = data, drop.unused.levels = T)
mf <- mf[c(1L, m)]
mf$drop.unused.levels <- TRUE
mf[[1L]] <- as.name("model.frame")
mf <- eval(mf, parent.frame()) ### 执行上面构造好的调用,得到 model.frame 的对象
mt <- attr(mf, "terms") ### 提出 model.frame 对象中的符号
y <- model.extract(mf, "response") ### 提出 model.frame 对象中 response 所对应的数据
x <- model.matrix(mt, mf) ### 根据 model.frame 对象构造设计矩阵
}
```
这段代码非常神奇地,把存储于 `data` 中的数据,根据 `formula` 的形式,分离了出来。这里会用到一个除非开发包否则不太常用的类型 `model.frame` 用于描述模型,它可以将 `formula` 对象和 `data.frame` 对象存储到一起,并进行一些操作(例如代码中设定的 `drop.unused.levels` 就是去掉没有使用的 `factor` 类型的因素)。`formula` 对象的 `terms` 被存储到了 `data.frame` 对象中,成为后者的一个 `attr` (属性),我们可以通过 `attributes()` 函数查看。
```plaintext
attributes(mf)
$names
[1] "Murder" "Rape" "Assault"
$terms
Murder ~ Rape + Assault
attr(,"variables")
list(Murder, Rape, Assault)
attr(,"factors")
Rape Assault
Murder 0 0
Rape 1 0
Assault 0 1
attr(,"term.labels")
[1] "Rape" "Assault"
attr(,"order")
[1] 1 1
attr(,"intercept")
[1] 1
attr(,"response")
[1] 1
attr(,".Environment")
<environment: R_GlobalEnv>
attr(,"predvars")
list(Murder, Rape, Assault)
attr(,"dataClasses")
Murder Rape Assault
"numeric" "numeric" "numeric"
$row.names
[1] "Alabama" "Alaska" "Arizona" "Arkansas"
$class
[1] "data.frame"
```
所以我们将 `mf` 的 `terms` 属性提取出来,交给 `model.matrix()` 函数,就可以提取设计矩阵。但事实上,这里直接传入一个 `formula` 也是可以的。
这种方式还是有局限性的,几乎仅限于普通线性回归所支持的表达式。如果有其他特殊标记,可以自己实现解析的方法。
感谢您的阅读。本网站「地与码之间」对本文保留所有权利。