объединить строки данных, если они имеют последовательные интервалы времени - PullRequest
0 голосов
/ 06 июля 2018

У меня есть набор данных для лекарств пациентов с Start.Date и Stop.Date. Каждый представлен в ряд. Я хотел бы объединить строки, где интервалы времени последовательны, как показано ниже:

ID = c(2, 2, 2, 2, 3, 5) 
Medication = c("aspirin", "aspirin", "aspirin", "tylenol", "lipitor", "advil") 
Start.Date = c("05/01/2017", "05/05/2017", "06/20/2017", "05/01/2017", "05/06/2017", "05/28/2017")
Stop.Date = c("05/04/2017", "05/10/2017", "06/27/2017", "05/15/2017", "05/12/2017", "06/13/2017")
df = data.frame(ID, Medication, Start.Date, Stop.Date) 


  ID Medication Start.Date  Stop.Date
   2    aspirin 05/01/2017 05/04/2017
   2    aspirin 05/05/2017 05/10/2017
   2    aspirin 06/20/2017 06/27/2017
   2    tylenol 05/01/2017 05/15/2017
   3    lipitor 05/06/2017 05/12/2017
   5      advil 05/28/2017 06/13/2017

Я хотел бы уменьшить количество строк по идентификатору и лекарству, если Stop.Date для одного дня за день до следующей Start.Date. Это должно выглядеть так:

  ID Medication Start.Date  Stop.Date
   2    aspirin 05/01/2017 05/10/2017
   2    aspirin 06/20/2017 06/27/2017
   2    tylenol 05/01/2017 05/15/2017
   3    lipitor 05/06/2017 05/12/2017
   5      advil 05/28/2017 06/13/2017

Ответы [ 3 ]

0 голосов
/ 06 июля 2018

Как насчет этого?

df %>%
    mutate_at(vars(ends_with("Date")), function(x) as.Date(x, format = "%m/%d/%Y")) %>%
    group_by(ID, Medication) %>%
    mutate(
        isConsecutive = lead(Start.Date) - Stop.Date == 1,
        isConsecutive = ifelse(
            is.na(isConsecutive) & lag(isConsecutive) == TRUE, FALSE, isConsecutive),
        grp = cumsum(isConsecutive)) %>%
    group_by(ID, Medication, grp) %>%
    mutate(Start.Date = min(Start.Date), Stop.Date = max(Stop.Date)) %>%
    slice(1) %>%
    ungroup() %>%
    select(-isConsecutive, -grp)
## A tibble: 5 x 4
#     ID Medication Start.Date Stop.Date
#  <dbl> <fct>      <date>     <date>
#1    2. aspirin    2017-05-01 2017-05-10
#2    2. aspirin    2017-06-20 2017-06-27
#3    2. tylenol    2017-05-01 2017-05-15
#4    3. lipitor    2017-05-06 2017-05-12
#5    5. advil      2017-05-28 2017-06-13

Лучше всего проверить это на нескольких примерах, чтобы убедиться в надежности. Давайте попробуем более сложный пример

df <- structure(list(ID = c(2, 2, 2, 2, 2, 3, 5, 5), Medication = structure(c(2L,
2L, 2L, 2L, 4L, 3L, 1L, 1L), .Label = c("advil", "aspirin", "lipitor",
"tylenol"), class = "factor"), Start.Date = structure(c(1L, 2L,
6L, 7L, 1L, 3L, 4L, 5L), .Label = c("05/01/2017", "05/05/2017",
"05/06/2017", "05/28/2017", "06/14/2017", "06/20/2017", "06/28/2017"
), class = "factor"), Stop.Date = structure(c(2L, 3L, 8L, 1L,
5L, 4L, 6L, 7L), .Label = c("04/30/2017", "05/04/2017", "05/10/2017",
"05/12/2017", "05/15/2017", "06/13/2017", "06/20/2017", "06/27/2017"
), class = "factor")), .Names = c("ID", "Medication", "Start.Date",
"Stop.Date"), row.names = c(NA, -8L), class = "data.frame")
df;
#    ID Medication Start.Date  Stop.Date
#1  2    aspirin 05/01/2017 05/04/2017
#2  2    aspirin 05/05/2017 05/10/2017
#3  2    aspirin 06/20/2017 06/27/2017
#4  2    aspirin 06/28/2017 04/30/2017
#5  2    tylenol 05/01/2017 05/15/2017
#6  3    lipitor 05/06/2017 05/12/2017
#7  5      advil 05/28/2017 06/13/2017
#8  5      advil 06/14/2017 06/20/2017

