基于dplyr::select兼容的类/类型选择列

hiz5n14c  于 2022-12-20  发布在  其他
关注(0)|答案(4)|浏览(109)

实际问题

如何定义一个选择助手,它根据列的类/类型选择列,并且还与dplyr的架构兼容?
尽职调查
我查看了https://cran.r-project.org/web/packages/dplyr/vignettes/introduction.htmldplyr::select_helpers的帮助,但没有找到任何允许我基于类/类型进行选择的内容

示例

引入一些变化WRT等级/类型:

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函数传递额外的参数(我就是这样做的)。

vzgqcmou

vzgqcmou1#

我想dplyr::select_if()可能是你要找的。例如

dat <- mtcars %>% 
       mutate(mpg = as.character(mpg),
              wt = as.character(wt),
              vs = as.character(vs)
       ) %>% 
       select_if(is.character)
lndjwyie

lndjwyie2#

如果我们可以从自定义函数返回字符向量而不是list,那么我们可以使用one_of

has_class_v1 <- function(.data, cls = "numeric") {
  names(.data)[sapply(.data,
                      function(vec, clss) class(vec) %in% clss, clss = cls)]
  }

has_class_v1(dat, "character")
# [1] "mpg" "wt"  "vs" 

# use one_of
dat %>%
  select(one_of(has_class_v1(.,"character"))) %>% 
  head
#    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
# 5 18.7  3.44  0
# 6 18.1  3.46  1
fgw7neuy

fgw7neuy3#

在我看来,在利用dplyr时,实现这一点的最简化和最通用的方法是使用dplyr::select_if,但要比@wjchulme建议的方法更直接(尽管这是一个不错的技巧):

dat %>%
select_if(sapply(., class) %in% c("numeric", "character"))

等等,如果需要的话可以上更多的课。希望这能有所帮助。

gzszwxb4

gzszwxb44#

dplyr包在2018年的答案之后的某个时候添加了where()函数。以下是where()文档中的link

dat |> 
    select(where(is.factor) | where(is.character))

相关问题