从具有开始/结束日期的行创建年份序列的行的数据帧

时间:2018-06-23 00:56:26

标签: r date sequence lubridate

我是R和编码领域的相对较新的用户,我已经搜索过并且无法解决此问题。我有以下数据:

groupid  start.date   end.date    Status
1        2014-01-01   2017-01-01  A
1        2018-01-01   2020-01-01  D
2        2014-01-01   2017-01-01  B

如何生成一个数据框,其中每个观测值都是一年,而不是groupid和时间段的组合。

我正在寻找的输出是:

groupid  year   status
1        2014  A
1        2015  A
1        2016  A
1        2017  A
1        2018  D
1        2019  D
1        2020  D
2        2014  B
2        2015  B
2        2016  B
2        2017  B

我尝试了多种方法,但我认为我的最佳尝试是:

df <- df %>% 
group_by(rn=row_number()) %>% 
  mutate(d = list(seq(start.date, end.date, by='1 year'))) 
%>%
  unnest()

但是我知道了

Error: Each column must either be a list of vectors or a list of data frames 
[d]

搜索错误并没有帮助我进一步弄清错误所在。开始日期和结束日期存储为日期。如果很重要,它们是由仅具有四位数年份数字的两个列向量生成的,因此我应用了以下代码将其更改为可用的日期格式:

df$start.date <- as.Date(ISOdate(df$from, 1, 1)) 
df$end.date <- as.Date(ISOdate(df$to, 1, 1))  

2 个答案:

答案 0 :(得分:0)

您快到了!根据ID和状态对数据进行分组,因为这两个变量的组合是开始和结束日期的来源。

library(tidyverse)

df <- "groupid  start.date   end.date    Status
1        2014-01-01   2017-01-01  A
1        2018-01-01   2020-01-01  D
2        2014-01-01   2017-01-01  B" %>% read_table2()

df %>%
  group_by(groupid, Status) %>%
  mutate(dates = list(seq(from = start.date, to = end.date, by = "1 year"))) %>%
  unnest()
#> # A tibble: 11 x 5
#> # Groups:   groupid, Status [3]
#>    groupid start.date end.date   Status dates     
#>      <int> <date>     <date>     <chr>  <date>    
#>  1       1 2014-01-01 2017-01-01 A      2014-01-01
#>  2       1 2014-01-01 2017-01-01 A      2015-01-01
#>  3       1 2014-01-01 2017-01-01 A      2016-01-01
#>  4       1 2014-01-01 2017-01-01 A      2017-01-01
#>  5       1 2018-01-01 2020-01-01 D      2018-01-01
#>  6       1 2018-01-01 2020-01-01 D      2019-01-01
#>  7       1 2018-01-01 2020-01-01 D      2020-01-01
#>  8       2 2014-01-01 2017-01-01 B      2014-01-01
#>  9       2 2014-01-01 2017-01-01 B      2015-01-01
#> 10       2 2014-01-01 2017-01-01 B      2016-01-01
#> 11       2 2014-01-01 2017-01-01 B      2017-01-01

要获取所需的格式,您可以从日期序列中提取年份并删除多余的列:

df %>%
  group_by(groupid, Status) %>%
  mutate(dates = list(seq(from = start.date, to = end.date, by = "1 year"))) %>%
  unnest() %>%
  mutate(year = lubridate::year(dates)) %>%
  select(groupid, year, Status)
#> # A tibble: 11 x 3
#> # Groups:   groupid, Status [3]
#>    groupid  year Status
#>      <int> <dbl> <chr> 
#>  1       1  2014 A     
#>  2       1  2015 A     
#>  3       1  2016 A     
#>  4       1  2017 A     
#>  5       1  2018 D     
#>  6       1  2019 D     
#>  7       1  2020 D     
#>  8       2  2014 B     
#>  9       2  2015 B     
#> 10       2  2016 B     
#> 11       2  2017 B

reprex package(v0.2.0)于2018-06-22创建。

答案 1 :(得分:0)

camille的答案基于groupidStatus的组合是唯一的隐式假设。但是,这不能保证。此外,OP本身已选择按行号进行分组以确保安全。

分组是必需的,因为seq()和单个冒号运算符:不接受vetors作为输入。

dplyr / tidyr解决方案

此方法按行号分组,并提取创建序列的之前年。 df1是OP给出的小标题(请参阅下面的Data部分)。

library(dplyr)
library(tidyr)
library(lubridate)
df1 %>% 
  group_by(rn = row_number()) %>% 
  mutate(year = list(year(start.date):year(end.date))) %>% 
  unnest() %>% 
  ungroup() %>% 
  select(groupid, year, Status)
# A tibble: 11 x 3
   groupid  year Status
     <int> <int> <chr> 
 1       1  2014 A     
 2       1  2015 A     
 3       1  2016 A     
 4       1  2017 A     
 5       1  2018 D     
 6       1  2019 D     
 7       1  2020 D     
 8       2  2014 B     
 9       2  2015 B     
10       2  2016 B     
11       2  2017 B

data.table方法

data.table允许使用更简洁的代码实现相同的结果:

library(data.table)
setDT(df1)[, .(groupid, year = year(start.date):year(end.date), Status), 
  by = .(rn = 1:nrow(df1))][
    , rn := NULL][] 
    groupid year Status
 1:       1 2014      A
 2:       1 2015      A
 3:       1 2016      A
 4:       1 2017      A
 5:       1 2018      D
 6:       1 2019      D
 7:       1 2020      D
 8:       2 2014      B
 9:       2 2015      B
10:       2 2016      B
11:       2 2017      B

OP的原始问题

OP公开了start.dateend.date 是从只有四位数年份数字的两个列向量生成的。

不必将这些年份数字预先转换为日期。它们可以直接用于创建年份序列:

library(dplyr)
library(tidyr)
df2 %>% 
  group_by(rn = row_number()) %>% 
  mutate(year = list(from:to)) %>% 
  unnest() %>% 
  ungroup() %>% 
  select(groupid, year, Status)
# A tibble: 11 x 3
   groupid  year Status
     <int> <int> <chr> 
 1       1  2014 A     
 2       1  2015 A     
 3       1  2016 A     
 4       1  2017 A     
 5       1  2018 D     
 6       1  2019 D     
 7       1  2020 D     
 8       2  2014 B     
 9       2  2015 B     
10       2  2016 B     
11       2  2017 B

或者,以data.table语法:

library(data.table)
setDT(df2)[, .(groupid, year = from:to, Status), by = .(rn = 1:nrow(df2))][
    , rn := NULL][] 

根据help(":"),字符参数被强制为数字,因此不需要显式强制。

数据

df1 <- readr::read_table(
  "groupid  start.date   end.date    Status
1        2014-01-01   2017-01-01  A
1        2018-01-01   2020-01-01  D
2        2014-01-01   2017-01-01  B"
)

df2 <- readr::read_table(
  "groupid  from   to    Status
1        2014   2017  A
1        2018   2020  D
2        2014   2017  B"
)