代码之家  ›  专栏  ›  技术社区  ›  Konrad

在dplyr的mutate中使用switch语句

  •  3
  • Konrad  · 技术社区  · 8 年前

    我想在dplyr的mutate中使用switch语句。我有一个简单的函数,它执行一些操作,并通过开关分配可选值,例如:

    convert_am <- function(x) {
        x <- as.character(x)
        switch(x,
               "0" = FALSE,
               "1" = TRUE,
               NA)
    }
    

    当应用于标量时,这会按需要工作:

    >> convert_am(1)
    [1] TRUE
    >> convert_am(2)
    [1] NA
    >> convert_am(0)
    [1] FALSE
    

    我希望通过 mutate 呼叫:

    mtcars %>% mutate(am = convert_am(am))
    

    此操作失败:

    中出错 mutate_impl(.data, dots) :评估错误:EXPR必须 是长度为1的向量。

    我理解这是因为传递给开关ar的值不是单个的,例如:

    convert_am(c(1,2,2)) switch(x, 0 = FALSE, 1 = TRUE, NA) :EXPR必须是长度为1的向量

    矢量化

    尝试矢量化也会产生预期结果:

    convert_am <- function(x) {
        x <- as.character(x)
    
        fun_switch <- function(x) {
            switch(x,
                   "0" = FALSE,
                   "1" = TRUE,
                   NA)
        }
    
        vf <- Vectorize(fun_switch, "x")
    }
    
    >> mtcars %>% mutate(am = convert_am(am))
    Error in mutate_impl(.data, dots) : 
      Column `am` is of unsupported type function
    

    笔记

    • 我知道 case_when switch 内部工作变异
    • 理想的解决方案将允许进一步扩展使用 mutate_at 变量传递为 .
    2 回复  |  直到 8 年前
        1
  •  5
  •   konvas    8 年前

    switch 未矢量化,因此为了提高效率,您需要使用 ifelse case_when -但是你的问题是关于 ,您可以通过矢量化实现您想要的,例如。

    convert_am <- Vectorize(function(x) {
        x <- as.character(x)
        switch(x,
           "0" = FALSE,
           "1" = TRUE,
           NA)
    })
    

    convert_am <- function(x) {
        x <- as.character(x)
        sapply(x, function(xx) switch(xx,
           "0" = FALSE,
           "1" = TRUE,
           NA))
    }
    

    它们都是低效的,因为它们涉及引擎盖下的环路。

        2
  •  2
  •   Mark Otto    4 年前
    This is simple enough to handle with ifelse directly:
            
                Test <- tibble::tibble(
                  am = c(-1:5,NA,1, 0)
                ) 
            
                Test %>%
                  mutate(
                    newam = ifelse(am == 1, TRUE,
                           ifelse(am == 0, FALSE, NA))
                  )
            
            
    With more categories, use a named vector:
                Test %>%
                  mutate(
                    newam = ifelse(is.na(am) | !am %in% c(1,3,5), NA,
                                   c("1" = "in1", "3" = "in3", "5" = "in5")[as.character(am)])
                  )
            
    In fact if the value is not in the named list it will default to an NA
    I think this will be pretty efficient
                Test %>%
                  mutate(
                    newam = c("1" = "in1", "3" = "in3", "5" = "in5")[as.character(am)]
                  )