Обратите внимание, что здесь у нас есть два последовательных блока для ID=2 (строки 1 + 2 и строки 3 + 4), а также один последовательный блок для ID=5 (строки 7 + 8).

Выход

df %>%
    mutate_at(vars(ends_with("Date")), function(x) as.Date(x, format = "%m/%d/%Y")) %>%
    group_by(ID, Medication) %>%
    mutate(
        isConsecutive = lead(Start.Date) - Stop.Date == 1,
        isConsecutive = ifelse(
            is.na(isConsecutive) & lag(isConsecutive) == TRUE, FALSE, isConsecutive),
        grp = cumsum(isConsecutive)) %>%
    group_by(ID, Medication, grp) %>%
    mutate(Start.Date = min(Start.Date), Stop.Date = max(Stop.Date)) %>%
    slice(1) %>%
    ungroup() %>%
    select(-isConsecutive, -grp)
## A tibble: 5 x 4
#     ID Medication Start.Date Stop.Date
#  <dbl> <fct>      <date>     <date>
#1    2. aspirin    2017-05-01 2017-05-10
#2    2. aspirin    2017-06-20 2017-06-27
#3    2. tylenol    2017-05-01 2017-05-15
#4    3. lipitor    2017-05-06 2017-05-12
#5    5. advil      2017-05-28 2017-06-20

Результаты кажутся достоверными.

0 голосов
/ 06 июля 2018
library(tidyverse)
library(lubridate)
df%>%
  group_by(Medication)%>%
  mutate_at(vars(3:4),mdy)%>%
  mutate(Start.Date = coalesce(
                 if_else((Start.Date-lag(Stop.Date))==1,lag(Start.Date),Start.Date),Start.Date),
         s = lead(Start.Date)!=Start.Date)%>%
  filter(s|is.na(s))%>%
  select(-s)

# A tibble: 5 x 4
# Groups:   ID, Medication [4]
     ID Medication Start.Date Stop.Date 
  <dbl> <chr>      <date>     <date>    
1     2 aspirin    2017-05-01 2017-05-10
2     2 aspirin    2017-06-20 2017-06-27
3     2 tylenol    2017-05-01 2017-05-15
4     3 lipitor    2017-05-06 2017-05-12
5     5 advil      2017-05-28 2017-06-13
0 голосов
/ 06 июля 2018

Преобразование столбцов дат «Старт» и «Стоп» в класс Date с mdy (из lubridate), сгруппированные по «ID», «Лекарство», filter разница abs 'lead` для' Start.Date 'и' Stop.Date ', которые не равны 1

library(dplyr)
library(lubridate)
df %>%
  mutate_at(3:4, mdy) %>% 
  group_by(ID, Medication) %>%
  filter(abs(lead(Start.Date, default = last(Start.Date)) - Stop.Date) != 1)
# A tibble: 5 x 4
# Groups:   ID, Medication [4]
#     ID Medication Start.Date Stop.Date 
#  <dbl> <fct>      <date>     <date>    
#1     2 aspirin    2017-05-05 2017-05-10
#2     2 aspirin    2017-06-20 2017-06-27
#3     2 tylenol    2017-05-01 2017-05-15
#4     3 lipitor    2017-05-06 2017-05-12
#5     5 advil      2017-05-28 2017-06-13

Или используя аналогичную методологию в data.table

library(data.table)
setDT(df)[df[, (shift(mdy(Start.Date), type = 'lead', 
         fill = last(Start.Date)) - mdy(Stop.Date)) != 1 , ID]$V1]
#  ID Medication Start.Date  Stop.Date
#1:  2    aspirin 05/05/2017 05/10/2017
#2:  2    aspirin 06/20/2017 06/27/2017
#3:  2    tylenol 05/01/2017 05/15/2017
#4:  3    lipitor 05/06/2017 05/12/2017
#5:  5      advil 05/28/2017 06/13/2017

ПРИМЕЧАНИЕ. Мы могли бы сначала преобразовать столбцы Date в класс Date, как и раньше

ПРИМЕЧАНИЕ 2. Оба эти метода простые, основанные на примере, представленном в OP

.
...