Изменение диапазона дат на серию дат (от широкой к длинной) - PullRequest
0 голосов
/ 01 марта 2019

Я хотел бы взять что-то вроде данных ниже

data<- data.frame("Subject" = c("13434","14544", "14544", 
                             "22222","22222","22222"), 
                  "Period" = c("MAD", "MAD", "OSE", "MAD","OSE","OSE"), 
                  "Dose" = c(400, 800, 800, 400, 800, 1200), 
                  "Start" = as.Date(c('2017-04-18','2017-06-13'
                        ,"2018-09-27", "2017-06-06","2018-08-21","2018-12-12")), 
                  "End" = as.Date(c("2017-05-16","2017-07-11", "2019-02-09",
                      "2017-07-04", "2018-12-11","2019-02-05")))

 data
Subject Period Dose  Start   End 
 13434  MAD  400    2017-04-18  2017-05-16
 14544  MAD  800    2017-06-13  2017-07-11
 14544  OSE  800    2018-09-27  2019-02-09
 22222  MAD  400    2017-06-06  2017-07-04
 22222  OSE  800    2018-08-21  2018-12-11
 22222  OSE  1200   2018-12-12  2019-02-05

и преобразовать их в что-то вроде ниже, где каждой дате в диапазоне дан ряд, а доза является аддитивной по дням в диапазоне,В идеальном мире, когда период меняет период, накопленная доза будет продолжаться с того места, где заканчивается предыдущий период.

Subject Period Sum_Dose   Day
 13434  MAD    400   2017-04-18
 13434  MAD    800   2017-04-19
 13434  MAD   1200   2017-04-20
 13434  MAD   1600   2017-04-21
 13434  MAD   2000   2017-04-22
 13434  MAD   2400   2017-04-23
 Etc. 

для каждого субъекта в течение заданного периода и дозы.

Ответы [ 3 ]

0 голосов
/ 02 марта 2019

Спасибо @utubun!Я закончил с этим,

library(dplyr)
library(tidyr)
dose.long <- data %>% 
  gather(g, DAY, Start, End) %>% 
  select(-g) %>%
  group_by(Subject, Period, Dose) %>% arrange(Subject, DAY) %>% 
  filter(is.na(DAY) == F) %>% 
  # Create a list column that includes all grades between existing
  summarize(DAY = list(full_seq(DAY, 1))) %>%
  # unnest the list
  unnest() %>% ungroup()%>%
  group_by(Subject)%>%
  mutate(Sum_Dose = cumsum(Dose))
0 голосов
/ 02 марта 2019

Если я правильно понимаю, ОП хочет

  1. расширить каждую строку в последовательность дней между указанными Start и End датами,
  2. накапливать Dose для каждого Subject во все дни.

Изменение формы " от ширины к длине ", например, gather() или melt(), здесь не требуется (и указывает нанеправильное направление, ИМХО).

dplyr и tidyr

Вот реализация, использующая dplyr и tidyr.Поскольку seq() не принимает векторные аргументы, нам нужно сгруппировать по каждой строке и unnest() расширенные дни.

library(dplyr)
library(tidyr)
dat %>% 
  group_by(rn = row_number()) %>%
  mutate(Day = list(seq(Start, End, "1 day"))) %>% 
  unnest() %>% 
  arrange(Subject, Day) %>% 
  group_by(Subject)%>%
  mutate(Sum_Dose = cumsum(Dose)) %>% 
  select(Subject, Period, Sum_Dose, Day)

Обратите внимание, что упорядочение по Day перед вызовом cumsum() - это простопредварительная осторожность в случае, если dat еще не заказан или в случае перекрывающихся диапазонов дат.

# A tibble: 392 x 5
# Groups:   Subject [3]
   Subject Period  Dose DAY        Sum_Dose
   <fct>   <fct>  <dbl> <date>        <dbl>
 1 13434   MAD      400 2017-04-18      400
 2 13434   MAD      400 2017-04-19      800
 3 13434   MAD      400 2017-04-20     1200
 4 13434   MAD      400 2017-04-21     1600
 5 13434   MAD      400 2017-04-22     2000
 6 13434   MAD      400 2017-04-23     2400
 7 13434   MAD      400 2017-04-24     2800
 8 13434   MAD      400 2017-04-25     3200
 9 13434   MAD      400 2017-04-26     3600
10 13434   MAD      400 2017-04-27     4000
# ... with 382 more rows

data.table

Версия data.table реализуеттот же подход, но менее многословный, так как «непринятие» делается косвенно.

library(data.table)
setDT(dat)[, rn := .I][
  , .(Subject, Period, Dose, Day = seq(Start, End, "1 day")), by = rn][
    order(Day), .(Period, Sum_Dose = cumsum(Dose), Day), keyby = Subject]
     Subject Period Sum_Dose        Day
  1:   13434    MAD      400 2017-04-18
  2:   13434    MAD      800 2017-04-19
  3:   13434    MAD     1200 2017-04-20
  4:   13434    MAD     1600 2017-04-21
  5:   13434    MAD     2000 2017-04-22
 ---                                   
388:   14544    OSE   128800 2019-02-05
389:   14544    OSE   129600 2019-02-06
390:   14544    OSE   130400 2019-02-07
391:   14544    OSE   131200 2019-02-08
392:   14544    OSE   132000 2019-02-09
0 голосов
/ 01 марта 2019

Таким образом?

library(tidyverse)

dat %>%
  group_by(Subject, Period, Dose) %>%
  summarize(Day = list(seq(Start, End, by = 'day'))) %>% 
  unnest(Day) %>%
  mutate(Dose = cumsum(Dose)) %>%
  ungroup()

Вывод:

# A tibble: 392 x 4
   Subject Period  Dose Day       
   <fct>   <fct>  <dbl> <date>    
 1 13434   MAD      400 2017-04-18
 2 13434   MAD      800 2017-04-19
 3 13434   MAD     1200 2017-04-20
 4 13434   MAD     1600 2017-04-21
 5 13434   MAD     2000 2017-04-22
 6 13434   MAD     2400 2017-04-23
 7 13434   MAD     2800 2017-04-24
 8 13434   MAD     3200 2017-04-25
 9 13434   MAD     3600 2017-04-26
10 13434   MAD     4000 2017-04-27
# ... with 382 more rows

Я предполагаю, что кортежи (Subject, Period, Dose) уникальны.Если нет, то вы можете добавить группировку по Start End.

, а к «идеальному миру» можно подойти следующим образом:

dat %>%
  group_by(Subject, Period, Dose) %>%
  summarize(Day = list(seq(Start, End, by = 'day'))) %>% 
  unnest(Day) %>%
  group_by(Subject) %>%
  arrange(Day) %>%
  mutate(Dose = cumsum(Dose)) %>%
  ungroup() 

Если мы добавим следующую строку в кодвыше:

... %>% filter(Day >= as.Date("2018-12-11"), Day <= as.Date("2018-12-12"), 
               Subject == "22222")

Он выведет:

  Subject Period   Dose Day       
  <fct>   <fct>   <dbl> <date>    
1 22222   OSE    102000 2018-12-11
2 22222   OSE    103200 2018-12-12

Так что, похоже, он правильно вычисляет cumsum (прибавляя 1200, что является следующей дозой для следующего периода) дляпериоды, которые следуют один за другим.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...