R dplyr суммирует данные по интервалам

Наверное, это Rdplyrsummarise вопрос. У меня есть data.frame со значениями, записанными для субъектов с 5-минутными интервалами времени, и он имеет следующие три столбца: id: идентификатор субъекта, value: записанное значение в определенный момент времени и cum_time: совокупное значение времени для каждого id:

library(dplyr)
set.seed(1)
df <- data.frame(id = c(rep("id1", 100), rep("id2", 100), rep("id3", 100)),
                 value = runif (300, 10, 20)) %>%
  dplyr::group_by(id) %>%
  dplyr::mutate(cum_time = 5 * (dplyr::row_number()-1))

Я хотел бы вычислить data.frame с medians value за 60-минутные интервалы, чтобы получить такой результат data.frame:

rbind(data.frame(id = "id1", median_value = c(median(dplyr::filter(df, id == "id1" & cum_time >= 0 & cum_time <= 60)$value),
                                              median(dplyr::filter(df, id == "id1" & cum_time >= 65 & cum_time <= 120)$value),
                                              median(dplyr::filter(df, id == "id1" & cum_time >= 125 & cum_time <= 180)$value),
                                              median(dplyr::filter(df, id == "id1" & cum_time >= 185 & cum_time <= 240)$value),
                                              median(dplyr::filter(df, id == "id1" & cum_time >= 245 & cum_time <= 300)$value)),
                 cum_time = c(60, 120, 180, 240, 300)),
      data.frame(id = "id2", median_value = c(median(dplyr::filter(df, id == "id2" & cum_time >= 0 & cum_time <= 60)$value),
                                              median(dplyr::filter(df, id == "id2" & cum_time >= 65 & cum_time <= 120)$value),
                                              median(dplyr::filter(df, id == "id2" & cum_time >= 125 & cum_time <= 180)$value),
                                              median(dplyr::filter(df, id == "id2" & cum_time >= 185 & cum_time <= 240)$value),
                                              median(dplyr::filter(df, id == "id2" & cum_time >= 245 & cum_time <= 300)$value)),
                 cum_time = c(60, 120, 180, 240, 300)),
      data.frame(id = "id3", median_value = c(median(dplyr::filter(df, id == "id3" & cum_time >= 0 & cum_time <= 60)$value),
                                              median(dplyr::filter(df, id == "id3" & cum_time >= 65 & cum_time <= 120)$value),
                                              median(dplyr::filter(df, id == "id3" & cum_time >= 125 & cum_time <= 180)$value),
                                              median(dplyr::filter(df, id == "id3" & cum_time >= 185 & cum_time <= 240)$value),
                                              median(dplyr::filter(df, id == "id3" & cum_time >= 245 & cum_time <= 300)$value)),
                 cum_time = c(60, 120, 180, 240, 300)))

    id median_value cum_time
1  id1     15.72853       60
2  id1     15.74687      120
3  id1     14.87811      180
4  id1     16.00048      240
5  id1     14.57858      300
6  id2     15.98761       60
7  id2     14.65317      120
8  id2     15.36035      180
9  id2     15.16835      240
10 id2     13.90954      300
11 id3     12.68951       60
12 id3     15.79852      120
13 id3     14.03968      180
14 id3     14.29187      240
15 id3     15.11250      300

Не совсем 60-минутный интервал, если мы начинаем с 65 во 2-м интервале. Если бы между интервалами не было промежутка, мы могли бы использовать cut()

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

Ответы 3

Ответ принят как подходящий

Возможно, этот подход подойдет вам (с дополнением благодаря @thelatemail)?

df %>% 
  filter(cum_time<=300) %>% 
  group_by(id, grp=cut(cum_time, seq(0, max(cum_time),60), include.lowest = T)) %>% 
  summarize(median_value = median(value), cum_time=max(cum_time), .groups = "drop") %>% 
  select(-grp)

Выход:

    id median_value cum_time
1  id1     15.72853       60
2  id1     15.74687      120
3  id1     14.87811      180
4  id1     16.00048      240
5  id1     14.57858      300
6  id2     15.98761       60
7  id2     14.65317      120
8  id2     15.36035      180
9  id2     15.16835      240
10 id2     13.90954      300
11 id3     12.68951       60
12 id3     15.79852      120
13 id3     14.03968      180
14 id3     14.29187      240
15 id3     15.11250      300

