Rellene los NA en una columna solo si el valor posterior coincide con un valor específico

Resuelto s_alt asked hace 54 años • 2 respuestas

Tengo algunos datos longitudinales de pacientes que incluyen una columna que describe si el paciente es o ha sido fumador actualmente. Quiero completar los valores faltantes solo si posteriormente se registra que el paciente nunca ha sido fumador. No puedo simplemente usar tiydr::fill , ya que no permite discriminar el valor.

Dado el siguiente ejemplo, quiero que los 'NA' de id==1sean reemplazados por never_smoker, mientras que id==2deben permanecer sin cambios, ya que no podemos inferir con precisión cuándo comenzó a fumar el paciente.

df <- tibble::tribble(
  ~id, ~visit, ~smoking,
  1, 1, NA,
  1, 2, NA,
  1, 3, "never_smoker",
  2, 1, NA,
  2, 2, NA,
  2, 3, "current_smoker"
)

Debería resultar en

expected_result <- tibble::tribble(
  ~id, ~visit, ~smoking,
  1, 1, "never_smoker",
  1, 2, "never_smoker",
  1, 3, "never_smoker",
  2, 1, NA,
  2, 2, NA,
  2, 3, "current_smoker"
)

Se me ocurrió esta solución, que parece funcionar, pero requiere invertir la columna dos veces. Supongo que debe haber una mejor manera de hacer esto.

df %>%
    group_by(id) %>%
    mutate(smoking = rev(accumulate(rev(smoking), ~ ifelse(is.na(.y) & .x == "never_smoker", "never_smoker", .y))))
s_alt avatar Jan 01 '70 08:01 s_alt
Aceptado

Puede identificar el más alto visitcon un valor de "never_smoker"y luego completar las visitas inferiores a este.

library(dplyr)

df %>%
  group_by(id) %>%
  mutate(smoking = if_else(
    visit < suppressWarnings(max(visit[!is.na(smoking) & smoking == "never_smoker"])), 
    "never_smoker", 
    smoking
  ))

suppressWarnings()Se incluye porque max()advertirá sobre la devolución -Infsi no hay un valor de "never_smoker", pero en este caso ese es el comportamiento que queremos.

Resultado:

# A tibble: 6 × 3
# Groups:   id [2]
     id visit smoking       
  <dbl> <dbl> <chr>         
1     1     1 never_smoker  
2     1     2 never_smoker  
3     1     3 never_smoker  
4     2     1 <NA>          
5     2     2 <NA>          
6     2     3 current_smoker
zephryl avatar Feb 16 '2024 13:02 zephryl

Si realiza la prueba lógica !(smoking != 'never_smoker' | is.na(smoking)), obtendrá TRUEsi la entrada es "nunca fumé" y FALSEen caso contrario. Si invierte este vector y hace una suma acumulativa, e invierte ese resultado para volver a colocarlo en el orden original, entonces cualquier valor que ocurra en o antes de "nunca fumar" será mayor que 0. Esto permite ifelseetiquetar la smokingcolumna de manera simple como "nunca fume" si la entrada es positiva y, en caso contrario, déjela como está.

library(dplyr)

df %>%
  mutate(smoking = ifelse(rev(cumsum(
    rev(!(smoking != 'never_smoker' | is.na(smoking))))) > 0, 
    'never smoker', smoking), .by = 'id')
#> # A tibble: 6 x 3
#>      id visit smoking       
#>   <dbl> <dbl> <chr>         
#> 1     1     1 never smoker  
#> 2     1     2 never smoker  
#> 3     1     3 never smoker  
#> 4     2     1 NA            
#> 5     2     2 NA            
#> 6     2     3 current_smoker
Allan Cameron avatar Feb 16 '2024 13:02 Allan Cameron