Coding a series of columns with a numerical condition

Viewed 76

I have a series of columns which are numeric ranged from 0 to 8. I want to make a binominal variable when a row just one time reported 3 or more than coded as "high" otherwise "low".

structure(list(AE_1 = c(0L, 1L, 0L, 0L, 0L, 2L, 0L), AE_2 = c(0L, 
1L, 2L, 1L, 0L, 0L, 0L), AE_3 = c(1L, 4L, 1L, 8L, 0L, 8L, 1L), 
    AE_4 = c(0L, 1L, 1L, 0L, 0L, 0L, 0L), AE_5 = c(0L, 0L, 1L, 
    1L, 0L, 0L, 1L), AE_6 = c(0L, 5L, 1L, 3L, 0L, 4L, 1L), AE_7 = c(0L, 
    1L, 1L, 1L, 0L, 2L, 0L), AE_8 = c(0L, 2L, 1L, 2L, 0L, 0L, 
    0L), new_AE = c("low", "low", "low", "low", "low", "low", 
    "low")), class = "data.frame", row.names = c(NA, -7L))

original data I had this code and the outcome is low for all rows.


df<-df%>%
     mutate(new_AE=  pmap_chr(select(., starts_with('AE')), ~ 
       case_when(any(c(...) <= 2) ~ "low" , any(c(...) >=3) ~ "high")))

while I want something like this : what I want

3 Answers

This may be done esaily be checking max of each row in base R using pmax. Now of course, you won't write 8 col names into pmax so do this.

df[,9] <- c("low", "high")[ 1 + (do.call(pmax, df[,-9]) >= 3)]

> df
  AE_1 AE_2 AE_3 AE_4 AE_5 AE_6 AE_7 AE_8 new_AE
1    0    0    1    0    0    0    0    0    low
2    1    1    4    1    0    5    1    2   high
3    0    2    1    1    1    1    1    1    low
4    0    1    8    0    1    3    1    2   high
5    0    0    0    0    0    0    0    0    low
6    2    0    8    0    0    4    2    0   high
7    0    0    1    0    1    1    0    0    low

see that expr inside [] returns true/false as per your desired condition

# this returns max of each row
do.call(pmax, df[,-9])
[1] 1 5 2 8 0 8 1

# this checks whether max of each row is 3 or more
do.call(pmax, df[,-9]) >= 3
[1] FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE

So if you aren't comfortable using this strategy, you may use replace instead

df$new_AE <- replace(df$new_AE, do.call(pmax, df[,-9]) >= 3, "high")

The issue is that case_when with the first condition is all TRUE, thus we are only getting the 'low' values. Here, we don't even need a case_when as there are only two categories, and this can be created by converting the logical to numeric index and replace with a vector of labels

library(dplyr)
df %>%
  rowwise %>%
  mutate(new_AE = c('low', 'high')[1+ any(c_across(where(is.numeric)) >=3)]) %>%
  ungroup

-output

# A tibble: 7 x 9
#   AE_1  AE_2  AE_3  AE_4  AE_5  AE_6  AE_7  AE_8 new_AE
#  <int> <int> <int> <int> <int> <int> <int> <int> <chr> 
#1     0     0     1     0     0     0     0     0 low   
#2     1     1     4     1     0     5     1     2 high  
#3     0     2     1     1     1     1     1     1 low   
#4     0     1     8     0     1     3     1     2 high  
#5     0     0     0     0     0     0     0     0 low   
#6     2     0     8     0     0     4     2     0 high  
#7     0     0     1     0     1     1     0     0 low  

Or this may be done more easily with rowSums from base R

df$new_AE <- c("low", "high")[(!!rowSums(df >= 3)) + 1]
df$new_AE
#[1] "low"  "high" "low"  "high" "low"  "high" "low" 

While applying case_when have to consider the order of logical statements or make sure to do corrections in the succeeding expressions. if we test the second of OP's data

v1 <- c(1, 1, 4, 1, 0, 5, 1)
any(v1 <= 2)
#[1] TRUE

which is the first expression in case_when. As the first one is already executed and found a match, the subsequent expressions are not executed

case_when(any(v1 <=2) ~ 'low', any(v1 >=3) ~ 'high')
#[1] "low"

By reversing the order, we get "high"

case_when( any(v1 >=3) ~ 'high', any(v1 <=2) ~ 'low')
#[1] "high"

So, make sure which one is more priority and set the order of those expressions based on that

Update I made a slight modification to my solution as it appears new_AE column exists from the beginning and only the values were not right so here is also another solution just in case you would like to use pmap in one go. However, you already received some fabulous solutions.

library(dplyr)
library(purrr)

df %>%
  mutate(new_AE = pmap(df %>% 
                         select(-9), ~ ifelse(any(c(...) >= 3), "high", "low")))


  AE_1 AE_2 AE_3 AE_4 AE_5 AE_6 AE_7 AE_8 new_AE
1    0    0    1    0    0    0    0    0    low
2    1    1    4    1    0    5    1    2   high
3    0    2    1    1    1    1    1    1    low
4    0    1    8    0    1    3    1    2   high
5    0    0    0    0    0    0    0    0    low
6    2    0    8    0    0    4    2    0   high
7    0    0    1    0    1    1    0    0    low
Related