web-dev-qa-db-fra.com

Comment obtenir les numéros de semaine à partir de dates?

À la recherche d’une fonction dans R pour convertir les dates en numéros de semaine (de l’année), j’ai opté pour week du paquet 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

Pourquoi le numéro de semaine passe-t-il à 12 le mardi, au lieu de lundi? Qu'est-ce que je rate? (Le fuseau horaire ne devrait pas être pertinent car il n’ya que des dates?!)

D'autres suggestions pour les fonctions (de base) R sont également appréciées.

39
Christian Borck

Paquet de base  

Utilisation de la fonction strftime en passant l'argument %V pour obtenir la semaine de l'année sous forme de nombre décimal (01 à 53) tel que défini dans la norme ISO 8601. (Plus de détails dans le document:? Strftime)

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

Sortie:

[1] "11" "12" "12" "01"
39
mpalanco

si vous essayez avec lubrifier:

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

[1] 11 11 12  1

Le motif est le même. Essayez isoweek

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

En fait, je pense que vous avez peut-être découvert un bogue dans la fonction week(...), ou du moins une erreur dans la documentation. J'espère que quelqu'un va intervenir et expliquer pourquoi je me trompe.

En regardant le code:

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

La documentation indique:

Semaines est le nombre de périodes complètes de sept jours qui se sont produites entre la date et le 1er janvier plus une.

Mais puisque le 1er janvier est le premier jour de l'année (pas le zéro), la première "semaine" sera une période de six jours. Le code devrait (??) être 

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

NB: Vous utilisez week(...) dans le package data.table, qui est le même code que lubridate::week sauf que tout est converti en entier plutôt qu'en numérique pour plus d'efficacité. Donc, cette fonction a le même problème (??).

5
jlhoward

Je pense que le problème est que le calcul week utilise en quelque sorte le premier jour de l'année. Je ne comprends pas les mécanismes internes, mais vous pouvez voir ce que je veux dire par cet exemple: 

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

Ma solution consiste à utiliser cette fonction: 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])
}

Bien sûr, vous pouvez facilement changer le décalage pour que le lundi soit le premier ou peu importe. La meilleure façon de le faire serait d'ajouter un décalage à l'offset ... mais je ne l'ai pas encore fait.

J'ai fourni un lien vers mon simple paquet geneorama, mais s'il vous plait, n'y comptez pas trop car il est susceptible de changer et n'est pas très documenté. 

4
geneorama

si vous voulez obtenir le numéro de semaine avec l'année, utilisez: "%Y-W%V":

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

alors 

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

devient: 

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

3
Grant Shannon

Je comprends le besoin de paquets dans certaines situations, mais le langage de base est si élégant et si éprouvé (et mis au point et optimisé).

Pourquoi pas:

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

Et ensuite, vous choisissez si la première semaine de l'année est zéro (comme dans l'indexation en C) ou 1 (comme dans l'indexation dans R).

Pas de paquets à apprendre, mettre à jour, inquiétez-vous des bugs dans. 

2
user3229754

En utilisant uniquement la base, j’ai écrit la fonction suivante.

Remarque:

  1. Suppose que le lundi est le jour 1 de la semaine
  2. La première semaine est la semaine 1
  3. Renvoie 0 si la semaine est 52 de l'année dernière

Ajustez selon vos besoins.

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
0
Wael Hussein