R Populate multi-dimensional array with input from alternating vectors - r

I'm trying to populate a multi-dimensional array with two vectors of the same length. The input data should alternate between the vectors, so that the first input is the first object of the first vector, the second input is the first object of the second vector and so on.
I searched for similar problems on this site and found the function rbind(), however, this will not work as soon as my third dimension is unequal to 1.
In short, I want to achieve this:
a <- 1:6
b <- c("a","b","c","d","e","f")
# output array
, , 1
[,1] [,2]
[1,] "1" "a"
[2,] "2" "b"
[3,] "3" "c"
, , 2
[,1] [,2]
[1,] "4" "d"
[2,] "5" "e"
[3,] "6" "f"
I have a working solution below using three for-loops, but this seems overly complicated.
a <- 1:6
b <- c("a","b","c","d","e","f")
len <- prod(length(a)+length(b))
myarray <- array(rep(F,len),dim=c(3,2,2))
counter <- 1
for (n in 1:dim(myarray)[3]) { # n 2
for (r in 1:dim(myarray)[1]) { # rows 3
for (c in 1:dim(myarray)[2]) { # columns 2
if (c %% 2 != 0) {
myarray[r,c,n] <- a[counter]
} else {
myarray[r,c,n] <- b[counter]
}
}
counter <- counter + 1
}
}
Is there an easier approach?
(I'm sure I'm missing something very simple here, but I'm new to R and can't figure it out myself)
Thank you for reading!
[EDIT]
The code should be applyable to a data set with any vector length and any dimension dim = c(x,y,z).
Example data can be found on Dryad Database https://doi.org/10.5061/dryad.mp713, "Table 1 Arctic char landmarks", which contains 13 pairs of x-y-coordinates from 121 individuals of arctic char fish (dim=c(13,2,121)).

Here is my solution for the problem with dim = c(13,2,121):
M <- cbind(a, b)
array(sapply(seq(1, length(a), 13), function(i) M[i:(i+12),]), c(13,2,121))
Do not forget to store the result Mneu <- ...
For your small example:
M <- cbind(a, b);
array(sapply(seq(1, length(a), 3), function(i) M[i:(i+2),]), c(3,2,2))

Form an array and then permute the dimensions:
aperm(array(cbind(a, b), c(3, 2, 2)), c(1, 3:2))
giving:
, , 1
[,1] [,2]
[1,] "1" "a"
[2,] "2" "b"
[3,] "3" "c"
, , 2
[,1] [,2]
[1,] "4" "d"
[2,] "5" "e"
[3,] "6" "f"
Note
We can generalize the example slightly:
n <- 6 # must be 26 or less so that we can use letters below
a <- 1:n
b <- head(letters, n)
aperm(array(cbind(a, b), c(n/2,2,2)), c(1, 3:2))

Related

I would like to obtain an ARRAY of matrices like the one shown here

