Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

fcase to multiple outputs

Tags:

r

case

data.table

Assume the following table:

data <- data.table(dummy=1:10)

I know that you can do the following things:

data[dummy < 5, c("test1", "test2") := list("Yes", 1)]

and:

data[, test1 := fcase(dummy < 5, "Yes")]
data[, test2 := fcase(dummy < 5, 1)]

I am trying to combine these into one like so:

data[, c("test1", "test2") := fcase(dummy < 5, list("Yes", 1))]

But it gives me the following error:

Error in fcase(dummy < 5, list("Yes", 1)) : 
  Length of output value #2 must either be 1 or length of logical condition.

I need to go through multiple filters so it makes sense to use fcase. I can always resort to using the first solution for each filter like so:

data[dummy < 5, c("test1", "test2") := list("Yes", 1)]
data[dummy > 7, c("test1", "test2") := list("No", 0)]
data[between(dummy, 5, 7), c("test1", "test2") := list("Maybe", NA)]

but I am wondering if there isn't something more possible. There's also the solution of creating a table with each combination of test1 and test2 and merge this table with the data table after doing an fcase for only test1 like so:

tests <- data.table(test1 = c("Yes", "No", "Maybe"),
                    test2 = c(1, 0, NA))

data[, test1 := fcase(dummy < 5, "Yes",
                      dummy > 7, "No",
                      between(dummy, 5, 7), NA_character_)]
merge(data, tests, by = "test1", all.x = T, sort = F)

But this seems inefficient for a large and complex datatable

like image 889
koolmees Avatar asked Sep 18 '25 17:09

koolmees


1 Answers

With rbindlist:

data[, c("test1", "test2") := rbindlist(fcase(dummy < 5, .(.("Yes", 1)),
                                              dummy > 7, .(.("No", 0)),
                                              default = .(.("Maybe", NA))))]
data
#>     dummy test1 test2
#>  1:     1   Yes     1
#>  2:     2   Yes     1
#>  3:     3   Yes     1
#>  4:     4   Yes     1
#>  5:     5 Maybe    NA
#>  6:     6 Maybe    NA
#>  7:     7 Maybe    NA
#>  8:     8    No     0
#>  9:     9    No     0
#> 10:    10    No     0

do.call will let you put your fcase conditions into a list and the values in another list of nested lists:

data[, c("test1", "test2") := rbindlist(do.call(fcase, rbind(.(dummy < 5, dummy <= 7, dummy > 7),
                                                             .(.(.("Yes", 1)), .(.("Maybe", NA)), .(.("No", 0))))))]

Or with the tests example:

tests <- data.table(test1 = c("Yes", "Maybe", "No"),
                    test2 = c(1, NA, 0))
tests[, val := .(.(.(.SD))), by = 1:nrow(tests)]
data[, c("test1", "test2") := rbindlist(do.call(fcase, rbind(.(dummy < 5, dummy <= 7, dummy > 7), tests$val)))]
like image 134
jblood94 Avatar answered Sep 20 '25 10:09

jblood94