Related
In a 4D array, I would like to find the unique minimum value across the 4th dimension. I want to get a matrix of the array indices for the minimum.
I have tried solving the issue with the following code block. I would have liked using which.min, but I haven't found a good way to return the array indices.
dims =c(3,3,3,4)
# create sample data with multiple mins in [,,,1]
mat_rep = array(c(rep(0,3),sample(1:prod(dims))), dim = dims)
pos_rep = apply(mat_rep, 4, function(x) which(x == min(x), arr.ind = T)) # get position of unique minimum
# create sample data with unique min
mat_norep = array(sample(1:prod(dims)), dim = dims)
pos_norep = apply(mat_norep, 4, function(x) which(x == min(x), arr.ind = T))
# formating depending on class of pos_ object
format_pos = function(x, dims){
if(class(x) == "matrix") x = t(x)
if(class(x) == "list") x = do.call(rbind, lapply(x, head, 1))
x = cbind(x, 1:dims[4]) # add 4th dimension
return(x)
}
format_pos(pos_norep, dims = dims)
format_pos(pos_rep, dims = dims)
The described solution works, however it doesn't work generally and the if(class()) and cbind(x, 1:dims[4]) in my opinion is prone to producing errors.
Does someone have a cleaner way of solving this issue?
To create uniform outputs you can call arrayInd explicitly on the output of apply(..., which.min), instead of implicitly as in which(..., arr.ind = TRUE). The fourth dimension indices still need to be added manually though:
## add 4D indices using 1D values
start.ind <- (seq_len(dims[4]) - 1) * prod(head(dims, 3))
arrayInd(apply(mat_rep, 4, which.min) + start.ind, .dim = dims)
#> [,1] [,2] [,3] [,4]
#> [1,] 1 1 1 1
#> [2,] 1 3 3 2
#> [3,] 1 3 2 3
#> [4,] 3 1 1 4
arrayInd(apply(mat_norep, 4, which.min) + start.ind, .dim = dims)
#> [,1] [,2] [,3] [,4]
#> [1,] 2 1 3 1
#> [2,] 1 2 1 2
#> [3,] 1 2 1 3
#> [4,] 2 2 3 4
## add 4D indices using cbind
cbind(arrayInd(apply(mat_rep, 4, which.min), .dim = head(dims, 3)), seq_len(dims[4]))
#> [,1] [,2] [,3] [,4]
#> [1,] 1 1 1 1
#> [2,] 1 3 3 2
#> [3,] 1 3 2 3
#> [4,] 3 1 1 4
cbind(arrayInd(apply(mat_norep, 4, which.min), .dim = head(dims, 3)), seq_len(dims[4]))
#> [,1] [,2] [,3] [,4]
#> [1,] 2 1 3 1
#> [2,] 1 2 1 2
#> [3,] 1 2 1 3
#> [4,] 2 2 3 4
Data
dims <- c(3,3,3,4)
mat_rep <- array(c(rep(0,3),sample(1:prod(dims))), dim = dims)
mat_norep <- array(sample(1:prod(dims)), dim = dims)
I feel like this is a fairly simple question. I am dealing with 217 different "text" (they're actually .edf files I've converted to text) files, each of which containing a 9 column table of attributes. The data is coming from sensors that were dropped to the ocean floor, so each has a different number of rows. I would like to combine all of these files into one array in R of dimension 9399x9x217. (9399 is the number of rows in the largest file). Currently I'm using this code:
omgdatlist <- list.files(pattern ="*.edf")
named.list <- array(0, dim = c(9399, 9, 217))
for (i in 1:217)
{
named.list[i] <- matrix(assign(omgdatlist[i], read.table(omgdatlist[i], header = FALSE, skip = 51, col.names = c("Time(sec)", "Frame", "Depth(m)", "Temperature(C)", "Conductivity", "Salinity", "Sound Velocity", "Density", "Status"))), nrow = 9399, ncol = 9)
}
However, it's returning a list of 18356247. Is using an array even the most efficient way of doing this?
Because you want to access the ith 2D cell of your array, you need to replace named.list[i] by named.list[,,i].
Here is a minimum working example:
named.list <- array(0, dim = c(3, 1, 2))
for (i in 1:dim(named.list)[3])
{
named.list[,,i] <- array(i, dim = c(3, 1))
}
named.list
which returns
> named.list
, , 1
[,1]
[1,] 1
[2,] 1
[3,] 1
, , 2
[,1]
[1,] 2
[2,] 2
[3,] 2
As for efficiency, you might want to look at ?sapply and ?apply.
I have two high dimensional arrays, both with 1000 rows, 3 columns, 10 slices and 4 groups of slices.
I want to bind them but matching the columns between them. Example below:
#dumb data
array1 <- array(1:4, dim = c(2,1,2,2),dimnames = list(NULL,"Ca",1:2,1:2)); array1
array2 <- array(5:8, dim = c(2,1,2,2),dimnames = list(NULL,"Cb",1:2,1:2)); array2
#desired result
cbind(array1[,,1,1],array2[,,1,1],array1[,,2,1],array2[,,2,1],
array1[,,1,2],array2[,,1,2],array1[,,2,2],array2[,,2,2])
the outcome is:
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,] 1 5 3 7 1 5 3 7
[2,] 2 6 4 8 2 6 4 8
Although column names do not show up, I have ordered manually the columns to be: First column of the first array, first column of the second array, second column of the first array, second column of the second array, and so on.
Is there a other way to do this than manually using cbind?
Thanks in advance!
Here is one way to accomplish this for the example, but might require some modification for the actual data set:
m1 <- cbind(array1, array2)
l1 <- lapply(seq_len(nrow(m1) / 2), function(x) m1[seq(2 * x - 1, 2 * x), ])
m2 <- do.call(cbind, l1)
> m2
array1 array2 array1 array2 array1 array2 array1 array2
[1,] 1 5 3 7 1 5 3 7
[2,] 2 6 4 8 2 6 4 8
I have a list of 3D arrays which looks like this:
my.arrs <- list(array(1:5, c(5,4,2)),array(1:5, c(5,4,2)),array(1:5, c(5,4,2)));
my.arrs
What I wish to have is a new list with each matrix contained in my list of arrays separated (ideally it should look like this):
my.new.matrices (list of 6)
my.array[[1]][1]
my.array[[1]][2]
my.array[[2]][1]
my.array[[2]][2]
my.array[[3]][1]
my.array[[3]][2]
Before having this issue, I worked on something very similar. I had this as the result of a simulation I run in R:
> TBM
, , 1
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] 0.05151012 0.345498935 0.26056614 0.04567956 0.073153163 -0.070264403 0.158124924
, , 2
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] -0.65883235 -0.43591955 -0.116739746 -0.28835563 0.04351086 -0.03692388 0.60592379
, , 3
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] 0.2816988 0.3726166 0.4434252 0.4204302 0.2684518 0.454951339 0.64363895
And I wrote this for() loop:
TBM.vector3 <- list()
for(i in 1:dim(TBM)[3]) {
print(i)
TBM.vector3[[i]] <- as.vector(TBM[,,i])
}
TBM.vector3
which makes exactly what I want i.e., separate each matrix in the third dimension and store them in a list of vector. I want to automatize this process a list of array like my.arrs.
1) tapply Each of 3 arrays is made up of 2 5x4 matrices so create a long vector out of it all and then grab successive vectors of 5*4 components and turn them into a matrix of the desired shape:
tapply(unlist(my.arrs), gl(3*2, 5*4), matrix, 5)
or more generally:
dims <- dim(my.arrs[[1]]) # c(5, 4, 2)
tapply(unlist(my.arrs), gl(length(my.arrs) * dims[3], prod(dims[1:2])), matrix, dims[1])
2) sapply/lapply Another possibility (where dims is defined above):
c(sapply(my.arrs, function(x) lapply(1:dims[3], function(i) x[,,i])))
If I understand what you are asking for, here is one approach:
unlist(lapply(my.arrs, function(x) {
lapply(1:dim(x)[3], function(y) {
x[,,y]
})
}), recursive = FALSE)
#[[1]]
# [,1] [,2] [,3] [,4]
#[1,] 1 1 1 1
#[2,] 2 2 2 2
#[3,] 3 3 3 3
#[4,] 4 4 4 4
#[5,] 5 5 5 5
## ...
## ...
#[[6]]
# [,1] [,2] [,3] [,4]
#[1,] 11 11 11 11
#[2,] 12 12 12 12
#[3,] 13 13 13 13
#[4,] 14 14 14 14
#[5,] 15 15 15 15
The outer lapply iterates over each array
The inner lapply iterates over each slice (3rd dimension) of each array, so that 2 is not hardcoded into the function
Calling unlist with recursive = FALSE (which is not the default behavior) expands the lapply(lapply(...)) result from a length 3 list of length 2 lists into a single length 6 list.
Data:
my.arrs <- list(
array(1:5, c(5,4,2)),
array(6:10, c(5,4,2)),
array(11:15, c(5,4,2))
)
Thank you #G. Grothendieck and #nrussell for your answers,
I was also working on my one meanwhile, had simular results with unlist() and found what is my actual problem.
The output of the simulation I run for n <- 2, gives a list of this format (later I want to make it run for a larger data set and 100 of realizations, so let's start small):
TBM List of 3
tbm1: num [1:29, 1:28, 1:2] ...
tbm2: num [1:29, 1:28, 1:2] ...
tbm3: num [1:29, 1:28, 1:2] ...
... being my output data.
Because I will have a large data set and n <- 100, I can not unlist manually. Thus I wrote this:
TBM.n <- rep(list(matrix(nrow=29, ncol=28)),6)
for(j in 1:length(TBM)){
for(jj in 1:dim(TBM[[i]])[3]){
print(jj)
print(unlist(TBM[[j]][,,jj]))
TBM.n[[j]] <- unlist(TBM[[j]][,,jj])
}
}
print(jj) gives:
1
2
1
2
1
2
print(unlist(TBM[[j]][,,jj])) gives my data split as I want.
And there here come my actual problem, the storage. When I write:
TBM.n[[j]] <- unlist(TBM[[j]][,,jj])
or
TBM.n[[jj]] <- unlist(TBM[[j]][,,jj])
I got the data stored for tbm1[,,2], tbm2[,,2], tbm3[,,2] and tbm2[,,2], tbm3[,,2] repectively.
Until now I did not find a solution for storing the whole 6 matrices. I have the feeling it is an indexing problem, still trying, not solving.
Do you have any suggestions ?
Thank you Marion H
EDIT
Here is my final code I adapted from #nrussel:
TBM.n <- list()
for (i3 in 1){ #in length(TBM)
TBM.n[[i3]] <- unlist(lapply(TBM, function(x){
lapply(1:dim(x)[3], function(y){
x[,,y]
})
}), recursive = FALSE
)
}
The output gives me the desired results:
TBM.n List of 1
:List of 6
..$ tbm11: num[1:29, 1:28] ...
..$ tbm12: num[1:29, 1:28] ...
..$ tbm21: num[1:29, 1:28] ...
..$ tbm22: num[1:29, 1:28] ...
..$ tbm31: num[1:29, 1:28] ...
..$ tbm32: num[1:29, 1:28] ...
Again thank you for your help !
I need to create a static array for my data structure class and I chose to code in R instead of java.
How can I create a static array? I have tried
> array <- array(1:10, dim=c(1,10))
> array
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 1 2 3 4 5 6 7 8 9 10
but I can still add a 11th value to the array
> array[11] <- 11
> array
[1] 1 2 3 4 5 6 7 8 9 10 11
How can I prevent this from happening?
I am also not aware of a mechanism to achieve this. But the following might give you an idea how something like a static array might be achieved.
I assing a class static_vector to a vector as follows:
x <- 1:10
class(x) <- "static_vector"
Next I define a new method for the assingment operator [<- for this class:
"[<-.static_vector" <- function(x, i, value) {
if (any(i > length(x))) {
warning("Size of vector can not be changed!")
} else {
class(x) <- NULL
x[i] <- value
class(x) <- "static_vector"
}
return (x)
}
Edit: Following nicola's remark, I added any() in the first line of the function body.
Now, the following will still work and do what is expected
x[5] <- 0
but this will issue a warning and leave x unchanged:
x[11] <- 11
There are probably other methods to define before you can be sure that there is no way to change the size of x.
You can give your array a class attribute then define a [ method for it. A simple version could be something like,
`[.thing` <- function(x, i) {
if (i > length(x)) stop("index outside range")
else x[[i]]
}
arr <- array(1:10, dim=c(1,10))
class(arr) <- c("thing", class(arr))
arr[1]
# [1] 1
arr[11]
# Error in `[.thing`(arr, 11) (from ...) : index outside range