предоставленный интервал не является непрерывным. второй интервал начинается с 65, а не с 61. Я предлагаю добавить в интервал больше разрезов, а затем отфильтровать их перед суммированием.

Derf 17.05.2024 03:08

Если вы добавите cum_time = max(cum_time) к вызову summarise, вы даже сможете пометить его по запросу.

thelatemail 17.05.2024 03:10

Непоследовательные интервалы сложны; возможно, вы могли бы «разбить его на две части», а затем соединить результат? Например.

Пример данных:

library(dplyr)
library(RcppRoll)

set.seed(1)
df <- data.frame(id = c(rep("id1", 100), rep("id2", 100), rep("id3", 100)),
                 value = runif (300, 10, 20)) %>%
  dplyr::group_by(id) %>%
  dplyr::mutate(cum_time = 5 * (dplyr::row_number()-1))

expected_outcome <- rbind(data.frame(id = "id1", median_value = c(median(dplyr::filter(df, id == "id1" & cum_time >= 0 & cum_time <= 60)$value),
                                                                  median(dplyr::filter(df, id == "id1" & cum_time >= 65 & cum_time <= 120)$value),
                                                                  median(dplyr::filter(df, id == "id1" & cum_time >= 125 & cum_time <= 180)$value),
                                                                  median(dplyr::filter(df, id == "id1" & cum_time >= 185 & cum_time <= 240)$value),
                                                                  median(dplyr::filter(df, id == "id1" & cum_time >= 245 & cum_time <= 300)$value)),
                                     cum_time = c(60, 120, 180, 240, 300)),
                          data.frame(id = "id2", median_value = c(median(dplyr::filter(df, id == "id2" & cum_time >= 0 & cum_time <= 60)$value),
                                                                  median(dplyr::filter(df, id == "id2" & cum_time >= 65 & cum_time <= 120)$value),
                                                                  median(dplyr::filter(df, id == "id2" & cum_time >= 125 & cum_time <= 180)$value),
                                                                  median(dplyr::filter(df, id == "id2" & cum_time >= 185 & cum_time <= 240)$value),
                                                                  median(dplyr::filter(df, id == "id2" & cum_time >= 245 & cum_time <= 300)$value)),
                                     cum_time = c(60, 120, 180, 240, 300)),
                          data.frame(id = "id3", median_value = c(median(dplyr::filter(df, id == "id3" & cum_time >= 0 & cum_time <= 60)$value),
                                                                  median(dplyr::filter(df, id == "id3" & cum_time >= 65 & cum_time <= 120)$value),
                                                                  median(dplyr::filter(df, id == "id3" & cum_time >= 125 & cum_time <= 180)$value),
                                                                  median(dplyr::filter(df, id == "id3" & cum_time >= 185 & cum_time <= 240)$value),
                                                                  median(dplyr::filter(df, id == "id3" & cum_time >= 245 & cum_time <= 300)$value)),
                                     cum_time = c(60, 120, 180, 240, 300)))

Пример кода:

first_block <- df %>%
  filter(cum_time <= 60) %>%
  mutate(median_value = median(value)) %>%
  select(-value) %>%
  filter(cum_time == 60)

second_time_block <- df %>%
  filter(cum_time >= 65) %>%
  mutate(median_value = roll_median(value, n = 12, by = 12, fill = NA,
                                    align = c("right"))) %>%
  select(-value) %>%
  filter(cum_time %% 60 == 0)

outcome <- first_block %>%
  bind_rows(second_time_block) %>%
  arrange(cum_time, .by_group = TRUE) %>%
  filter(cum_time <= 300)

all_equal(expected_outcome, outcome)
#> [1] TRUE

Created on 2024-05-17 with reprex v2.1.0

Желаемый интервал выглядит как (0-60,65-120,125-180,...) с пробелами.

Мы можем использовать cut() и, поскольку эта функция требует непрерывных интервалов без пробелов, потому что вы объявляете breaks интервалов в cut(). Поэтому нам просто нужно будет удалить интервалы, которые после этого не нужны.

foo = function(x){
  cut(x,breaks=c(-1,60,64,120,124,180,184,240,244,300),      
 labels=c("60","remove","120","remove","180","remove","240","remove","300"))
}

df |> 
  mutate(cum_interval=foo(cum_time))|>
  filter(cum_interval! = "remove") |>
  group_by(id,cum_interval)|>
  summarise(median_value=median(value))

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