Reputation: 341
I'm sure this is a fairly straightforward question but I'm not sure how to do this. I have given an example array with dimensions (4,4,5) as follows:
[,1] [,2] [,3] [,4]
[1,] 1 0 5 0
[2,] 0 NA 0 6
[3,] 0 0 0 0
[4,] 0 0 0 0
[,1] [,2] [,3] [,4]
[1,] 1 0 10 0
[2,] 0 NA 0 12
[3,] 0 0 0 0
[4,] 0 0 0 0
[,1] [,2] [,3] [,4]
[1,] 1 0 15 0
[2,] 0 NA 0 18
[3,] 0 0 0 0
[4,] 0 0 0 0
[,1] [,2] [,3] [,4]
[1,] 1 0 20 0
[2,] 0 NA 0 24
[3,] 0 0 0 0
[4,] 0 0 0 0
[,1] [,2] [,3] [,4]
[1,] 1 0 25 0
[2,] 0 NA 0 30
[3,] 0 0 0 0
[4,] 0 0 0 0
Basically, for this example array I'd like the elements at [1,3] and [2,4] to change along the 3rd dimension but I'm not sure how to write this code in R. I've tried using variations of the code array(c(1,0,0,0,0,NA,0,0,5,0,0,0,0,6,0,0), dim=c(4,4,3))
and I tried checking online but I can't seem to find anything which helps with this issue, so any help I can get would be greatly appreciated, thanks in advance.
Upvotes: 1
Views: 64
Reputation: 11255
If you're updating based on values, you apply a condition:
arr[arr > 4 ] <- 100
What's happening is that the inside arr > 4
is generating an array:
, , 1
[,1] [,2] [,3] [,4]
[1,] FALSE FALSE TRUE FALSE
[2,] FALSE NA FALSE TRUE
[3,] FALSE FALSE FALSE FALSE
[4,] FALSE FALSE FALSE FALSE
, , 2
[,1] [,2] [,3] [,4]
[1,] FALSE FALSE TRUE FALSE
[2,] FALSE NA FALSE TRUE
[3,] FALSE FALSE FALSE FALSE
[4,] FALSE FALSE FALSE FALSE
, , 3
[,1] [,2] [,3] [,4]
[1,] FALSE FALSE TRUE FALSE
[2,] FALSE NA FALSE TRUE
[3,] FALSE FALSE FALSE FALSE
[4,] FALSE FALSE FALSE FALSE
And then we're just saying assign a value to the conditions which are true. We can also use which(arr > 4, arr.ind = T)
to return a matrix similar to @thelatemail's solution without the typing. This allows us to get to your original post answer:
which_cond <- which(arr>4, arr.ind = T)
arr[which_cond] <- arr[which_cond] * which_cond[, 3]
arr
, , 1
[,1] [,2] [,3] [,4]
[1,] 1 0 5 0
[2,] 0 NA 0 6
[3,] 0 0 0 0
[4,] 0 0 0 0
, , 2
[,1] [,2] [,3] [,4]
[1,] 1 0 10 0
[2,] 0 NA 0 12
[3,] 0 0 0 0
[4,] 0 0 0 0
, , 3
[,1] [,2] [,3] [,4]
[1,] 1 0 15 0
[2,] 0 NA 0 18
[3,] 0 0 0 0
[4,] 0 0 0 0
which_cond
dim1 dim2 dim3
[1,] 1 3 1
[2,] 2 4 1
[3,] 1 3 2
[4,] 2 4 2
[5,] 1 3 3
[6,] 2 4 3
Performance:
#4x4x3 array
Unit: microseconds
expr min lq mean median uq max neval
maur_improved 2.4 3.55 5.42 4.90 5.95 24.4 100
latemail_all_at_once 6.4 8.70 14.00 15.20 18.40 25.3 100
maur_for_loop 3280.0 3510.00 3810.00 3630.00 3770.00 6430.0 100
cole_subset_mat 2.0 3.05 4.71 4.05 6.50 10.2 100
cole_which 27.9 34.50 47.70 45.40 54.80 228.0 100
#4x4x3E6 array
Unit: milliseconds
expr min lq mean median uq max neval
maur_improved 82.9 84.8 89.7 85.8 87.4 165 100
latemail_all_at_once 347.0 361.0 391.0 378.0 417.0 564 100
maur_for_loop 422.0 432.0 462.0 451.0 486.0 721 100
cole_subset_mat 304.0 330.0 369.0 354.0 395.0 527 100
cole_which 783.0 842.0 899.0 878.0 928.0 1370 100
And code:
arr <- array(c(1,0,0,0,0,NA,0,0,5,0,0,0,0,6,0,0), dim=c(4,4,3))
library(microbenchmark)
x = microbenchmark(
maur_improved = {
arr[1,3, ] <- 100
arr[2, 4, ] <- 100
},
latemail_all_at_once = {
arr[cbind(c(1,2),c(3,4),rep(seq_len(dim(arr)[[3]]), each=2))] <- c(80,100)
},
maur_for_loop = {
for (i in seq_len(dim(arr)[3])) {
arr[1, 3, i] <- 100; # Change entry (1, 3) of every 2d matrix
arr[2, 4, i] <- 100; # Change entry (2, 4) of every 2d matrix
}
},
cole_subset_mat = {
arr[arr > 4] <- 100
}
, cole_which = {
which_cond <- which(arr>4, arr.ind = T)
arr[which_cond] <- arr[which_cond] * which_cond[, 3]
}
)
print(x, signif = 3)
Upvotes: 1
Reputation: 50678
I'm not entirely sure on your expected output, but perhaps something like this using a for
loop?
arr <- array(c(1,0,0,0,0,NA,0,0,5,0,0,0,0,6,0,0), dim=c(4,4,3))
for (i in seq_len(dim(arr)[3])) {
arr[1, 3, i] <- 100; # Change entry (1, 3) of every 2d matrix
arr[2, 4, i] <- 100; # Change entry (2, 4) of every 2d matrix
}
arr
#, , 1
#
# [,1] [,2] [,3] [,4]
#[1,] 1 0 100 0
#[2,] 0 NA 0 100
#[3,] 0 0 0 0
#[4,] 0 0 0 0
#
#, , 2
#
# [,1] [,2] [,3] [,4]
#[1,] 1 0 100 0
#[2,] 0 NA 0 100
#[3,] 0 0 0 0
#[4,] 0 0 0 0
#
#, , 3
#
# [,1] [,2] [,3] [,4]
#[1,] 1 0 100 0
#[2,] 0 NA 0 100
#[3,] 0 0 0 0
#[4,] 0 0 0 0
As pointed out by @Cole, in this (simple) case there's no need for a for
loop
arr[1, 3, ] <- 100
arr[2, 4, ] <- 100
is much faster than.
Upvotes: 2
Reputation: 93813
You can do it with one assignment too using matrix indexing:
arr[cbind(c(1,2),c(3,4),rep(seq_len(dim(arr)[[3]]), each=2))] <- c(80,100)
arr
#, , 1
#
# [,1] [,2] [,3] [,4]
#[1,] 1 0 80 0
#[2,] 0 NA 0 100
#[3,] 0 0 0 0
#[4,] 0 0 0 0
#
#, , 2
#
# [,1] [,2] [,3] [,4]
#[1,] 1 0 80 0
#[2,] 0 NA 0 100
#[3,] 0 0 0 0
#[4,] 0 0 0 0
#
#, , 3
#
# [,1] [,2] [,3] [,4]
#[1,] 1 0 80 0
#[2,] 0 NA 0 100
#[3,] 0 0 0 0
#[4,] 0 0 0 0
The part inside []
gives the indexes row/col/strata
for each value to replace:
cbind(c(1,2),c(3,4),rep(seq_len(dim(arr)[[3]]), each=2))
# row col strata
# [,1] [,2] [,3]
#[1,] 1 3 1
#[2,] 2 4 1
#[3,] 1 3 2
#[4,] 2 4 2
#[5,] 1 3 3
#[6,] 2 4 3
Upvotes: 2