如何从日期获得周数?

时间:2014-03-16 16:29:35

标签: r date data.table week-number

在R中寻找一个函数将日期转换为周数(一年)我从包week获取了data.table。 但是,我发现了一些奇怪的行为:

> week("2014-03-16") # Sun, expecting 11
[1] 11
> week("2014-03-17") # Mon, expecting 12
[1] 11
> week("2014-03-18") # Tue, expecting 12
[1] 12

为什么星期二的周数转换为12,而不是星期一?我错过了什么? (时区应该是无关紧要的,因为只有日期?!)

对(基本)R函数的其他建议也很受欢迎。

8 个答案:

答案 0 :(得分:44)

基础包

使用函数strftime传递参数%V以获取十进制数字(01-53)如ISO 8601中所定义。(文档中的更多细节:?strftime)

strftime(c("2014-03-16", "2014-03-17","2014-03-18", "2014-01-01"), format = "%V")

输出:

[1] "11" "12" "12" "01"

答案 1 :(得分:28)

如果您尝试使用lubridate:

library(lubridate)
lubridate::week(ymd("2014-03-16", "2014-03-17","2014-03-18", '2014-01-01'))

[1] 11 11 12  1

模式是一样的。试试isoweek

lubridate::isoweek(ymd("2014-03-16", "2014-03-17","2014-03-18", '2014-01-01'))
[1] 11 12 12  1

答案 2 :(得分:5)

实际上,我认为您可能在week(...)函数中发现了一个错误,或者至少是文档中的错误。希望有人会跳进去解释我错的原因。

查看代码:

library(lubridate)
> week
function (x) 
yday(x)%/%7 + 1
<environment: namespace:lubridate>

文档说明:

  

周数是在日期和1月1日之间发生的完整七天时段加上一个。

但是,自1月1日是一年的第一天(不是第0天),第一个&#34;周&#34;将是一个六天的时间。代码应该(??)

(yday(x)-1)%/%7 + 1

注意:您在week(...)包中使用data.table,这与lubridate::week的代码相同,不同之处在于它将所有内容强制转换为整数而不是数字。所以这个函数有同样的问题(??)。

答案 3 :(得分:4)

我认为问题是week计算以某种方式使用了一年中的第一天。我不了解内部机制,但你可以看到我对这个例子的意思:

library(data.table)

dd <- seq(as.IDate("2013-12-20"), as.IDate("2014-01-20"), 1)
# dd <- seq(as.IDate("2013-12-01"), as.IDate("2014-03-31"), 1)

dt <- data.table(i = 1:length(dd),
                 day = dd,
                 weekday = weekdays(dd),
                 day_rounded = round(dd, "weeks"))
## Now let's add the weekdays for the "rounded" date
dt[ , weekday_rounded := weekdays(day_rounded)]
## This seems to make internal sense with the "week" calculation
dt[ , weeknumber := week(day)]
dt 

    i        day   weekday day_rounded weekday_rounded weeknumber
1:  1 2013-12-20    Friday  2013-12-17         Tuesday         51
2:  2 2013-12-21  Saturday  2013-12-17         Tuesday         51
3:  3 2013-12-22    Sunday  2013-12-17         Tuesday         51
4:  4 2013-12-23    Monday  2013-12-24         Tuesday         52
5:  5 2013-12-24   Tuesday  2013-12-24         Tuesday         52
6:  6 2013-12-25 Wednesday  2013-12-24         Tuesday         52
7:  7 2013-12-26  Thursday  2013-12-24         Tuesday         52
8:  8 2013-12-27    Friday  2013-12-24         Tuesday         52
9:  9 2013-12-28  Saturday  2013-12-24         Tuesday         52
10: 10 2013-12-29    Sunday  2013-12-24         Tuesday         52
11: 11 2013-12-30    Monday  2013-12-31         Tuesday         53
12: 12 2013-12-31   Tuesday  2013-12-31         Tuesday         53
13: 13 2014-01-01 Wednesday  2014-01-01       Wednesday          1
14: 14 2014-01-02  Thursday  2014-01-01       Wednesday          1
15: 15 2014-01-03    Friday  2014-01-01       Wednesday          1
16: 16 2014-01-04  Saturday  2014-01-01       Wednesday          1
17: 17 2014-01-05    Sunday  2014-01-01       Wednesday          1
18: 18 2014-01-06    Monday  2014-01-01       Wednesday          1
19: 19 2014-01-07   Tuesday  2014-01-08       Wednesday          2
20: 20 2014-01-08 Wednesday  2014-01-08       Wednesday          2
21: 21 2014-01-09  Thursday  2014-01-08       Wednesday          2
22: 22 2014-01-10    Friday  2014-01-08       Wednesday          2
23: 23 2014-01-11  Saturday  2014-01-08       Wednesday          2
24: 24 2014-01-12    Sunday  2014-01-08       Wednesday          2
25: 25 2014-01-13    Monday  2014-01-08       Wednesday          2
26: 26 2014-01-14   Tuesday  2014-01-15       Wednesday          3
27: 27 2014-01-15 Wednesday  2014-01-15       Wednesday          3
28: 28 2014-01-16  Thursday  2014-01-15       Wednesday          3
29: 29 2014-01-17    Friday  2014-01-15       Wednesday          3
30: 30 2014-01-18  Saturday  2014-01-15       Wednesday          3
31: 31 2014-01-19    Sunday  2014-01-15       Wednesday          3
32: 32 2014-01-20    Monday  2014-01-15       Wednesday          3
     i        day   weekday day_rounded weekday_rounded weeknumber

