假设我有一个数据框,如下所示:
df <- data.frame(group = c("a", "a", "b"),
start = as.Date(c("2018-01-01", "2018-09-01", "2018-02-01")),
end = as.Date(c("2018-02-15", "2018-12-31", "2018-03-30")))
group start end
a 2018-01-01 2018-02-15
a 2018-09-01 2018-12-31
b 2018-02-01 2018-03-30
我想获得以下预期输出:
output <- data.frame(group = c("a", "a", "a", "a", "a", "a", "b", "b"),
start = as.Date(c("2018-01-01", "2018-02-01", "2018-09-01",
"2018-10-01", "2018-11-01", "2018-12-01",
"2018-02-01", "2018-03-01")),
end = as.Date(c("2018-01-31", "2018-02-15", "2018-09-30",
"2018-10-31", "2018-11-30", "2018-12-31",
"2018-02-28", "2018-03-30")))
group start end
a 2018-01-01 2018-01-31
a 2018-02-01 2018-02-15
a 2018-09-01 2018-09-30
a 2018-10-01 2018-10-31
a 2018-11-01 2018-11-30
a 2018-12-01 2018-12-31
b 2018-02-01 2018-02-28
b 2018-03-01 2018-03-30
对于序列中的每个月,我想获得一个单独的行,该行将由1)序列的开始日期定界,如果序列的开始日期>大于月初或月初&2)结束日期如果后者>序列的结束日期或序列的结束日期,则为月份。
关于如何执行此操作的任何想法?
答案 0 :(得分:2)
我最喜欢选择这类问题的武器是data.table
非常快的foverlaps
df <- data.frame(group = c("a", "a", "b"),
start = as.Date(c("2018-01-01", "2018-09-01", "2018-02-01")),
end = as.Date(c("2018-02-15", "2018-12-31", "2018-03-30")))
#create data-frame with from-to by month
df2 <- data.frame( start = seq( as.Date("2018-01-01"), length = 12, by = "1 month" ),
end = seq( as.Date( "2018-02-01"), length = 12, by= "1 month" ) - 1,
stringsAsFactors = FALSE )
library(data.table)
#setDT on both data.frames... df2 needs to be keyed in order for foverlaps to work.
dt <- foverlaps( setDT( df ), setDT( df2, key = c("start", "end") ), type = "any", mult = "all" )[]
#choose keep the right columns (start/end)
dt[ start < i.start, start := i.start ]
dt[ end > i.end, end := i.end ]
#cleaning
dt[, `:=`(i.start = NULL, i.end = NULL)][]
# start end group
# 1: 2018-01-01 2018-01-31 a
# 2: 2018-02-01 2018-02-15 a
# 3: 2018-09-01 2018-09-30 a
# 4: 2018-10-01 2018-10-31 a
# 5: 2018-11-01 2018-11-30 a
# 6: 2018-12-01 2018-12-31 a
# 7: 2018-02-01 2018-02-28 b
# 8: 2018-03-01 2018-03-30 b
与@AntoniosK的tidyverse解决方案(效果一样好,并且更具可读性;-)相比,foverlaps
可以在50%的时间内完成工作
# Unit: milliseconds
# expr min lq mean median uq max neval
# tidyverse 10.418585 10.79064 12.531207 11.080309 11.753030 93.110804 100
# foverlaps 5.320911 5.59506 5.861865 5.846766 6.009146 9.606981 100
答案 1 :(得分:1)
dialogHide
答案 2 :(得分:1)
这是另一种可能的data.table
方法:
library(data.table)
setDT(df)
#to create a data.table of monthly periods
earliest <- as.POSIXlt(df[,min(start)])
earliest$mday <- 1L
earliest <- as.Date(earliest)
latest <- as.POSIXlt(df[,max(end)])
latest$mday <- 1L
latest <- seq(as.Date(latest), by="1 month", length.out=2L)[2L]
startOfMonths <- seq(earliest, latest, by="1 month")
monthsDT <- data.table(
som=startOfMonths[-length(startOfMonths)],
eom=startOfMonths[-1L] - 1L)
#perform non-equi join where som falls within start and end
ans <- monthsDT[df, .(group, start, som=x.som, end, eom=x.eom),
by=.EACHI, on=.(som>=start, som<=end)][, -(1L:2L)]
#get desired output according to OP's requirement
ans[, .(group, start=max(start, som), end=min(end, eom)), by=seq_len(ans[,.N])][, -1L]
输出:
group start end
1: a 2018-01-01 2018-01-31
2: a 2018-02-01 2018-02-15
3: a 2018-09-01 2018-09-30
4: a 2018-10-01 2018-10-31
5: a 2018-11-01 2018-11-30
6: a 2018-12-01 2018-12-31
7: b 2018-02-01 2018-02-28
8: b 2018-03-01 2018-03-30