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` 也是可以的。 这种方式还是有局限性的,几乎仅限于普通线性回归所支持的表达式。如果有其他特殊标记,可以自己实现解析的方法。
感谢您的阅读。本网站 MyZone 对本文保留所有权利。