R: How do I translate solve_LSAP into a dataframe? - r

I'm trying to do a large assignment problem, and am using LSAP. It works, but I am trying to get the output into a dataframe so that I can do more with it. However, the documentation on the function says "An object of class "solve_LSAP" with the optimal assignment of rows to columns", with no further information on the data. I cannot seem to crack open the class to break the data out into a more usable form.
I've provided their example code.
x <- matrix(c(5, 1, 4, 3, 5, 2, 2, 4, 4), nrow = 3)
y <- solve_LSAP(x, maximum = FALSE)
y
Output:
Optimal assignment:
1 => 3, 2 => 1, 3 => 2
I have 200+ assignments, and the baseline output is simply not usable for me. How can I translate it into a dataframe, or at least a matrix that looks something like the below?
Row Column
1 3
2 1
3 2

The solve_LSAP returns column indices by each row, therefore comprising all you need for reconstruction:
len <- length(y)
parsedMat <- cbind(
1:len,
as.integer(y)
)
parsedMat
[,1] [,2]
[1,] 1 3
[2,] 2 1
[3,] 3 2
This can be turned into a solved matrix by:
solvedMat <- matrix(0, nrow = len, ncol = len)
solvedMat[parsedMat] <- 1
solvedMat
[,1] [,2] [,3]
[1,] 0 0 1
[2,] 1 0 0
[3,] 0 1 0
You can also turn this into a function that will return both outputs in form of a list, e.g.:
parseClueOutput <- function(x) {
len <- length(x)
parsedMat <- cbind(
1:len,
as.integer(x)
)
solvedMat <- matrix(0, nrow = len, ncol = len)
solvedMat[parsedMat] <- 1
return(
list(
parsedMat = parsedMat,
solvedMat = solvedMat
)
)
}
And use it as:
parseClueOutput(y)
$parsedMat
[,1] [,2]
[1,] 1 3
[2,] 2 1
[3,] 3 2
$solvedMat
[,1] [,2] [,3]
[1,] 0 0 1
[2,] 1 0 0
[3,] 0 1 0
As for the structure, the solve_LSAP is not really a complicated object, it's essentially a numeric vector which you can see with:
is.numeric(y)
[1] TRUE
Or:
str(y)
'solve_LSAP' num [1:3] 3 1 2
You can also turn the solvedMat or parsedMat into a dataframe easily - for example, the parsedMat:
setNames(as.data.frame(parsedMat), c('Row', 'Column'))
Row Column
1 1 3
2 2 1
3 3 2

Related

How do I make an array of matrices in R, like the data from dataset_mnist()? (keras)

I want to make my own array of (N x N) matrices that matches the keras-compatible format that is loaded with dataset_mnist(). As
mnist <- dataset_mnist()
x_train <- mnist$train$x
str (x_train)
yields
int [1:60000, 1:28, 1:28] 0 0 0 0 0 0 0 0 0 0 ...
I want to make my own data in this format. Let's say I have 2000 different 100x100 matrices of integers: mat1, mat2, mat3, mat4... mat2000.
How can I combine them to produce an object with the structure:
int [1:2000, 1:100, 1:100] ...
that I can then use as input data for keras models?
I've tried:
as.vector (c(mat1, mat2))
as.array (c(mat1, mat2))
rbind (mat1, mat2)
But it does not produce the correctly structured data.
Thank you for your help!
concatenate your matrices and make array. Also concatenate matrix dimension and third dimension, which is the number of matrices. Example:
m1 <- matrix(1, 2, 3)
m2 <- matrix(2, 2, 3)
m3 <- matrix(3, 2, 3)
array(c(m1, m2, m3), c(dim(m1), 3))
# , , 1
#
# [,1] [,2] [,3]
# [1,] 1 1 1
# [2,] 1 1 1
#
# , , 2
#
# [,1] [,2] [,3]
# [1,] 2 2 2
# [2,] 2 2 2
#
# , , 3
#
# [,1] [,2] [,3]
# [1,] 3 3 3
# [2,] 3 3 3
You can also take a look the package listarrays.
m <- matrix(1:4, ncol = 2)
listarrays::bind_as_rows(m, m, m) |> str()
# int [1:3, 1:2, 1:2] 1 1 1 2 2 2 3 3 3 4 ...

