Есть ли элегантный способ заменить NA значениями из соответствующего столбца для нескольких столбцов в R?

Я работаю с данными результатов анализа крови участников испытаний с некоторыми спорадическими отсутствующими значениями (аналит не прошел). К счастью, у нас есть две точки времени, довольно близкие друг к другу, поэтому для отсутствующих значений в момент времени 1 я надеюсь ввести соответствующее значение из момента времени 2. Мне просто интересно, есть ли элегантный способ закодировать это в R/tidyverse для нескольких результатов теста?

Вот некоторые примеры данных:

           timepoint = c(1,1,1,1,1,2,2,2,2,2),
           fst_test = c(NA,sample(1:40,9, replace =F)),
           scd_test = c(sample(1:20,8, replace = F),NA,NA))

До сих пор я делал поворот шире, а затем вручную объединял соответствующие результаты теста, например:

test %>% 
  pivot_wider(names_from = timepoint, 
              values_from = fst_test:scd_test) %>%
  mutate(fst_test_imputed = coalesce(fst_test_1, fst_test_2),
         scd_test_imputed = coalesce(scd_test_1, scd_test_2)) %>% 
  select(ID, fst_test_imputed, scd_test_imputed)

Однако для 15 тестов это громоздко... Я подумал, что может быть элегантное решение R/dplyr для этой ситуации?

Заранее большое спасибо за помощь!!

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
61
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете повернуть свои данные так, чтобы «временная точка» определяла столбцы со всеми вашими тестами в строках. Чтобы выполнить этот поворот без создания столбцов списка, нам нужно сгруппировать по «временной точке» и создать индекс для каждой строки в группе:

test <- tibble(
  timepoint = c(1,1,1,1,1,2,2,2,2,2),
  fst_test = c(NA,sample(1:40,9, replace =F)),
  scd_test = c(sample(1:20,8, replace = F),NA,NA))
)

test_pivoted <- test %>% 
  group_by(timepoint) %>% 
  mutate(idx = row_number()) %>% 
  pivot_longer(-c(timepoint, idx)) %>% 
  pivot_wider(names_from = timepoint, values_from = value, names_prefix = 'timepoint')

     idx name     timepoint1 timepoint2
   <int> <chr>         <int>      <int>
 1     1 fst_test         NA         39
 2     1 scd_test          5         10
 3     2 fst_test         37          7
 4     2 scd_test         20          3
 5     3 fst_test          5         26
 6     3 scd_test         19         11
 7     4 fst_test         17         28
 8     4 scd_test          9         NA
 9     5 fst_test         14         32
10     5 scd_test          8         NA

Теперь мы можем coalesce один раз пройти через два момента времени для всех тестов:

test_pivoted %>%   
  mutate(
    imputed = coalesce(timepoint1, timepoint2)
  )

     idx name     timepoint1 timepoint2 imputed
   <int> <chr>         <int>      <int>   <int>
 1     1 fst_test         NA         39      39
 2     1 scd_test          5         10       5
 3     2 fst_test         37          7      37
 4     2 scd_test         20          3      20
 5     3 fst_test          5         26       5
 6     3 scd_test         19         11      19
 7     4 fst_test         17         28      17
 8     4 scd_test          9         NA       9
 9     5 fst_test         14         32      14
10     5 scd_test          8         NA       8

И если вы хотите немного подчистить результат:

test_pivoted %>%   
  mutate(
    imputed = coalesce(timepoint1, timepoint2)
  ) %>% 
  select(name, idx, imputed) %>% 
  pivot_wider(names_from = name, values_from = imputed)

    idx fst_test scd_test
  <int>    <int>    <int>
1     1       39        5
2     2       37       20
3     3        5       19
4     4       17        9
5     5       14        8

спасибо! Этот метод отлично работал, а также имел универсальность. Т.е. меня также интересовала разница в % между результатами в двух временных точках (для измерения аналитического шума), которую было очень легко вычислить таким образом!

agun7 20.03.2022 22:25
Ответ принят как подходящий

Мы могли бы использовать fill после создания столбца группировки с rowid в «временной точке» (как упоминалось в OP, чтобы заменить соответствующую точку данных в столбце «временная точка»). Затем нам просто нужно fill и указать .direction как «updown», чтобы сначала заполнить NA в предыдущем значении последующим не-NA (если нужно позаботиться только о «NA» в «timepoint» 1, тогда измените .direction = "up")

library(dplyr)
library(tidyr)
library(data.table)
test %>%
    group_by(grp = rowid(timepoint)) %>%
    fill(fst_test, scd_test, .direction = "updown") %>%
    ungroup %>% 
    select(-grp)

данные

test <- structure(list(timepoint = c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2), 
fst_test = c(NA, 
16L, 30L, 29L, 14L, 32L, 21L, 20L, 3L, 23L), scd_test = c(18L, 
17L, 8L, 20L, 1L, 10L, 14L, 19L, NA, NA)),
 class = "data.frame", row.names = c(NA, 
-10L))

Большое спасибо! Получилось прелестно и красиво и лаконично :).

agun7 20.03.2022 22:26

Другие вопросы по теме