0

A possible duplicate is here but it does not help me. My case is different perhaps.

Consider a sample data.table.

set.seed(21)
DT <- data.table(col1=sample(x=1:10,size = 5),col2=sample(x=10:20,size=5))
DT
   col1 col2
1:    8   20
2:    3   11
3:    6   19
4:    2   17
5:   10   15

And consider a function check I want to apply to each row:

check <- function(x,y) if ((y-x)>10) "A" else "B"

Now if I run this assignment by reference calling the function check in the j section of DT, it fails miserably,

DT[,state:=check(col1,col2)]

And throws this warning

Warning message:
In if ((y - x) > 10) "A" else "B" :
  the condition has length > 1 and only the first element will be used

Obviously, I guessed that data.table processes the whole vector at once and the function needs one value so I thought the last value is the current value in the vector so I made this change, which seems another stupid move :-)

DT[,state:=check(last(col1),last(col2))]

DT

   col1 col2 state
1:    8   20     B
2:    3   11     B
3:    6   19     B
4:    2   17     B
5:   10   15     B

I see the check() function was computed for the last value and overwrote all previous values every time. What I want is this output:

 DT

   col1 col2 state
1:    8   20     A
2:    3   11     A
3:    6   19     A
4:    2   17     A
5:   10   15     B

So how do I make a function call work in a data.table for each row independently?

ACTUAL FUNCTION: After I received a suggestion from akrun to use ifelse, which works fine for a simple condition like check() but I have a slightly more complicated function here. So please replace check by allowed_state

allowed_state <- function(old=NULL,new=NULL){
legalst<-data.table(from=c(1,1,9,9,7,8,7,10,10,11,11,10,11,8,7),to=c(11,7,10,7,8,7,9,9,7,1,7,1,7,9,10)) # all legal state transitions for a user
if(new %in% legalst[from==old,to]) "Legal" else "Illegal state change"

}

Lazarus Thurston
  • 1,197
  • 15
  • 33
  • YOu need `ifelse` instead of `if/else` i.e. `check <- function(x,y) ifelse((y-x)>10, "A", "B")` – akrun Dec 08 '17 at 11:41
  • actually, my actual function `check` is not that simple to convert to ifelse. Unless you can suggest a way to convert it. Let me add my actual function also, in the question. – Lazarus Thurston Dec 08 '17 at 11:46
  • that would be helpful – akrun Dec 08 '17 at 11:47
  • @akrun could you look up the actual function (added) and now let's see how `ifelse` can be applied. I tried but there's a different warning about the size of the vector. It is not happy with the `legalst[from==old,to]` part. – Lazarus Thurston Dec 08 '17 at 12:36

1 Answers1

1

If I have understood correctly, the OP wants to mark those state changes which are legal or illegal according to a given table legalst of legal state changes.

This can solved using an update on join:

set.seed(21)
DT <- data.table(col1 = sample(x = c(1, 7:11), size = 5),
                 col2 = sample(x = c(1, 7:11), size = 5))
legalst <- data.table(from = c(1,1,9,9,7,8,7,10,10,11,11,10,11,8,7), 
                      to = c(11,7,10,7,8,7,9,9,7,1,7,1,7,9,10))

DT[legalst, on = .(col1 = from, col2 = to), state := "Legal"][
  is.na(state), state := "Illegal state change"][]
   col1 col2                state
1:   10   11 Illegal state change
2:    7    1 Illegal state change
3:    8   10 Illegal state change
4:    1    8 Illegal state change
5:   11    7                Legal
Uwe
  • 41,420
  • 11
  • 90
  • 134