With map
you can apply a function to each pair of arguments :
> mapply(rep, 1:4, 4:1)
[[1]]
[1] 1 1 1 1
[[2]]
[1] 2 2 2
[[3]]
[1] 3 3
[[4]]
[1] 4
I expected to do something similar but for each combination of arguments with outer
but it does not work because the output of outer
should be a 4x4 array in this example :
> outer(1:4, 4:1, rep)
Error in dim(robj) <- c(dX, dY) :
dims [produit 16] ne correspond pas à la longueur de l'objet [40]
Is there some R function (base R ideally) to do this as easily as mapply
or outer
?
The expected output in this minimal example should look like this :
> c(mapply(rep, 1:4, 4), mapply(rep, 1:4, 3), mapply(rep, 1:4, 2), mapply(rep, 1:4, 1))
[1] 1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 1 1 1 2 2 2 3 3 3 4 4 4 1 1 2 2 3 3 4 4 1 2 3 4
# Or
> c(rep(1:4, each = 4), rep(1:4, each = 3), rep(1:4, each = 2), rep(1:4, each = 1))
[1] 1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 1 1 1 2 2 2 3 3 3 4 4 4 1 1 2 2 3 3 4 4 1 2 3 4
I could use a combination of expand.grid
and apply
but I expect that there is some easier solution :
> unlist(apply(expand.grid(1:4, 4:1), 1, function(x) rep(x[1], x[2])))
Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1
1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 1 1
Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1 Var1
1 2 2 2 3 3 3 4 4 4 1 1 2 2 3 3 4 4
Var1 Var1 Var1 Var1
1 2 3 4
Based on 李哲源 Zheyuan Li' reply (for the record) : with outer
and Vectorize
you obtain a unusual object : a matrix with each element being a list :
> res <- outer(1:4, 4:1, Vectorize(rep.int))
> class(res)
[1] "matrix"
> typeof(res)
[1] "list"
> res
[,1] [,2] [,3] [,4]
[1,] Integer,4 Integer,3 Integer,2 1
[2,] Integer,4 Integer,3 Integer,2 2
[3,] Integer,4 Integer,3 Integer,2 3
[4,] Integer,4 Integer,3 Integer,2 4
You can unlist
it to obtain a vector or remove the dimension attribute to obtain a regular list :
> unlist(res)
[1] 1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 1 1 1 2 2 2 3 3 3 4 4 4 1 1 2 2 3 3 4 4 1 2 3 4
>
> dim(res) <- NULL
> # res <- c(res) # equivalent
> res[1:5] # show the first 5 elements of the list
[[1]]
[1] 1 1 1 1
[[2]]
[1] 2 2 2 2
[[3]]
[1] 3 3 3 3
[[4]]
[1] 4 4 4 4
[[5]]
[1] 1 1 1
In this case outer(1:4, 4:1, Vectorize(rep.int))
works but in many situations you will need outer(1:4, 4:1, Vectorize(rep.int, SIMPLIFY = TRUE))
.
See 李哲源 Zheyuan Li answer for more explanations
Here, the output of myfun is a length 2 vector --> works only with SIMPLIFY = FALSE
because you need that the result of each run of myfun
is a length 1 list
> myfun <- function(x, y) c(sum(x,y), mean(x,y))
> myfun(1,4)
[1] 5 1
> res <- outer(1:4, 4:1, Vectorize(myfun, SIMPLIFY = TRUE))
Error in dim(robj) <- c(dX, dY) :
dims [produit 16] ne correspond pas à la longueur de l'objet [32]
> res <- outer(1:4, 4:1, Vectorize(myfun, SIMPLIFY = FALSE))
>
Here the output of myfun is a length 2 list --> works only with SIMPLIFY = FALSE
> myfun <- function(x, y) list(sum(x,y), mean(x,y))
> myfun(1,4)
[[1]]
[1] 5
[[2]]
[1] 1
> res <- outer(1:4, 4:1, Vectorize(myfun, SIMPLIFY = TRUE))
Error in dim(robj) <- c(dX, dY) :
dims [produit 16] ne correspond pas à la longueur de l'objet [32]
> res <- outer(1:4, 4:1, Vectorize(myfun, SIMPLIFY = FALSE))
>
Here the output of myfun is a length 1 list containing a length 2 vector
--> works with both SIMPLIFY = FALSE
and SIMPLIFY = TRUE
> myfun <- function(x, y) return(list(c(sum(x,y),mean(x,y))))
> myfun(1,4)
[[1]]
[1] 5 1
> res <- outer(1:4, 4:1, Vectorize(myfun, SIMPLIFY = TRUE))
> res <- outer(1:4, 4:1, Vectorize(myfun, SIMPLIFY = FALSE))