30

I have a data frame in R with the week of the year that I would like to convert to a date. I know I have to pick a year and a day of the week so I am fixing those values at 2014 and 1. Converting this to a date seems simple:

as.Date(paste(2014,df$Week,1,sep=""),"%Y%U%u")

But this code only works if week is greater than 9. Week 1 - 9 returns NA. If I change the week to 01,02,03... it still returns NA.

Anyone see what I am missing?

Jaap
  • 81,064
  • 34
  • 182
  • 193
Aaron Soderstrom
  • 599
  • 1
  • 6
  • 12
  • 3
    Possible duplicate of [Transform Year/Week to date object in R](https://stackoverflow.com/questions/45549449/transform-year-week-to-date-object-in-r) – Uwe Sep 14 '17 at 06:57
  • Beware of the different conventions on how to count week-of-the-year. Please, see [this answer](https://stackoverflow.com/a/45587644/3817004) for a discussion. – Uwe Sep 14 '17 at 07:21
  • 1
    @Uwe I think it is better to post your answer here and close the other Q as a duplicate – Jaap Jan 27 '20 at 10:16

5 Answers5

50

as.Date is calling the 1 to 9 as NA as it is expects two digits for the week number and can't properly parse it.

To fix it, add in some - to split things up:

as.Date(paste(2014, df$Week, 1, sep="-"), "%Y-%U-%u")
jeremycg
  • 24,657
  • 5
  • 63
  • 74
  • r strips out the `0` in 01 in numeric data - see the output of `paste(2014,01,1,sep="")` - "201411". You could do `paste(2014,"01",1,sep="")` to make it character, but if your data is numeric that won't easily work. – jeremycg Sep 09 '15 at 03:48
  • 5
    Beware of the different conventions on how to count week-of-the-year. Please, see [this answer](https://stackoverflow.com/a/45587644/3817004) for a discussion. – Uwe Sep 14 '17 at 07:22
16

An alternative solution is to use date arithmetic from the lubridate package:

lubridate::ymd( "2014-01-01" ) + lubridate::weeks( df$Week - 1 )

The -1 is necessary because 2014-01-01 is already week 1. In other words, we want:

  • df$Week == 1 to map to 2014-01-01 (which is ymd("2014-01-01") + weeks(1-1))
  • df$Week == 2 to map to 2014-01-08 (which is ymd("2014-01-01") + weeks(2-1))
  • and so on.
Marcus Campbell
  • 2,746
  • 4
  • 22
  • 36
Artem Sokolov
  • 13,196
  • 4
  • 43
  • 74
  • 1
    How would you generalize this to take into account the change of years? – Phil Sep 13 '17 at 13:35
  • 3
    Beware of the different conventions on how to count week-of-the-year. Please, see [this answer](https://stackoverflow.com/a/45587644/3817004) for a discussion. – Uwe Sep 14 '17 at 07:22
  • 1
    @Phil `ymd("2014-01-01") + weeks(df$Week - 1) + years(df$Year - 2014)` (assuming that a `Year` variable is there). – ahorn Oct 10 '20 at 17:16
5

Another option with lubridate

lubridate::parse_date_time(paste(2014, df$Week, 1, sep="/"),'Y/W/w')

W - week number, w - weekday number, 0-6 (Sun-Sat)

Yuriy Barvinchenko
  • 1,465
  • 1
  • 12
  • 17
2

Another alternative is to make sure that week numbers have two digits, which can be done using stringr::str_pad(), which will add a pad="0" to make sure there are width=2 digits:

year <- 2015
week <- 1
as.Date(paste(year, week, "1", sep=""), "%Y%U%u")
#> [1] NA
as.Date(paste(year, stringr::str_pad(week,width=2, pad="0"), "1", sep=""), "%Y%U%u")
#> [1] "2015-01-05"
as.Date(paste(year, week, "1", sep="-"), "%Y-%U-%u")
#> [1] "2015-01-05"

Created on 2021-04-19 by the reprex package (v1.0.0)

Matifou
  • 7,968
  • 3
  • 47
  • 52
-1
It will be like using 2nd year = (week-52), 3rd year  = (week -104)...so on

for(i in 1:456548)
{
  if (train[i,2] > 0 & train[i,2] <53)
  {
    train["weekdate"] <- as.Date(paste(2016, train$week, 1, sep="-"), "%Y-%U-%u")
  }
  if (train[i,2] > 52 & train[i,2] <105)
  {
    train["weekdate"] <- as.Date(paste(2017, (train$week-52), 1, sep="-"), "%Y-%U-%u")
  }
  if (train[i,2] > 104 & train[i,2] <150)
  {
    train["weekdate"] <- as.Date(paste(2018, (train$week-104), 1, sep="-"), "%Y-%U-%u")
  }

}