Home > Back-end >  create sequence of numbers which are cyclic
create sequence of numbers which are cyclic

Time:09-26

I have a vector of months

m_vec <- c(3, 7, 11)

These months represent the start month of a season. All the months in each season are shown below:

season1 <- c(3,4,5,6)
season2 <- c(7,8,9,10)
season3 <- c(11,12,1,2)

I want to create a small function that takes a vector of start months and generate the vector of months in each season. Some more examples are show below:

m_vec <- c(9,12,4,8)
season1 <- c(9,10,11)
season2 <- c(12,1,2,3)
season3 <- c(4,5,6,7,8)

m_vec <- c(12, 5, 9)
season1 <- c(12, 1, 2,3,4)
season2 <- c(5,6,7,8)
season3 <- c(9,10,11)

My for loop is not complete and I can't seem to even know where to get started with the logic

n_season <- length(m_vec)
temp_list <- list()
for(m in seq_along(m_vec)){
      
   month_start <- m_vec[m]
   month_start_next <- m_vec[m   1]
    
   month_start:month_start_next
}      

CodePudding user response:

First we can create some helper functions

cycle <- function(n) { function(x) (x-1) %% n   1 }
split_at <- function(b) { function(x) split(x, cumsum(x %in% b)) }

The cycle() helper will return a function that will keep values in the range from 1 to the n you pass in. It does that using the modulus % operator. The split_at helper will return a function that takes a vector and splits it up when the values you pass in are found. It does that by using cumsum() to count when each of the break points are found.

Then we can take your input, create a vector of 12 months from your first starting month, wrap in in a cycler to keep it from 1-12, and then we can use split it up using your season breakpoints. Here's what that would look like:

month_cycle <- cycle(12)
season_splitter <- split_at(m_vec)

m_vec <- c(12, 5, 9)

seq(m_vec[1], length.out=12) |>
  month_cycle() |>
  season_splitter()
# $`1`
# [1] 12  1  2  3  4
# $`2`
# [1] 5 6 7 8
# $`3`
# [1]  9 10 11

CodePudding user response:

m_vec <- c(12, 5, 9)
Map(function(x, y) c(x[1], c((x %% 12):(y - 1))[-1]), m_vec, c(m_vec[-1], m_vec[1]))
#[[1]]
#[1] 12  1  2  3  4

#[[2]]
#[1] 5 6 7 8

#[[3]]
#[1]  9 10 11

CodePudding user response:

One option is to convert to Date class, get the sequence and extract the months

library(lubridate)
fn1 <- function(mvec) {
    new <- pmax(mvec-1, 1)
    out <- Map(function(i, j) {
       date1 <- mdy(i, truncated = 2)
       date2 <- mdy(j, truncated = 2)
      if(date1 > date2)  {
        date2 <- date2   years(1)}
       month(seq(date1, date2, by = "month"))
       }, mvec, c(new[-1], new[1]))
    if(length(out[[length(out)]]) < 2) {
           out[[length(out)-1]] <- c(out[[length(out)-1]], out[[length(out)]])
           out[[length(out)]] <- NULL
   }
    names(out) <- paste0("season", seq_along(out))
    return(out)
  }

-testing

> fn1(m_vec)
$season1
[1] 3 4 5 6

$season2
[1]  7  8  9 10

$season3
[1] 11 12  1  2

> fn1(c(9, 12, 4, 8))
$season1
[1]  9 10 11

$season2
[1] 12  1  2  3

$season3
[1] 4 5 6 7 8
> fn1(c(1, 5, 11))
$season1
[1] 1 2 3 4

$season2
[1]  5  6  7  8  9 10

$season3
[1] 11 12  1
  • Related