I have a dataframe object, and among the fields in it, I have a dates:
df$dates
I need to add a column which is \'Week Starting\', i.e.
A simple base-R way if your dates are properly coded as date class in R: as.Date(unclass(dates)-unclass(dates)%%7-3)
. You unclass it do get number of days since 1970-01-01. Then subtract remainder from division on 7 (day of the week!). Then subtract 3 because 1970-01-01 was Thursday –
Also you can group your data by week, and then create a column of "minimal date of that week". Here is how to do it in data.table
package:
df=data.table(df)
df[,lastMonday:=min(dates),by=.(week(dates))]
It should work if you dont have spaces in dates. Also, in some locales week starts with sunday, so you should be careful. And you will need additional grouping variable, if your dates span for more than a year
How about just subtracting from the dates the number of days required to get to the previous Monday? e.g if your data is
dates <- as.Date(c("2000-07-12", "2005-02-19", "2010-09-01"))
weekdays(dates)
# [1] "Wednesday" "Saturday" "Wednesday"
then you can compare this to a vector
wdays <- setNames(0:6, c("Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday", "Sunday"))
and subtract the required number of days from each date, ie
dates - wdays[weekdays(dates)]
# Wednesday Saturday Wednesday
#"2000-07-10" "2005-02-14" "2010-08-30"
will give the dates of the Monday preceding each date in dates
. To test:
weekdays(dates - wdays[weekdays(dates)])
#Wednesday Saturday Wednesday
# "Monday" "Monday" "Monday"
Everything can be written also in one line as
dates - match(weekdays(dates), c("Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday", "Sunday")) + 1
#"2000-07-10" "2005-02-14" "2010-08-30"
Simples:
dates <-(Sys.Date()+1:30)
week.starts <- as.Date(sapply (dates, function(d) { return (d + (-6 - as.POSIXlt(d)$wday %% -7 ))}), origin = "1970-01-01")
and running as
d <- data.frame(dataes=dates, monday=week.starts)
gives
dataes monday
1 2015-09-25 2015-09-21
2 2015-09-26 2015-09-21
3 2015-09-27 2015-09-21
4 2015-09-28 2015-09-28
5 2015-09-29 2015-09-28
6 2015-09-30 2015-09-28
7 2015-10-01 2015-09-28
8 2015-10-02 2015-09-28
9 2015-10-03 2015-09-28
10 2015-10-04 2015-09-28
11 2015-10-05 2015-10-05
12 2015-10-06 2015-10-05
13 2015-10-07 2015-10-05
14 2015-10-08 2015-10-05
15 2015-10-09 2015-10-05
16 2015-10-10 2015-10-05
17 2015-10-11 2015-10-05
18 2015-10-12 2015-10-12
19 2015-10-13 2015-10-12
20 2015-10-14 2015-10-12
21 2015-10-15 2015-10-12
22 2015-10-16 2015-10-12
23 2015-10-17 2015-10-12
24 2015-10-18 2015-10-12
25 2015-10-19 2015-10-19
26 2015-10-20 2015-10-19
27 2015-10-21 2015-10-19
28 2015-10-22 2015-10-19
29 2015-10-23 2015-10-19
30 2015-10-24 2015-10-19
Similar approach, example:
# data
d <- data.frame(date = as.Date( c("20/09/2015","24/09/2015","28/09/2015","01/10/2015"), "%d/%m/%Y"))
# get monday
d$WeekStart <- d$date - 6 - (as.POSIXlt(d$date)$wday %% -7)
d
# result
# date WeekStart
# 1 2015-09-20 2015-09-14
# 2 2015-09-24 2015-09-21
# 3 2015-09-28 2015-09-28
# 4 2015-10-01 2015-09-28
a[1] <-as.Date("2016-08-20")
Finding Next day (Here "Monday")
a[1] + match("Monday",weekdays(seq(a[1]+1, a[1]+6,"days")))
"2016-08-22"
Finding Last Day (Here "Friday")
a[1] + (match("Friday",weekdays(seq(a[1]+1, a[1]+6,"days")))-7)
"2016-08-19"
A base R
approach with the function strftime
.
df$Week.Start <- dates-abs(1-as.numeric(strftime(df$dates, "%u")))
This can be a one-liner but we'll create a few variables to see what's happening. The %u
format pattern for dates returns the day of the week as a single decimal number. We can convert that number to numeric
and subtract the distance from our dates. We can then subtract that vector from our date column.
day_of_week <- as.numeric(strftime(df$dates, "%u"))
day_diff <- abs(1-day_of_week)
df$Week.Start <- dates-day_diff
# dates Week.Start
# 1 2042-10-22 2042-10-20
# 2 2026-08-14 2026-08-10
# 3 2018-11-23 2018-11-19
# 4 2017-08-21 2017-08-21
# 5 2022-05-26 2022-05-23
# 6 2037-05-27 2037-05-25
Data
set.seed(7)
all_dates <- seq(Sys.Date(), Sys.Date()+10000, by="days")
dates <- sample(all_dates, 20)
df <- data.frame(dates)
If you want nearest any day and hour to the current date, use this function:
dayhour <- function(day,hour){
k <- as.Date(Sys.time())+day-as.numeric(format(strptime(Sys.time(),format="%Y-%m-%d %H:%M:%S"), format ='%u'))
dh <- format(strptime(paste(k,hour), format="%Y-%m-%d %H"), format="%A %H")
return(dh)
}
For the weekdays use 0 to 6 as day argument for sunday to saturday respectively:
> dayhour(0,17)
[1] "Sunday 17"