我有一张桌子:
Name| Start | Finish |
----|-----------|-----------|
A |2015-01-22 |2015-02-04 |
B |2015-01-02 |2015-01-10 |
A |2015-01-22 |2015-02-14 |
B |2015-01-02 |2015-02-10 |
我需要按月分解。如果一个时期在一个月内开始并在下一个时期结束,那么我需要将其分成两个时期。如果一个时期在同一个月开始和结束,那么它应该是原样的。假设期间不能超过一个月的第一天。换句话说,每行可以分割不超过两行。完成(期间结束)总是大于开始。
这就是我想要的:
Name| Start | Finish |
----|-----------|-----------|
A |2015-01-22 |2015-01-31 |
A |2015-02-01 |2015-02-04 |
A |2015-01-22 |2015-01-31 |
A |2015-02-01 |2015-02-14 |
B |2015-01-02 |2015-01-10 |
B |2015-01-02 |2015-01-31 |
B |2015-02-01 |2015-02-10 |
输出行的顺序不是问题。
以下是该表的代码:
Name = c("A", "B", "A", "B")
Start = c(as.Date("2015-01-22"), as.Date("2015-01-02"), as.Date("2015-01-22"), as.Date("2015-01-02"))
Finish = c(as.Date("2015-02-04"), as.Date("2015-01-10"), as.Date("2015-02-14"), as.Date("2015-02-10"))
df = data.frame(Name, Start, Finish)
有什么建议可以做到吗?
答案 0 :(得分:2)
问题已经改变。最初Name
列唯一标识了该行,但问题的更改版本不再具有该行。此处的答案已相应修改,现在我们按行号识别行,即1:nrow(df)
,而不是df$Name
的第二个参数中的by
。否则,代码不会改变。
使用by
逐行拆分数据,给出单行,并使用匿名函数对每行进行操作。它计算Start的月末(eom
),如果Finish更大,则输出两行数据帧,否则返回相同的数据帧。将所有内容与rbind
放在一起。
library(zoo)
do.call("rbind", by(df, 1:nrow(df), function(x) with(x, {
eom <- as.Date(as.yearmon(Start), frac = 1)
if (eom < Finish)
data.frame(Name, Start = c(Start, eom+1), Finish = c(eom, Finish))
else x
})))
,并提供:
Name Start Finish
1.1 A 2015-01-22 2015-01-31
1.2 A 2015-02-01 2015-02-04
2 B 2015-01-02 2015-01-10
3.1 A 2015-01-22 2015-01-31
3.2 A 2015-02-01 2015-02-14
4.1 B 2015-01-02 2015-01-31
4.2 B 2015-02-01 2015-02-10
答案 1 :(得分:2)
修改强>
这回答了原来的问题:
require(dplyr)
require(zoo)
df %>%
filter(Finish>as.Date(as.yearmon(Start),frac=1)) %>%
group_by(Name) %>%
do(rbind(.,c(.$Name,
paste(as.Date(as.yearmon(.$Start),frac=1)+1),
.$Finish))) %>%
mutate(Finish:=ifelse(as.Date(as.yearmon(Start),frac=1)<Finish,
paste(as.Date(as.yearmon(Start),frac=1)),Finish))
输出:
Name Start Finish
1 A 2015-01-22 2015-01-31
2 A 2015-02-01 2015-02-04
3 B 2015-03-02 2015-03-31
4 B 2015-04-01 2015-04-10
示例数据:
require(data.table)
df <- fread("Name Start Finish
A 2015-01-22 2015-02-01
B 2015-03-02 2015-04-01")
答案 2 :(得分:2)
这是基础R中的另一种方法:
idx <- with(df, format(Finish, "%Y-%m") > format(Start, "%Y-%m"))
rbind(df[!idx,],
transform(df[idx,], Finish = as.Date(paste0(format(Finish, "%Y-%m"), "-01"))-1),
transform(df[idx,], Start = as.Date(paste0(format(Finish, "%Y-%m"), "-01"))))
# Name Start Finish
#2 B 2015-01-02 2015-01-10
#1 A 2015-01-22 2015-01-31
#3 A 2015-01-22 2015-01-31
#4 B 2015-01-02 2015-01-31
#11 A 2015-02-01 2015-02-04
#31 A 2015-02-01 2015-02-14
#41 B 2015-02-01 2015-02-10