Я работаю с данными результатов анализа крови участников испытаний с некоторыми спорадическими отсутствующими значениями (аналит не прошел). К счастью, у нас есть две точки времени, довольно близкие друг к другу, поэтому для отсутствующих значений в момент времени 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 для этой ситуации?
Заранее большое спасибо за помощь!!
Вы можете повернуть свои данные так, чтобы «временная точка» определяла столбцы со всеми вашими тестами в строках. Чтобы выполнить этот поворот без создания столбцов списка, нам нужно сгруппировать по «временной точке» и создать индекс для каждой строки в группе:
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
Мы могли бы использовать 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))
Большое спасибо! Получилось прелестно и красиво и лаконично :).
спасибо! Этот метод отлично работал, а также имел универсальность. Т.е. меня также интересовала разница в % между результатами в двух временных точках (для измерения аналитического шума), которую было очень легко вычислить таким образом!