按月分割日期,并在必要时添加行

时间:2017-09-12 21:08:55

标签: r date

我有一张桌子:

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)

有什么建议可以做到吗?

3 个答案:

答案 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