Подтвердить что ты не робот

Как получить номера недели из дат?

Ищете функцию в 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-функций.

4b9b3361

Ответ 1

Базовый пакет

Использование функции 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"

Ответ 2

если вы попытаетесь использовать 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

Ответ 3

На самом деле, я думаю, вы, возможно, обнаружили ошибку в функции week(...) или, по крайней мере, ошибку в документации. Надеюсь, кто-то скачет и объяснит, почему я ошибаюсь.

Взглянув на код:

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

В документации указано:

Неделя - это число полных семидневных периодов, которые произошли между датой и 1 января, плюс один.

Но с 1 января - первый день года (не нулевой), первая "неделя" будет шестидневным периодом. Код должен (??) быть

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

NB: вы используете week(...) в пакете data.table, который представляет собой тот же код, что и lubridate::week, за исключением того, что он использует все для целого числа, а не числового для эффективности. Таким образом, эта функция имеет ту же проблему (??).

Ответ 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 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])
}

Конечно, вы можете легко изменить смещение, чтобы сделать первый понедельник или что-то еще. Лучший способ сделать это - добавить смещение к смещению... но я еще этого не сделал.

Я предоставил ссылку на мой простой пакет для геннорамы, но, пожалуйста, не полагайтесь на него слишком сильно, потому что он может измениться и не очень документирован.

Ответ 5

Я понимаю необходимость пакетов в определенных ситуациях, но базовый язык настолько изящный и настолько проверенный (и отлаживаемый, и оптимизированный).

Почему бы и нет:

dt < - as.Date( "2014-03-16" )

dt2 < - as.POSIXlt(dt)

dt2 $yday

[1] 74

И тогда ваш выбор будет ли первая неделя года равна нулю (как при индексировании на C) или 1 (как при индексировании в R).

Нет пакетов для изучения, обновления, исправления ошибок.

Ответ 6

Используя только базу, я написал следующую функцию.

Примечание:

  • Предполагается, что Mon - день № 1 в неделю
  • Первая неделя - неделя 1
  • Возвращает 0, если неделя равна 52 с прошлого года.

Тонкая настройка в соответствии с вашими потребностями.

findWeekNo <- function(myDate){
  # Find out the start day of week 1; that is the date of first Mon in the year
  weekday <- switch(weekdays(as.Date(paste(format(as.Date(myDate),"%Y"),"01-01", sep = "-"))),
                    "Monday"={1},
                    "Tuesday"={2},
                    "Wednesday"={3},
                    "Thursday"={4},
                    "Friday"={5},
                    "Saturday"={6},
                    "Sunday"={7}
  )

  firstMon <- ifelse(weekday==1,1, 9 - weekday )

  weekNo <- floor((as.POSIXlt(myDate)$yday - (firstMon-1))/7)+1
  return(weekNo)
}


findWeekNo("2017-01-15") # 2