Task is reshape 3d array from [row, col, slice] to [slice,row,col]. I tried implement base::aperm analog with Rcpp.
// [[Rcpp::export]]
Rcpp::NumericVector array_perm(const Rcpp::NumericVector& x) {
if (Rf_isNull(x.attr("dim"))) {
throw std::runtime_error("'x' does not have 'dim' attibute.");
}
Rcpp::Dimension d = x.attr("dim");
if (d.size() != 3) {
throw std::runtime_error("'x' must have 3 dimensions.");
}
std::size_t n = d[2];
std::size_t n_vec = d[0] * d[1];
std::size_t n_out = n_vec * n;
Rcpp::NumericVector res = Rcpp::no_init(n_out);
std::size_t ind_from = 0;
for (std::size_t i = 0; i < n; ++i) {
std::size_t ind_to = i;
for (std::size_t j = 0; j < n_vec; ++j) {
res[ind_to] = x[ind_from];
ind_to += n;
ind_from += 1;
}
}
res.attr("dim") = Rcpp::Dimension(d[2], d[0], d[1]);
return res;
}
How can I improve it?
For testing code:
# Observations
n <- 1000
# Dimension
d <- 100
# Array
a <- replicate(n, matrix(seq_len(d * d), nrow = d, ncol = d))
# Desired result
res <- aperm(a, c(3, 1, 2))
res
Small benchmark current variant of the code:
b <- bench::mark(aperm(a, c(3, 1, 2)), array_perm(a))
b[, c("expression", "min", "median", "max", "itr/sec")]
#> expression min median max `itr/sec`
#> <chr> <bch:tm> <bch:tm> <bch:tm> <dbl>
#> 1 aperm(a, c(3, 1, 2)) 84.9ms 85.1ms 85.5ms 11.7
#> 2 array_perm(a) 124.8ms 125.2ms 127.2ms 7.96
RcppArmadillo solution
// [[Rcpp::export]]
arma::Cube<double> array_perm2(const arma::Cube<double>& x) {
std::size_t rows = x.n_rows;
std::size_t cols = x.n_cols;
std::size_t slices = x.n_slices;
arma::Cube<double> res(slices, rows, cols);
for(std::size_t i = 0; i < rows; ++i) {
for(std::size_t j = 0; j < cols; ++j) {
for(std::size_t k = 0; k < slices; ++k) {
res(k, i, j) = x(i, j, k);
}
}
}
return res;
}
Benchmark:
expression min median max `itr/sec`
<chr> <bch:tm> <bch:tm> <bch:tm> <dbl>
1 aperm(a, c(3, 1, 2)) 85.8ms 86.4ms 87.7ms 11.6
2 array_perm(a) 116.1ms 127.3ms 129.6ms 8.08
3 array_perm2(a) 217.4ms 219.7ms 222.1ms 4.55