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

Posted

技术标签:

【中文标题】在 R 中使用 switch() 替换向量值【英文标题】:Use of switch() in R to replace vector values 【发布时间】:2015-09-18 08:28:09 【问题描述】:

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

我想使用 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 的优势在于矢量化。提前致谢!

【问题讨论】:

向量“测试”的长度为 4。它不能工作。见test &lt;- "He is" 你想达到这个目的code &lt;- c("He is"=1, "She is"=1, "He has"=2, "She has"=2); code[test] 哇@ExperimenteR,这很优雅...我不知道如果我使用字符选择具有名称属性的数字向量的对象,R 将使用名称属性来匹配数字的数值向量到我的字符向量。这是我最喜欢的解决方案,但我不能选择它作为答案,因为问题是如何正确使用 switch()。竖起大拇指 【参考方案1】:

这是矢量化函数的正确方法,例如切换

# 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

我希望这会有所帮助。

【讨论】:

【参考方案2】:

你可以试试

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)

【讨论】:

这似乎是解决方案,谢谢,实际上以更简单的形式,其中“test”是 sapply 的对象 对不起,另一个答案包括对问题背后概念的广泛解释。但我保证,只要我的声誉在几周内允许,我会立即为你投票。【参考方案3】:

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 视为控制流 语句,而不是数据转换运算符。您可以使用它们来控制算法的执行,例如测试收敛性或选择要采用的执行路径。在大多数情况下,您不会使用它们直接操作数据。

【讨论】:

【参考方案4】:

我发现这种方法最易读:

# 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 才能使用它。

【讨论】:

【参考方案5】:

虽然我通常更喜欢基本 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

【讨论】:

【参考方案6】:

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

【讨论】:

【参考方案7】:

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

# 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")

【讨论】:

在这里提一下:recode 也在 dplyr 包中。【参考方案8】:

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

【讨论】:

【参考方案9】:

只是为了好玩:

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)

【讨论】:

【参考方案10】:

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

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 年 4 月 11 日创建

【讨论】:

【参考方案11】:

使用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() 迭代第一个参数并返回第二个参数返回的任何值。第二个参数是一个函数,其中purrr 允许以一种不太复杂的方式编写它:而不是编写function(x) x,可以简单地编写~ .~ .x~ .1(最后一个工作无限变量数)。

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

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

【讨论】:

以上是关于在 R 中使用 switch() 替换向量值的主要内容,如果未能解决你的问题,请参考以下文章

如果焦点变量是NA,则在R中创建具有“替换”变量值的汇总变量

jQuery - 用变量值替换文本中的单词

c语言中switch可用啥语句替换

C#中一种替换switch语句更优雅的写法

在python中将变量值替换为命令

有没有办法在jenkins管道中用另一个变量值替换映射值?