Search code examples
rlapplyseqrecycle

Generate Sequence of Every 3 Month Values


Question

How can I modify seq() to generate a list of 12 numeric vectors, each vector containing the months that appear every 3 months from the month of interest?

I require each month to have at least 3 values in each vector. I'm unsure how to recycle the values 1 through 12 in my current use of seq. Any guidance would be much appreciated!

Reproducible Example

# create every three month values
list.of.month.values <-
    list( JAN = 1
          , FEB = 2
          , MAR = 3
          , APR = 4
          , MAY = 5
          , JUN = 6
          , JUL = 7
          , AUG = 8
          , SEP = 9
          , OCT = 10
          , NOV = 11
          , DEC = 12 )

# find each month's 
# values that appear every three months
every.three.month.values <-
    lapply(
        X = list.of.month.values
        , FUN = function( i )
            seq( from = i, to = 12, by = 3 )
    )

# view results
every.three.month.values
# $JAN
# [1]  1  4  7 10
# 
# $FEB
# [1]  2  5  8 11
# 
# $MAR
# [1]  3  6  9 12
# 
# $APR
# [1]  4  7 10
# 
# $MAY
# [1]  5  8 11
# 
# $JUN
# [1]  6  9 12
# 
# $JUL
# [1]  7 10
# 
# $AUG
# [1]  8 11
# 
# $SEP
# [1]  9 12
# 
# $OCT
# [1] 10
# 
# $NOV
# [1] 11
# 
# $DEC
# [1] 12

# This works for the first 6 months.
# But we need all twelve months
every.three.month.values$JUL <- c( 7, 10, 1 )
every.three.month.values$AUG <- c( 8, 11, 2 )
every.three.month.values$SEP <- c( 9, 12, 3 )
every.three.month.values$OCT <- c( 10, 1, 4 )
every.three.month.values$NOV <- c( 11, 2, 5 )
every.three.month.values$DEC <- c( 12, 3, 6 )

# end of script #

Desired Output

Here is my desired output:

structure(list(JAN = c(1, 4, 7, 10), FEB = c(2, 5, 8, 11), MAR = c(3, 
6, 9, 12), APR = c(4, 7, 10), MAY = c(5, 8, 11), JUN = c(6, 9, 
12), JUL = c(7, 10, 1), AUG = c(8, 11, 2), SEP = c(9, 12, 3), 
    OCT = c(10, 1, 4), NOV = c(11, 2, 5), DEC = c(12, 3, 6)), .Names = c("JAN", 
"FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", 
"NOV", "DEC"))

Solution

  • You could do something like this:

    df <- sapply(seq(1, 12, by = 3), function(x) 
        ((seq(x, 12 + x - 1, by = 1) - 1) %% 12) + 1);
    rownames(df) <- names(list.of.month.values);
    #    [,1] [,2] [,3] [,4]
    #JAN    1    4    7   10
    #FEB    2    5    8   11
    #MAR    3    6    9   12
    #APR    4    7   10    1
    #MAY    5    8   11    2
    #JUN    6    9   12    3
    #JUL    7   10    1    4
    #AUG    8   11    2    5
    #SEP    9   12    3    6
    #OCT   10    1    4    7
    #NOV   11    2    5    8
    #DEC   12    3    6    9
    

    Note that this is not exactly the same as your expected outcome; I don't understand the logic behind why in your output JAN, FEB and MAR have 4 elements, but the rest of the months have only 3 elements.

    You can reproduce your expected output by doing

    lst <- apply(df, 1, function(x) if (x[1] < 4) x else x[-length(x)]);
    names(lst) <- names(list.of.month.values);
    #$JAN
    #[1]  1  4  7 10
    #
    #$FEB
    #[1]  2  5  8 11
    #
    #$MAR
    #[1]  3  6  9 12
    #
    #$APR
    #[1]  4  7 10
    #
    #$MAY
    #[1]  5  8 11
    #
    #$JUN
    #[1]  6  9 12
    #
    #$JUL
    #[1]  7 10  1
    #
    #$AUG
    #[1]  8 11  2
    #
    #$SEP
    #[1]  9 12  3
    #
    #$OCT
    #[1] 10  1  4
    #
    #$NOV
    #[1] 11  2  5
    #
    #$DEC
    #[1] 12  3  6
    

    but this seems unnecessarily ugly/kludgy.