使用 dplyr 将开始和结束日期扩展到不平衡的月度面板
Expand start and end dates to unbalanced monthly panel with dplyr
我有活动的开始和结束日期,我想将其扩展到每月一次的面板中,我想知道 dplyr
中是否有任何工具可以解决这个问题。下面的代码完成了我想用 ddply()
做的事情。它首先创建一个示例 tibble
data.frame
(称为“wide”),其中“id”代表一个人,“HomeNum”是该人的一个事件。下一行创建一个“日期”变量,它是每个“id”中“HomeNum”组中从“StartDate”到“FinishDate”的月度系列。
library(plyr)
library(dplyr)
library(tibble)
wide =
tibble(
id = c(1, 1, 2, 2, 2),
HomeNum = c(0,1,0,1,2),
StartDate = as.Date(c("2001-01-01", "2001-03-01", "2000-04-01", "2001-02-01", "2002-08-01")),
FinishDate = as.Date(c("2001-02-01", "2002-05-01", "2001-01-01", "2002-07-01", "2002-12-01"))
)
panel =
ddply(wide,
~id+HomeNum,
transform,
date = seq.Date(StartDate, FinishDate, by = "month")
)
我假设 dplyr
作为“plyr
的下一个迭代”,必须有一些方法来实现类似的解决方案(并输出一个 tibble
),但是以下无效:
panel =
wide %>%
group_by(id, HomeNum) %>%
mutate(date = seq.Date(StartDate, FinishDate, by = "month"))
并返回
Error in mutate_impl(.data, dots) :
Column `date` must be length 1 (the group size), not 2
坦率地说,令我惊讶的是 ddply()
解决方案有效并且没有抛出类似的错误。
我对 ddply()
的实现与 this question 的答案类似。
您可以将 date
的元素强制转换为列表和 unnest
。
library(tidyverse)
wide %>%
group_by(id, HomeNum) %>%
mutate(date = list(seq.Date(StartDate, FinishDate, by = "month"))) %>%
unnest(date)
在日期列表中使用 unnest
是 tidyr
以前版本中的一个问题。我遇到了同样的错误并找到了解决方法,但是一旦我更新到 tidyr
0.8.1,就不再需要解决方法了。 GitHub 上的几个问题中记录了这个问题——#407 and #450 是我看过的问题。
如果您的版本无法取消嵌套日期,则可以通过将日期转换为字符串、取消嵌套,然后将字符串转换回日期来构建@hpesoj626 的答案。
library(tidyverse)
wide <- tibble(
id = c(1, 1, 2, 2, 2),
HomeNum = c(0,1,0,1,2),
StartDate = as.Date(c("2001-01-01", "2001-03-01", "2000-04-01", "2001-02-01", "2002-08-01")),
FinishDate = as.Date(c("2001-02-01", "2002-05-01", "2001-01-01", "2002-07-01", "2002-12-01"))
)
# with previous versions of tidyr
wide %>%
group_by(id, HomeNum) %>%
mutate(date = list(seq.Date(StartDate, FinishDate, by = "month") %>% as.character())) %>%
tidyr::unnest() %>%
mutate(date = as.Date(date))
#> # A tibble: 50 x 5
#> # Groups: id, HomeNum [5]
#> id HomeNum StartDate FinishDate date
#> <dbl> <dbl> <date> <date> <date>
#> 1 1 0 2001-01-01 2001-02-01 2001-01-01
#> 2 1 0 2001-01-01 2001-02-01 2001-02-01
#> 3 1 1 2001-03-01 2002-05-01 2001-03-01
#> 4 1 1 2001-03-01 2002-05-01 2001-04-01
#> 5 1 1 2001-03-01 2002-05-01 2001-05-01
#> 6 1 1 2001-03-01 2002-05-01 2001-06-01
#> 7 1 1 2001-03-01 2002-05-01 2001-07-01
#> 8 1 1 2001-03-01 2002-05-01 2001-08-01
#> 9 1 1 2001-03-01 2002-05-01 2001-09-01
#> 10 1 1 2001-03-01 2002-05-01 2001-10-01
#> # ... with 40 more rows
否则,像他们发布的那样的解决方案应该可行:
# with tidyr 0.8.1
wide %>%
group_by(id, HomeNum) %>%
mutate(date = list(seq.Date(StartDate, FinishDate, by = "month"))) %>%
tidyr::unnest()
#> # A tibble: 50 x 5
#> # Groups: id, HomeNum [5]
#> id HomeNum StartDate FinishDate date
#> <dbl> <dbl> <date> <date> <date>
#> 1 1 0 2001-01-01 2001-02-01 2001-01-01
#> 2 1 0 2001-01-01 2001-02-01 2001-02-01
#> 3 1 1 2001-03-01 2002-05-01 2001-03-01
#> 4 1 1 2001-03-01 2002-05-01 2001-04-01
#> 5 1 1 2001-03-01 2002-05-01 2001-05-01
#> 6 1 1 2001-03-01 2002-05-01 2001-06-01
#> 7 1 1 2001-03-01 2002-05-01 2001-07-01
#> 8 1 1 2001-03-01 2002-05-01 2001-08-01
#> 9 1 1 2001-03-01 2002-05-01 2001-09-01
#> 10 1 1 2001-03-01 2002-05-01 2001-10-01
#> # ... with 40 more rows
另一种选择是 gather
将数据转换为长格式,其中观察结果有一个 type
列显示它是开始日期还是结束日期。然后使用 complete
填写每个组的最小和最大日期之间缺失的日期。收集保留 type
列,对于添加的日期,该列被填充为 NA
。如果 type
列不再有用,您可以删除它。
wide %>%
gather(key = type, value = date, StartDate, FinishDate) %>%
group_by(id, HomeNum) %>%
complete(date = seq.Date(min(date), max(date), by = "month"))
#> # A tibble: 50 x 4
#> # Groups: id, HomeNum [5]
#> id HomeNum date type
#> <dbl> <dbl> <date> <chr>
#> 1 1 0 2001-01-01 StartDate
#> 2 1 0 2001-02-01 FinishDate
#> 3 1 1 2001-03-01 StartDate
#> 4 1 1 2001-04-01 <NA>
#> 5 1 1 2001-05-01 <NA>
#> 6 1 1 2001-06-01 <NA>
#> 7 1 1 2001-07-01 <NA>
#> 8 1 1 2001-08-01 <NA>
#> 9 1 1 2001-09-01 <NA>
#> 10 1 1 2001-10-01 <NA>
#> # ... with 40 more rows
由 reprex package (v0.2.0) 创建于 2018-05-22。
我有活动的开始和结束日期,我想将其扩展到每月一次的面板中,我想知道 dplyr
中是否有任何工具可以解决这个问题。下面的代码完成了我想用 ddply()
做的事情。它首先创建一个示例 tibble
data.frame
(称为“wide”),其中“id”代表一个人,“HomeNum”是该人的一个事件。下一行创建一个“日期”变量,它是每个“id”中“HomeNum”组中从“StartDate”到“FinishDate”的月度系列。
library(plyr)
library(dplyr)
library(tibble)
wide =
tibble(
id = c(1, 1, 2, 2, 2),
HomeNum = c(0,1,0,1,2),
StartDate = as.Date(c("2001-01-01", "2001-03-01", "2000-04-01", "2001-02-01", "2002-08-01")),
FinishDate = as.Date(c("2001-02-01", "2002-05-01", "2001-01-01", "2002-07-01", "2002-12-01"))
)
panel =
ddply(wide,
~id+HomeNum,
transform,
date = seq.Date(StartDate, FinishDate, by = "month")
)
我假设 dplyr
作为“plyr
的下一个迭代”,必须有一些方法来实现类似的解决方案(并输出一个 tibble
),但是以下无效:
panel =
wide %>%
group_by(id, HomeNum) %>%
mutate(date = seq.Date(StartDate, FinishDate, by = "month"))
并返回
Error in mutate_impl(.data, dots) :
Column `date` must be length 1 (the group size), not 2
坦率地说,令我惊讶的是 ddply()
解决方案有效并且没有抛出类似的错误。
我对 ddply()
的实现与 this question 的答案类似。
您可以将 date
的元素强制转换为列表和 unnest
。
library(tidyverse)
wide %>%
group_by(id, HomeNum) %>%
mutate(date = list(seq.Date(StartDate, FinishDate, by = "month"))) %>%
unnest(date)
在日期列表中使用 unnest
是 tidyr
以前版本中的一个问题。我遇到了同样的错误并找到了解决方法,但是一旦我更新到 tidyr
0.8.1,就不再需要解决方法了。 GitHub 上的几个问题中记录了这个问题——#407 and #450 是我看过的问题。
如果您的版本无法取消嵌套日期,则可以通过将日期转换为字符串、取消嵌套,然后将字符串转换回日期来构建@hpesoj626 的答案。
library(tidyverse)
wide <- tibble(
id = c(1, 1, 2, 2, 2),
HomeNum = c(0,1,0,1,2),
StartDate = as.Date(c("2001-01-01", "2001-03-01", "2000-04-01", "2001-02-01", "2002-08-01")),
FinishDate = as.Date(c("2001-02-01", "2002-05-01", "2001-01-01", "2002-07-01", "2002-12-01"))
)
# with previous versions of tidyr
wide %>%
group_by(id, HomeNum) %>%
mutate(date = list(seq.Date(StartDate, FinishDate, by = "month") %>% as.character())) %>%
tidyr::unnest() %>%
mutate(date = as.Date(date))
#> # A tibble: 50 x 5
#> # Groups: id, HomeNum [5]
#> id HomeNum StartDate FinishDate date
#> <dbl> <dbl> <date> <date> <date>
#> 1 1 0 2001-01-01 2001-02-01 2001-01-01
#> 2 1 0 2001-01-01 2001-02-01 2001-02-01
#> 3 1 1 2001-03-01 2002-05-01 2001-03-01
#> 4 1 1 2001-03-01 2002-05-01 2001-04-01
#> 5 1 1 2001-03-01 2002-05-01 2001-05-01
#> 6 1 1 2001-03-01 2002-05-01 2001-06-01
#> 7 1 1 2001-03-01 2002-05-01 2001-07-01
#> 8 1 1 2001-03-01 2002-05-01 2001-08-01
#> 9 1 1 2001-03-01 2002-05-01 2001-09-01
#> 10 1 1 2001-03-01 2002-05-01 2001-10-01
#> # ... with 40 more rows
否则,像他们发布的那样的解决方案应该可行:
# with tidyr 0.8.1
wide %>%
group_by(id, HomeNum) %>%
mutate(date = list(seq.Date(StartDate, FinishDate, by = "month"))) %>%
tidyr::unnest()
#> # A tibble: 50 x 5
#> # Groups: id, HomeNum [5]
#> id HomeNum StartDate FinishDate date
#> <dbl> <dbl> <date> <date> <date>
#> 1 1 0 2001-01-01 2001-02-01 2001-01-01
#> 2 1 0 2001-01-01 2001-02-01 2001-02-01
#> 3 1 1 2001-03-01 2002-05-01 2001-03-01
#> 4 1 1 2001-03-01 2002-05-01 2001-04-01
#> 5 1 1 2001-03-01 2002-05-01 2001-05-01
#> 6 1 1 2001-03-01 2002-05-01 2001-06-01
#> 7 1 1 2001-03-01 2002-05-01 2001-07-01
#> 8 1 1 2001-03-01 2002-05-01 2001-08-01
#> 9 1 1 2001-03-01 2002-05-01 2001-09-01
#> 10 1 1 2001-03-01 2002-05-01 2001-10-01
#> # ... with 40 more rows
另一种选择是 gather
将数据转换为长格式,其中观察结果有一个 type
列显示它是开始日期还是结束日期。然后使用 complete
填写每个组的最小和最大日期之间缺失的日期。收集保留 type
列,对于添加的日期,该列被填充为 NA
。如果 type
列不再有用,您可以删除它。
wide %>%
gather(key = type, value = date, StartDate, FinishDate) %>%
group_by(id, HomeNum) %>%
complete(date = seq.Date(min(date), max(date), by = "month"))
#> # A tibble: 50 x 4
#> # Groups: id, HomeNum [5]
#> id HomeNum date type
#> <dbl> <dbl> <date> <chr>
#> 1 1 0 2001-01-01 StartDate
#> 2 1 0 2001-02-01 FinishDate
#> 3 1 1 2001-03-01 StartDate
#> 4 1 1 2001-04-01 <NA>
#> 5 1 1 2001-05-01 <NA>
#> 6 1 1 2001-06-01 <NA>
#> 7 1 1 2001-07-01 <NA>
#> 8 1 1 2001-08-01 <NA>
#> 9 1 1 2001-09-01 <NA>
#> 10 1 1 2001-10-01 <NA>
#> # ... with 40 more rows
由 reprex package (v0.2.0) 创建于 2018-05-22。