Как разобрать год + номер недели в R? - PullRequest
17 голосов
/ 21 февраля 2012

Есть ли хороший способ преобразовать число года + недели в дату в R? Я пробовал следующее:

> as.POSIXct("2008 41", format="%Y %U")
[1] "2008-02-21 EST"
> as.POSIXct("2008 42", format="%Y %U")
[1] "2008-02-21 EST"

Согласно ?strftime:

% Y Год с веком. Обратите внимание, что в то время как не было нуля в оригинальный григорианский календарь, ISO 8601: 2004 определяет его как действительный (интерпретируется как 1BC): см. http://en.wikipedia.org/wiki/0_(year). Примечание что стандарт также говорит, что годы до 1582 года в своем календаре должен использоваться только с согласия вовлеченных сторон.

% U Неделя года в виде десятичного числа (00–53) с использованием воскресенья в качестве первый день 1 недели (и обычно первое воскресенье год как день 1 недели 1). Конвенция США.

Ответы [ 3 ]

22 голосов
/ 21 февраля 2012

Это похоже на другой вопрос , который вы, возможно, видели раньше . :)

Ключевой вопрос: какой день должен указывать номер недели? Это первый день недели? Последний? Это неоднозначно. Я не знаю, является ли первая неделя первым днем ​​года или седьмым днем ​​года, или, возможно, первым воскресеньем или понедельником года (что является частой интерпретацией). (И это еще хуже: обычно они имеют индекс 0, а не 1). Таким образом, необходимо указать перечисляемый день недели.

Например, попробуйте это:

as.POSIXlt("2008 42 1", format = "%Y %U %u")

Индикатор %u указывает день недели.

Дополнительное примечание: см. ?strptime для различных вариантов преобразования формата. Важно соблюдать осторожность при перечислении недель, так как они могут быть разбиты на конец года, а день 1 неоднозначен: он указан на основе воскресенья или понедельника или с первого дня года? Все это должно быть указано и протестировано в различных системах, где будет выполняться код R. Я не уверен, что системы Windows и POSIX поют одну и ту же мелодию на некоторых из этих преобразований, поэтому я бы протестировал и протестировал снова.

5 голосов
/ 21 февраля 2012

День недели == ноль в системе POSIXlt DateTimesClasses - воскресенье.Не совсем библейский и не согласуется с индексированием R, которое также начинается с «1», но это так.Нулевая неделя - первая (частичная) неделя в году.Первая неделя (но день нулевой недели) начинается с первого воскресенья.И все остальные типы последовательностей в POSIXlt имеют 0 в качестве отправной точки.Интересно посмотреть, как работают элементы списка объектов POSIXlt.Единственный способ реально изменить дату POSIXlt - это изменить элементы $ year, $ mon или $ mday.Другие кажутся эпифеноменами.

  today <- as.POSIXlt(Sys.Date())
  today  # Tuesday
#[1] "2012-02-21 UTC"
     today$wday <- 0  # attempt to make it Sunday
     today
# [1] "2012-02-21 UTC"   The attempt fails
 today$mday <- 19
 today
#[1] "2012-02-19 UTC"   Success
1 голос
/ 05 июля 2012

Я не придумал это сам (взято из сообщения в блоге от Forester), но, тем не менее, я решил добавить это в список ответов, потому что это первая реализация ISO 8601 номер недели, который я видел в R.

Без сомнения, номера недель - очень неоднозначная тема, но я предпочитаю стандарт ISO, а не текущую реализацию номеров недель через format(..., "%U"), потому что кажется, что это то, с чем большинство людей согласились, по крайней мере, в Германии (календари и т. Д.) .).

Я поместил фактическую функцию def внизу, чтобы упростить фокусировку на выходе. Кроме того, я просто наткнулся на пакет ISOweek , возможно, стоит попробовать.

Сравнение подходов

x.days  <- c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
x.names <- sapply(1:length(posix), function(x) {
    x.day <- as.POSIXlt(posix[x], tz="Europe/Berlin")$wday
    if (x.day == 0) {
        x.day <- 7
    }
    out <- x.days[x.day]
})

data.frame(
    posix, 
    name=x.names,
    week.r=weeknum, 
    week.iso=ISOweek(as.character(posix), tzone="Europe/Berlin")$weeknum
)

