[R] modify subset of array in list in a function
Tamas Papp
tkpapp at gmail.com
Thu Jan 23 12:51:08 CET 2014
Hi,
I am trying to implement a function that would allow functional
transformations of posterior simulations (useful in posterior predictive
checks after MCMC, eg in Stan).
A posterior simulation is a list of vectors and arrays. Usually, one
uses loops to transform it, but that's error prone. I was thinking of
ending up with an interface like this (toy example):
--8<---------------cut here---------------start------------->8---
posterior <- list(a=1:3,b=matrix(4:9,nrow=3))
map_posterior(posterior, function(a,b) {
list(d=a*sum(b))
}) # should be equivalent to list(d=posterior$a + rowSums(posterior$b))
--8<---------------cut here---------------end--------------->8---
I started coding it: I make an arglist for do.call, matching names,
then deconstruct the value and save it where it belongs. But the last
part is giving me trouble, since in R calls are by value, not reference,
so I don't end up modifying the original array in the code below (when
set_subarray is called):
--8<---------------cut here---------------start------------->8---
leading_dimension <- function(array) {
if (is.vector(array))
length(array)
else
dim(array)[1]
}
common_leading_dimension <- function(array_list) {
## if all leading dimensions are the same, return it, otherwise signal an error
length_ <- length(array_list)
stopifnot(length_ > 0)
ld <- leading_dimension(array_list[[1]])
if (length_ > 1)
for (i in 2:length_)
stopifnot(leading_dimension(array_list[[i]])==ld)
ld
}
subarray <- function(array, index) {
## equivalent to array[index, , ...]
if (is.vector(array))
array[index]
else {
rank_ <- length(dim(array))
stopifnot(rank_ >= 1)
do.call("[",c(list(array,index),rep(TRUE,rank_-1)))
}
}
set_subarray <- function(value, array, index) {
## equivalent to array[index, , ...] <- value
if (is.vector(array))
array[index] <- value
else {
rank_ <- length(dim(array))
stopifnot(rank_ >= 1)
do.call("[<-",c(list(array,value,index),rep(TRUE,rank_-1)))
}
}
map_posterior <- function(posterior,f) {
names_ <- names(posterior)
ld <- common_leading_dimension(posterior)
result <- NULL
for (index in 1:ld) {
row_args <- Map(function(name) subarray(posterior[[name]],index),names_)
names(row_args) <- names_
row_result <- do.call(f,row_args)
if (is.null(result)) {
result <- Map(function(value) {
dims <- if(is.vector(value)) {
length_ <- length(value)
if (length_ == 1)
NULL
else
length_
} else {
dim(value)
}
array(NA,c(ld,dims))
},row_result)
names(result) <- names(row_result)
}
Map(function(row_result_name,row_result_value) {
set_subarray(row_result_value,result[[row_result_name]],index)
})
}
result
}
--8<---------------cut here---------------end--------------->8---
Any help or hints on how to do this would be appreciated, including
alternative approaches of doing/programming the same thing.
Best,
Tamas
More information about the R-help
mailing list