Suppose I have an R function like foo
below. This function has 4 fixed arguments, and any number of arbitrary arguments defined in ...
.
All input values for foo
arguments are stored in THIS CSV file.
In my code below, I can successfully run foo
using the 4 fixed arguments imported from the CSV file in a lapply
loop. BUT I'm wondering how I can insert the arguments defined in ...
in the lapply
command?
foo <- function(n = NULL, r = NULL, post, control, ...){ ## the function
data.frame(n = n, r = r, post, control, ...)
}
D <- read.csv("https://raw.githubusercontent.com/izeh/i/master/j.csv", h = T) # CSV file
L <- split(D, D$study.name) ; L[[1]] <- NULL
# the fixed args values:
n <- lapply(1:length(L), function(i) L[[i]]$n)
r <- lapply(1:length(L), function(i) L[[i]]$r)
post <- lapply(1:length(L), function(i) L[[i]]$post)
control <- lapply(1:length(L), function(i) L[[i]]$control)
# names of args defined in `...`:
dot.names <- names(L[[1]])[!names(L[[1]]) %in% formalArgs(foo)][-1]
# the `...` args values:
a <- lapply(dot.names, function(i) lapply(L, function(j) j[grep(i, names(j))]))
## RUN `foo` function:
lapply(1:length(L), function(i) foo(n = n[[i]], r = r[[i]], post = post[[i]],
control = control[[i]])) # BUT! how can I insert the
# arguments defined in `...`
# in the function?
We can also use Map
with do.call
. We can extract the arguments to the foo
in a single call to lapply
by extracting the columns 'n', 'r', 'post', control' and the extra columns (...
) based on output of 'dot.names', then transpose
(from purrr
- or use the same approach as mentioned here) and pass it on Map
args <- lapply(L, function(x) unclass(x[c("n", "r", "post", "control", dot.names)]))
library(purrr)
unname(do.call(Map, c(f = foo, transpose(args))))
#[[1]]
# n r post control ESL prof scope type
#1 13 0.5 1 FALSE 1 2 0 1
#2 13 0.5 2 FALSE 1 2 0 1
#3 15 0.5 1 FALSE 1 2 0 1
#4 15 0.5 2 FALSE 1 2 0 1
#5 16 0.5 1 TRUE 1 2 0 1
#6 16 0.5 2 TRUE 1 2 0 1
#[[2]]
# n r post control ESL prof scope type
#1 13 0.5 1 FALSE 0 1 1 0
#2 13 0.5 2 FALSE 0 1 1 0
#3 15 0.5 1 FALSE 0 1 1 0
#4 15 0.5 2 FALSE 0 1 1 0
#5 16 0.5 1 TRUE 0 1 1 0
#6 16 0.5 2 TRUE 0 1 1 0
#[[3]]
# n r post control ESL prof scope type
#1 13 0.5 1 FALSE 1 3 0 1
#2 13 0.5 2 FALSE 1 3 0 1
#3 13 0.5 3 FALSE 1 3 0 1
#4 15 0.5 1 FALSE 1 3 0 1
#5 15 0.5 2 FALSE 1 3 0 1
#6 15 0.5 3 FALSE 1 3 0 1
#7 16 0.5 1 TRUE 1 3 0 1
#8 16 0.5 2 TRUE 1 3 0 1
#9 16 0.5 3 TRUE 1 3 0 1
The OP mentioned about replacing the transpose
with a base R
option
m1 <- simplify2array(lapply(names(args[[1]]), function(nm)
lapply(args, function(l1) l1[nm])))
do.call(Map, c(f = foo, unname(split(m1, col(m1)))))
Of if we can use tidyverse
library(tidyverse)
map(L, ~
.x %>%
select(n, r, post, control, dot.names) %>%
as.list) %>%
transpose %>%
pmap(., foo)
#$Ellis.sh1
# n r post control ESL prof scope type
#1 13 0.5 1 FALSE 1 2 0 1
#2 13 0.5 2 FALSE 1 2 0 1
#3 15 0.5 1 FALSE 1 2 0 1
#4 15 0.5 2 FALSE 1 2 0 1
#5 16 0.5 1 TRUE 1 2 0 1
#6 16 0.5 2 TRUE 1 2 0 1
#$Goey1
# n r post control ESL prof scope type
#1 13 0.5 1 FALSE 0 1 1 0
#2 13 0.5 2 FALSE 0 1 1 0
#3 15 0.5 1 FALSE 0 1 1 0
#4 15 0.5 2 FALSE 0 1 1 0
#5 16 0.5 1 TRUE 0 1 1 0
#6 16 0.5 2 TRUE 0 1 1 0
#$kabla
# n r post control ESL prof scope type
#1 13 0.5 1 FALSE 1 3 0 1
#2 13 0.5 2 FALSE 1 3 0 1
#3 13 0.5 3 FALSE 1 3 0 1
#4 15 0.5 1 FALSE 1 3 0 1
#5 15 0.5 2 FALSE 1 3 0 1
#6 15 0.5 3 FALSE 1 3 0 1
#7 16 0.5 1 TRUE 1 3 0 1
#8 16 0.5 2 TRUE 1 3 0 1
#9 16 0.5 3 TRUE 1 3 0 1
Update
Based on the example showed here, the structure is slightly different, so we can transpose the list
with names
(for base R
)
argsT <- setNames(lapply(names(args[[1]]),
function(nm) lapply(args, `[[`, nm)), names(args[[1]]))
out1 <- unname(do.call(Map, c(f = d.prepos, argsT)))
out2 <- unname(do.call(Map, c(f = d.prepos, purrr::transpose(args))))
identical(out1, out2)
#[1] TRUE
Use mapply
for this type of problem.
In the code below I have changed the way you define n
, r
, post
and control
.
n <- lapply(L, `[[`, 'n')
r <- lapply(L, `[[`, 'r')
post <- lapply(L, `[[`, 'post')
control <- lapply(L, `[[`, 'control')
The only difference is that these results have their names
attribute set.
Then also change the way the list of lists a
is created. Swap the two cycles.
a <- lapply(L, function(i) lapply(dot.names, function(k) i[grep(k, names(i))]))
Now the solution to the problem. It is mandatory to set SIMPLIFY = FALSE
, the default TRUE
gives a really bad output.
mapply(FUN = foo, n, r, post, control, a, SIMPLIFY = FALSE)