在 R 中用 round_date() 舍入日期

Rounding dates with round_date() in R

我尝试仅在 R 中将日期格式 yyyymmdd 转换为 yyyy。 在 中提出了一个非常有趣的答案,因为它设法使 R 理解将润滑包中的 8 位条目 (yyyymmdd) 转换为 4 位年份 (yyyy),这对我来说非常好.

在旧代码中我使用了 round_date()

   date2<-c('01/01/2000','08/08/2000','16/03/2001','25/12/2000','29/02/2000')
    name<-c('A','B','C','D','E')
    
    df<-data.frame(date2,name)
    
    df2 <- df %>%
      mutate(date2 = dmy(date2)) %>%
      mutate(year_date = round_date(date2,'year'))
    
    df2
    str(df2)

date2<date> name<chr> year_date <date>
2000-01-01    A         2000-01-01      
2000-08-08    B         2001-01-01      
2001-03-16    C         2001-01-01      
2000-12-25    D         2001-01-01      
2000-02-29    E         2000-01-01  

但是当我发现例如日期 2000-08-08 被四舍五入到 2001-01-01 年而不是我预期的 2001-01-01 时,我的统计分析开始出现问题。

这对我来说是一个非常大的问题,因为属于 2005 年的信息已移至 2006 年,考虑到我的数据库中有 1400 多行。

我注意到年中之后(6 月之后)的日期会四舍五入到下一年,这很糟糕。

如何将 2000-08-08 日期四舍五入到 2000 而不是 2001?

这个(更简单,也只有 base R)操作不是你想要的吗?

> date2 <- c('01/01/2000','08/08/2000','16/03/2001','25/12/2000','29/02/2000')
> dd <- as.Date(date2, "%d/%m/%Y")
> yd <- format(dd, "%Y-01-01")
> dt <- as.Date(yd)
> D <- data.frame(date2=date2, date=dd, y=yd, d=dt)
> D
       date2       date          y          d   
1 01/01/2000 2000-01-01 2000-01-01 2000-01-01
2 08/08/2000 2000-08-08 2000-01-01 2000-01-01
3 16/03/2001 2001-03-16 2001-01-01 2001-01-01
4 25/12/2000 2000-12-25 2000-01-01 2000-01-01
5 29/02/2000 2000-02-29 2000-01-01 2000-01-01
>   

本质上我们只是从(解析为日期)Date 对象中提取年份部分并附加 -01-01.

编辑: DateDatetime 对象也有 trunc() 操作。奇怪的是,截断年份仅适用于 Datetime(有关更多信息,请参阅 trunc.Date 的帮助页面)所以这也适用:

> as.Date(trunc(as.POSIXlt(dd), "years"))
[1] "2000-01-01" "2000-01-01" "2001-01-01" "2000-01-01" "2000-01-01"
> 

编辑 2: 我们可以在 data.frame 中使用最后一步更清洁/更简单的解决方案,其中三列用于 input 数据(作为字符),parse 数据作为适当的 Date 类型和所需的 truncated 年份数据——全部使用 base R没有进一步的依赖。当然,如果你想要可以通过管道重写它并且lubridate通过稍微慢一点的路径重写它以获得相同的结果(这只对“大”数据很重要)。

> date2 <- c('01/01/2000','08/08/2000','16/03/2001','25/12/2000','29/02/2000')
> pd <- as.Date(date2, "%d/%m/%Y")
> td <- as.Date(trunc(as.POSIXlt(pd), "years"))
> D <- data.frame(input = date2, parsed = pd, output = td)
> D
       input     parsed     output
1 01/01/2000 2000-01-01 2000-01-01
2 08/08/2000 2000-08-08 2000-01-01
3 16/03/2001 2001-03-16 2001-01-01
4 25/12/2000 2000-12-25 2000-01-01
5 29/02/2000 2000-02-29 2000-01-01
> 

对于真正的“生产”用途,您可能不需要 data.frame 并且不需要保留导致单行的中间结果:

> as.Date(trunc(as.POSIXlt( as.Date(date2, "%d/%m/%Y") ), "years"))
[1] "2000-01-01" "2000-01-01" "2001-01-01" "2000-01-01" "2000-01-01"
> 

这可能是您可以获得的最紧凑、最高效的转换。

如果您只想要年份(而不是对应于一年的第一天的日期),您可以使用 lubridate::year().

df %>% mutate(across(date2,dmy),
              year_date=year(date2))

如果您确实想要一年的第一天,那么 floor_date() 就可以了。

df %>% mutate(across(date2,dmy),
               year_date=floor_date(date2,"year"))

或者如果您只需要截断的日期,您可以直接转到 mutate(year_date=floor_date(dmy(date2)))

在 base R 中,year() 将是 format(date2, "%Y"),如@DirkEddelbuettel 的回答所示。

如果您查阅 round_date 帮助页面,您还会看到 floor_date:

library("lubridate")
library("dplyr")

date2 <- c('01/01/2000','08/08/2000','16/03/2001','25/12/2000','29/02/2000')
name <- c('A','B','C','D','E')

df <- data.frame(date2,name)

df2 <- df %>%
  mutate(date2 = dmy(date2)) %>%
  mutate(year_date = floor_date(date2,'year'))

df2