R logical test + replace output within group

Viewed 60

I have a large dataframe. As an example:

Week <- c(1, 1, 1, 2, 2, 2)
Outcome <- c( FALSE, FALSE , TRUE , TRUE,  FALSE,  FALSE)

df <- data.frame(Week, Outcome)

  Week Outcome
1    1   FALSE
2    1   FALSE
3    1    TRUE
4    2    TRUE
5    2   FALSE
6    2   FALSE

In Outcome I would like to change FALSE to NA in case Outcome consists TRUE within the same Week.

So in this case the result will be:

Week Outcome
1    1   NA
2    1   NA
3    1   TRUE
4    2   TRUE
5    2   NA
6    2   NA

Thanks in advance for suggestions.

2 Answers

If you have cases when a week could be all FALSE and you don't want to change those ones, you can do it all in one pass like:

df$Outcome[df$Week %in% unique(df$Week[df$Outcome]) & (!df$Outcome)] <- NA
df

#  Week Outcome
#1    1      NA
#2    1      NA
#3    1    TRUE
#4    2    TRUE
#5    2      NA
#6    2      NA

Extended example:

Week <- c(1, 1, 1, 2, 2, 2, 3, 3)
Outcome <- c( FALSE, FALSE , TRUE , TRUE,  FALSE,  FALSE, FALSE, FALSE)

df2 <- data.frame(Week, Outcome)

df2$Outcome[df2$Week %in% unique(df2$Week[df2$Outcome]) & (!df2$Outcome)] <- NA
df2

#  Week Outcome
#1    1      NA
#2    1      NA
#3    1    TRUE
#4    2    TRUE
#5    2      NA
#6    2      NA
#7    3   FALSE
#8    3   FALSE

We can negate the 'Outcome' to convert the FALSE to TRUE and assign those values to NA

df$Outcome[!df$Outcome] <- NA

-output

df
#  Week Outcome
#1    1      NA
#2    1      NA
#3    1    TRUE
#4    2    TRUE
#5    2      NA
#6    2      NA

If we have cases where there are no TRUE, an option is

library(dplyr)
df2 %>% 
   group_by(Week) %>%
   mutate(Outcome = replace(Outcome, any(Outcome) & !Outcome, NA))

-output

# A tibble: 8 x 2
# Groups:   Week [3]
#   Week Outcome
#  <dbl> <lgl>  
#1     1 NA     
#2     1 NA     
#3     1 TRUE   
#4     2 TRUE   
#5     2 NA     
#6     2 NA     
#7     3 FALSE  
#8     3 FALSE  

Or using base R

df2$Outcome <-  with(df2, !(NA^ave(Outcome, Week, FUN = any) & !Outcome))

data

df2 <- structure(list(Week = c(1, 1, 1, 2, 2, 2, 3, 3), Outcome = c(FALSE, 
FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE)), class = "data.frame", 
row.names = c(NA, 
-8L))
Related