Making a matrix in R without typing out specific elements, excluding some

How do I make a matrix with the sequence 1:16, where all values except 2,3,6,9 and 16 are equal to 0?
I've tried a bunch of different things.
You could do this:
m1 <- 1:16
m2 <- rep(0, 16)
indices <- c(2,3,6,9,16)
m2[indices] <- m1[indices]
matrix(m2, nrow = 4, byrow = TRUE)
# [,1] [,2] [,3] [,4]
# [1,] 0 2 3 0
# [2,] 0 6 0 0
# [3,] 9 0 0 0
# [4,] 0 0 0 16
Depends if the values you want to be non-zero are always going to be at their indices when the matrix is created by row.
You can generalise this method into a function:
create_matrix <- function(max_val, nrow, non_zero_indices) {
m1 <- 1:max_val
m2 <- rep(0, max_val)
m2[non_zero_indices] <- m1[non_zero_indices]
matrix(m2, nrow = nrow, byrow = TRUE)
}
create_matrix(16,4, c(2,3,6,9,16))
# [,1] [,2] [,3] [,4]
# [1,] 0 2 3 0
# [2,] 0 6 0 0
# [3,] 9 0 0 0
# [4,] 0 0 0 16
#akrun's suggestion in the comments will also work if you add byrow=TRUE, so it looks like:
matrix(replace(1:16, !1:16 %in% c(2, 3, 6, 9, 16), 0), 4, 4, byrow=TRUE)
It's a matter of taste.
EDIT: Generation of indices
No one asked for this but I noticed that your indices follow a sequence - specifically they are OEIS A081660 + 1. So instead of typing them directly you could generate them with:
get_indices <- function(n) {
2^(n+1)/3+n+(-1)^n/3 + 1
}
get_indices(0:4)
# [1] 2 3 6 9 16

Rank values of an array in R with apply

I need to rank the values of an array, on the third axe.
I have an array like so :
a <- array(c(1:9,11:19,21:29),dim = c(3,3,3))
The expected result is an matrix with the rank of the first one, that I expected to compute like this : apply(X = a, MARGIN = c(1,2), FUN = rank)[, ,1]
# expected result because a[,,1] is the minimal value of my array
[,1] [,2] [,3]
[1,] 1 1 1
[2,] 1 1 1
[3,] 1 1 1
However the apply function return me another array, which is strange because it return a matrix when I use other functions like mean, max or sort.
I've checked that apply(a, c(1,2), print) and it print the vector I'm interested in ranking (example it print 1 11 21 on the first iteration).
I can code this with a for-loop but at least I'm curious on what exactly is the issue in this case.
# The for loop to obtain the good result
a2 <- a[,,1]
for(i in seq(dim(a)[1])){
for(j in seq(dim(a)[2])){
a2[i,j] <- rank(a[i,j,])[1]
}
}
Thanks in advance !
You can use [1,,] instead of [,,1], i.e.,
apply(X = a, MARGIN = c(1,2), FUN = rank)[1,,]
such that
> apply(X = a, MARGIN = c(1,2), FUN = rank)[1,,]
[,1] [,2] [,3]
[1,] 1 1 1
[2,] 1 1 1
[3,] 1 1 1
Try
apply(a, c(1,2), function(x) rank(x)[1])
# [,1] [,2] [,3]
# [1,] 1 1 1
# [2,] 1 1 1
# [3,] 1 1 1

Write a value for maximum/minimum between two values

