在 R 中使用 switch() 来替换向量值

Use of switch() in R to replace vector values

这应该很简单,但即使在检查了所有文档和在线示例之后我还是不明白。

我想使用 switch() 来替换字符向量的值。

一个假的、极其简单的、可重现的例子:

test<-c("He is", "She has", "He has", "She is")

假设我想将“1”分配给包含动词 "to be" 的句子,将“2”分配给包含动词 "to have" 的句子。以下不工作:

test<-switch(test,
                "He is"=1,
                "She is"=1,
                "He has"=2,
                "She has"=2)

错误信息:

+ + + + Error in switch(test, `He is` = 1, `She is` = 1, `He has` = 2, `She has` = 2) : 
  EXPR must be a length 1 vector

我认为 EXPR 确实是一个长度为 1 的向量,所以有什么问题吗?

我想也许 R 期望字符作为替换,但既没有将 switch() 包装到 "as.integer" 中,也没有进行以下工作:

test<-switch(test,
                "He is"="1",
                "She is"="1",
                "He has"="2",
                "She has"="2")

也许它没有向量化,我应该做一个循环?是吗?考虑到 R 的强项是矢量化,这会令人失望。提前致谢!

你可以试试

test_out <- sapply(seq_along(test), function(x) switch(test[x],
  "He is"=1,
  "She is"=1,
  "He has"=2,
  "She has"=2))

或等同于

test_out <- sapply(test, switch,
  "He is"=1,
  "She is"=1,
  "He has"=2,
  "She has"=2)

if 的向量化形式是 ifelse:

test <- ifelse(test == "He is", 1,
        ifelse(test == "She is", 1,
        ifelse(test == "He has", 2,
        2)))

test <- ifelse(test %in% c("He is", "She is"), 1, 2)

switch 基本上是一种编写嵌套 if-else 测试的方法。您应该将 ifswitch 视为 控制流 语句,而不是数据转换运算符。您可以使用它们来控制算法的执行,例如测试收敛性或选择要采用的执行路径。在大多数情况下,您不会使用它们直接操作数据。

这是向量化函数的正确方法,例如开关:

# Data vector:
test <- c("He is",
          "She has",
          "He has",
          "She is")

# Vectorized SWITCH:
foo <- Vectorize(vectorize.args = "a",
                 FUN = function(a) {
                   switch(as.character(a),
                          "He is" = 1,
                          "She is" = 1,
                          "He has" = 2,
                          2)})

# Result:
foo(a = test)

  He is She has  He has  She is 
      1       2       2       1

希望对您有所帮助。

"Vectorize" 基于 "mapply" 函数,而 "ifelse" 是一个应该已经向量化的基函数。所以就性能而言 "Vectorize" 可能会更慢。 使用 'apply' 系列很容易对 R 函数进行向量化,但性能通常是大容量的问题。最好使用经过优化以处理向量的基本函数。

我发现这种方法最易读:

# input
test <-c("He is", "She has", "He has", "She is", "Unknown", "She is")

# mapping
map <- c(
  "He is" = 1, 
  "She has" = 2, 
  "He has" = 2, 
  "She is" = 1)

answer <- map[test]

# output
answer
He is She has  He has  She is    <NA>  She is 
    1       2       2       1      NA       1 

如果 test 是数字,必须将值转换为 character 才能使用。

虽然我通常更喜欢基本的 R 方法,但有一个具有矢量化开关功能的包。

library(broman)

switchv(c("horse", "fish", "cat", "bug"),
horse="fast",
cat="cute",
"what?")

根据评论添加以使用 OP 数据。

library(broman)

test<-c("He is", "She has", "He has", "She is")


test<-switchv(test,
                "He is"="1",
                "She is"="1",
                "He has"="2",
                "She has"="2")

test

这是 recode() 来自 car 的解决方案:

# Data vector:
x <- c("He is", "She has", "He has", "She is")

library("car")
recode(x, "'He is'=1; 'She is'=1; 'He has'=2; 'She has'=2") # or
recode(x, "c('He is', 'She is')=1; c('He has', 'She has')=2")

纯属娱乐:

vSwitch <- function(vExpr,...) {
  l <- list(...)
  if(names(l)[[length(l)]] != '') stop('Last item in match list must be unnamed')
  i <- 0
  recurse <- function(v) {
    i <<- i + 1
    if(names(l[i+1]) != "") {
      ifelse(v == names(l)[[i]],l[[i]], recurse(v))
    } else {
      ifelse(v == names(l)[[i]],l[[i]], l[[i+1]])
    }
  } 
  recurse(vExpr)
}

您可以使用命名向量和简单的基本子集方法。例如

test <- c("He is", "She has", "He has", "She is")

named_vec <- c(
  "He is" = 1,
  "She is" = 1,
  "He has" = 2,
  "She has" = 2
)

named_vec[test]
#>   He is She has  He has  She is 
#>       1       2       2       1

reprex package (v0.3.0)

于 2020-04-11 创建

CRAN 上的软件包 kit 有一个用 C 语言编写的向量化开关函数,称为 vswitch。您可能还想知道它有一个名为 nif 的嵌套 if 函数和一个名为 iif 的快速 ifelse 函数。请查看文档,与基本 R 相比,这些函数确实非常快。

purrr 包做的花哨而整洁的方法是这样的:

purrr::map_int(c("He is", "She has", "He has", "She had", "She is", NA),
    ~ purrr::when(.,
         .x %in% c("He is", "She is") ~ 1L,
         .x %in% c("He has", "She has") ~ 2L,
         ~ NA))

此处,purrr::map() 遍历第一个参数,returns 遍历第二个参数 returns 的任何值。第二个参数是一个函数,其中 purrr 允许以更简单的方式编写它:而不是编写 function(x) x,可以简单地编写 ~ .~ .x~ .1(最后一个变量数量不受限制)。

然后,我们得到 purrr::when(),它采用单个值并作为一系列 ifelse 语句运行。这些语句采用 LHS ~ RHS 的形式。 LHS应该是一个逻辑表达式,也可以像上面一样使用引用变量的方式; RHS 是与此条件关联的值。返回的值是第一个适合的值。当 LHS 为空时(如最后一行),则将其视为 else 子句。

purrr::map_int()purrr::map() 的不同之处仅在于它保证返回整数向量(数字、逻辑和字符串有类似的函数)。

使用 plyr 包中的 revalue() 函数。

library(plyr)
test<-c("He is", "She has", "He has", "She is")
test<-revalue(test,
              c("He is"=1,
                "She is"=1,
                "He has"=2,
                "She has"=2))
test

这是输出。

[1] "1" "2" "2" "1"