How to Get the Same Weekday Last Year Given any Given Year? - r

I would like to get the same day last year given any year. How can I best do this in R. For example, given Sunday 2010/01/03, I would like to obtain the Sunday of the same week the year before.
# "Sunday"
weekdays(as.Date("2010/01/03", format="%Y/%m/%d"))
# "Saturday"
weekdays(as.Date("2009/01/03", format="%Y/%m/%d"))

To find the same weekday one year ago, simply subtract 52 weeks or 364 days from the given date:
d <- as.Date("2010-01-03")
weekdays(d)
#[1] "Sunday"
d - 52L * 7L
#[1] "2009-01-04"
weekdays(d - 52L * 7L)
#[1] "Sunday"
Please note that the calendar year has 365 days (or 366 days in a leap year) which is one or two days more than 52 weeks. So, the calendar date of the same weekday one year ago moves on by one or two days. (Or, it explains why New Year's Eve is always on a different weekday.)

Using lubridate the following formula will give you the corresponding weekday in the same week in the previous year:
as.Date(dDate - 364 - ifelse(weekdays( dDate - 363) == weekdays( dDate ), 1, 0))
Where dDate is some date, i.e. dDate <- as.Date("2016-02-29"). The ifelse accounts for leap years.

Here's a simple algorithm. subtract 365 days from the day of interest. Adjust that day to the closest matching day of the week using the Tableau code below (easily translatable into other languages). This is equivalent to the rule in the table below (with 1 = Monday and 7 = Sunday). Basically you adjust day - 365 to be on the correct day of the week either in the same week if that moves <= 3 days otherwise you use the matching weekday from the previous/next week. It'll choose whichever leads to the least difference in terms of # of days.
[day prior year raw] = [day] - 365
[matching day prior year] =
if abs(datepart('weekday',[day]) - datepart('weekday',[day prior year raw]))<= 3
then [day prior year raw]+datepart('weekday',[day]) - datepart('weekday',[day prior year raw])
else [day prior year raw]+(if datepart('weekday',[day]) > datepart('weekday',[day prior year raw])
then -7+(datepart('weekday',[day]) - datepart('weekday',[day prior year raw]))
else 7+(datepart('weekday',[day]) - datepart('weekday',[day prior year raw])) end
)
end)

Look at ?years in package lubridate. This creates a period object which correctly spans a period, across leap years.
> library(lubridate)
> # set the reference date
> d1 = as.Date("2017/01/03", format="%Y/%m/%d")
>
> # verify across years and leap years
> d1 - years(1)
[1] "2016-01-03"
> d1 - years(2)
[1] "2015-01-03"
> d1 - years(3)
[1] "2014-01-03"
> d1 - years(4)
[1] "2013-01-03"
> d1 - years(5)
[1] "2012-01-03"
>
> weekdays(d1 - years(1))
[1] "Sunday"
> weekdays(d1 - years(2))
[1] "Saturday"
>
> # feb 29 on year period in yields NA
> ymd("2016/02/29") - years(1)
[1] NA
>
> # feb 29 in a non-leap year fails to convert
> ymd("2015/02/29") - years(1)
[1] NA
Warning message:
All formats failed to parse. No formats found.
>
> # feb 29, leap year with 4 year period works.
> ymd("2016/02/29") - years(4)
[1] "2012-02-29"
>

Related

How do I find the first and last day of next month?

If I have a given date, how do I find the first and last days of the next month?
For example,
today <- as.Date("2009-04-04")
I want to find
# first date in next month
"2009-05-01"
# last date in next month
"2009-05-31"
You can do this with base R:
today <- as.Date("2009-04-04")
first <- function(x) {
x <- as.POSIXlt(x)
x$mon[] <- x$mon + 1
x$mday[] <- 1
x$isdst[] <- -1L
as.Date(x)
}
first(today)
#[1] "2009-05-01"
first(first(today)) - 1
#[1] "2009-05-31"
lubridate has some useful tools for this purpose.
library(lubridate)
today <- ymd("2009-04-12")
# First day of next month
first <- ceiling_date(today, unit = "month")
# Last day of next month
last <- ceiling_date(first, unit= "month") -1
first
#"2009-05-01"
last
#"2009-05-31"
Here are some solutions. We use today from the question to test. In both cases the input may be a Date class vector.
1) Base R Define function fom to give the first of the month of its Date
argument. Using that we can get the date of the first and last of the next month as follows. We use the facts that 31 and 62 days after the first of the month is necessarily a date in the next month and month after the next month.
fom <- function(x) as.Date(cut(x, "month"))
fom(fom(today) + 31)
## [1] "2009-05-01"
fom(fom(today) + 62) - 1
## [1] "2009-05-31"
2) yearmon yearmon class objects internally represent a year and month as the year plus 0 for January, 1/12 for Febrary, 2/12 for March and so on. Using as.Date.yearmon the frac argument specifies the fraction of the way through the month to output. The default is frac = 0 and results in the first of the month being output and frac = 1 means the end of the month.
library(zoo)
as.Date(as.yearmon(today) + 1/12)
## [1] "2009-05-01"
as.Date(as.yearmon(today) + 1/12, frac = 1)
## [1] "2009-05-31"

