У меня есть данные с повторными измерениями по каждому предмету (id) в разное количество моментов времени. Я хотел бы сохранить две строки для каждого субъекта, момент времени == 0 и момент времени, ближайший к 4. В случае строк с двумя временными точками-кандидатами, одинаково удаленными от 4, например. (3, 5), я хочу выбрать самый низкий (3).
Как показано в столбце «выбор» на изображении ниже, строки с «x» не будут сохранены.
dat <- structure(list(id = c(172507L, 172507L, 172507L, 172525L, 172525L,
172525L, 172526L, 172526L, 172526L, 172527L, 172527L, 172527L,
172527L, 172527L), timepoint = c(0L, 2L, 6L, 0L, 4L, 5L, 0L,
5L, 2L, 2L, 3L, 5L, 6L, 0L)), class = "data.frame", row.names = c(NA,
-14L))
Я тоже в замешательстве. Зачем отказываться от одиночных измерений для id = 172529
и id = 172530
?
Я считаю, что данные теперь соответствуют первым двум столбцам изображения, как и предполагалось.
Что-то вроде этого должно работать:
zeros <-
dat %>%
filter(timepoint == 0) %>%
transmute(id, timepoint)
nonzeros <-
dat %>%
filter(timepoint != 0) %>%
mutate(diff = abs(timepoint - 4)) %>%
group_by(id) %>%
filter(diff == min(diff)) %>%
arrange(timepoint) %>%
slice(1) %>%
ungroup() %>%
transmute(id, timepoint)
df <-
bind_rows(zeros, nonzeros) %>%
arrange(id, timepoint)
Вероятно, есть способ сделать это в одной трубе, но мне было легче визуализировать то, что происходит таким образом.
Можете ли вы сделать что-то вроде этого. При упорядочении по расстоянию, а затем по моменту времени сначала будет помещено наименьшее ближайшее значение. Затем вы можете использовать функцию first()
, чтобы получить первое значение или отфильтровать, когда момент времени равен нулю.
library(tidyverse)
dat %>%
mutate(dist = abs(4-timepoint)) %>%
arrange(id, dist, timepoint) %>%
group_by(id) %>%
filter(timepoint %in% c(0, first(timepoint))) %>%
ungroup() %>%
arrange(id, timepoint)
Мы могли бы arrange
с помощью id
и timepoint
и для каждой группы выбрать первое вхождение, когда timepoint == 0
, и минимальное абсолютное значение между 4 - timepoint
. Поскольку мы расположили его по timepoint
which.min
, выберем первый timepoint
с меньшим значением (в случае ничьей).
library(dplyr)
dat %>%
arrange(id, timepoint) %>%
group_by(id) %>%
slice(c(which.max(timepoint == 0), which.min(abs(4- timepoint))))
# id timepoint
# <int> <int>
#1 172507 0
#2 172507 2
#3 172525 0
#4 172525 4
#5 172526 0
#6 172526 5
#7 172527 0
#8 172527 3
Хороший. Есть ли более чистый аргумент, чем which.max(timepoint == 0)
?
@cardinal40 Что "нечистого" в which.max(timepoint == 0)
? Действительно, очень аккуратное решение +1.
@cardinal40 вы могли бы сделать which(timepoint == 0)[1]
, но это определенно не чище.
Если вы уверены, что каждый идентификатор имеет момент времени 0, вы можете заменить его на 1, потому что вы упорядочили набор данных. slice(c(1, which.min...)
@ Коул, да, отличное замечание, но для этого нам также нужно убедиться, что в столбце timepoint
нет отрицательных чисел.
Хорошая точка зрения. Еще одна альтернатива, которую я указал в своем ответе, это match(TRUE, timepoint == 0)
. Или вы могли бы сделать first(which(timepoint == 0))
. Тем не менее, они не яснее, чем which.max(timepoint == 0))
.
Вот решение data.table
. Он основан на предположении, что каждый идентификатор будет иметь момент времени 0. В противном случае вы должны использовать which.max(timepoint == 0)
. Спасибо Ронаку Шаху за подход which.min.
Редактировать: Изменено на match(TRUE, timepoint == 0)
и исправлена проблема с базовым подходом R.
library(data.table)
dt <- as.data.table(dat)
dt[order(timepoint),
.SD[c(match(TRUE, timepoint == 0), which.min(abs(4- timepoint)))],
by = id]
Для удовольствия вот база R:
do.call(rbind, by(dat[order(dat$timepoint), ], dat[order(dat$timepoint), ], function(x) x[c(match(TRUE, x$timepoint == 0), which.min(abs(4-x$timepoint))),]) )
Какова логика сохранения
timepoint = 3
для одного экземпляраid = 172528
, но отбрасыванияtimepoint = 5
для одного экземпляра172529
илиtimepoint = 6
для одного экземпляра172530
?