Rellene los NA en una columna solo si el valor posterior coincide con un valor específico
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==1
sean reemplazados por never_smoker
, mientras que id==2
deben 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))))
Puede identificar el más alto visit
con 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 -Inf
si 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
Si realiza la prueba lógica !(smoking != 'never_smoker' | is.na(smoking))
, obtendrá TRUE
si la entrada es "nunca fumé" y FALSE
en 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 ifelse
etiquetar la smoking
columna 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