I am wondering whether there is a variant of ifelse()
that can allow three branches instead of two branches.
For example, without the there branch version, I have to do something like this.
R> x=letters[1:3]
R> ifelse(x=="a", 1, ifelse(x=="b", 2, 3))
[1] 1 2 3
If there is a three branch version, then I may write something like ifelseifelse(x=="a", x=="b", 1, 2, 3)
. What is the best way to handle three-branch more ifelse?
CodePudding user response:
Compare some methods regarding time:
x <- sample(letters[1:3], 1e5, TRUE)
microbenchmark::microbenchmark(
"case_when" = dplyr::case_when(
x == "a" ~ 1,
x == "b" ~ 2,
TRUE ~ 3,
)
, "namesSubset" = c(a=1, b=2, c=3)[x] #But no default (maybe NA as def)
, "match" = c(1,2,3)[match(x, c("a", "b"), 3L)]
, "fmatch" = c(1,2,3)[fastmatch::fmatch(x, c("a", "b"), 3L)]
, "fcase" = data.table::fcase(x == 'a', 1
, x == 'b', 2
, default = 3
)
, "ifelse" = ifelse(x=="a", 1, ifelse(x=="b", 2, 3))
, "switch" = sapply(x, \(y) switch(y, "a" = 1, "b" = 2, 3))
, "envir" = unlist(mget(x, list2env(list(a = 1, b = 2)), ifnotfound=3), FALSE, FALSE)
, "hashtab " = local({h1 <- hashtab() #since R 4.2.0
sethash(h1, "a", 1)
sethash(h1, "b", 2)
sapply(x, gethash, h=h1, nomatch = 3, USE.NAMES = FALSE)})
, "chmatch" = data.table::chmatch(x, c("a", "b"), 3L))
Result:
Unit: microseconds
expr min lq mean median uq max neval
case_when 5933.597 6306.912 6940.0865 6503.2410 6752.8335 20929.469 100
namesSubset 967.524 1032.356 2202.6872 1057.2725 1243.1865 101670.424 100
match 1221.310 1285.947 1378.3095 1328.2960 1379.8575 1956.359 100
fmatch 763.101 806.007 868.1639 818.5500 872.9625 1608.286 100
fcase 1380.228 1446.713 1567.9658 1469.8270 1598.8585 6644.801 100
ifelse 3257.279 3556.419 4798.8369 3813.0255 4141.7075 33525.300 100
switch 48908.629 67749.721 85646.5315 83486.5935 95161.8280 208888.521 100
envir 7520.874 7727.216 7907.5232 7921.0045 8010.3320 8562.948 100
hashtab 86159.386 103942.424 114552.4228 111634.4995 121062.4745 195154.515 100
chmatch 659.948 703.449 764.3200 714.8205 766.4475 1752.777 100
CodePudding user response:
leveraging data.table
's fcase
's clean syntax and speed
library(data.table)
# dummy data
x <- letters[1:3]
# fast case
fcase(x == 'a', 1
, x == 'b', 2
, default = 3
)
CodePudding user response:
For a base
approach, you could use switch()
. One annoyance with switch()
is that it doesn't take vector inputs for the first argument, but you can kick around this easily with sapply()
:
sapply(letters[1:3], function(y) switch(y, "a" = 1, "b" = 2, "c" = 3, NA))
## a b c
## 1 2 3
The unnamed final parameter is the catch-all for when no match is found:
sapply(letters[1:4], function(y) switch(y, "a" = 1, "b" = 2, "c" = 3, NA))
## a b c d
## 1 2 3 NA
EDIT: I should acknowledge, as @GKi has helpfully pointed out, that this approach has remarkably terrible runtimes.