How do I find the last weekday (e.g., Wednesday) of the month using R? In the code below, I calculate the month, day of the month, week of the month, and weekday. There are 5 Wednesdays in January 2014, but only 4 Wednesdays in February 2014, so I cannot use max(week of the month) as a filter. Any help is appreciated although I prefer to use the base R functions.
DF <- data.frame(DATE = seq(as.Date("2014-01-01"), as.Date("2014-12-31"), "day"))
DF$MONTH <- as.numeric(format(DF$DATE, "%m"))
DF$DAY_OF_MONTH <- as.numeric(format(DF$DATE, "%d"))
DF$WEEK_OF_MONTH <- ceiling(as.numeric(format(DF$DATE, "%d")) / 7)
DF$WEEKDAY <- format(DF$DATE, "%A")
DF
I think this is what you're after:
DF$last_weekday_o_month <- ave(
weekdays(DF$DATE),
months(DF$DATE),
FUN = function(x) tail(x[ !(x %in% c("Saturday","Sunday")) ], 1)
)
To find the particular date that is the last weekday....
DF$last_weekdaydate_o_month <- ave(
DF$DATE,
months(DF$DATE),
FUN = function(x) tail(x[ !(weekdays(x) %in% c("Saturday","Sunday")) ], 1)
)
the result looks like...
DATE last_weekday_o_month last_weekdaydate_o_month
1 2014-01-01 Friday 2014-01-31
2 2014-01-02 Friday 2014-01-31
3 2014-01-03 Friday 2014-01-31
4 2014-01-04 Friday 2014-01-31
5 2014-01-05 Friday 2014-01-31
6 2014-01-06 Friday 2014-01-31
...
360 2014-12-26 Wednesday 2014-12-31
361 2014-12-27 Wednesday 2014-12-31
362 2014-12-28 Wednesday 2014-12-31
363 2014-12-29 Wednesday 2014-12-31
364 2014-12-30 Wednesday 2014-12-31
365 2014-12-31 Wednesday 2014-12-31
If you did this first, of course you could compute last_weekday_o_month as weekdays(last_weekdaydate_o_month).
With a couple packages, this can be done more elegantly/readably, as suggested by #RichardScriven:
library(data.table)
setDT(DF)[,
last_weekdaydate_o_month := last(DATE[!chron::is.weekend(DATE)])
, by = month(DATE)]
which gives
DATE last_weekdaydate_o_month
1: 2014-01-01 2014-01-31
2: 2014-01-02 2014-01-31
3: 2014-01-03 2014-01-31
4: 2014-01-04 2014-01-31
5: 2014-01-05 2014-01-31
---
361: 2014-12-27 2014-12-31
362: 2014-12-28 2014-12-31
363: 2014-12-29 2014-12-31
364: 2014-12-30 2014-12-31
365: 2014-12-31 2014-12-31
Here is a method using dplyr. Essentially you group by the month, filter out the 'weekend' days and return the weekday of the last (i.e. max) day.
library(dplyr)
DF <- data.frame(DATE = seq(as.Date("2014-01-01"), as.Date("2014-12-31"), "day"))
DF %>%
mutate(month = months(DATE), weekday = weekdays(DATE)) %>%
group_by(month) %>%
filter(!weekday %in% c("Saturday", "Sunday")) %>%
summarise(last_weekday = weekdays(max(DATE)))
Source: local data frame [12 x 2]
month last_weekday
1 April Wednesday
2 August Friday
3 December Wednesday
4 February Friday
5 January Friday
6 July Thursday
7 June Monday
8 March Monday
9 May Friday
10 November Friday
11 October Friday
12 September Tuesday
library(lubridate)
x <- seq(as.Date("2007-12-31"), by="1 day", length.out=(Sys.Date() - as.Date("2007-12-31")))
library(plyr)
df <- data.frame(date=x, year=year(x), month=month(x))
df[,"weekday"] <- weekdays(df[,"date"])
df<- df[! df[,"weekday"] %in% c("Saturday", "Sunday"),]
df <- ddply(df, .(year, month), summarize, last=max(date))
Related
I have data in the zoo format in the following structure
date val
2020-11-01 3244
2020-11-02 3273
2020-11-03 2974
2020-11-04 3283
2020-11-05 3922
2020-11-06 3669
2020-11-07 4246
2020-11-08 4594
2020-11-09 4086
2020-11-10 4302
2020-11-11 4559
2020-11-12 4981
2020-11-13 4741
2020-11-14 5267
that I am trying to get into this form
date val
Mon 2020-11-01 3244
Tue 2020-11-02 3273
Wed 2020-11-03 2974
Thu 2020-11-04 3283
Fri 2020-11-05 3922
Sat 2020-11-06 3669
Sun 2020-11-07 4246
Mon 2020-11-08 4594
Tue 2020-11-09 4086
Wed 2020-11-10 4302
Thu 2020-11-11 4559
Fri 2020-11-12 4981
Sat 2020-11-13 4741
Sun 2020-11-14 5267
In order to count the number of time I observe the smallest of the values per week.
Mon = 1
Tue = 1
Wed = 0
Thu = 0
Fri = 0
Sat = 0
Sun = 0
I tried to let the data in the flat format before adding the date with zoo and added the weekdays but failed to count with it. Does anyone know an easier way to do it? I am open to visual solutions
If you store the data in a dataframe you can create a new column with weekdays and week number, for each week keep the row with minimum value and count number of weekdays that have the minimum value.
library(dplyr)
df %>%
mutate(date = as.Date(date),
weekday = factor(weekdays(date)),
week_year = format(date, '%Y-%W')) %>%
group_by(week_year) %>%
slice(which.min(val)) %>%
ungroup %>%
count(weekday, .drop = FALSE)
The following should do the trick:
library(lubridate)
df$day <- weekdays(as.Date(df$date))
# Note:
# There is one way to define a week
df$week <- week(df$date)
# And there is also another. Make sure to pick.
df$isoweek <- isoweek(df$date)
df <- df %>% group_by(isoweek) %>% mutate(min_here = val == min(val))
df %>% group_by(day) %>% summarise(sum(min_here))
# A tibble: 7 x 2
day `sum(min_here)`
<chr> <int>
1 Friday 0
2 Monday 1
3 Saturday 0
4 Sunday 1
5 Thursday 0
6 Tuesday 1
7 Wednesday 0
Base R
... though it seems a little clumsier in comparison to dplyr's mechanics in RonakShah's answer, or data.table below:
ismin <- ave(dat$val, list(format(dat$date, format = "%U")),
FUN = function(z) seq_along(z) == which.min(z))
aggregate(ismin, list(weekday = weekdays(dat$date)), FUN = sum)
# weekday x
# 1 Friday 0
# 2 Monday 1
# 3 Saturday 0
# 4 Sunday 0
# 5 Thursday 0
# 6 Tuesday 1
# 7 Wednesday 0
(The order is not emphasized here.)
data.table
library(data.table)
DT <- as.data.table(dat)
DT[, ismin := seq_len(.N) == which.min(val), by = format(date, format = "%U")
][, weekday := weekdays(date)][, .(n = sum(ismin)), by = .(weekday) ]
# weekday n
# <char> <int>
# 1: Sunday 0
# 2: Monday 1
# 3: Tuesday 1
# 4: Wednesday 0
# 5: Thursday 0
# 6: Friday 0
# 7: Saturday 0
Data
dat <- structure(list(date = structure(c(18567, 18568, 18569, 18570, 18571, 18572, 18573, 18574, 18575, 18576, 18577, 18578, 18579, 18580), class = "Date"), val = c(3244L, 3273L, 2974L, 3283L, 3922L, 3669L, 4246L, 4594L, 4086L, 4302L, 4559L, 4981L, 4741L, 5267L)), class = "data.frame", row.names = c(NA, -14L))
This question already has answers here:
How to filter or subset specific date and time intervals in R? Lubridate?
(2 answers)
Closed 2 years ago.
I am working on a project and would be happy about your help.
I am working with stocks and the effect of weekdays on performance, is there a way to take all the values (for instance the S&P 500) of a data frame (df) from a specific weekday (e.g. Tuesday) and enter these values in a different data frame (df2) in a new column?
Thank you very much,
Ferdinand
df <- read.csv("AAPL.csv") # from Yahoo! Finance
> head(df)
Date Open High Low Close Adj.Close Volume
1 2019-07-10 201.85 203.73 201.56 203.23 200.8332 17897100
2 2019-07-11 203.31 204.39 201.71 201.75 199.3706 20191800
3 2019-07-12 202.45 204.00 202.20 203.30 200.9023 17595200
4 2019-07-15 204.09 205.87 204.00 205.21 202.7898 16947400
5 2019-07-16 204.59 206.11 203.50 204.50 202.0882 16866800
6 2019-07-17 204.05 205.09 203.27 203.35 200.9517 14107500
df$Day <- format(as.Date(df$Date), "%A") # Get the day
idx <- df$Day == "Tuesday" # Where are the Tuesdays ?
df2 <- df[idx, ] # Logical indexing
> head(df2)
Date Open High Low Close Adj.Close Volume Day
5 2019-07-16 204.59 206.11 203.50 204.50 202.0882 16866800 Tuesday
10 2019-07-23 208.46 208.91 207.29 208.84 206.3770 18355200 Tuesday
15 2019-07-30 208.76 210.16 207.31 208.78 206.3177 33935700 Tuesday
20 2019-08-06 196.31 198.07 194.04 197.00 194.6766 35824800 Tuesday
25 2019-08-13 201.02 212.14 200.48 208.97 207.2901 47218500 Tuesday
30 2019-08-20 210.88 213.35 210.32 210.36 208.6689 26884300 Tuesday
Your function :
myfunction <- function(mydf) {
df$Day <- format(as.Date(df$Date), "%A")
idx <- df$Day == "Tuesday"
df2 <- df[idx, ]
}
Testing myfunction :
> out = myfunction(df)
> head(out)
Date Open High Low Close Adj.Close Volume Day
5 2019-07-16 204.59 206.11 203.50 204.50 202.0882 16866800 Tuesday
10 2019-07-23 208.46 208.91 207.29 208.84 206.3770 18355200 Tuesday
15 2019-07-30 208.76 210.16 207.31 208.78 206.3177 33935700 Tuesday
20 2019-08-06 196.31 198.07 194.04 197.00 194.6766 35824800 Tuesday
25 2019-08-13 201.02 212.14 200.48 208.97 207.2901 47218500 Tuesday
30 2019-08-20 210.88 213.35 210.32 210.36 208.6689 26884300 Tuesday
I have dates formatted
as.Date(variable, format="%Y%m%d")
I extracted the weekday from that using
weekdays(as.Date(variable))
I now need to be able to say which occurrence of the day of the week the date was. For example, this was the second Tuesday of February, or this is the 4th Friday of March.
The occurrence is simply the ceiling of (day of month / 7) and day of month can be extracted using as.POSIXlt so put all together:
d <- as.Date(variable, format="%Y%m%d")
occ <- c("1st", "2nd", "3rd", "4th", "5th")
paste(occ[ceiling(as.POSIXlt(d)$mday / 7L)], weekdays(d), "of", months(d))
You can find the nth weekday of the year with (as.integer(format(x, "%d")) - 1) %/% 7 + 1:
days <- as.Date("2017-03-01") + 0:9
wdays <- weekdays(days)
nth <- (as.integer(format(days, "%d")) - 1) %/% 7 + 1
(Put in a data.frame for easy alignment:)
cbind.data.frame(days, wdays, nth)
# days wdays nth
# 1 2017-03-01 Wednesday 1
# 2 2017-03-02 Thursday 1
# 3 2017-03-03 Friday 1
# 4 2017-03-04 Saturday 1
# 5 2017-03-05 Sunday 1
# 6 2017-03-06 Monday 1
# 7 2017-03-07 Tuesday 1
# 8 2017-03-08 Wednesday 2
# 9 2017-03-09 Thursday 2
# 10 2017-03-10 Friday 2
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.
df[,'WeekStart']= manipulation
Where the week start is the date of the Monday of that week. i.e.: today is Thursday 24/09/15, would have an entry as '21-Sept'. Next thursday, 01/10/15, would be '28-Sept'.
I see that there is a weekday() function which will convert a day into a week-day, but how can I deal with this most recent monday?
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)
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
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"
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 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
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"
Looking for a function in R to convert dates into week numbers (of year) I went for week from package data.table.
However, I observed some strange behaviour:
> 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
Why is the week number switching to 12 on tuesday, instead of monday? What am I missing? (Timezone should be irrelevant as there are just dates?!)
Other suggestions for (base) R functions are appreciated as well.
Base package Using the function strftime passing the argument %V to obtain the week of the year as decimal number (01–53) as defined in ISO 8601. (More details in the documentarion: ?strftime)
strftime(c("2014-03-16", "2014-03-17","2014-03-18", "2014-01-01"), format = "%V")
Output:
[1] "11" "12" "12" "01"
if you try with lubridate:
library(lubridate)
lubridate::week(ymd("2014-03-16", "2014-03-17","2014-03-18", '2014-01-01'))
[1] 11 11 12 1
The pattern is the same. Try isoweek
lubridate::isoweek(ymd("2014-03-16", "2014-03-17","2014-03-18", '2014-01-01'))
[1] 11 12 12 1
I understand the need for packages in certain situations, but the base language is so elegant and so proven (and debugged and optimized).
Why not:
dt <- as.Date("2014-03-16")
dt2 <- as.POSIXlt(dt)
dt2$yday
[1] 74
And then your choice whether the first week of the year is zero (as in indexing in C) or 1 (as in indexing in R).
No packages to learn, update, worry about bugs in.
Actually, I think you may have discovered a bug in the week(...) function, or at least an error in the documentation. Hopefully someone will jump in and explain why I am wrong.
Looking at the code:
library(lubridate)
> week
function (x)
yday(x)%/%7 + 1
<environment: namespace:lubridate>
The documentation states:
Weeks is the number of complete seven day periods that have occured between the date and January 1st, plus one.
But since Jan 1 is the first day of the year (not the zeroth), the first "week" will be a six day period. The code should (??) be
(yday(x)-1)%/%7 + 1
NB: You are using week(...) in the data.table package, which is the same code as lubridate::week except it coerces everything to integer rather than numeric for efficiency. So this function has the same problem (??).
if you want to get the week number with the year use: "%Y-W%V":
e.g yearAndweeks <- strftime(dates, format = "%Y-W%V")
so
> strftime(c("2014-03-16", "2014-03-17","2014-03-18", "2014-01-01"), format = "%Y-W%V")
becomes:
[1] "2014-W11" "2014-W12" "2014-W12" "2014-W01"
If you want to get the week number with the year, Grant Shannon's solution using strftime works, but you need to make some corrections for the dates around january 1st. For instance, 2016-01-03 (yyyy-mm-dd) is week 53 of year 2015, not 2016. And 2018-12-31 is week 1 of 2019, not of 2018. This codes provides some examples and a solution. In column "yearweek" the years are sometimes wrong, in "yearweek2" they are corrected (rows 2 and 5).
library(dplyr)
library(lubridate)
# create a testset
test <- data.frame(matrix(data = c("2015-12-31",
"2016-01-03",
"2016-01-04",
"2018-12-30",
"2018-12-31",
"2019-01-01") , ncol=1, nrow = 6 ))
# add a colname
colnames(test) <- "date_txt"
# this codes provides correct year-week numbers
test <- test %>%
mutate(date = as.Date(date_txt, format = "%Y-%m-%d")) %>%
mutate(yearweek = as.integer(strftime(date, format = "%Y%V"))) %>%
mutate(yearweek2 = ifelse(test = day(date) > 7 & substr(yearweek, 5, 6) == '01',
yes = yearweek + 100,
no = ifelse(test = month(date) == 1 & as.integer(substr(yearweek, 5, 6)) > 51,
yes = yearweek - 100,
no = yearweek)))
# print the result
print(test)
date_txt date yearweek yearweek2
1 2015-12-31 2015-12-31 201553 201553
2 2016-01-03 2016-01-03 201653 201553
3 2016-01-04 2016-01-04 201601 201601
4 2018-12-30 2018-12-30 201852 201852
5 2018-12-31 2018-12-31 201801 201901
6 2019-01-01 2019-01-01 201901 201901
I think the problem is that the week calculation somehow uses the first day of the year. I don't understand the internal mechanics, but you can see what I mean with this example:
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
My workaround is this function:
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])
}
Of course, you can easily change the offset to make Monday first or whatever. The best way to do this would be to add an offset to the offset... but I haven't done that yet.
I provided a link to my simple geneorama package, but please don't rely on it too much because it's likely to change and not very documented.
Using only base, I wrote the following function.
Note:
Assumes Mon is day number 1 in the week
First week is week 1
Returns 0 if week is 52 from last year
Fine-tune to suit your needs.
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