I would like to generate an array of data matrices, where each matrix will refer to a different scenario, for the purpose of example I have included only 1
p13=0.493;p43=0.325;p25=0.335;p35=0.574;p12=0.868
std_e2=sqrt(1-p12^2);std_e2
std_e3=sqrt(1-(p13^2+p43^2));std_e3
std_e5=sqrt(1-(p25^2+p35^2+2*p25*p35*(p13*p12)));std_e5
scenario_1<-matrix(c(3,0,0,0,0),ncol = 5,nrow = 1);scenario_1
genereting_fuction<- function(n,scenario){
sample <- vector("list")
for (i in scenario){
x1=rnorm(n)+scenario[i,1]
x4=rnorm(n)+scenario[i,4]
x2=x1*p12+std_e2*rnorm(n)+scenario[i,2]
x3=x1*p13+x4*p43+std_e3*rnorm(n)+scenario[i,3]
x5=x2*p25+x3*p35+std_e5*rnorm(n)+scenario[i,5]
sample[[i]]=cbind(x1,x2,x3,x4,x5)
colnames(sample[[i]])<-c("x1","x2","x3","x4","x5")
}
sample
}
array_scenari<-array(dim=c(5,5,2));array_scenari
for(j in 1:nrow(scenario_1)){
set.seed(1234)
dati_prova<- sapply(rep(1,5), function(x) genereting_fuction(x,scenario_1),simplify = 'array');dati_prova
dati_prova<-do.call(rbind.data.frame, dati_prova);dati_prova<-as.matrix(dati_prova);dati_prova
dati_prova<-as.matrix(dati_prova)
array_scenari[,,j]<-dati_prova[]
}
I can't understand why it doesn't work and gives me an error
It's hard to give a concrete answer without a working reproducible example, since your function contains externally defined variables. However, the source of the error is clear. When you create an empty array with array() it has a single fixed dimension:
matrix_classification <- array()
dim(matrix_classification)
#> [1] 1
And if you try to write into its third dimension you get an error:
k <- 1
matrix_classification[, , k] <- "x"
#> Error in matrix_classification[, , k] <- "x": incorrect number of subscripts
If you want to write into an array you should define its dimensions first. For example, the following creates an empty 5 x 5 x 5 array:
matrix_classification <- array("", dim = c(5, 5, 5))
dim(matrix_classification)
#> [1] 5 5 5
And if we want to write a matrix into the kth slice we can do:
matrix_classification[, , k] <- matrix(sample(letters, 25), nrow = 5)
matrix_classification[,,1]
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] "a" "d" "k" "t" "c"
#> [2,] "f" "b" "n" "m" "s"
#> [3,] "u" "q" "y" "o" "j"
#> [4,] "l" "g" "h" "w" "v"
#> [5,] "r" "i" "e" "p" "z"
Created on 2022-03-06 by the reprex package (v2.0.1)

Get a random column from R matrix

In R language, I defined a matrix this way:
data <- matrix(c("A","B","C","D","E","F"), nrow = 2)
This gives me something like this:
"A" | "C" | "E"
"B" | "D" | "F"
now, How do I get a random column of the matrix?
If I do:
sample(x = data, n = 2)
I get random elements from all around the matrix, like "A" and "F". What I want is to get a column like "A" and "B", or "C" and "D" or "E" and "F"
I am new to R so any help is really apreciated
I'd use something like this:
f <- function(mat) {
j <- sample(seq_len(ncol(mat)), size=1)
## (Use `drop=FALSE` to say "don't convert 1-column matrices to vectors")
data[, j, drop=FALSE]
}
## Try it out
f(data)
# [,1]
# [1,] "E"
# [2,] "F"

Matching and replacing with for loops

Having issues with a for loop.
I am trying to take elements a b c d from each pathway (pathway matrix) and match them to expression data (expression matrix) and put them into a new matrix which look similar to pathway matrix but now contains the elements from expression matrix.
I am trying to acheve this final matrix outcome.
a <- c("pathway","1","4","7","pathway-2","1","e","g","pathway-3","4","g","h")
pathway<-matrix(a,3,4, byrow=T)
The code will be easier to understand than my wording I hope.
a <- c("pathway","b","c","d","pathway-2","b","e","g","pathway-3","c","g","h")
pathway<-matrix(a,3,4, byrow=T)
b <- c("b",1,"c",4,"d",7)
expression<-matrix(b,3,2, byrow=T)
new<-matrix("a",3,4)
new[1:3,1]<-pathway[,1]
for (x in 1:nrow(expression)){
for (y in 1:ncol(pathway)){
if(expression[x,1]==pathway[x,y]){
new[x,y]<-expression[x,2]
}
}
}
Here is one way to do it. We match each column of pathway[,-1] with the expression[,1] matrix, and use the resulting matrix as index for the values from expression[,2]. The ones not found return NA so we index them and replace them from the original matrix. Then cbind as usual to get desired matrix.
new_m <- apply(pathway[, -1], 2, function(i) expression[,2][match(i, expression[,1])])
new_m[which(is.na(new_m))] <- pathway[,-1][which(is.na(new_m))]
cbind(pathway[,1], new_m)
# [,1] [,2] [,3] [,4]
#[1,] "pathway" "1" "4" "7"
#[2,] "pathway-2" "1" "e" "g"
#[3,] "pathway-3" "4" "g" "h"

Replace Items In Matrix Based on Value