I have a two-column matrix and I want to produce a new matrix/data.frame where Col N has 1 if is maximum, 0 otherwise (they are never equal). This is my attempt:
testM <- matrix(c(1,2,3, 1,1,5), ncol = 2, byrow = T)
>testM
V1 V2
1 1 2
2 3 1
3 1 5
apply(data.frame(testM), 1, function(row) ifelse(max(row[1],row[2]),1,0))
I expect to have:
0 1
1 0
0 1
because of the 0,1 parameters in max() function, but I just get
[1] 1 1 1
Any ideas?
Or using pmax
testM <- matrix(c(1,2,3, 1,1,5), ncol = 2, byrow = T)
--(testM==pmax(testM[,1],testM[,2]))
V1 V2
[1,] 0 1
[2,] 1 0
[3,] 0 1
You can perform arithmetic on Booleans in R! Just check if an element in each row is equal to it's max value and multiply by 1.
t(apply(testM, 1, function(row) 1*(row == max(row))))
You can use max.col and col to produce a logical matrix:
res <- col(testM) == max.col(testM)
res
[,1] [,2]
[1,] FALSE TRUE
[2,] TRUE FALSE
[3,] FALSE TRUE
If you want it as 0/1, you can do:
res <- as.integer(col(testM) == max.col(testM)) # this removes the dimension
dim(res) <- dim(testM) # puts the dimension back
res
[,1] [,2]
[1,] 0 1
[2,] 1 0
[3,] 0 1

create a frequency matrix from a 4 dim matrix in R

I have a matrix in k*4 dimension that each row is one of a combination of (1:20,1:20,1:20,1:20) and specify type of quadruplet node . For example for k=3 I have 3 tetrahedron that type of node is here
X <- matrix(c(1, 3, 1 ,4,
2, 5, 6 ,1,
12,20,15 ,3), 3,4,byrow=T)
Now I want to create a frequency table in dim 20*8000 from it that record the frequency of each node in contact with the three remaining node. On the other hand I want to know that each node in quadruplet is in contact with which type of node.
For example for the first row I have a one in 1,(1,3,4)th of F and also in 3,(1,1,4) and in 4,(1,1,3).
I hope that I could explain my problem good to understand.
Please help me in code of this conversion
Note:
As the first row of my X matrix is 1,3,1,4 the output matrix(F) should record a one in the
F[1,which(colnames(F)=="1 3 4") <- F[1,which(colnames(F)=="1 3 4") +1
F[1,which(colnames(F)=="1 3 4") <- F[1,which(colnames(F)=="1 3 4") +1
F[3,which(colnames(F)=="1 1 4") <- F[3,which(colnames(F)=="1 1 4") +1
F[4,which(colnames(F)=="1 1 3") <- F[4,which(colnames(F)=="1 1 3")+1
It means that each row add 4 ones to the frequency matrix in the 4 row of it and it may be the same for 2,3 or 4 of it. For example because of one is repeated in the row one, adds two records to F[1,which(colnames(F)=="1 3 4")
I'm not sure I understand, and if I do, then you are not doing this correctly because you are not properly ordering your triplets, so this is a guess. I'm thinking the vector c(3,1,4) should be different than the vector c(1,3,4). Correct me if I'm wrong about that.
I thought trying to work with a 20^4 array was rather excessive so I constructed an input matrix that would fit in a 5^4 array:
X <- matrix(c(1, 3, 1 ,4,
2, 5, 2 ,1,
3, 2, 5 ,4), 3,4, byrow=T)
We produce the combinations of 4 items taken three at a time from each row and arrange it in column major fashion:
array( apply( X, 1, function(x) combn(x, 3) ), dim=c(3,4,3) )
, , 1
[,1] [,2] [,3] [,4]
[1,] 1 1 1 3
[2,] 3 3 1 1
[3,] 1 4 4 4
, , 2
[,1] [,2] [,3] [,4]
[1,] 2 2 2 5
[2,] 5 5 2 2
[3,] 2 1 1 1
, , 3
[,1] [,2] [,3] [,4]
[1,] 3 3 3 2
[2,] 2 2 5 5
[3,] 5 4 4 4
I found an elementary answer to my question. But I think it's not quick as well as it should be.
For example I have a matrix in dim (3*4) and for simplicity I suppose I have 5 type only.
so to find the frequncy table for this situation I write the below codes:
n <- 5
k <- dim(X)[1]
F <- matrix(0,n,n^3)
colnames(F) <- simplify2array(apply(expand.grid(1:n,1:n,1:n ), 1, paste, collapse =" ", sep = ""))
for(i in 1:k)
{
for(j in 1:4){
per <- simplify2array(permn(X[i,-j]))
pert_charac <- apply(per,2,paste,sep="",collapse=" ")
num <- sapply(pert_charac,f <- function(x) which(colnames(F)==x))
F[X[i,j],num] <- F[X[i,j],num]+1
}
}

Resources