Я пытаюсь рассчитать количество дней, в течение которых все объекты в группе перекрываются с каждым членом группы. Для этого я хочу сравнить каждую строку одного столбца в группе с каждой другой строкой в этом столбце в той же группе. Однако я не могу найти для этого простого решения; большая часть моих усилий была связана с вариантами карт из муррр. Кроме того, я прошел несколько вложенных циклов (:-/), вложенных кроличьих норок; но я подозреваю, что есть очень простой способ выполнить это сравнение.
По сути, я хочу, чтобы сумма пересечения каждого интервала в группе с одной строкой группы.
Входные данные: (формат с интервалами)
ID Group year interval_obs
1 A 2020 2020-04-29 UTC--2020-05-19 UTC
2 A 2020 2020-05-04 UTC--2020-05-29 UTC
3 A 2020 2020-05-09 UTC--2020-05-24 UTC
4 A 2020 2020-04-24 UTC--2020-04-28 UTC
5 A 2020 2020-05-30 UTC--2020-06-03 UTC
6 B 2020 2019-12-31 UTC--2020-01-20 UTC
7 B 2020 2020-01-10 UTC--2020-01-30 UTC
8 B 2020 2020-01-20 UTC--2020-02-09 UTC
9 B 2020 2020-01-15 UTC--2020-02-04 UTC
Входные данные (более удобочитаемые?) - где каждое начало/конец - это день года (doy)
ID Group Year start end
1 A 2020 120 140
2 A 2020 125 150
3 A 2020 130 145
4 A 2020 115 119
5 A 2020 151 155
6 B 2020 0 20
7 B 2020 10 30
8 B 2020 20 40
9 B 2020 15 35
Желаемые результаты:
ID total_overlap
1 25
2 30
3 25
4 0
5 0
6 15
7 35
8 25
9 35
обратите внимание, что желаемое общее перекрытие в днях, сумма всех дней 4 других наблюдений в группе A перекрываются. Группа B с 4 записями для указания переменной длины.
пример данных для задачи
data <- structure(list(
ID = 1:9,
group = c("A", "A", "A", "A", "A", "B", "B", "B", "B"),
year = c(2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L),
start = c(120L, 125L, 130L, 115L, 151L, 0L, 10L, 20L, 15L),
end = c(140L, 150L, 145L, 119L, 155L, 20L, 30L, 40L, 35L)),
class = "data.frame",
row.names = c(NA, -9L))
data <- data %>%
group_by(group, year) %>% # real dataset has several combos - both vars left as reminder
mutate(across(c(start, end), ~ as_date(., origin = paste0(year-1, "-12-31")))) %>% #this year-1 term is due to leap years etc.
mutate(interval_obs = interval(ymd(start), ymd(end))) %>%
dplyr::select(-start, -end)
output <- data %>% map(.x = .$interval_obs, # this code at least runs
.f = ~{results = sum(as.numeric(intersect(.x, .y$interval_obs)))})
Небольшой фрагмент выше - это один из многих способов, которыми я подошел к этому (map2, map_df и т. д.), и хотя он не работает, я полагаю, что (...) решение находится на этом приблизительном уровне. Обратите внимание, что вывод моего примера имеет две особенности: 1) единицы конвертируются в дни, 2) вычитается «самопересечение». Не беспокойтесь об этих функциях. У меня есть способы сделать и то, и другое, я просто не включил их, потому что они могут запутать проблему. Однако, если это поможет...
mutate(self_intersection = as.numeric(intersect(interval_obs, interval_obs2))) %>%
mutate(results = results - self_intersection) %>%
mutate(total_overlap = as.numeric(results)/86400))
Я пытался хранить данные в смазанном или другом формате даты, чтобы в будущем можно было легко приспособить различные временные разрешения (например, часы, минуты).
(данные воспроизведены здесь)
ID Group Year start end
1 A 2020 120 140
2 A 2020 125 150
3 A 2020 130 145
4 A 2020 115 119
5 A 2020 151 155
для группы № 1 цифры после «сравнения» относятся к идентификатору.
comparison 1 - 2. End1 - Start2 = 15 days
comparison 1 - 3. End1 - Start2 = 10 days
comparison 1 - 4. NO OVERLAP = 0 days
comparison 1 - 5. NO OVERLAP = 0 days
total_overlap 25 days
Я думал, что ваша логика df1 %>% group_by(Group) %>% transmute(total_overlap = map_dbl(row_number(), ~ sum(end[-.x] - start[-.x]) - (end[.x] - start[.x])))
, но она не дает ожидаемых цифр
Это то, что вы ищите?
Общее перекрытие в третьей строке отличается от желаемого результата, но это может быть опечатка?
library(tidyverse)
library(lubridate)
data |>
group_by(group) |>
mutate(total_overlap = map_dbl(interval_obs,
\(x) x |>
intersect(interval_obs) |>
int_length() |>
sum(na.rm = T) - int_length(x)
) / 86400
)
#> # A tibble: 9 × 5
#> # Groups: group [2]
#> ID group year interval_obs total_overlap
#> <int> <chr> <int> <Interval> <dbl>
#> 1 1 A 2020 2020-04-29 UTC--2020-05-19 UTC 25
#> 2 2 A 2020 2020-05-04 UTC--2020-05-29 UTC 30
#> 3 3 A 2020 2020-05-09 UTC--2020-05-24 UTC 25
#> 4 4 A 2020 2020-04-24 UTC--2020-04-28 UTC 0
#> 5 5 A 2020 2020-05-30 UTC--2020-06-03 UTC 0
#> 6 6 B 2020 2019-12-31 UTC--2020-01-20 UTC 15
#> 7 7 B 2020 2020-01-10 UTC--2020-01-30 UTC 35
#> 8 8 B 2020 2020-01-20 UTC--2020-02-09 UTC 25
#> 9 9 B 2020 2020-01-15 UTC--2020-02-04 UTC 35
Что \ ? Это работает на паре компьютеров под управлением Linux, но RStudio на Mac не распознает это как специальный оператор.
Это новое сокращение для функций, представленное в R 4.1. В более старых версиях вы могли либо заменить \(x) ...
на function(x) {...}
, либо использовать лямбда-стиль purrr с ~
и .x
.
Если вы используете более старую версию R, вам также придется заменить собственный канал |>
на канал magrittr %>%
Спасибо, я совсем пропустил это. Обновления r-bloggers.com/2021/05/new-features-in-r-4-1-0 задокументированы здесь для всех, кто не сразу это слышит.
В «начальных», «конечных» данных по вашему желанию вы можете показать расчет, который выводит 25, 30 и т. д.