[r] How to include NA in ifelse?

I am trying to create a column ID based on logical statements for values of other columns. For example, in the following dataframe

test <- structure(list(time = c(10L, 20L, NA, 30L), type = structure(c(1L, 
2L, 3L, NA), .Label = c("A", "B", "C"), class = "factor"), ID = c(NA, 
"1", NA, NA)), .Names = c("time", "type", "ID"), row.names = c(NA, 
-4L), class = "data.frame")

which looks like

    time    type
1   10      A
2   20      B
3   NA      C
4   30      NA

I want to make a new column ID containing a value of 1 for all time that are not NA and all type that are not A. I am using the following code for this:

test$ID <- ifelse(is.na(test$time) | test$type == "A", NA, "1")

This gives the result as

    time    type    ID
1   10      A       NA
2   20      B       1
3   NA      C       NA
4   30      NA      NA

However, this code ignores the NA in column type, resulting in a value of NA in column ID. I need this to be a value of 1, so my needed solution should give:

    time    type    ID
1   10      A       NA
2   20      B       1
3   NA      C       NA
4   30      NA      1

Can anyone tell me how I might do this? I could get this to work with my existing code if I could somehow change the result of is.na(test$type) to return FALSE instead of TRUE, but I'm not sure how to do that. Or, maybe the structure of my existing code needs to be entirely changed? I appreciate any help!

This question is related to r if-statement

The answer is


@AnandaMahto has addressed why you're getting these results and provided the clearest way to get what you want. But another option would be to use identical instead of ==.

test$ID <- ifelse(is.na(test$time) | sapply(as.character(test$type), identical, "A"), NA, "1")

Or use isTRUE:

test$ID <- ifelse(is.na(test$time) | Vectorize(isTRUE)(test$type == "A"), NA, "1")

You might also try an elseif.

x <- 1
if (x ==1){
    print('same')
} else if (x > 1){
    print('bigger')
} else {
    print('smaller')
}

So, I hear this works:

Data$X1<-as.character(Data$X1)
Data$GEOID<-as.character(Data$BLKIDFP00)
Data<-within(Data,X1<-ifelse(is.na(Data$X1),GEOID,Data$X2)) 

But I admit I have only intermittent luck with it.


It sounds like you want the ifelse statement to interpret NA values as FALSE instead of NA in the comparison. I use the following functions to handle this situation so I don't have to continuously handle the NA situation:

falseifNA <- function(x){
  ifelse(is.na(x), FALSE, x)
}

ifelse2 <- function(x, a, b){
  ifelse(falseifNA(x), a, b)
}

You could also combine these functions into one to be more efficient. So to return the result you want, you could use:

test$ID <- ifelse2(is.na(test$time) | test$type == "A", NA, "1")