Search code examples
rfunctionnarecode

How can I create a new variable using conditions applied to several columns of data?


I am a R novice currently experiencing some difficulty with my code. Essentially, I have several variables in a dataset that contain information on which types of activities an individual frequently participates in (e.g. 1 = reading, 2 = arts and crafts, 3 = gardening, ect..).

Some simulated data:

df = data.frame(ID = c(1001, 1002, 1003, 1004, 1005,1006,1007,1008,1009,1010,1011),
                    orig_1 = c('-7', '2','1','1','NA','2', '3','NA','NA','2', '2'),
                    orig_2 = c('1','1','2','1','3','2', '2', '3','NA','2', '2'),
                    orig_3 = c('-7','3','NA','1','NA','2','NA','1','NA','2', '2'))

Based on these variables, I would like to create new variables that, for instance, reflects whether a person participates in a specific (e.g. 0 = no, 1 = yes). The first thing I have done is code values that correspond to "do not know" as NA:

#Recode variables
df$orig_1[df$orig_1==-7] <- NA

df$orig_2[df$orig_2==-7] <- NA

df$orig_3[df$orig_3==-7] <- NA

Then I have created my new 'activity' variables:

# create new activity variable
df$activity_1 <- NA
df$activity_2 <- NA
df$activity_3 <- NA

Next, I have adapted a function (kindly suggested by @Sonny) to search through the following columns and return a "1" (for those reporting to have participated in an activity) or otherwise a "0":

df$activity_1 <- na.omit(apply(df[, 2:4], 1, function(x) {
  if(any(x %in% c(1))) {
    return(1)
  } else {
    return(0)
  }
}))

df$activity_2 <- na.omit(apply(df[, 2:4], 1, function(x) {
  if(any(x %in% c(2))) {
    return(1)
  } else {
    return(0)
  }
}))

df$activity_3 <- na.omit(apply(df[, 2:4], 1, function(x) {
  if(any(x %in% c(3))) {
    return(1)
  } else {
    return(0)
  }
}))

This part does not work but the idea here was to introduce Na's to the new variables if all original variables were equivalent to "NA":

df$activity_1[df$orig_1==NA & df$orig_2==NA & df$orig_3==NA] <- NA

Ideally, the resulting dataframe should look like this:

     ID orig_1 orig_2 orig_3 activity_1 activity_2 activity_3
1  1001      NA      1    NA          1          0          0
2  1002      2      1     NA          1          1          0
3  1003      1      2     NA          1          1          0
4  1004      1      1      1          1          0          0
5  1005     NA      3     NA          0          0          1
6  1006      2      2      2          0          1          0
7  1007      3      2     NA          0          1          1
8  1008     NA     NA      1          1          0          0
9  1009     NA     NA     NA          NA         NA         NA
10 1010      2      2      2          0          1          0
11 1011      2      2      2          0          1          0

I would greatly appreciate any suggestions you may have on improving this code!


Solution

  • First, you need to make true NAs. You're doing 'NA' which is a string and different from NA. We can fix this like so:

    df[df == "NA"] <- NA
    

    Then we could look in an apply where all columns 2:4 are NA and set the activity_* columns accordingly.

    df[apply(df[2:4], 1, function(x) all(is.na(x))), 5:7] <- NA
    

    Or vectorized, as @akrun suggested:

    df[!rowSums(!is.na(df[2:4])), 5:7] <- NA
    

     

    df
    #      ID orig_1 orig_2 orig_3 activity_1 activity_2 activity_3
    # 1  1001     -7      1     -7          1          0          0
    # 2  1002      2      1      3          1          1          1
    # 3  1003      1      2   <NA>          1          1          0
    # 4  1004      1      1      1          1          0          0
    # 5  1005   <NA>      3   <NA>          0          0          1
    # 6  1006      2      2      2          0          1          0
    # 7  1007      3      2   <NA>          0          1          1
    # 8  1008   <NA>      3      1          1          0          1
    # 9  1009   <NA>   <NA>   <NA>         NA         NA         NA
    # 10 1010      2      2      2          0          1          0
    # 11 1011      2      2      2          0          1          0
    

    Data

    df <- structure(list(ID = c(1001, 1002, 1003, 1004, 1005, 1006, 1007, 
    1008, 1009, 1010, 1011), orig_1 = structure(c(NA, 3L, 2L, 2L, 
    5L, 3L, 4L, 5L, 5L, 3L, 3L), .Label = c("-7", "1", "2", "3", 
    "NA"), class = "factor"), orig_2 = structure(c(1L, 1L, 2L, 1L, 
    3L, 2L, 2L, 3L, 4L, 2L, 2L), .Label = c("1", "2", "3", "NA"), class = "factor"), 
        orig_3 = structure(c(NA, 4L, 5L, 2L, 5L, 3L, 5L, 2L, 5L, 
        3L, 3L), .Label = c("-7", "1", "2", "3", "NA"), class = "factor"), 
        activity_1 = c(1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0), activity_2 = c(0, 
        1, 1, 0, 0, 1, 1, 0, 0, 1, 1), activity_3 = c(0, 1, 0, 0, 
        1, 0, 1, 1, 0, 0, 0)), .Names = c("ID", "orig_1", "orig_2", 
    "orig_3", "activity_1", "activity_2", "activity_3"), row.names = c(NA, 
    -11L), class = "data.frame")