Aggregate ISO weeks into months with a dataset containing just ISO weeks

Viewed 1726

My data is in a dataframe which has a structure like this:

df2 <- data.frame(Year = c("2007"), Week = c(1:12), Measurement = c(rnorm(12, mean = 4, sd = 1)))

Unfortunately I do not have the complete date (e.g. days are missing) for each measurement, only the Year and the Weeks (these are ISO weeks).

Now I want to aggregate the Median of a Month's worth of measurements (e.g. the weekly measurements per month of the specific year) into a new column, Months. I did not find a convenient way to do this without having the exact day of the measurements available. Any inputs are much appreciated!

4 Answers

When it is necessary to allocate a week to a single month, the rule for first week of the year might be applied, although ISO 8601 does not consider this case. (Wikipedia)

For example, the 5th week of 2007 belongs to February, because the Thursday of the 5th week was the 1st of February.

I am using data.table and ISOweek packages. See the example how to compute the month of the week. Then you can do any aggregation by month.

require(data.table)
require(ISOweek)

df2 <- data.table(Year = c("2007"), Week = c(1:12),
                  Measurement = c(rnorm(12, mean = 4, sd = 1)))

# Generate Thursday as year, week of the year, day of week according to ISO 8601
df2[, thursday_ISO := paste(Year, sprintf("W%02d", Week), 4, sep = "-")]

# Convert Thursday to date format
df2[, thursday_date := ISOweek2date(thursday_ISO)]

# Compute month
df2[, month := format(thursday_date, "%m")]
df2

Suggestion by Uwe to compute a year-month string.

# Compute year-month
df2[, yr_mon := format(ISOweek2date(sprintf("%s-W%02d-4", Year, Week)), "%Y-%m")]
df2

And finally you can do an aggregation to the new table or by adding median as a column.

df2[, median(Measurement), by = yr_mon]

df2[, median := median(Measurement), by = yr_mon]
df2

If I understand correctly, you don't know the exact day, but only the week number and year. My answer takes the first day of the year as a starting date and then compute one week intervals based on that. You can probably refine the answer.

Based on an answer by mnel, using the lubridate package.

library(lubridate)

# Prepare week, month, year information ready for the merge
# Make sure you have all the necessary dates
wmy <- data.frame(Day = seq(ymd('2007-01-01'),ymd('2007-04-01'), 
                            by = 'weeks')) 
wmy <- transform(wmy, 
                 Week = isoweek(Day),
                 Month = month(Day),
                 Year = isoyear(Day))

# Merge this information with your data
merge(df2, wmy, by = c("Year", "Week"))

   Year Week Measurement        Day Month
1  2007    1    3.704887 2007-01-01     1
2  2007   10    1.974533 2007-03-05     3
3  2007   11    4.797286 2007-03-12     3
4  2007   12    4.291169 2007-03-19     3
5  2007    2    4.305010 2007-01-08     1
6  2007    3    3.374982 2007-01-15     1
7  2007    4    3.600008 2007-01-22     1
8  2007    5    4.315184 2007-01-29     1
9  2007    6    4.887142 2007-02-05     2
10 2007    7    4.155411 2007-02-12     2
11 2007    8    4.711943 2007-02-19     2
12 2007    9    2.465862 2007-02-26     2

using dplyr you can try:

require(dplyr)

df2 %>% mutate(Date = as.Date(paste("1", Week, Year, sep = "-"), format = "%w-%W-%Y"),
            Year_Mon = format(Date,"%Y-%m")) %>% group_by(Year_Mon) %>%
            summarise(result = median(Measurement))

As @djhrio pointed out, Thursday is used to determine the weeks in a month. So simply switch paste("1", to paste("4", in the code above.

This can be done relatively simply in dplyr.

library(dplyr)

df2 %>% 
  mutate(Month = rep(1:3, each = 4)) %>% 
  group_by(Month) %>% 
  summarise(MonthlyMedian = stats::median(Measurement))

Basically, add a new column to define your months. I'm presuming since you don't have days, you are going to allocate 4 weeks per month? Then you just group by your Month variable and calculate the median. Very simple

Hope this helps

Related