Slicing high-dimensional R arrays with index pairs - r

For the matrix M <- matrix(1:24, nrow = 6, ncol = 4), if you want to subset its elements by the index pairs, which are stored as rows in matrix indexes <- cbind(1:3, 2:4), you can do:
M[indexes]
# [1] 7 14 21
However, I haven't found a simple way to combine index pairs and index grids in slicing higher-dimensional arrays. For example, we have the array A <- array(1:24, dim = c(2,3,4), of which the first dimension should be kept and the last two dimensions are to be subsetted by index pairs. A wordy solution might be:
sapply(1:nrow(indexes), \(i) A[, indexes[i,1], indexes[i,2]])
# and structure the matrix back to the array form, if A has even higher dimension.
# [,1] [,2] [,3]
# [1,] 7 15 23
# [2,] 8 16 24
But I still want something as clean as
A[, indexes[,1], indexes[,2], pairwise = TRUE] # does it exist?
Update 1
Moreover, I have checked what the Numpy in Python has for their arrays, and I found the following example from this site:
X = np.arange(12).reshape((3, 4))
X
# array([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11]])
row = np.array([0, 1, 2])
col = np.array([2, 1, 3])
X[row, col]
# array([ 2, 5, 11])
X[row[:, np.newaxis], col] # broadcasting rules
# array([[ 2, 1, 3],
# [ 6, 5, 7],
# [10, 9, 11]])
The python version of the solution is quite simple and clear. In the same spirit,

Related

Cross products of each row of a matrix with the corresponding row of another matrix

Suppose I have the following 2 matrices
A <- matrix(nrow = 5, ncol = 3, byrow = TRUE, c(
5, 1, 2,
3, 1, 5,
5, 1, 3,
7, 2, 8,
2, 4, 2
))
B <- matrix(nrow = 5, ncol = 2, byrow = TRUE, c(
2, 1,
3, 5,
1, 4,
3, 3,
3, 4
))
and I want to create an array
arr <- array(NA, dim = c(ncol(A), ncol(B), nrow(A)))
to store the cross product of row i of matrix A with the same row i of matrix B. For example, slice 2 of arr would be
A[2, ] %*% t(B[2, ]
[,1] [,2]
[1,] 9 15
[2,] 3 5
[3,] 15 25
Using a for loop, I can do
for (i in 1:nrow(A)) {
arr[, , i] <- A[i, ] %*% t(B[i, ])
}
Or using an sapply function
temp <- sapply(1:nrow(A), function(i) A[i, ] %*% t(B[i, ]), simplify = FALSE)
arr <- array(unlist(temp), dim = c(ncol(A), ncol(B), nrow(A)))
I am wondering if there is more clever way using matrix products or built-in functions in this case. Thank you so much.
We can try base R option with mapply + tcrossprod like below
array(
mapply(tcrossprod, asplit(A, 1), asplit(B, 1)),
c(ncol(A), ncol(B), nrow(A))
)
1) einsum Using einstein summation notation
library(einsum)
arr1 <- einsum("ij,ik->jki", A, B)
identical(arr1, arr)
## [1] TRUE
2) Khatri Rao Product The product in question is related to the column-wise Khatri Rao product sometimes just called the Khatri Rao product. Note that Matrix comes with R so it does not have to be installed.
library(Matrix)
arr2 <- array(KhatriRao(t(B), t(A)), c(ncol(A), rev(dim(B))))
identical(arr2, arr)
## [1] TRUE
3) Kronecker Product If X and Y are matrices with the same number of columns and Im and In are column vectors of ones with m = nrow(X) and n = nrow(B) elements then we can use the identity X ⊙ Y = (X ⊗ In) ∘ (Im ⊗ Y) that expresses the Khatri Rao columnwise product ⊙ in terms of the Kronecker product ⊗ and Hadamard, i.e. element-wise, product ∘ to derive another base solution.
arr3 <- array( (t(B) %x% rep(1, ncol(A)) ) *
( rep(1, ncol(B)) %x% t(A)), c(ncol(A), rev(dim(B))))
identical(arr3, arr)
## [1] TRUE

Create a matrix with vector (not scalar) elements

