首页 > 解决方案 > 将 n 对开始日期和结束日期的宽格式转换为长格式

问题描述

我有一个包含 51 列、id 和 50 对宽格式的开始日期和结束日期的表格。我想将其转换为带有开始和结束日期的长格式。下面给出的示例代码仅适用于 2 对开始日期和结束日期。还必须在“end”的结尾和“start”的开头之间插入新行。此外,每个 id 应该有两行从 'pre' 到第一个开始日期和另一行,从最后一个结束日期到每个 id 的 'post'。另请注意,end2 可能与 post 相同。

library(data.table)
table1 <- fread("
id  pre               post       start1     end1       start2        end2        var1
A   01/01/1992   12/31/1992   03/15/1992  03/20/1992  03/25/1992  03/30/1992       1
C   04/01/1992   06/30/1992   05/28/1992  06/30/1992   NA          NA              5
D   05/01/1992   06/01/1992     NA           NA         NA         NA  NA
E   07/18/1992   08/02/1992     07/15/1992  07/22/1992  NA         NA   1
F   06/02/1992   09/03/1992   07/15/1992   07/17/1992  07/20/1992 09/10/1992      2" )

cols <- c("pre","post","start1", "end1","start2", "end2")
table1[, (cols) := lapply(.SD, as.Date, format="%m/%d/%Y"), .SDcols=cols]


Final table should be-
id  start         end       var1
A   01/01/1992   03/14/1992  -99
A   03/15/1992  03/20/1992   1
A   03/21/1992  03/24/1992  -99
A   03/25/1992  03/30/1992   1
A   04/01/1992  12/31/1992   -99
C   04/01/1992  05/27/1992   -99
C   05/28/1992  06/30/1992   5
D   05/01/1992   06/01/1992  NA
E   07/18/1992   07/22/1992  NA
E   07/23/1992   08/02/1992  1 
F   06/02/1992   07/14/1992  2
F   07/15/1992   07/17/1992  2
F  07/18/1992    07/19/1992  2
F  07/20/1992    09/03/1992  2

更正我的原始帖子:我发现在我的数据中,对于某些 ID,前后日期可能不会分别出现在 start1 和 end2 之前和之后。我在上面添加了 3 个额外的 ID 作为示例。也就是说,提前日期可能介于 start1 和 end1 或 end1 和 start1 之间。同样,发布日期可能位于时间线上的任何位置,但会在发布日期之后。最终数据应以pre开头,以post结尾,保留所有行的开始和结束范围,但在某些情况下,某些 ID 的第一行和最后一行除外。

标签: rdplyrdata.table

解决方案


这是一个假设 pre 和 post 覆盖整个范围的选项:

setnames(table1, c("pre", "post"), c("start0", "end0"))
mDT <- melt(table1, id.vars=c("id", "var1"), measure.vars=patterns("start","end"), 
    na.rm=TRUE, value.name=c("start","end"))

ans <- mDT[, {
        b <- c(start, end[.N] + 1L)
        e <- c(start[2L] - 1L, end[-1L], end[1L])
        .(start=c(b, e[-.N]+1L), end=c(e, b[-1L]-1L))
    }, id][start <= end]
setkey(ans, id, start, end)
ans[, var1 := -99L][mDT, on=.(id, start, end), var1 := i.var1]

编辑:对于新数据,当 pre-post 不涵盖 start1 到 endN 的范围时,pre-post 期间是否也应该具有相同的 var1 存在一些不一致。但是,如果我们假设 pre-post 不应该具有 var1 值,那么对于 pre-post 不涵盖所有开始和结束日期的情况,这里有一个选项:

setnames(table1, c("pre", "post"), c("start0", "end0"))
mDT <- melt(table1, id.vars=c("id", "var1"), measure.vars=patterns("start","end"),
    value.name=c("start_d","end_d"), na.rm=TRUE)

#identify the pre-post range
mDT[, ri := rowid(id, var1)]

#as per before, we create continuous intervals out of these start and end dates
ans <- mDT[order(id, start_d, end_d), {
    if (.N > 1L) {
        ms <- start_d[1L]
        me <- end_d[1L]
        dates <- c(start_d, end_d+1L)
        sdates <- sort(dates[ms <= dates & dates < me])
        .(start_d=sdates, end_d=c(sdates[-1L]-1L, me))
    } else
        .(start_d, end_d)
}, .(id, var1)][start_d <= end_d]
setkey(ans, id, start_d, end_d)

#identify which ranges falls within the non pre-post periods
w <- unique(ans[mDT[ri!=1L], on=.(id, start_d>=start_d, end_d<=end_d),
    mult="last", nomatch=0L, which=TRUE])
ans[!w, var1 := -99L]

输出:

    id var1    start_d      end_d
 1:  A  -99 1992-01-01 1992-03-14
 2:  A    1 1992-03-15 1992-03-20
 3:  A  -99 1992-03-21 1992-03-24
 4:  A    1 1992-03-25 1992-03-30
 5:  A  -99 1992-03-31 1992-12-31
 6:  C  -99 1992-04-01 1992-05-27
 7:  C    5 1992-05-28 1992-06-30
 8:  D  -99 1992-05-01 1992-06-01
 9:  E  -99 1992-07-15 1992-07-17
10:  E    1 1992-07-18 1992-07-22
11:  F  -99 1992-06-02 1992-07-14
12:  F    2 1992-07-15 1992-07-17
13:  F  -99 1992-07-18 1992-07-19
14:  F    2 1992-07-20 1992-09-03

推荐阅读