Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Grab triangles within a lower triangle

Tags:

r

I have the need to grab all the three element triangles that make up the lower triangle of a symmetric matrix. I can not think of how to grab all these pieces in the order of far left column working down and then next column to the right and so on. I know that the numbe rof mini triangles inside of the lower triangle is:

n = x(x - 1)/2
where: x = nrow(mats[[i]])

Here I've created three matrices with letters (it's easier for me to conceptualize this way) and the elements in the order I'm looking for:

FUN <- function(n) {
    matrix(LETTERS[1:(n*n)], n)
}

mats <- lapply(3:5, FUN)

So this is the output I'd like to get (I put it in code rather than output format) for each of the matrices created above:

list(c("B", "C", "F"))

list(c("B", "C", "G"), c("C", "D", "H"), c("G", "H", "L"))

list(c("B", "C", "H"), c("C", "D", "I"), c("D", "E", "J"), 
    c("H", "I", "N"), c("I", "J", "O"), c("N", "O", "T"))

How can I do this task in the fastest manner possible while staying in base R?

Not sure if this visual of what I'm after is helpful but it may be:

enter image description here

like image 214
Tyler Rinker Avatar asked Dec 07 '12 03:12

Tyler Rinker


1 Answers

Nice problem! Here is how you can solve it using a bit of recursion (followed by a MUCH simpler version)

triangle <- function(base.idx, mat) {
    upper.idx <- base.idx - 1L
    right.idx <- base.idx + nrow(mat)
    paste(mat[c(upper.idx, base.idx, right.idx)], collapse = " ")
}

get.triangles <- function(mat) {
    N <- nrow(mat)
    if (N == 3L) {
        return(triangle(3L, mat))
    } else {
        left.idx  <- 3:N
        right.mat <- mat[2:N, 2:N]
        left.triangles  <- sapply(left.idx, triangle, mat)
        right.triangles <- Recall(right.mat) 
        return(c(left.triangles, right.triangles))
    }
}

x <- lapply(mats, get.triangles)

# [[1]]
# [1] "B C F"
# 
# [[2]]
# [1] "B C G" "C D H" "G H L"
# 
# [[3]]
# [1] "B C H" "C D I" "D E J" "H I N" "I J O" "N O T"

I'll just comment on the output not being exactly as you asked. It is because creating recursive functions that return a flat list are always difficult to work with: somehow you always end up with nested lists...

So the last step should be:

lapply(x, strsplit, split = " ")

and it will be in the same format you asked for.


And here is an even simpler version (forget about recursion!)

get.triangles <- function(mat) {
    base.idx  <- seq_along(mat)[row(mat) > col(mat) + 1]
    upper.idx <- base.idx - 1L
    right.idx <- base.idx + nrow(mat)

    lapply(mapply(c, upper.idx, base.idx, right.idx, SIMPLIFY = FALSE),
           function(i)mat[i])
}
like image 188
flodel Avatar answered Sep 27 '22 19:09

flodel