amisos55
amisos55

Reputation: 1979

How to get element order information from a lower triangle of a matrix in r

I have a 5x5 covariance matrix as below:

unique.items <- c(1,2,3,4)
diag <- rep("Free",length(unique.items)+1)
offdiag <- rep("0.0", (length(unique.items)+1)*length(unique.items)/2 )
m <- matrix(NA, ncol = length(diag), nrow = length(diag))
m[lower.tri(m)] <- offdiag
m[upper.tri(m)] <- t(m)[upper.tri(t(m))]
diag(m) <- diag
> m
     [,1]   [,2]   [,3]   [,4]   [,5]  
[1,] "Free" "0.0"  "0.0"  "0.0"  "0.0" 
[2,] "0.0"  "Free" "0.0"  "0.0"  "0.0" 
[3,] "0.0"  "0.0"  "Free" "0.0"  "0.0" 
[4,] "0.0"  "0.0"  "0.0"  "Free" "0.0" 
[5,] "0.0"  "0.0"  "0.0"  "0.0"  "Free"

Then, I get the lower triangle by:

lower.triangle <- paste(tapply(m[lower.tri(m, diag = TRUE)], 
             row(m)[lower.tri(m, diag = TRUE)], FUN = toString), 
      collapse=",")

> lower.triangle
"Free,
 0.0, Free,
 0.0, 0.0, Free,
 0.0, 0.0, 0.0, Free,
 0.0, 0.0, 0.0, 0.0, Free"

I need to generate a character variable including a grouping variable as below.

group <- c(1,2,3,4)

desired output

"Equal = (G4, Covariance[2]), (G1, Covariance[2]), (G2, Covariance[2]), (G3, Covariance[2]);
 Equal = (G4, Covariance[5]), (G1, Covariance[5]), (G2, Covariance[5]), (G3, Covariance[5]);
 Equal = (G4, Covariance[9]), (G1, Covariance[9]), (G2, Covariance[9]), (G3, Covariance[9]);
 Equal = (G4, Covariance[14]), (G1, Covariance[14]), (G2, Covariance[14]), (G3, Covariance[14]);"

G1, G2, G3, and G4 are for grouping. The numbers in Covariance[#] are for the order of the diagonal elements in the lower triangle of the matrix.

  1. The first (Free) element's order is 0 in the lower.triangle object. I do not need that in the desired output.
  2. The third element's ( Free) order number should be then 2 because the order starts with 0.
  3. So the order numbers for the diagonal elements are 2,5,9,14 that need to be in Covariance[#].

Any help is appreciated. Thanks!

Upvotes: 0

Views: 166

Answers (2)

Ronak Shah
Ronak Shah

Reputation: 389185

Set the upper triangular matrix to NA and get the index of 'Free' values in m omitting NA values. Use that index to produce the text that you want using paste0 and sprintf.

group <- c(4,1:3)
m[upper.tri(m)] <- NA
inds <- which(na.omit(c(t(m))) == 'Free')[-1] - 1
#first -1 because you want to ignore first 'Free' and 
#second -1 because indexing start from 0 in your case.
inds
#[1]  2  5  9 14

sapply(inds, function(x)paste0('Equal = ', 
         paste0(sprintf('(G%d, Covariance[%d])', group, x), collapse = " , ")))

#[1] "Equal = (G4, Covariance[2]) , (G1, Covariance[2]) , (G2, Covariance[2]) , (G3, Covariance[2])"    
#[2] "Equal = (G4, Covariance[5]) , (G1, Covariance[5]) , (G2, Covariance[5]) , (G3, Covariance[5])"    
#[3] "Equal = (G4, Covariance[9]) , (G1, Covariance[9]) , (G2, Covariance[9]) , (G3, Covariance[9])"    
#[4] "Equal = (G4, Covariance[14]) , (G1, Covariance[14]) , (G2, Covariance[14]) , (G3, Covariance[14])"

Upvotes: 1

DaveArmstrong
DaveArmstrong

Reputation: 22024

The data statement above makes a 6x6 matrix, so I edited it to make a 5x5 instead, to replicate what you've got above. Then, by using the upper triangle, it's actually easier to find the order number of the free elements.

unique.items <- c(1,2,3,4,5)
diag <- rep("Free",length(unique.items))
offdiag <- rep("0.0", (length(unique.items)-1)*length(unique.items)/2 )
m <- matrix(NA, ncol = length(diag), nrow = length(diag))
m[lower.tri(m)] <- offdiag
m[upper.tri(m)] <- t(m)[upper.tri(t(m))]
diag(m) <- diag

Since you don't want the first element, we could do the following:

ut <- m[,-1][upper.tri(m, diag=TRUE)[,-1]]
ut
# [1] "0.0"  "Free" "0.0"  "0.0"  "Free" "0.0"  "0.0"  "0.0"  "Free" "0.0"  "0.0"  "0.0" 
# [13] "0.0"  "Free"

This removes the first column from m and then finds the upper triangle of m, but cuts the first column of the output off. Next, just find which observations are "Free" and those are the order numbers we're calling inds.

inds <- which(ut == "Free")

We then can define the group variable. We can also define the two pieces of each of the string - the group and the covariance statement.

group <- c(1,2,3,4)
eg <- expand.grid(group = paste0("G", group), cov=paste0(" Covariance[", inds, "]"))
head(eg)
#   group            cov
# 1    G1  Covariance[2]
# 2    G2  Covariance[2]
# 3    G3  Covariance[2]
# 4    G4  Covariance[2]
# 5    G1  Covariance[5]
# 6    G2  Covariance[5]

Next, we split the data frame based on the cov variable, so that all of the same covariance groups are together.

eg <- split(eg, eg$cov)

eg is now a list with four groups, one for each covariance group.

Now, with a bunch of paste statements, we could put all of the pieces together.

## collapses all of the pasted statements together by a new-line character \n
out <- paste(
  ## does the paste functions to each element of the list
  sapply(eg, function(x)
    ## puts Equal = and ; around the result below
    paste0("Equal = ", 
      ## pastes the (G#, covariance[#]) together and collapses by a ,
      paste(
        ## makes (G#, covariance[#])
        paste0("(", x$group, ",", x$cov, ")"), 
        collapse=", "), 
       ";")
    ), 
  collapse="\n")
cat(out)
# Equal = (G1, Covariance[2]), (G2, Covariance[2]), (G3, Covariance[2]), (G4, Covariance[2]);
# Equal = (G1, Covariance[5]), (G2, Covariance[5]), (G3, Covariance[5]), (G4, Covariance[5]);
# Equal = (G1, Covariance[9]), (G2, Covariance[9]), (G3, Covariance[9]), (G4, Covariance[9]);
# Equal = (G1, Covariance[14]), (G2, Covariance[14]), (G3, Covariance[14]), (G4, Covariance[14]);

Upvotes: 1

Related Questions