Convert date to day-of-week in R

I have a date in this format in my data frame:
"02-July-2015"
And I need to convert it to the day of the week (i.e. 183). Something like:
df$day_of_week <- weekdays(as.Date(df$date_column))
But this doesn't understand the format of the dates.
You could use lubridate to convert to day of week or day of year.
library(lubridate)
# "02-July-2015" is Thursday
date_string <- "02-July-2015"
dt <- dmy(date_string)
dt
## [1] "2015-07-02 UTC"
### Day of week : (1-7, Sunday is 1)
wday(dt)
## [1] 5
### Day of year (1-366; for 2015, only 365)
yday(dt)
## [1] 183
### Or a little shorter to do the same thing for Day of year
yday(dmy("02-July-2015"))
## [1] 183
day = as.POSIXct("02-July-2015",format="%d-%b-%Y")
# see ?strptime for more information on date-time conversions
# Day of year as decimal number (001–366).
format(day,format="%j")
[1] "183"
#Weekday as a decimal number (1–7, Monday is 1).
format(day,format="%u")
[1] "4"
This is what anotherFishGuy supposed, plus converting the values to as.numeric so they fit through classifier.
# day <- Sys.time()
as.num.format <- function(day, ...){
as.numeric(format(day, ...))
}
doy <- as.num.format(day,format="%j")
doy <- as.num.format(day,format="%u")
hour <- as.num.format(day, "%H")

convert year week string to date

I have a column of strings in my data set formatted as year week (e.g. '201401' is equivalent to 7th April 2014, or the first fiscal week of the year)
I am trying to convert these to a proper date so I can manipulate them later, however I always receive the dame date for a given year, specifically the 14th of April.
e.g.
test_set <- c('201401', '201402', '201403')
as.Date(test_set, '%Y%U')
gives me:
[1] "2014-04-14" "2014-04-14" "2014-04-14"
Try something like this:
> test_set <- c('201401', '201402', '201403')
>
> extractDate <- function(dateString, fiscalStart = as.Date("2014-04-01")) {
+ week <- substr(dateString, 5, 6)
+ currentDate <- fiscalStart + 7 * as.numeric(week) - 1
+ currentDate
+ }
>
> extractDate(test_set)
[1] "2014-04-07" "2014-04-14" "2014-04-21"
Basically, I'm extracting the weeks from the start of the year, converting it to days and then adding that number of days to the start of the fiscal year (less 1 day to make things line up).
Not 100% sure what is your desired output but this may work
as.Date(paste0(substr(test_set, 1, 4), "-04-07")) +
(as.numeric(substr(test_set, 5, 6)) - 1) * 7
# [1] "2014-04-07" "2014-04-14" "2014-04-21"

Finding the first day of specific months in R