# Result

        posix name week.r week.iso
1  2012-01-01  Sun      1  4480458
2  2012-01-02  Mon      1        1
3  2012-01-03  Tue      1        1
4  2012-01-04  Wed      1        1
5  2012-01-05  Thu      1        1
6  2012-01-06  Fri      1        1
7  2012-01-07  Sat      1        1
8  2012-01-08  Sun      2        1
9  2012-01-09  Mon      2        2
10 2012-01-10  Tue      2        2
11 2012-01-11  Wed      2        2
12 2012-01-12  Thu      2        2
13 2012-01-13  Fri      2        2
14 2012-01-14  Sat      2        2
15 2012-01-15  Sun      3        2
16 2012-01-16  Mon      3        3
17 2012-01-17  Tue      3        3
18 2012-01-18  Wed      3        3
19 2012-01-19  Thu      3        3
20 2012-01-20  Fri      3        3
21 2012-01-21  Sat      3        3
22 2012-01-22  Sun      4        3
23 2012-01-23  Mon      4        4
24 2012-01-24  Tue      4        4
25 2012-01-25  Wed      4        4
26 2012-01-26  Thu      4        4
27 2012-01-27  Fri      4        4
28 2012-01-28  Sat      4        4
29 2012-01-29  Sun      5        4
30 2012-01-30  Mon      5        5
31 2012-01-31  Tue      5        5

Функция Def

Это взято прямо из сообщения в блоге , я только что изменил пару мелких вещей. Эта функция все еще немного схематична (например, номер недели первого свидания далек), но я считаю, что это хорошее начало!

ISOweek <- function(
    date, 
    format="%Y-%m-%d", 
    tzone="UTC", 
    return.val="weekofyear"
){
  ##converts dates into "dayofyear" or "weekofyear", the latter providing the ISO-8601 week
  ##date should be a vector of class Date or a vector of formatted character strings
  ##format refers to the date form used if a vector of
  ##  character strings  is supplied

  ##convert date to POSIXt format 
  if(class(date)[1]%in%c("Date","character")){
    date=as.POSIXlt(date,format=format, tz=tzone)
  }

#  if(class(date)[1]!="POSIXt"){
  if (!inherits(date, "POSIXt")) {
    print("Date is of wrong format.")
    break
  }else if(class(date)[2]=="POSIXct"){
    date=as.POSIXlt(date, tz=tzone)
  }
print(date)

  if(return.val=="dayofyear"){
    ##add 1 because POSIXt is base zero
    return(date$yday+1)
  }else if(return.val=="weekofyear"){
    ##Based on the ISO8601 weekdate system,
    ## Monday is the first day of the week
    ## W01 is the week with 4 Jan in it.
    year=1900+date$year
    jan4=strptime(paste(year,1,4,sep="-"),format="%Y-%m-%d")
    wday=jan4$wday

    wday[wday==0]=7  ##convert to base 1, where Monday == 1, Sunday==7

    ##calculate the date of the first week of the year
    weekstart=jan4-(wday-1)*86400  
    weeknum=ceiling(as.numeric((difftime(date,weekstart,units="days")+0.1)/7))

    #########################################################################
    ##calculate week for days of the year occuring in the next year's week 1.
    #########################################################################
    mday=date$mday
    wday=date$wday
    wday[wday==0]=7
    year=ifelse(weeknum==53 & mday-wday>=28,year+1,year)
    weeknum=ifelse(weeknum==53 & mday-wday>=28,1,weeknum)

    ################################################################
    ##calculate week for days of the year occuring prior to week 1.
    ################################################################

    ##first calculate the numbe of weeks in the previous year
    year.shift=year-1
    jan4.shift=strptime(paste(year.shift,1,4,sep="-"),format="%Y-%m-%d")
    wday=jan4.shift$wday
    wday[wday==0]=7  ##convert to base 1, where Monday == 1, Sunday==7
    weekstart=jan4.shift-(wday-1)*86400
    weeknum.shift=ceiling(as.numeric((difftime(date,weekstart)+0.1)/7))

    ##update year and week
    year=ifelse(weeknum==0,year.shift,year)
    weeknum=ifelse(weeknum==0,weeknum.shift,weeknum)

    return(list("year"=year,"weeknum"=weeknum))
  }else{
    print("Unknown return.val")
    break
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...