2012-12-07 20 views
7

Mam potrzebę chwycić wszystkie trzy trójkąty elementów, które tworzą dolny trójkąt symetrycznej macierzy. Nie mogę wymyślić, jak złapać wszystkie te elementy w kolejności od lewej do lewej kolumny, a następnie następnej kolumny po prawej i tak dalej. Wiem, że numbe ROF mini trójkąty wewnątrz dolnego trójkąta jest:Złap trójkąty w niższym trójkącie

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

Tu Utworzyliśmy trzy matryce z liter (to jest mi łatwiej konceptualizacji ten sposób) i elementami w kolejności jestem Szukam:

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

mats <- lapply(3:5, FUN) 

Więc to jest wyjście Chciałbym dostać (I umieścić go w kodzie zamiast format) dla każdej z matryc stworzony powyżej:

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")) 

Jak mogę czy t jego zadanie w najszybszy możliwy sposób podczas pobytu w bazie R?

Nie wiem, czy to wizualny, co jestem po to pomocne, ale mogą to być:

enter image description here

+0

jest macierzą 5x5 największą że można oczekiwać, aby przetestować? –

+0

Nie, nie może być większy (choć mam wielką wątpliwość, czy kiedykolwiek byłby znacznie większy). –

+0

@ TylerRinker - Po prostu musiałem zamknąć sesję R podczas próby wykonania testu porównawczego na macierzy 10K * 10K. 1K * 1K to kwestia kilku sekund. Zastanawiam się, czy ludzie na zewnątrz mogą mieć bardziej wydajne wdrożenia. – thelatemail

Odpowiedz

5

Nicea problem! Oto w jaki sposób można go rozwiązać przy użyciu odrobiny rekursji (po której znacznie prostsza wersja)

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" 

będę tylko komentarz na wyjście nie jest dokładnie tak, jak prosiłeś. To właśnie z powodu tworzenia rekurencyjne funkcje zwracające listę płaskie są zawsze trudne do pracy z: jakoś zawsze skończyć z zagnieżdżonych list ...

więc ostatnim krokiem powinno być:

lapply(x, strsplit, split = " ") 

i będzie być w tym samym formacie, o jaki prosiłeś.


I tu jest jeszcze prostsza wersja (zapomnieć o rekursji!)

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]) 
} 
+0

dziękuję, że działa bardzo ładnie. Wykorzystam tę metodę bez rekurencji, ponieważ nie ma potrzeby używania 'strsplit' (a jeśli jest to macierz numeryczna, nie trzeba używać' as.numeric'). +1 –

3

Edited by dodać SIMPLIFY=FALSE które teraz daje dokładnie to, co chcesz:

Zasadniczo metoda ta dostaje indeksy wszystkich górnych lewych rogów trójkątów, które chcesz, a następnie chwyta [komórkę poniżej] + [komórkę poniżej + na prawo]. Dreszczyk. Jedną dodatkową zaletą tej metody jest to, że działa ona dla obiektów o rozmiarach matrix i .

bot.tris <- function(data) { 
    idx1 <- unlist(sapply((nrow(data)-2):1,function(x) tail(2:(nrow(data)-1),x))) 
    idx2 <- rep(1:(nrow(data)-2),(nrow(data)-2):1) 
    mapply(function(x,y) {c(data[x,y],data[x+1,y],data[x+1,y+1])},idx1,idx2,SIMPLIFY=FALSE) 
} 

A wynik:

> result <- lapply(mats,bot.tris) 
> str(result) 
List of 3 
$ :List of 1 
    ..$ : chr [1:3] "B" "C" "F" 
$ :List of 3 
    ..$ : chr [1:3] "B" "C" "G" 
    ..$ : chr [1:3] "C" "D" "H" 
    ..$ : chr [1:3] "G" "H" "L" 
$ :List of 6 
    ..$ : chr [1:3] "B" "C" "H" 
    ..$ : chr [1:3] "C" "D" "I" 
    ..$ : chr [1:3] "D" "E" "J" 
    ..$ : chr [1:3] "H" "I" "N" 
    ..$ : chr [1:3] "I" "J" "O" 
    ..$ : chr [1:3] "N" "O" "T" 
+1

To podejście jest zdecydowanie mniej kodowane i bardzo łatwe do zrozumienia.Porównywałem obie odpowiedzi, a flodel jest szybsza. Obie funkcje tutaj są szybsze niż to, co miałem (nic). Dziękuję bardzo za rozwiązanie problemu. +1 –

+0

* "Obie funkcje tutaj są szybsze niż to, co miałem (nic)" * - Podoba mi się to :-) – thelatemail