在 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
测试的方法。您应该将 if
和 switch
视为 控制流 语句,而不是数据转换运算符。您可以使用它们来控制算法的执行,例如测试收敛性或选择要采用的执行路径。在大多数情况下,您不会使用它们直接操作数据。
这是向量化函数的正确方法,例如开关:
# 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"
这应该很简单,但即使在检查了所有文档和在线示例之后我还是不明白。
我想使用 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
测试的方法。您应该将 if
和 switch
视为 控制流 语句,而不是数据转换运算符。您可以使用它们来控制算法的执行,例如测试收敛性或选择要采用的执行路径。在大多数情况下,您不会使用它们直接操作数据。
这是向量化函数的正确方法,例如开关:
# 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"