Implementation of standard recycling rules

Solution 1:

I've used this in the past,

expand_args <- function(...){
  dots <- list(...)
  max_length <- max(sapply(dots, length))
  lapply(dots, rep, length.out = max_length)
}

Solution 2:

I'd likely use the length.out argument of rep() to do most of the real work.

Here's an example that creates a better.data.frame() function (it should really be called "better".data.frame()), which places no restrictions on the lengths of the vectors it's handed as arguments. In this case, I recycle all of the vectors to the length of the the longest one, but you can obviously adapt this to serve your own recycling needs!

better.data.frame <- function(...) {
    cols <- list(...)
    names(cols) <- sapply(as.list(match.call()), deparse)[-1]

    # Find the length of the longest vector
    # and then recycle all columns to that length.
    n <- max(lengths(cols))
    cols <- lapply(cols, rep, length.out = n)

    as.data.frame(cols)
}

# Try it out
a <- Sys.Date() + 0:9
b <- 1:3
c <- letters[1:4]

data.frame(a,b,c)
# Error in data.frame(a, b, c) : 
#   arguments imply differing number of rows: 10, 3, 4

better.data.frame(a,b,c)
#             a b c
# 1  2012-02-17 1 a
# 2  2012-02-18 2 b
# 3  2012-02-19 3 c
# 4  2012-02-20 1 d
# 5  2012-02-21 2 a
# 6  2012-02-22 3 b
# 7  2012-02-23 1 c
# 8  2012-02-24 2 d
# 9  2012-02-25 3 a
# 10 2012-02-26 1 b

Solution 3:

One short-and-dirty route for numerical arguments is to rely on cbind's automatic recycling. For example:

f.abc <- function(a,b,c) {

     df.abc <- as.data.frame( suppressWarnings( cbind(a=a, b=b, c=c) ) )

     #Then use, for example, with() to use a, b and c inside the data frame, 
     #or apply(df.abc,1, ...) 
}

It does rely heavily on there being no other legitimate cause for warnings, though.