实际问题
如何定义一个选择器助手,根据它们的类/类型选择列,并且与 dplyr
的架构兼容?
尽职调查
我已经查看了https://cran.r-project.org/web/packages/dplyr/vignettes/introduction.html以及dplyr::select_helpers
的帮助文档,但是没有找到任何允许我基于类/类型进行选择的内容。
示例
根据类/类型引入一些变化:
dat <- mtcars
dat <- dat %>% mutate(
mpg = as.character(mpg),
wt = as.factor(wt),
vs = as.character(vs)
)
简而言之,我希望将这个方法推广到R中所有可能的类/类型(以及它们的组合):
dat[ , sapply(dat, is.character)]
# mpg wt vs
# 1 21 2.62 0
# 2 21 2.875 0
# 3 22.8 2.32 1
# 4 21.4 3.215 1
根据Subset variables in data frame based on column type,我可以这样做:
select_on_class <- function(.data, cls = "numeric") {
dat[ , names(.data)[sapply(.data,
function(vec, clss) class(vec) %in% clss, clss = cls)]]
}
dat %>% select_on_class(c("character", "factor"))
# mpg wt vs
# 1 21 2.62 0
# 2 21 2.875 0
# 3 22.8 2.32 1
# 4 21.4 3.215 1
我希望能在调用dplyr::select
函数时使用这个变量,所以我尝试了以下方法:
has_class <- function(.data, cls = "numeric") {
nms <- names(.data)[sapply(.data,
function(vec, clss) class(vec) %in% clss, clss = cls)]
sapply(nms, as.name)
}
dat %>% has_class(c("character", "factor"))
# $mpg
# mpg
#
# $wt
# wt
#
# $vs
# vs
问题在于
sapply(nms, as.name)
返回一个list
,而这与select
的内部机制不兼容(顺便说一句,我还没有完全理解它的内部机制):dat %>% select(has_class(c("character", "factor")))
# Error: All select() inputs must resolve to integer column positions.
# The following do not:
# * has_class("character")
dat %>% select_(has_class(c("character", "factor")))
# Error in UseMethod("as.lazy") :
# no applicable method for 'as.lazy' applied to an object of class "list"
编辑
根据使用 select_if
的答案,我试图进行泛化,但遇到了困难:
has_class <- function(.data, cls) {
sapply(.data, function(vec, clss) class(vec) %in% clss, clss = cls)
}
dat %>% has_class(c("character", "factor"))
# mpg cyl disp hp drat wt qsec vs am gear carb
# TRUE FALSE FALSE FALSE FALSE TRUE FALSE TRUE FALSE FALSE FALSE
dat %>% select_if(has_class, c("character", "factor"))
# Error in vapply(tbl, p, logical(1), ...) : values must be length 1,
# but FUN(X[[1]]) result is length 32
AFAIU,
.predicate
函数只需要返回一个逻辑向量(就像has_class
一样),我可以通过...
传递额外的参数给.predicate
函数(我已经这样做了)。那我还错在哪里呢?
dat %>% select(which(sapply(., is.character)))
可能是另一种选项。 - talatdplyr
选择助手的方式,但至少是一个不错的解决方法。感谢分享! - Rappster