我的解决方法是这个功能: https://github.com/geneorama/geneorama/blob/master/R/round_weeks.R

round_weeks <- function(x){
    require(data.table)
    dt <- data.table(i = 1:length(x),
                     day = x,
                     weekday = weekdays(x))
    offset <- data.table(weekday = c('Sunday', 'Monday', 'Tuesday', 'Wednesday', 
                                     'Thursday', 'Friday', 'Saturday'),
                         offset = -(0:6))
    dt <- merge(dt, offset, by="weekday")
    dt[ , day_adj := day + offset]
    setkey(dt, i)
    return(dt[ , day_adj])
}

当然,您可以轻松更改偏移量,使星期一先行或其他。这样做的最好方法是在偏移量上添加一个偏移量......但我还没有这样做。

我提供了一个简单的geneorama包的链接,但请不要太依赖它,因为它可能会改变而且没有很好的记录。

答案 4 :(得分:3)

如果您想获得使用年份的周数:"%Y-W%V"

e.g    yearAndweeks <- strftime(dates, format = "%Y-W%V")

所以

> strftime(c("2014-03-16", "2014-03-17","2014-03-18", "2014-01-01"), format = "%Y-W%V")

成为:

[1] "2014-W11" "2014-W12" "2014-W12" "2014-W01

答案 5 :(得分:2)

我理解在某些情况下需要包,但基本语言非常优雅且经过验证(并经过调试和优化)。

为什么不:

dt <- as.Date("2014-03-16")
dt2 <- as.POSIXlt(dt)
dt2$yday
[1] 74

然后选择一年中的第一周是零(如C中的索引)还是1(如R中的索引)。

没有包可以学习,更新,担心错误。

答案 6 :(得分:0)

仅使用base,我编写了以下函数。

注意:

  1. 假设周一是本周的第1天
  2. 第一周是第1周
  3. 如果一周是去年的52,则返回0
  4. 根据您的需要进行微调。

    xs = 0:3;
    ys = 0:3;
    out = [[xs[i] for i in 1:length(xs), j in 1:length(ys)][:] [ys[j] for i in 1:length(xs), j in 1:length(ys)][:]]
    

答案 7 :(得分:0)

如果您想获取年份的星期数,可以使用strftime解决格兰特·香农(Grant Shannon)的解决方案,但是您需要对1月1日前后的日期进行一些更正。例如,2016-01-03(yyyy-mm-dd)是2015年的第53周,而不是2016年。而2018-12-31是2019年的第1周,而不是2018年。此代码提供了一些示例和解决方案。在“ yearweek”列中,年份有时是错误的,在“ yearweek2”中,它们已被更正(第2行和第5行)。

library(dplyr)
library(lubridate)

# create a testset
test <- data.frame(matrix(data = c("2015-12-31",
                                   "2016-01-03",
                                   "2016-01-04",
                                   "2018-12-30",
                                   "2018-12-31",
                                   "2019-01-01") , ncol=1, nrow = 6 ))
# add a colname
colnames(test) <- "date_txt"

# this codes provides correct year-week numbers
test <- test %>%
        mutate(date = as.Date(date_txt, format = "%Y-%m-%d")) %>%
        mutate(yearweek = as.integer(strftime(date, format = "%Y%V"))) %>%
        mutate(yearweek2 = ifelse(test = day(date) > 7 & substr(yearweek, 5, 6) == '01',
                                 yes  = yearweek + 100,
                                 no   = ifelse(test = month(date) == 1 & as.integer(substr(yearweek, 5, 6)) > 51,
                                               yes  = yearweek - 100,
                                               no   = yearweek)))
# print the result
print(test)

    date_txt       date yearweek yearweek2
1 2015-12-31 2015-12-31   201553    201553
2 2016-01-03 2016-01-03   201653    201553
3 2016-01-04 2016-01-04   201601    201601
4 2018-12-30 2018-12-30   201852    201852
5 2018-12-31 2018-12-31   201801    201901
6 2019-01-01 2019-01-01   201901    201901