I want to create an object like a matrix with vectors for its elements. like:
(1,2,3) (1,3,6) (2,4,1)
(0,7,8) (2,3,4) (5,2,1)
(9,0,8) (8,4,6) (1,1,1)
What should I do?
You can use a matrix list. Initially you create a list of vectors:
vec <- list(1:3, 4:10, 2:5, letters[1:3])
then coerce it to a matrix:
mat1 <- matrix(vec, nrow = 2, ncol = 2)
# [,1] [,2]
#[1,] integer,3 integer,4
#[2,] integer,7 character,3
mat2 <- matrix(vec, nrow = 2, ncol = 2, byrow = TRUE)
# [,1] [,2]
#[1,] integer,3 integer,7
#[2,] integer,4 character,3
To extract its elements, use for example:
mat1[1, 1][[1]]
The first [1, 1] is getting matrix element. But since an element is a list, use an extra [[1]] to access the actual vector.
If you want to see its contents when printing it, then coerce it to a data frame instead. For example,
dat1 <- data.frame(mat1)
# X1 X2
#1 1, 2, 3 2, 3, 4, 5
#2 4, 5, 6, 7, 8, 9, 10 a, b, c
You can change column names later, like,
names(dat1) <- c("col1", "col2")
# col1 col2
#1 1, 2, 3 2, 3, 4, 5
#2 4, 5, 6, 7, 8, 9, 10 a, b, c
Accessing its columns by dat1$col1, dat1[[1]] or dat1[["col1"]]. In this case, each column is a list.
If your end goal is a data frame not a matrix, you can construct this data frame directly, but has to protect each column with I():
data.frame(col1 = I(list(1:3, 4:10)),
col2 = I(list(2:5, letters[1:3])) )
# col1 col2
#1 1, 2, 3 2, 3, 4, 5
#2 4, 5, 6,.... a, b, c
Anyway, I don't like these data structures. It potentially make further operation cumbersome.

Indexing using a boolean matrix in R

I'm stumped using indexing in R. I have two matrices, one with logical values and one with data. I want to use the first to index into the second one. However, I've noticed that R reorders my values when doing so.
My data looks roughly like this:
ind <- matrix(c(TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE), nrow = 3, ncol = 4, byrow = TRUE)
data <- matrix(c(1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4), nrow = 3, ncol = 4, byrow = TRUE)
Now, when indexing result <- data[ind], I was obtaining the following: c(1, 3, 4) when I was trying to obtain c(1, 4, 3).
How can I prevent R from reordering columwise? I'd appreciate any input. I'm sure it's an easy fix - I just don't know which.
Thank you!
When converting matrices to and from vectors, R makes the transformation columnwise.
as.vector(data)
[1] 1 1 1 2 2 2 3 3 3 4 4 4
As this happens to both your ind and your data this is generally not a problem.
So to retain your order, you have to transpose both matrices:
> t(data)[t(ind)]
[1] 1 4 3
PS: have you tried which with a matrix?
> which(arr.ind = T, ind)
row col
[1,] 1 1
[2,] 3 3
[3,] 2 4
Here is another base R trick (but I recommend the answer by #c0bra)
> rowSums(data * ind)
[1] 1 4 3

Find out if input is a Toeplitz Matrix in R

Given a random matrix (any size!), write a function that determines whether or not that matrix is a Toeplitz Matrix. In linear algebra, a Toeplitz matrix is one in which the elements on any given diagonal from top left to bottom right are identical.
Here is an example:
x <- structure(c(1, 5, 4, 7, 2, 1, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1, 8,
4, 3, 2), .Dim = 4:5)
[,1] [,2] [,3] [,4] [,5]
[1,] 1 2 3 4 8
[2,] 5 1 2 3 4
[3,] 4 5 1 2 3
[4,] 7 4 5 1 2
So our function should receive such matrix and return TRUE if it meets the conditions.
To test the function, one can use stats::toeplitz() to generate a toeplitz matrix. So for example, the expected output of our function should be:
> toeplitz_detector(stats::toeplitz(sample(5, 5)))
> [1] TRUE
I've solved the problem by defining the following function:
toeplitz_solver <- function(a) {
# re-order a backwards, because we need to check diagonals from top-left
# to bottom right. if we don't reorder, we'll end up with top-right to
# bottom-left.
a <- a[, ncol(a):1]
# get all i and j (coordinates for every element)
i <- 1:nrow(a)
j <- 1:ncol(a)
# get all combinations of i and j
diags <- expand.grid(i, j)
# the coordinates for the diagonals are the ones where
# the sum is the same, e.g.: (3,2), (4,1), (2,3), (1,4)
sums <- apply(diags, 1, sum)
indexes <- lapply(unique(sums), function(x) {
diags[which(sums == x), ]
})
# indexes is now a list where every element is a list of coordinates
# the first element is a list for every coordinates for the first diag
# so on and so forth
results <- sapply(indexes, function(x) {
y <- a[as.matrix(x)]
return(all(y == y[1]))
})
# if every diagonal meets the condition, it is safe to assume that the
# input matrix is in fact toeplitz.
return(all(results))
}

finding values in a range in r and sum the number of values

I have a question I have the following data
c(1, 2, 4, 5, 1, 8, 9)
I set a l = 2 and an u = 6
I want to find all the values in the range (3,7)
How can I do this?
In base R we can use comparison operators to create a logical vector and use that for subsetting the original vector
x[x > 2 & x <= 6]
#[1] 3 5 6
Or using a for loop, initialize an empty vector, loop through the elements of 'x', if the value is between 2 and 6, then concatenate that value to the empty vector
v1 <- c()
for(i in x) {
if(i > 2 & i <= 6) v1 <- c(v1, i)
}
v1
#[1] 3 5 6
data
x <- c(3, 5, 6, 8, 1, 2, 1)

Resources