À 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.
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"
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
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 (??).
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é.
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
"
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.
En utilisant uniquement la base, j’ai écrit la fonction suivante.
Remarque:
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