I have a matrix that contains integer values that represent the index of the item in an array and I'd like to switch out item 1 for the values[1] and so on for each item in the values array.
Some code to demonstrate what I'd like
> m = matrix(1:3, ncol=3, nrow=3)
> m
[,1] [,2] [,3]
[1,] 1 1 1
[2,] 2 2 2
[3,] 3 3 3
> replace(m, 1="a", 2="b", 3="c")
> m
[,1] [,2] [,3]
[1,] "a" "a" "a"
[2,] "b" "b" "b"
[3,] "c" "c" "c"
Basically it takes 1 and turns it into "a" and so on. It seems like if I try to do this with a for loop it changes after the first iteration from int to string and since I'd like to do this with any object type that's not great behavior.
I can think of three possibilities to solve this
m <- matrix(1:3, 3, 3) # Your data
1
Either define a function that will get a vector in the correct matching order (the first entry will match the first unique value in m, etc.)
vec <- c("Ralf", "Jhons", "Pete")
Then you can define a simple function such as
Match_func <- function(x, y) "dim<-"(y[match(unique(x), seq_along(y))], dim(x))
Test
Match_func(m, vec)
# [,1] [,2] [,3]
# [1,] "Ralf" "Ralf" "Ralf"
# [2,] "Jhons" "Jhons" "Jhons"
# [3,] "Pete" "Pete" "Pete"
2
The second option will be to define your manual replace function, something like
Match_func2 <- function(x, ...) {
temp <- list(...)[[1]]
"dim<-"(temp[match(x, as.numeric(names(temp)))], dim(x))
}
Test
Match_func2(m, c("1" = "a", "2" = "b", "3" = "c"))
# [,1] [,2] [,3]
# [1,] "a" "a" "a"
# [2,] "b" "b" "b"
# [3,] "c" "c" "c"
3
You can also make a use of plyr::revalue
library(plyr)
Match_func3 <- function(x, ...) {
temp <- list(...)[[1]]
"dim<-"(revalue(as.character(x), temp), dim(x))
}
Test
Match_func3(m, c("1" = "a", "2" = "b", "3" = "c"))
# [,1] [,2] [,3]
# [1,] "a" "a" "a"
# [2,] "b" "b" "b"
# [3,] "c" "c" "c"
Note: The last approach is the safest in case you don't want to replace all the unique values
Here's an option, starting with a character matrix so that you don't need to worry about making a copy or coercion of the original matrix.
m = matrix(as.character(1:3), ncol=3, nrow=3)
old <- as.character(1:3)
new <- c("a", "b", "c")
for (i in 1:length(old)) {
m <- ifelse(m == old[i], new[i], m)
}

positions of non-NA cells in a matrix

Consider the following matrix,
m <- matrix(letters[c(1,2,NA,3,NA,4,5,6,7,8)], 2, byrow=TRUE)
## [,1] [,2] [,3] [,4] [,5]
## [1,] "a" "b" NA "c" NA
## [2,] "d" "e" "f" "g" "h"
I wish to obtain the column indices corresponding to all non-NA elements, merged with the NA elements immediately following:
result <- c(list(1), list(2:3), list(4,5),
list(1), list(2), list(3), list(4), list(5))
Any ideas?
The column (and row) indicies of non-NA elements can be obtained with
which(!is.na(m), TRUE)
A full answer:
Since you want to work row-wise, but R treats vector column-wise, it is easier to work on the transpose of m.
t_m <- t(m)
n_cols <- ncol(m)
We get the array indicies as mentioned above, which gives the start point of each list.
ind_non_na <- which(!is.na(t_m), TRUE)
Since we are working on the transpose, we want the row indices, and we need to deal with each column separately.
start_points <- split(ind_non_na[, 1], ind_non_na[, 2])
The length of each list is given by the difference between starting points, or the difference between the last point and the end of the row (+1). Then we just call seq to get a sequence.
unlist(
lapply(
start_points,
function(x)
{
len <- c(diff(x), n_cols - x[length(x)] + 1L)
mapply(seq, x, length.out = len, SIMPLIFY = FALSE)
}
),
recursive = FALSE
)
This will get you close:
cols <- col(m)
cbind(cols[which(is.na(m))-1],cols[is.na(m)])
[,1] [,2]
[1,] 2 3
[2,] 4 5

Resources