4

I have the following data.table

library(data.table)
x <- data.table(a = 1:3, b = 1:6) 

And I would like to assign at the same time two columns by reference since both columns use the same type of check. Normally I would do: (Assign multiple columns using := in data.table, by group)

x[, c("col1", "col2") := list("Yes", b)]

But I need an ifelse construction for it. So I tried:

x[, c("col1", "col2") := ifelse(a > 2, list("Yes", b), list("No", a))]

But this does not work, I get an error:

Supplied 2 columns to be assigned 6 items

How can I work around it?

User2321
  • 2,952
  • 23
  • 46
  • like this? `x[, c("col1", "col2") := list(ifelse(a > 2, "Yes", "No"), b)]` – Donald Seinen Nov 17 '21 at 07:57
  • Hi, thank you. No that does not work as in the no case I need the value of the `a` column and not the `b` column. – User2321 Nov 17 '21 at 08:04
  • 1
    I see , `x[, c("col1", "col2") := list(ifelse(a > 2, "Yes", "No"), ifelse(a > 2, b, a))]` does that, but 2 `ifelse` is not elegant – Donald Seinen Nov 17 '21 at 08:13
  • Yes, that's the problem. I had two `ifelse` statements before but each `ifelse` is doing the same check (and in my actual data it is an expensive check unfortunately). – User2321 Nov 17 '21 at 08:26

2 Answers2

4

You could:

x[, c("col1", "col2") := .("No", a)] # Default value
x[a > 2, c("col1", "col2") := .("Yes", b)] # .() is short for list()

Another option that would generalise better to more complicated cases:

x[, test_a := a > 2]
update_table <- data.table(
  test_a = c(TRUE, FALSE),
  col1 = c('Yes', 'No'),
  col2 = c('a', 'b')
)
cols <- c('col1', 'col2')
x[, (cols) := update_table[.SD, on = 'test_a', .SD, .SDcols = cols]]
s_baldur
  • 29,441
  • 4
  • 36
  • 69
  • Thank you! This works for the example data (and I will accept if nothing better comes along) but for my actual data I have a 5 case `ifelse` so this does not fit as well unfortunately. – User2321 Nov 17 '21 at 12:43
  • @User2321 Added another option inspired by yours. – s_baldur Nov 17 '21 at 13:00
1

You can try the code below using if ... else ..., instead of ifelse()

x <- data.table(a = 1:3, b = 1:6)
x[,
  c("col1", "col2") :={
    if (a > 2) list("Yes", b) else list("No", a)
  },
  by = 1:nrow(x)
][]

which gives

   a b col1 col2
1: 1 1   No    1
2: 2 2   No    2
3: 3 3  Yes    3
4: 1 4   No    1
5: 2 5   No    2
6: 3 6  Yes    6
ThomasIsCoding
  • 96,636
  • 9
  • 24
  • 81
  • Thank you! It works but my God is it slow (which I guess it is expected since it goes row by row). – User2321 Nov 17 '21 at 10:22
  • @User2321 yes, it is slow due to the row-wise operation. I think the answer by sindri_baldur would be much faster – ThomasIsCoding Nov 17 '21 at 10:28
  • Yes but it is using assumptions that are not true in my data (but are present in the example data to be fair!). For example, the ifelse in my actual data has 5 cases not a simple Yes/No... – User2321 Nov 17 '21 at 12:36