I currently have a column "Month" & a column "DayWeek" with the Month and Day of the week written out. Using the code below I can get a column with a 1 for each Wednesday in Feb, May, Aug & Nov. Im struggling to find a way to get a column with 1s just for the first Wednesday of each of the 4 months I just mentioned. Any ideas or do I have to create a loop for it?
testPrices$Rebalance <- ifelse((testPrices$Month=="February" & testPrices$DayWeek == "Wednesday"),1,ifelse((testPrices$Month=="May" & testPrices$DayWeek == "Wednesday"),1,ifelse((testPrices$Month=="August" & testPrices$DayWeek == "Wednesday"),1,ifelse((testPrices$Month=="November" & testPrices$DayWeek == "Wednesday"),1,0))))
Well, without a reproducible example, I couldn't come up with a complete solution, but here is a way to generate the first Wednesday date of each month. In this example, I start at 1 JAN 2013 and go out 36 months, but you can figure out what's appropriate for you. Then, you can check against the first Wednesday vector produced here to see if your dates are members of the first Wednesday of the month group and assign a 1, if so.
# I chose this as an origin
orig <- "2013-01-01"
# generate vector of 1st date of the month for 36 months
d <- seq(as.Date(orig), length=36, by="1 month")
# Use that to make a list of the first 7 dates of each month
d <- lapply(d, function(x) as.Date(seq(1:7),origin=x)-1)
# Look through the list for Wednesdays only,
# and concatenate them into a vector
do.call('c', lapply(d, function(x) x[strftime(x,"%A")=="Wednesday"]))
Output:
[1] "2013-01-02" "2013-02-06" "2013-03-06" "2013-04-03" "2013-05-01" "2013-06-05" "2013-07-03"
[8] "2013-08-07" "2013-09-04" "2013-10-02" "2013-11-06" "2013-12-04" "2014-01-01" "2014-02-05"
[15] "2014-03-05" "2014-04-02" "2014-05-07" "2014-06-04" "2014-07-02" "2014-08-06" "2014-09-03"
[22] "2014-10-01" "2014-11-05" "2014-12-03" "2015-01-07" "2015-02-04" "2015-03-04" "2015-04-01"
[29] "2015-05-06" "2015-06-03" "2015-07-01" "2015-08-05" "2015-09-02" "2015-10-07" "2015-11-04"
[36] "2015-12-02"
Note: I adapted this code from answers found here and here.
I created a sample dataset to work with like this (Thanks #Frank!):
orig <- "2013-01-01"
d <- data.frame(date=seq(as.Date(orig), length=1000, by='1 day'))
d$Month <- months(d$date)
d$DayWeek <- weekdays(d$date)
d$DayMonth <- as.numeric(format(d$date, '%d'))
From a data frame like this, you can extract the first Wednesday of specific months using subset, like this:
subset(d, Month %in% c('January', 'February') & DayWeek == 'Wednesday' & DayMonth < 8)
This takes advantage of the fact that the day number (1..31) will always be between 1 to 7, and obviously there will be precisely one such day. You could do similarly for 2nd, 3rd, 4th Wednesday, changing the condition to accordingly, for example DayMonth > 7 & DayMonth < 15.

How to calculate date based on week number in R

I was wondering if there is a way to get the begin of the week date based on a week number in R? For example, if I enter week number = 10, it should give me 9th March, 2014.
I know how to get the reverse (aka..given a date, get the week number by using as.POSIX functions).
Thanks!
Prakhar
You can try this:
first.day <- as.numeric(format(as.Date("2014-01-01"), "%w"))
week <- 10
as.Date("2014-01-01") + week * 7 - first.day
# [1] "2014-03-09"
This assumes weeks start on Sundays. First, find what day of the week Jan 1 is, then, just add 7 * number of weeks to Jan 1, - the day of week Jan 1 is.
Note this is slightly different to what you get if you use %W when doing the reverse, as from that perspective the first day of the week seems to be Monday:
format(seq(as.Date("2014-03-08"), by="1 day", len=5), "%W %A %m-%d")
# [1] "09 Saturday 03-08" "09 Sunday 03-09" "10 Monday 03-10" "10 Tuesday 03-11"
# [5] "10 Wednesday 03-12"
but you can adjust the above code easily if you prefer the Monday centric view.
You may try the ISOweek2date function in package ISOweek.
Create a function which takes year, week, weekday as arguments and returns date(s):
date_in_week <- function(year, week, weekday){
w <- paste0(year, "-W", sprintf("%02d", week), "-", weekday)
ISOweek2date(w)
}
date_in_week(year = 2014, week = 10, weekday = 1)
# [1] "2014-03-03"
This date is corresponds to an ISO8601 calendar (see %V in ?strptime). I assume you are using the US convention (see %U in ?strptime). Then some tweeking is needed to convert between ISO8601 and US standard:
date_in_week(year = 2014, week = 10 + 1, weekday = 1) - 1
# [1] "2014-03-09"
You can enter several weekdays, e.g.
date_in_week(year = 2014, week = 10 + 1, weekday = 1:3) - 1
# [1] "2014-03-09" "2014-03-10" "2014-03-11"
You can also use strptime to easily get dates from weeks starting on Mondays:
first_date_of_week <- function(year, week){
strptime(paste(year, week, 1), format = "%Y %W %u")
}
You can accomplish this using the package lubridate
library(lubridate)
start = ymd("2014-01-01")
#[1] "2014-01-01 UTC"
end = start+10*weeks()
end = end-(wday(end)-1)*days()
#[1] "2014-03-09 UTC"

Resources