【问题标题】:Expand start and end dates to unbalanced monthly panel with dplyr使用 dplyr 将开始和结束日期扩展到不平衡的每月面板
【发布时间】:2018-05-21 02:25:10
【问题描述】:

我有活动的开始日期和结束日期,我想将这些活动扩展到每月的面板中,我想知道dplyr 中是否有任何工具可以解决这个问题。下面的代码做了我想用ddply() 做的事情。它首先创建一个示例tibbledata.frame(称为“wide”),其中“id”代表个人,“HomeNum”是该个人的事件。下一行创建一个“日期”变量,它是按“HomeNum”组在每个“id”中从“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 的答案。

【问题讨论】:

    标签: r dplyr plyr tidyverse


    【解决方案1】:

    您可以将date 的元素强制转换为列表和unnest

    library(tidyverse)
    wide %>%
      group_by(id, HomeNum) %>%
      mutate(date = list(seq.Date(StartDate, FinishDate, by = "month"))) %>%
      unnest(date)
    

    【讨论】:

    • 我无法让它工作。我得到“错误:每一列必须是向量列表或数据框列表”。
    • 取消嵌套日期列表是tidyr 中的一个问题,应该在最新版本中修复。引用here。我收到此错误,更新到 0.8.1,不再收到,但会发布解决方法
    【解决方案2】:

    在日期列表上使用 unnest 是以前版本的 tidyr 中的一个问题。我遇到了同样的错误并找到了解决方法,但是一旦我更新到tidyr 0.8.1,就不再需要解决方法了。这个问题记录在 GitHub 上的几个问题中——#407#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 填写每个组的最小和最大日期之间的缺失日期。 Gathering 会保留 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 年 5 月 22 日创建。

    【讨论】:

    • 我编辑了您的 0.8,1 解决方案以从 mutate() 函数中删除 = as.Date(date),因为该版本中的日期已存储为日期。另外,第二个mutate() 函数有什么好处吗?没有它,代码似乎有相同的输出。
    • @randy 感谢您的发现,第二个变异是在早期版本的调试中遗留下来的
    猜你喜欢
    • 2021-07-14
    • 2023-03-17
    • 2015-10-07
    • 2017-12-28
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 2021-09-26
    • 1970-01-01
    相关资源
    最近更新 更多