如何从日期获得周数?

Chr*_*rck 47 r date week-number data.table

在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
Run Code Online (Sandbox Code Playgroud)

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

关于(基本)R函数的其他建议也受到赞赏.

mpa*_*nco 48

基础包

使用strftime传递参数的函数%V获取一年中的星期作为ISO 8601中定义的十进制数(01-53).(文档中的更多细节:?strftime)

strftime(c("2014-03-16", "2014-03-17","2014-03-18", "2014-01-01"), format = "%V")
Run Code Online (Sandbox Code Playgroud)

输出:

[1] "11" "12" "12" "01"
Run Code Online (Sandbox Code Playgroud)

  • `2014-01-01`和`2014-12-29`都会得到`01`。 (2认同)
  • @giordano 按照 ISO 8601 的定义,这是正确的。如果包含 1 月 1 日的那一周(从星期一开始)在新的一年中有四天或更多天,则将其视为第一周。您可以在任何 ISO 中仔细检查这一点8601在线周计算器。 (2认同)

Pau*_*oso 30

如果你尝试使用lubridate:

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

[1] 11 11 12  1
Run Code Online (Sandbox Code Playgroud)

模式是一样的.尝试isoweek

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

  • @ChristianBorck `isoweek` 是你需要的吗? (2认同)

Gra*_*non 7

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

e.g    yearAndweeks <- strftime(dates, format = "%Y-W%V")
Run Code Online (Sandbox Code Playgroud)

所以

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

变成:

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

  • 这是危险的: `strftime(c(as.Date("2014-01-01"),as.Date("2014-12-29")), format = "%YW%V")` 给出 `[1 ] “2014-W01”“2014-W01”`。 (3认同)

jlh*_*ard 6

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

看代码:

library(lubridate)
> week
function (x) 
yday(x)%/%7 + 1
<environment: namespace:lubridate>
Run Code Online (Sandbox Code Playgroud)

文件说明:

周数是在日期和1月1日之间发生的完整七天期间的数量加一.

但是,由于1月1日是一年中的第一天(不是第0天),第一个"周"将是6天.代码应该(??)

(yday(x)-1)%/%7 + 1
Run Code Online (Sandbox Code Playgroud)

注意:你正在使用week(...)data.table包中,这是一个相同的代码,lubridate::week除了它将所有内容强制转换为整数而不是数字以提高效率.所以这个函数有同样的问题(??).


Eri*_*ers 6

如果您想获得年份中的周数,Grant Shannon 使用 strftime 的解决方案可行,但您需要对 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

Run Code Online (Sandbox Code Playgroud)


小智 5

我理解在某些情况下需要包,但基本语言是如此优雅和成熟(并经过调试和优化)。

为什么不:

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

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

无需学习、更新、担心错误的软件包。

  • @ChristianBorck - 不要进一步混淆事情,但“正确”取决于您对“周”的定义。[ISO-8601 标准](http://en.wikipedia.org/wiki/Week_number#Week_numbering) 定义了从星期一开始的一周,尽管周编号取决于 1 月 1 日是哪一天。`week(...)` 函数没有声明使用这个标准。我的观点是`week(...)` 似乎不符合它自己的定义。如果您想要 ISO-8601 周(顺便说一下,这是一个很好的做法),请使用 `isoweek(...)`。 (3认同)

gen*_*ama 5

我认为问题在于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
Run Code Online (Sandbox Code Playgroud)

我的解决方法是这个函数: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])
}
Run Code Online (Sandbox Code Playgroud)

当然,您可以轻松地更改偏移量以首先制作星期一或其他任何内容。最好的方法是在偏移量中添加一个偏移量......但我还没有这样做。

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