Subsetting time series data by 3 days and saving in the list

Viewed 86

Sub setting time series by 3 days and keep saving in list. Such that let suppose first subset is from day 1 to day 3 then the second subset would be from day 2 to day 4 such that every subset has a data of 3 days. Note this is 10 minute data time stamped. And saving every subset in the list depending on the total number of days dat available in the data. i have tried reproducing it.

 time_10 <- seq(ISOdatetime(2001,2,1,0,0,0), ISOdatetime(2001,3,31,0,0,0), by=(200))

    a <- as.data.frame(matrix(, nrow = length(time_10), ncol = 4))
    names(a)<- c("time_10","var1","var2","var3")
    a$time_10 <- time_10
    a$var1 <- runif(nrow(a), min=20, max=70)
    a$var2 <- runif(nrow(a), min=10, max=50)
    a$var3 <- runif(nrow(a), min= 3, max=10)
    head(a)  

enter image description here

Here is the image I am attaching which will give idea of sub setting the time series data and save it in a list.Here D1:day1,D2:day2,D3:day3 respectively. Using For loop or any other optimum method is appreciated .Note : So inside the For loop 3 days of sub setting of data will keep on happening and getting saved in a list with index as (Subset_n) where n is subset number.

2 Answers

This appears to work for your case. What I do is extract the date and roll a 3 day window (ww) from beginning to the end. For each step, I subset a data.frame based on days in the window and store it into a list.

time_10 <- seq(ISOdatetime(2001,2,1,0,0,0), ISOdatetime(2001,3,31,0,0,0), by=(200))
var1 <- runif(length(time_10), min=20, max=70)
var2 <- runif(length(time_10), min=50, max=90)
var3 <- runif(length(time_10), min=50, max=90)

a <- as.data.frame(matrix(, nrow = length(time_10), ncol = 4))
names(a)<- c("time_10","var1","var2","var3")
a$time_10 <- time_10
a$var1 <- var1
a$var2 <- var2
a$var3 <- var3

date <- strptime(a$time_10, format = "%Y-%m-%d")
td10 <- sort(unique(date))

ww <- 3
out <- vector("list", length(td10) - round(ww/2))  # preallocate a list

for (i in 1:length(td10)) {
  bb <- i:(i + ww - 1) # this is the bounding box

  if (max(bb) > length(td10)) {
    message("End of time series reached, exiting.")
    return(NULL)
  }

  out[[i]] <- a[date %in% td10[bb], ]
}

# check ranges of dates for each subset
lapply(out, FUN = function(x) range(x$time_10))

I believe the following code does what is asked for. It uses function minutes from package lubridate to make date/time arithmetics easier.

days3 <- lubridate::days(3)
d1 <- a$time_10[1]
d2 <- a$time_10[nrow(a)] - lubridate::days(2)

res <- lapply(seq(d1, d2, by = "1 days"), function(d){
  i <- which(d <= a$time_10 & a$time_10 < d + days3)
  a[i, ]
})

Edit.
I find the number of rows in each dataframe of res cumbersome, making it difficult to check whether the code above did produce the expected result. Here is a way to check it.

check <- lapply(res, function(DF) lubridate::day(DF$time_10))
check <- sapply(check, function(x) rle(x)$values)
head(check, 3)
#[[1]]
#[1] 1 2 3
#
#[[2]]
#[1] 2 3 4
#
#[[3]]
#[1] 3 4 5

rm(check)    # tidy up

Data.

I will repost the data creation code, since the original wasn't reproducible.

set.seed(8893)

time_10 <- seq(ISOdatetime(2001,2,1,0,0,0), ISOdatetime(2001,3,31,0,0,0), by=(200))
var1 <- runif(length(time_10), min=20, max=70)
var2 <- runif(length(time_10), min=50, max=90)
var3 <- runif(length(time_10), min=50, max=90)

a <- data.frame(time_10, var1, var2, var3)
Related