Объединить ближайшую предыдущую дату и между датами - PullRequest
1 голос
/ 19 марта 2020

У меня есть 2 dfs, которые выглядят так:

df1 <- data.frame(ID = c("1", "1", "1", "2", "2", "2"),
                  testdate = as.POSIXct(c("2010-3-20", "2018-04-12","2018-04-25","2011-04-17","2011-09-05","2019-04-16")),
                  testvalue = (c(17, 35, 44, 65, 21, 22)))

df2 <- data.frame(ID = c("1", "1", "2", "2", "2"),
                  begindate = as.POSIXct(c("2018-04-10","2018-04-30","2011-04-12","2011-07-15","2018-01-21")),
                  enddate = as.POSIXct(c("2018-04-22","2018-05-12","2011-04-30","2011-07-30","2018-01-29")),
                  Dose = (c("2x per day", "1x per day", "1x morning", "2x morning", "3x per day")))

Df1 имеет тестовые значения предметов в определенный день. Df2 содержит предписания, которые субъект имел между определенным периодом.

Я хотел бы объединить два значения dfs на ID, и если testdate находится между df2.begindate и df2.enddate или testdate имеет предыдущий «рецепт» в df2, тогда я бы хотел ближайший «рецепт» (видно в строке 3 нового df).

Конечный df должен выглядеть следующим образом, я также хотел бы сохранить все данные в df1.

   ID   testdate   testvalue  begindate   enddate       dose
1   1   2010-03-20  17        NA          NA            NA
2   1   2018-04-12  35        2018-04-10  2018-04-22    2x per day
3   1   2018-04-25  44        2018-04-10  2018-04-22    2x per day
4   2   2011-04-17  65        2011-04-12  2011-04-30    1x morning
5   2   2011-09-05  21        2011-07-15  2011-07-30    2x morning
6   2   2019-04-16  22        2018-01-21  2018-01-29    3x per day

Я пробовал это: Найти ближайшие предшествующие и последующие даты между фреймами данных , но безуспешно. Я продолжаю получать несколько строк, содержащих не только ближайший «рецепт», но и все до определенной даты тестирования, а это не то, что мне хотелось бы.

РЕДАКТИРОВАТЬ: я пробовал это:

setDT(df1)
setDT(df2)

setkey(df1, ID, testdate)
setkey(df2, ID, begindate)[, PrecedingDate:=begindate]

result <- df2[df1, roll=Inf]

Но это не работает для строки 3 в новом df и откорректировать дату моего значения теста, которое я хотел бы иметь в df.

Ответы [ 2 ]

1 голос
/ 19 марта 2020

1) Для каждой строки это находит интервал, содержащий тестовое значение с наибольшим начальным значением и одинаковым идентификатором, или, если его нет, он находит интервал с самым большим начальным значением, не превышающим тестовое значение с тем же идентификатором.

Сначала добавьте номер строки seq в df1, создав временную таблицу df1s, а затем присоедините к каждой строке в df1s строку со строкой в ​​df2, которая содержит тестовое значение и имеет тот же идентификатор и наибольшее значение. Кроме того, он создает временную таблицу df1b, которая находит наибольшее число перед тестовой датой и имеет тот же идентификатор. Наконец, он присоединяется к df1a и df1b в последующем, получая начальное значение, конечную дату и дозу от df1a, если они существуют, и от df2, если нет.

library(sqldf)

sqldf("with df1s as (
  select rowid as seq, * from df1
),
df1a as (  -- nearest preceding containing interval having same ID
  select max(b.begindate) as begindate, a.*, b.begindate, b.enddate, b.Dose
  from df1s a
  left join df2 b on a.ID = b.ID and a.testdate between b.begindate and b.enddate
  group by a.seq),
df1b as (  -- nearest preceding begindate having same ID
  select max(b.begindate), a.*, b.begindate, b.enddate, b.Dose
    from df1s a 
    left join df2 b on a.ID = b.ID and b.begindate <= a.testdate
    group by a.seq)
-- pick out interval in df1a or if none in df1b
select a.ID, a.testdate, a.testvalue, 
    coalesce(a.begindate, b.begindate) as begindate,
    coalesce(a.enddate, b.enddate) as enddate,
    coalesce(a.Dose, b.Dose) as Dose
  from df1a a 
  left join df1b b on a.seq = b.seq")

, что дает следующее с использованием пересмотренных данных в вопросе:

  ID   testdate testvalue  begindate    enddate       Dose
1  1 2010-03-20        17       <NA>       <NA>       <NA>
2  1 2018-04-12        35 2018-04-10 2018-04-22 2x per day
3  1 2018-04-25        44 2018-04-10 2018-04-22 2x per day
4  2 2011-04-17        65 2011-04-12 2011-04-30 1x morning
5  2 2011-09-05        21 2011-07-15 2011-07-30 2x morning
6  2 2019-04-16        22 2018-01-21 2018-01-29 3x per day

2) Если уменьшить (1) до df1b, мы получим гораздо более короткое решение, хотя, очевидно, не эквивалентное. Он просто принимает самое большое значение в df2, которое не больше, чем testdate в df1 и имеет тот же идентификатор. Возможно, что он предпочтет совпадение, которое не охватывает тестовое значение, даже если есть интервал, который существует в случае, если есть интервал, содержащий тестовое значение, но другой интервал содержится в этом интервале и заканчивается перед тестовым значением; однако, кроме этого, все должно быть в порядке. Используйте (1), если это не так.

library(sqldf)

sqldf("select a.*, max(b.begindate) as begindate, b.enddate, b.Dose
  from df1 a
  left join df2 b on a.ID = b.ID and b.begindate <= a.testdate
  group by a.rowid")

, используя следующие данные, используя пересмотренные данные в вопросе:

  ID   testdate testvalue  begindate    enddate       Dose
1  1 2010-03-20        17       <NA>       <NA>       <NA>
2  1 2018-04-12        35 2018-04-10 2018-04-22 2x per day
3  1 2018-04-25        44 2018-04-10 2018-04-22 2x per day
4  2 2011-04-17        65 2011-04-12 2011-04-30 1x morning
5  2 2011-09-05        21 2011-07-15 2011-07-30 2x morning
6  2 2019-04-16        22 2018-01-21 2018-01-29 3x per day
0 голосов
/ 20 марта 2020

Поскольку у вас есть некоторый код data.table в вашем вопросе и ссылка на вопрос data.table, вот вариант с использованием data.table:

#if testdate falls between df2.begindate and df2.enddate,
df1[, (cols) := 
    df2[.SD, on=.(ID, begindate<=testdate, enddate>=testdate), mget(xcols)]
]

#if testdate has a preceding "prescription" in df2, then I would like the nearest "prescription" (seen in row 3 of new df).
df1[is.na(begindate), (cols) := 
    df2[.SD, on=.(ID, enddate=testdate), roll=Inf, mget(xcols)]]

выход:

   ID   testdate testvalue  begindate    enddate       Dose
1:  1 2010-03-20        17       <NA>       <NA>       <NA>
2:  1 2018-04-12        35 2018-04-10 2018-04-22 2x per day
3:  1 2018-04-25        44 2018-04-10 2018-04-22 2x per day
4:  2 2011-04-17        65 2011-04-12 2011-04-30 1x morning
5:  2 2011-09-05        21 2011-07-15 2011-07-30 2x morning
6:  2 2019-04-16        22 2018-01-21 2018-01-29 3x per day

данные:

library(data.table)
setDT(df1)
setDT(df2)

cols <- setdiff(names(df2), "ID")
xcols <- paste0("x.", cols)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...