How to convert YearWeek format to date? - r

My data: c("202001", "202002").
It shows Year and the number of the week. I need to get the first day of the week added to my data.
How can I get it?
Desired output: 2020-01-06 and 2020-01-13.
Original data, for which the code should work
c(
"202001", "202002", "202003", "202004", "202005", "202006", "202007",
"202008", "202009", "202010", "202011", "202012", "202013", "202014",
"202015", "202016", "202017", "202018", "202019", "202020", "202021",
"202022", "202023", "202024", "202025", "202026", "202027", "202028",
"202029", "202030", "202031", "202032", "202033", "202034", "202035",
"202036", "202037", "202038", "202039", "202040", "202041", "202042",
"202043", "202044", "202045", "202046", "202047", "202048", "202049",
"202050", "202051", "202052", "202053", "202101", "202102", "202103")```

You can do this with base-r using as.Date and specifying the format.
Week of the year
US convention: Week of the year as decimal number (00–53) using Sunday as the first day 1 of the week (and typically with the first Sunday of the year as day 1 of week 1): %U
UK convention: Week of the year as decimal number (00–53) using Monday as the first day of week (and typically with the first Monday of the year as day 1 of week 1): %W
ISO 8601 definition: Week of the year as decimal number (01–53) as defined in ISO 8601. If the week (starting on Monday) containing 1 January has four or more days in the new year, then it is considered week 1. Otherwise, it is the last week of the previous year, and the next week is week 1: %V which is accepted but ignored on input.
Note that there is also a week-based year (%G and %g) which is to be used with %V as it may differ from the calendar year (%Y and %y).
Numeric weekday
Weekday as a decimal number (1–7, Monday is 1): %u
Weekday as decimal number (0–6, Sunday is 0): %w
I append 1 to create a date string referencing the first day of the year-week and then specify the format using strptime codes when calling as.date to convert it.
Example with US convention:
dateList <- c("202001", "202002")
dateList <- paste0(dateList, "1")
dateList <- as.Date(dateList , format = "%Y%W%u")
More information on the formats is available here:
https://www.rdocumentation.org/packages/base/versions/3.6.2/topics/strptime

One way would be to use lubridate package like this:
c("202001", "202002") -> x
lubridate::ymd('2019-12-30') + lubridate::weeks(as.numeric(substr(x, 5, 6)))
#"2020-01-06" "2020-01-13"

Related

How to get current week start date ( Monday ) and End Date ( Sunday) in R

How to get current week start date ( Monday ) and End Date ( Sunday) in R.
My business weeks start from Monday and ends on Sunday.
How do i retrieve Start Date and End Date from my current date.
Eg. Curent date is 18-07-2020. How to retrieve Monday Date ( 18-07-2020) and Sunday Date (19-07-2020)
My Code :
library(lubridate)
library(mailR)
library(htmlTable)
library(DBI)
todays_date <- Sys.Date()
stdt <- floor_date(todays_date, 'week') + 1
lsdt <- floor_date(todays_date, 'week') + 7
If I execute the above code on Sunday, it falls on to next week. Any workaround to make the code to run on Sunday as well, by considering Monday to Sunday as working days.
You can use lubridate's floor_date and ceiling_date with unit as "week". By default week starts on a Sunday in lubridate so to get start date as Monday we need to add 1 in floor_date.
library(lubridate)
todays_date <- as.Date('2020-07-18')
floor_date(todays_date, 'week') + 1
#[1] "2020-07-13"
ceiling_date(todays_date, 'week')
#[1] "2020-07-19"

R strptime: yyyyww conversion using %V behaves strange

I was trying to create a mapping from a week key to yyyyww. For example, week-1 is 200113, week-2 is 200114, and so on.
In my system, first week starts on 2001-03-26, week starts on Monday, and weeks of the year range from 01-53.
I was doing a test with %V striptime, but found strange behavior:
day1 <- as.Date("20010326", "%Y%m%d")
format(day1, "%Y%V") # output "200113", which is correct
However, when I do it inversely:
as.Date("200113", format = "%Y%V") # output "2001-11-01"
as.Date("200113 1", format = "%Y%V %u") # output "2001-11-01"
as.Date with %Y%V failed to return me the correct date. Did I miss something?
Additional testing:
format(as.Date("2001-03-26")+7*39, "%Y%V") # returns "200152"
format(as.Date("2001-03-26")+7*40, "%Y%V") # returns "200101"
format(as.Date("2001-12-31"), "%Y%V") # returns "200101"
The last two should return "200201", it seems that R did not automatically increment the year.
Additional testing: what I'm doing now is kind of nasty but working:
date <- as.Date("2001-03-26") + 7*(week_key-1),
year <- year(date),
month <- month(date),
week <- as.numeric(format(date, "%V")),
yyyyww <- ifelse(week==1 & month==12, 100*(year+1)+week, 100*year+week)

R + Adding n number of weeks to YYYYWW format

I am trying to add 52 weeks to a date variable which is in YYYYWW format. my initial date is 201616 (year 2016 and week 16) and i am trying to add 52 weeks to this date and the expected output is 201715.
I tried couple of things but no luck, here is what i tried so far
date <- as.Date(as.character(201616), "%Y%W")
seq(date, by = "1 week", length.out = 52)
I would greatly appreciate your input. Many Thanks for your time!
The problem is that there are 7 days in week #16 2016. You need to specify a day to convert it to a date that can be used to add days. In the code below %u indicates first day of the week. You can then add 52 weeks to this number.
date1 <- as.Date("201616 1", format = "%Y%U %u")
format(date1+(52*7), "%Y%U")
[1] "201716"
I'm not sure that as.Date can take %Y%W and generate a unique value. It appears to be populating date with the current month and day. If instead we specify a date in the 16th week:
date <- as.Date("2016-04-23")
and format that in your style
format(date, "%Y%W")
[1] "201616"
we can generate a sequence of 52 values from this
newdate_seq <- seq(date, by = "1 week", length.out = 52)
and change those to your format too
format(newdate_seq, "%Y%W")
[1] "201616" "201617" "201618" "201619" "201620" "201621" "201622" "201623" "201624" "201625" "201626" "201627"
[13] "201628" "201629" "201630" "201631" "201632" "201633" "201634" "201635" "201636" "201637" "201638" "201639"
[25] "201640" "201641" "201642" "201643" "201644" "201645" "201646" "201647" "201648" "201649" "201650" "201651"
[37] "201652" "201701" "201702" "201703" "201704" "201705" "201706" "201707" "201708" "201709" "201710" "201711"
[49] "201712" "201713" "201714" "201715"
which ends where you expect.
FYI, for next time, try highlighting what caused you to think there was "no luck" -- what errors did you produce, what results did you produce and how did they differ from what you expect to produce? Simply printing the date variable showed me that it wasn't doing what you expected.

POSIX date from dates in weekly time format

I have dates encoded in a weekly time format (European convention >> 01 through 52/53, e.g. "2016-48") and would like to standardize them to a POSIX date:
require(magrittr)
(x <- as.POSIXct("2016-12-01") %>% format("%Y-%V"))
# [1] "2016-48"
as.POSIXct(x, format = "%Y-%V")
# [1] "2016-01-11 CET"
I expected the last statement to return "2016-12-01" again. What am I missing here?
Edit
Thanks to Dirk, I was able to piece it together:
y <- sprintf("%s-1", x)
While I still don't get why this doesn't work
(as.POSIXct(y, format = "%Y-%V-%u"))
# [1] "2016-01-11 CET"
this does
(as.POSIXct(y, format = "%Y-%U-%u")
# [1] "2016-11-28 CET"
Edit 2
Oh my, I think using %V is a very bad idea in general:
as.POSIXct("2016-01-01") %>% format("%Y-%V")
# [1] "2016-53"
Should this be considered to be on a "serious bug" level that requires further action?!
Sticking to either %U or %W seems to be the right way to go
as.POSIXct("2016-01-01") %>% format("%Y-%U")
# [1] "2016-00"
Edit 3
Nope, not quite finished/still puzzled: the approach doesn't work for the very first week
(x <- as.POSIXct("2016-01-01") %>% format("%Y-%W"))
# [1] "2016-00"
as.POSIXct(sprintf("%s-1", x), format = "%Y-%W-%u")
# [1] NA
It does for week 01 as defined in the underlying convention when using %U or %W (so "week 2", actually)
as.POSIXct("2016-01-1", format = "%Y-%W-%u")
# [1] "2016-01-04 CET"
As I have to deal a lot with reporting by ISO weeks, I've created the ISOweek package some years ago.
The package includes the function ISOweek2date() which returns the date of a given weekdate (year, week of the year, day of week according to ISO 8601). It's the inverse function to date2ISOweek().
With ISOweek, your examples become:
library(ISOweek)
# define dates to convert
dates <- as.Date(c("2016-12-01", "2016-01-01"))
# convert to full ISO 8601 week-based date yyyy-Www-d
(week_dates <- date2ISOweek(dates))
[1] "2016-W48-4" "2015-W53-5"
# convert back to class Date
ISOweek2date(week_dates)
[1] "2016-12-01" "2016-01-01"
Note that date2ISOweek() requires a full ISO week-based date in the format yyyy-Www-d including the day of the week (1 to 7, Monday to Sunday).
So, if you only have year and ISO week number you have to create a character string with a day of the week specified.
A typical phrase in many reports is, e.g., "reporting week 31 ending 2017-08-06":h
yr <- 2017
wk <- 31
ISOweek2date(sprintf("%4i-W%02i-%1i", yr, wk, 7))
[1] "2017-08-06"
Addendum
Please, see this answer for another use case and more background information on the ISOweek package.

53rd week of the year in R?

I have week-date data in the form yyyy-ww where wwis the week number in two digits. The data span 2007-01 to 2010-30. The week counting convention is ISO 8601, which as you can see here on Wikipedia's "Week number" article, occasionally reaches 53 weeks in a year. For example 2009 had 53 weeks by this system, see the week numbers in this ISO 8601 calendar. (See other years; as per the Wikipedia article, 53rd weeks are fairly rare.)
Basically I want to read the week date in, convert it to a Date object and save this to a separate column in a data.frame. As a test, I reconverted the Date objects to yyyy-ww formats by format([Date-object], format = "%Y-%W", and this threw up an error at 2009-53. That week fails to be interpreted as a date by R. This is very odd, as other years which do not have a 53rd week (in ISO 8601 standard) are converted fine, such as 2007-53, whereas other years that also do not have a 53rd week (in ISO 8601 standard) also fail, such as 2008-53
The following minimal example demonstrates the issue.
Minimal example:
dates <- c("2009-50", "2009-51", "2009-52", "2009-53", "2010-01", "2010-02")
as.Date(x = paste(dates, 1), format = "%Y-%W %w")
# [1] "2009-12-14" "2009-12-21" "2009-12-28" NA "2010-01-04"
# [6] "2010-01-11"
other.dates <- c("2007-53", "2008-53", "2009-53", "2010-53")
as.Date(x = paste(other.dates, 1), format = "%Y-%W %w")
# [1] "2007-12-31" NA NA NA
The question is, how do I get R to accept week numbers in ISO 8601 format?
Note: This question summarises a problem I have been struggling with for a few hours. I have searched and found various helpful posts such as this, but none solved the problem.
The package ISOweek manages ISO 8601 style week numberings, converting to and from Date objects in R. See ISOweek for more. Continuing the example dates above, we first need to modify the formatting a bit. They must be in form yyyy-Www-w rather than yyyy-ww, i.e. 2009-W53-1. The final digit identifies which day of the week to use in identifying the week, in this case it is the Monday. The week number must be two-digit.
library(ISOweek)
dates <- c("2009-50", "2009-51", "2009-52", "2009-53", "2010-01", "2010-02")
other.dates <- c("2007-53", "2008-53", "2009-53", "2010-53")
dates <- sub("(\\d{4}-)(\\d{2})", "\\1W\\2-1", dates)
other.dates <- sub("(\\d{4}-)(\\d{2})", "\\1W\\2-1", other.dates)
## Check:
dates
# [1] "2009-W50-1" "2009-W51-1" "2009-W52-1" "2009-W53-1" "2010-W01-1"
# [6] "2010-W02-1"
(iso.date <- ISOweek2date(dates)) # deal correctly
# [1] "2009-12-07" "2009-12-14" "2009-12-21" "2009-12-28" "2010-01-04"
# [6] "2010-01-11"
(iso.other.date <- ISOweek2date(other.dates)) # also deals with this
# [1] "2007-12-31" "2008-12-29" "2009-12-28" "2011-01-03"
## Check that back-conversion works:
all(date2ISOweek(iso.date) == dates)
# [1] TRUE
## This does not work for the others, since the 53rd week of
## e.g. 2008 is back-converted to the first week of 2009, in
## line with the ISO 6801 standard.
date2ISOweek(iso.other.date) == other.dates
# [1] FALSE FALSE TRUE FALSE

Resources