How to add an element to a 3d array? - r

I am trying to add elements into a multidimensional array, that I don't know the length of when it is declared, but it is not behaving as i would expect. I have seen that arrays in r are not static, and I have tried a bunch of different approaches, so I'm starting to go a little mad.
i have the array:
diffAr <- array(0,dim = c(1,2))
that i set as part of a loop:
diffAr[t,] <- c(cordX,cordY)
which works fine
> diffAr
[,1] [,2]
[1,] 2 1
and the 1st go round this works fine, but on the second I get this error:
Error in '[<-'('tmp', 2, , value = c(3, 5)) : subscript out of bounds
if arrays in r are dynamic, shouldn't this work, and just add another element?

If you want to add rows to an array you can use rbind() and if you want to add columns you can use cbind(). In this case adding a second row with values 3 and 5 can be done like this (this will also work in your loop) :
diffAr <- array(0,dim = c(1,2))
diffAr[1,] <- c(2,1)
diffAr <- rbind(diffAr, c(3,5))
diffAr
> diffAr
[,1] [,2]
[1,] 2 1
[2,] 3 5

It's since you only have one row in your array.
here is what you set up:
diffAr <- array(0,dim = c(1,2))
which looks like this:
[,1] [,2]
[1,] 0 0
then, let's say you want to change it (in a loop or otherwise):
cordX <- 2
cordY <- 1
diffAr[1,] <- c(cordX,cordY)
now it looks like:
[,1] [,2]
[1,] 2 1
but if you (or your loop) asked for this (which is asking to put the new values in the second row, which doesn't exist):
diffAr[2,] <- c(cordX,cordY)
then you get the error since that row doesn't exist:
Error in `[<-`(`*tmp*`, 2, , value = c(2, 1)) : subscript out of bounds
but you could keep changing the first row if you wanted:
diffAr[1,] <- c(3,5)
which works...
[,1] [,2]
[1,] 3 5

Related

Apply() cannot be applied to this list?

I have created an example below, where I am trying to make a list of each row of a matrix, then use apply().
mat<-matrix(rexp(9, rate=.1), ncol=3)
my_list2 <- list()
for(i in 1:nrow(mat)) {
my_list2[[i]] <- mat[i,]
}
#DO NOT CHANGE THIS:
apply(my_list2[[i]],2,sum)
However the apply() function does not work, giving a dimension error. I understand that apply() is not the best function to use here but it is present in a function that I need so I cannot change that line.
Does anyone have any idea how I can change my "my_list2" to work better? Thank you!
Edit:
Here is an example that works (non reproducible)
Example
Note both the example above and this example have type "list"
This answer addresses "how to properly get a list of matrices", not how to resolve the use of apply.
By default in R, when you subset a matrix to a single column or a single row, it reduces the dimensionality. For instance,
mtx <- matrix(1:6, nrow = 2)
mtx
# [,1] [,2] [,3]
# [1,] 1 3 5
# [2,] 2 4 6
mtx[1,]
# [1] 1 3 5
mtx[,3]
# [1] 5 6
If you want a single row or column but to otherwise retain dimensionality, add the drop=FALSE argument to the [-subsetting:
mtx[1,,drop=FALSE]
# [,1] [,2] [,3]
# [1,] 1 3 5
mtx[,3,drop=FALSE]
# [,1]
# [1,] 5
# [2,] 6
In this way, your code to produce sample data can be adjusted to be:
set.seed(42) # important for reproducibility in questions on SO
mat<-matrix(rexp(9, rate=.1), ncol=3)
my_list2 <- list()
for(i in 1:nrow(mat)) {
my_list2[[i]] <- mat[i,,drop=FALSE]
}
my_list2
# [[1]]
# [,1] [,2] [,3]
# [1,] 1.983368 0.381919 3.139846
# [[2]]
# [,1] [,2] [,3]
# [1,] 6.608953 4.731766 4.101296
# [[3]]
# [,1] [,2] [,3]
# [1,] 2.83491 14.63627 11.91598
And then you can use akrun's most recent code to resolve how to get the row-wise sums within each list element, i.e., one of
lapply(my_list2, apply, 2, sum)
lapply(my_list2, function(z) apply(z, 2, sum))
lapply(my_list2, \(z) apply(z, 2, sum)) # R-4.1 or later
In your screenshot it works because the object part of the list ex[[1]] is an array. And in your example the elements of your list are vectors. You could try the following:
mat<-matrix(rexp(9, rate=.1), ncol=3)
my_list2 <- list()
for(i in 1:nrow(mat)) {
my_list2[[i]] <- as.matrix(mat[i,])
}
#DO NOT CHANGE THIS:
apply(my_list2[[1]],2,sum)
apply(my_list2[[2]],2,sum)
apply(my_list2[[3]],2,sum)
You should note that apply cannot be applied to all three elements of the array in one line. And to do it in one, that line should be changed.

Error assigning to a submatrix diagonal with drop=FALSE

When calling diag<-, you can pass a slice of the matrix and get the proper behavior, as long as you don't specify drop=FALSE.
> X <- matrix(0, 3, 3)
> diag(X[-1,]) <- c(1,2)
> X
[,1] [,2] [,3]
[1,] 0 0 0
[2,] 1 0 0
[3,] 0 2 0
Specifying drop=false is a different story
> diag(X[-1,,drop=FALSE]) <- c(3,4)
Error in diag(X[-1, , drop = FALSE]) <- c(3, 4) :
incorrect number of subscripts
Note:
> identical(X[-1,], X[-1,,drop=FALSE])
[1] TRUE
As noted by MrFlick, assignment to a slice when the drop argument results in the same error:
X[1,] <- 1
X[1,,drop=TRUE] <- 2
Error in X[1, , drop = TRUE] <- 2 : incorrect number of subscripts
Why is this happening?
According to the ?"[<-" help page, drop= "only works for extracting elements, not for the replacement" Thus you are not allowed to use a <- with drop which is basically what diag() is doing. As in my comment above, something like X[,,drop=TRUE] <- 1:9 is not allowed either. Too bad the error message isn't a bit more specific.

Modifying which.max function in R

I have two rasters with data as below:
library("raster")
mdata <- raster(matrix(c(0,2,3, 11,12,13), nrow = 2, ncol = 3, byrow = TRUE))
ndata <- raster(matrix(c(0,1,2, 11,14,13), nrow = 2, ncol = 3, byrow = TRUE))
I want to stack them and estimate the maximum position with the criteria that if both the raster has value of 0, I should be able to write 0. This means that the output raster/matrix should have either 0, 1 or 2 in this case.
I tried following codes but it does not perform quite exactly the way I want.
odata <- stack(mdata, ndata)
e <- which.max(odata)
How should I be able to introduce the criteria that checks if both matrices have value of 0 for same position and assign 0 if there is?
I really appreciate your feedback on this. Thanks!
How about this:
Rgames> foo<-matrix(rep(1,6),nr=2,nc=3)
Rgames> foo
[,1] [,2] [,3]
[1,] 1 1 1
[2,] 1 1 1
Rgames> foo[(ndata-mdata >0)] <-2
Rgames> foo[(mdata==0 & ndata==0)] <-0
Rgames> foo
[,1] [,2] [,3]
[1,] 0 1 1
[2,] 1 2 1
I still can't tell from your question whether you want to locate the maximum of mdata or all locations where mdata>ndata , but if you clarify that it's easy to modify the conditionals to match.
EDIT: discuss extending to N layers of a raster.
To find which layer has the max at each location, I might use an array.
cube <- array(c(data.1,data.2,...),dim=c(dim(data.1),N)) #for N layers
# and I apologize but I may have loaded this 3-D cube in the wrong order
maxvals<-array(dim=dim(data.1))
for (j in 1:dim(data.1)[1]) {
for (k in 1:dim(data.1)[2]) {
maxvals[j,k]<-which.max(cube[j,k,])
if(sum(cube[j,k,])==0 ) maxvals[j,k] <- 0
}
}
This can probably be done a lot more efficiently with aggregate or plyr tools but I hope this points the way.

How can I make processing of matrices and vectors regular (as, e.g., in Matlab)

Suppose I have a function that takes an argument x of dimension 1 or 2. I'd like to do something like
x[1, i]
regardless of whether I got a vector or a matrix (or a table of one variable, or two).
For example:
x = 1:5
x[1,2] # this won't work...
Of course I can check to see which class was given as an argument, or force the argument to be a matrix, but I'd rather not do that. In Matlab, for example, vectors are matrices with all but one dimension of size 1 (and can be treated as either row or column, etc.). This makes code nice and regular.
Also, does anyone have an idea why in R vectors (or in general one dimensional objects) aren't special cases of matrices (or multidimensional objects)?
Thanks
In R, it is the other way round; matrices are vectors. The matrix-like behaviour comes from some extra attributes on top of the atomic vector part of the object.
To get the behaviour you want, you'd need to make the vector be a matrix, by setting dimensions on the vector using dim() or explicit coercion.
> vm <- 1:5
> dim(vm) <- c(1,5)
> vm
[,1] [,2] [,3] [,4] [,5]
[1,] 1 2 3 4 5
> class(vm)
[1] "matrix"
Next you'll need to maintain the dimensions when subsetting; by default R will drop empty dimensions, which in the case of vm above is the row dimension. You do that using drop = FALSE in the call to '['(). The behaviour by default is drop = TRUE:
> vm[, 2:4]
[1] 2 3 4
> vm[, 2:4, drop = FALSE]
[,1] [,2] [,3]
[1,] 2 3 4
You could add a class to your matrices and write methods for [ for that class where the argument drop is set to FALSE by default
class(vm) <- c("foo", class(vm))
`[.foo` <- function(x, i, j, ..., drop = FALSE) {
clx <- class(x)
class(x) <- clx[clx != "foo"]
x[i, j, ..., drop = drop]
}
which in use gives:
> vm[, 2:4]
[,1] [,2] [,3]
[1,] 2 3 4
i.e. maintains the empty dimension.
Making this fool-proof and pervasive will require a lot more effort but the above will get you started.

cbind arbitrary length vectors with no warning

I would like to cbind different vectors of non-multiple length. The shorter ones are to be (partly) recycled as in vanilla cbind:
cbind(c(1,2,3),c(4,5))
[,1] [,2]
[1,] 1 4
[2,] 2 5
[3,] 3 4
Warning message:
In cbind(c(1, 2, 3), c(4, 5)) :
number of rows of result is not a multiple of vector length (arg 2)
The result is as desired, except for the warning. Since I want to use this in an extension, is there a possibility to suppress the warning or better: who knows a straightforeward solution producing the same results with no warning! -- thanks, S.
Here is one option, wrapping the key concept into a function that arranges for things to just work. The simplest way is just to use rep() on each element of ... to repeat each input vecotr in ... to a common length (i.e. the length of the longest input vector).
This is what I do below using the length.out argument of rep().
CBIND <- function(..., deparse.level = 1) {
dots <- list(...) ## grab the objects passed in via ... this is a list
len <- sapply(dots, length) ## find lengths of individual vectors
## this applies rep() over dots extending each component vector to length
## of longest vector in ...
dots <- lapply(seq_along(dots),
function(i, x, len) rep(x[[i]], length.out = len),
x = dots, len = max(len))
## need to put everything together so arrange for a call to cbind
## passing the extended list of vectors and passing along
## the deparse.level argument of cbind
do.call(cbind, c(dots, deparse.level = deparse.level))
}
This gives:
R> CBIND(c(1,2,3),c(4,5))
[,1] [,2]
[1,] 1 4
[2,] 2 5
[3,] 3 4
R> CBIND(c(1,2,3),c(4,5), deparse.level = 2)
c(1, 2, 3) c(4, 5, 4)
[1,] 1 4
[2,] 2 5
[3,] 3 4
I would certainly favour this over simply clobbering warnings with suppressWarnings() wrapped around the call. For production code you want to explicitly handle the cases you want to allow and let warnings propagate in circumstances where the user has done something you didn't account for.
You could use suppressWarnings, if you really want:
suppressWarnings(cbind(c(1,2,3),c(4,5)))
# [,1] [,2]
# [1,] 1 4
# [2,] 2 5
# [3,] 3 4

Resources