如何根据新变量的日期将现有的日期限制数据行拼接成两个新行?

How to splice an existing date-bounded row of data into two new rows based on the date of a new variable?

在我的纵向数据集中,每一行代表每个人的观察时间段,每一行都以开始和结束日期为界。这些行已编号 ('episode'),并且包含许多我需要在后续步骤中保留的特定于行的变量(例如,'edu_level')。

我创建了一个新的日期变量,hx_start,它可以通过 3 种方式中的一种(如下)与每行数据的开始和结束日期相关联。对于每个场景,我需要相应地编辑(拼接)现有的数据行,using dplyr:

1.在给定行的开始日期和结束日期之间(即,对于第 2 个人和第 4 个人) 在这种情况下,我想将现有行拼接成两个新行,以便日期 hx_start 是其中一行的开始日期。另一行将保留原始行的 开始日期和结束日期将比 hx_start.

日期早一天

2。在与某人的行开始日期相同的日期(即,人 1) 在这种情况下,不需要更改。

3。在与某人的行结束日期相同的日期(即第 3 个人) 与 #1 相同:我需要将现有行拼接成两个新行,以便 hx_start 的日期 是其中一行的开始日期。另一行将保留原始行的 开始日期和结束日期将比 hx_start.

日期早一天

到目前为止,我已经创建了一个新数据集,每行有 2 个副本,假设我需要对每个现有行最多编辑 2 行,然后删除原始数据(或仅保留原始数据,在第 1 个人的情况下)。重要的是,我需要一种方法将所有其他变量从原始行转移到所有新行 而无需 命名它们,如果可能的话(我的真实数据集中有很多)。

#Load packages
library(lubridate)
#> 
#> Attaching package: 'lubridate'
#> The following objects are masked from 'package:base':
#> 
#>     date, intersect, setdiff, union

#Create data set
person <- c(1, 2, 3, 4)
episode <- c(33, 50, 65, 70)
start <- c('2013-01-01', '2010-01-21', '2009-09-18', '2010-05-26')
end <- c('2013-06-04', '2010-06-19', '2009-12-31', '2010-12-24')
hx_start <- c('2013-01-01', '2010-03-09', '2009-12-31', '2010-07-04')
edu_level <- c(2, 3, 2, 1)

#Populate data frame
d <- cbind(person, episode, start, hx_start, end, edu_level)
d <- as.data.frame(d)
#Format dates and add to data frame
d$start <- as.Date(start, format = '%Y-%m-%d')
d$end <- as.Date(end, format = '%Y-%m-%d')
d$hx_start <- as.Date(hx_start, format = '%Y-%m-%d')

#Create 2 duplicates of this row for each person 
d1 <- d[rep(seq_len(nrow(d)), each = 3), ]

d1
#>     person episode      start   hx_start        end edu_level
#> 1        1      33 2013-01-01 2013-01-01 2013-06-04         2
#> 1.1      1      33 2013-01-01 2013-01-01 2013-06-04         2
#> 1.2      1      33 2013-01-01 2013-01-01 2013-06-04         2
#> 2        2      50 2010-01-21 2010-03-09 2010-06-19         3
#> 2.1      2      50 2010-01-21 2010-03-09 2010-06-19         3
#> 2.2      2      50 2010-01-21 2010-03-09 2010-06-19         3
#> 3        3      65 2009-09-18 2009-12-31 2009-12-31         2
#> 3.1      3      65 2009-09-18 2009-12-31 2009-12-31         2
#> 3.2      3      65 2009-09-18 2009-12-31 2009-12-31         2
#> 4        4      70 2010-05-26 2010-07-04 2010-12-24         1
#> 4.1      4      70 2010-05-26 2010-07-04 2010-12-24         1
#> 4.2      4      70 2010-05-26 2010-07-04 2010-12-24         1

reprex package (v2.0.0)

创建于 2022-03-23

您可以通过创建一个小的辅助函数来做到这一点。我使用 data.table 格式

完成了此操作
library(data.table)

f <- function(s,m,e) {
  if(m>s) return(list("start" = c(m,s),"hx_start" = c(m,m),"end" = c(e,m-1)))
  if(m == s) return (list("start" = s,"hx_start" = m,"end" =e))
}

setDT(d)[,!c(3:5)][d[ ,f(start,hx_start,end), by=person], on=.(person)]

输出:

   person episode edu_level      start   hx_start        end
1:      1      33         2 2013-01-01 2013-01-01 2013-06-04
2:      2      50         3 2010-03-09 2010-03-09 2010-06-19
3:      2      50         3 2010-01-21 2010-03-09 2010-03-08
4:      3      65         2 2009-12-31 2009-12-31 2009-12-31
5:      3      65         2 2009-09-18 2009-12-31 2009-12-30
6:      4      70         1 2010-07-04 2010-07-04 2010-12-24
7:      4      70         1 2010-05-26 2010-07-04 2010-07-03

注意:

  1. 对于第 2,4 个人,现在一行的开始日期是 hx_start,另一行是原来的开始日期,而结束日期是 hx_start 日期的前一天.
  2. 对于第 1 个人,没有变化
  3. 对于第 3 个人,一行现在将 hx_start 作为开始日期,另一行具有原始开始日期,而结束日期比 hx_start 日期早一天。

Tidyverse 选项(也使用上面的函数)

inner_join(
  d %>% select(-c(start,hx_start,end)), 
  d %>% 
  rowwise() %>% 
  summarize(person = max(person),
            dates = list(f(start,hx_start,end))) %>% 
  unnest_wider(dates) %>% 
  unnest(cols=everything()), 
  by = "person"
)

输出:

   person episode edu_level      start   hx_start        end
1:      1      33         2 2013-01-01 2013-01-01 2013-06-04
2:      2      50         3 2010-03-09 2010-03-09 2010-06-19
3:      2      50         3 2010-01-21 2010-03-09 2010-03-08
4:      3      65         2 2009-12-31 2009-12-31 2009-12-31
5:      3      65         2 2009-09-18 2009-12-31 2009-12-30
6:      4      70         1 2010-07-04 2010-07-04 2010-12-24
7:      4      70         1 2010-05-26 2010-07-04 2010-07-03