2015-05-04 6 views
8

Ich versuche alle möglichen Zeichen einzufügen, die in einer beliebigen Diagonalen innerhalb einer N * N Matrix angeordnet sind.Fügen Sie alle möglichen Diagonalen einer n * n Matrix oder eines Datenrahmens ein.

Betrachten wir zum Beispiel die folgenden 3 X 3-Matrix:

#Create matrix, convert to character dataframe 
matrix <- matrix(data=c('s','t','y','a','e','l','f','n','e'),nrow=3,ncol=3) 
matrix <- as.data.frame(matrix) 
for(i in 1:length(colnames(matrix))){ 
    matrix[,i] <- as.character(matrix[,i]) 
} 

In der Matrix oben habe ich die Diagonalen müssen einzufügen: "sehen", "schrullig", "EES" und "YEF". Ich kann diese in dem Datenrahmen mit dem folgenden Code finden:

diag <- paste(matrix[1,1],matrix[2,2],matrix[3,3],sep='') 
diag1 <- paste(matrix[1,3],matrix[2,2],matrix[3,1],sep='') 
diag2 <- paste(matrix[3,1],matrix[2,2],matrix[1,3],sep='') 
diag3 <- paste(matrix[3,3],matrix[2,2],matrix[1,1],sep='') 

Das Problem ist, dass ich mag, dies automatisieren, so dass es auf jeder N x N-Matrix arbeiten. (Ich schreibe eine Funktion, um die Diagonalen in jeder N X N-Matrix zu finden). Gibt es einen effizienten Weg, dies zu tun?

+1

Um Ihre ursprünglichen Daten zu erstellen, tun 'Matrix <- data.frame (Matrix (c ('s', 't', 'y', 'a',‘ e ',' l ',' f ',' n ',' e '), ncol = 3), stringsAsFactors = FALSE) ' –

+5

Wahrscheinlich möchte ich es nicht' matrix' nennen, da das auch ein Funktionsname ist. – Frank

Antwort

10

Oh, das ist einfach, wenn Sie Matrix anstelle von data.frame :) Wir haben gerade Matrixelemente können wählen, wie wir Vektorelemente nehmen können:

matrix[1:3] # First three elements == first column 

n <- ncol(matrix) 
(1:n-1)*n+1:n 
## [1] 1 5 9 
(1:n-1)*n+n:1 
## [1] 3 5 7 

So, jetzt können wir diese verwenden:

matrix[(1:n-1)*n+1:n] 
[1] "s" "e" "e" 
paste0(matrix[(1:n-1)*n+1:n],collapse="") 
[1] "see" 

Und wenn Sie es zurück wollen, Reverse nur der Vektor der IndizesmitFunktion:

paste0(matrix[rev((1:n-1)*n+1:n)],collapse="") 
[1] "ees" 

Einige Benchmarks:

rotate <- function(x) t(apply(x, 2, rev)) 
revMat <- function(mat, dir=0){ 
    x <- if(bitwAnd(dir,1)) rev(seq(nrow(mat))) else seq(nrow(mat)) 
    y <- if(bitwAnd(dir,2)) rev(seq(ncol(mat))) else seq(nrow(mat)) 
    mat[x,y] 
} 

bartek <- function(matrix){ 
    n <- ncol(matrix) 
    c(paste0(matrix[(1:n-1)*n+1:n],collapse=""), paste0(matrix[rev((1:n-1)*n+1:n)],collapse=""), 
     paste0(matrix[(1:n-1)*n+n:1],collapse=""), paste0(matrix[rev((1:n-1)*n+n:1)],collapse="")) 
} 

Joe <- function(matrix){ 
    diag0 <- diag(matrix) 
    diag1 <- diag(rotate(matrix)) 
    diag2 <- rev(diag0) 
    diag3 <- rev(diag1) 
    c(paste(diag0, collapse = ""),paste(diag1, collapse = ""), 
     paste(diag2, collapse = ""),paste(diag3, collapse = "")) 
} 

James <- function(mat){ 
    sapply(0:3,function(x) paste(diag(revMat(mat,x)),collapse="")) 
} 

matrix <- matrix(c('s','t','y','a','e','l','f','n','e'), ncol = 3) 

microbenchmark(bartek(matrix), Joe(matrix), James(matrix)) 
Unit: microseconds 
      expr  min  lq  mean median  uq  max neval 
bartek(matrix) 50.273 55.2595 60.78952 59.4390 62.438 134.880 100 
    Joe(matrix) 167.431 176.6170 188.46908 182.8260 192.646 337.717 100 
    James(matrix) 321.313 334.3350 346.15230 339.7235 348.565 447.115 100 


matrix <- matrix(1:10000, ncol=100) 
microbenchmark(bartek(matrix), Joe(matrix), James(matrix)) 
Unit: microseconds 
      expr  min  lq  mean median  uq  max neval 
bartek(matrix) 314.385 326.752 336.1194 331.936 337.9805 423.323 100 
    Joe(matrix) 2168.141 2221.477 2460.1002 2257.439 2298.4400 8856.482 100 
    James(matrix) 1200.572 1250.354 1407.5943 1276.307 1323.8845 7419.931 100 
+3

Wenn Sie die Geschwindigkeit testen möchten, können Sie den Paste-Teil weglassen: 'bartvec <- function (m) {n <- ncol (m); Liste (m [(1: n-1) * n + 1: n], m [rev ((1: n-1) * n + 1: n)], m [(1: n-1) * n + n: 1], m [rev ((1: n-1) * n + n: 1)])}; bartvec2 <- Funktion (m) {n <- ncol (m); v1 <- m [(1: n-1) * n + 1: n]; v2 <-m [rev ((1: n-1) * n + 1: n)]; Liste (v1, rev (v1), v2, rev (v2))}; bartmat <- Funktion (m) {n <- ncol (m); ix <- 1: n; v1 <- m [cbind (ix, ix)]; v2 <- m [cbind (ix, rev (ix)) ]; Liste (v1, rev (v1), v2, rev (v2))}; microbenchmark (bartvec (Matte), bartvec2 (Matte), bartmat (Matte)) 'wo' nc <- 1e4; mat <- matrix (Beispiel (Buchstaben, nc^2, ersetzen = TRUE), ncol = nc) ' – Frank

+1

Ich sehe Matrix-Subsetting etwas besser als Vektor-Subsetting, wie 10-15%. Jedes Ergebnis könnte durch 'sapply (res, paste0, collapse =" ")" – Frank

+3

übergeben werden Related: http://StackOverflow.com/Questions/20489636/the-diag-Function-in-Rr stellt sich heraus, 'diag' ist die schlechteste optimierte Basisfunktion, die ich je gesehen habe. – Frank

3

Für eine Matrix kann dies erreicht werden, indem die diag der vier möglichen Rotationen genommen wird. Wenn Sie eine Drehfunktion wie folgt festgelegt (credit), dies einfach wird:

> rotate <- function(x) t(apply(x, 2, rev)) 
> diag0 <- paste(diag(matrix), collapse = "") 
> diag1 <- paste(diag(rotate(matrix)), collapse = "") 
> diag2 <- paste(diag(rotate(rotate(matrix))), collapse = "") 
> diag3 <- paste(diag(rotate(rotate(rotate(matrix)))), collapse = "") 
> diag0 
[1] "see" 
> diag1 
[1] "yef" 
> diag2 
[1] "ees" 
> diag3 
[1] "fey" 

Wie Frank in Kommentaren darauf hingewiesen, dies für ausreichend große Matrizen langsam werden kann (auf meiner Maschine, rotate beginnt länger dauern als etwa eine Sekunde für Matrizen größer als 1000 X 1000). Sie können mithilfe rev vor dem Einfügen, zum Beispiel einige Zeit sparen:

> diag0 <- diag(matrix) 
> diag1 <- diag(rotate(matrix)) 
> diag2 <- rev(diag0) 
> diag3 <- rev(diag1) 
> paste(diag2, collapse = "") 
[1] "ees" 
> paste(diag3, collapse = "") 
[1] "fey" 
+3

Rotieren könnte teuer sein. Du könntest es mit zwei diags tun und dann ihre Ergebnisse umkehren, wie "yef" == "fey", du weißt – Frank

+1

@ Frank-fair.Wenn ich nur die vier Rotationen anführe, wird es mir klar, was der Code eigentlich tun soll, was normalerweise wichtiger ist als die Effizienz ... aber bei sehr großen Matrizen kann die Umkehr viel Zeit sparen. Ich werde es entsprechend aktualisieren. – Joe

3

Eine Möglichkeit ist diag auf der Matrix zu verwenden, die so genannte hier mat mit dem Funktionsnamen zu vermeiden Kollision, und die Umkehrung der Zeilen- und/oder Spalten Aufträge um jede Diagonale und Richtung zu bekommen.

Sie können es mit einer zusätzlichen Funktion tun, um die Umkehrungen systematisch zu machen, so dass Sie zu Schleife verwenden können.

revMat <- function(mat, dir=0) 
{ 
    x <- if(bitwAnd(dir,1)) rev(seq(nrow(mat))) else seq(nrow(mat)) 
    y <- if(bitwAnd(dir,2)) rev(seq(ncol(mat))) else seq(nrow(mat)) 
    mat[x,y] 
} 

sapply(0:3,function(x) paste(diag(revMat(mat,x)),collapse="")) 
[1] "see" "yef" "fey" "ees" 
+2

Ich denke, dass Umkehr-Matrix ist so teuer wie rotierende :) – bartektartanus

+0

@bartektanus Ja, obwohl es scheint, Ihre Benchmark schlägt vor, dass es Größe abhängig ist. – James

3

Convert matrix eine tatsächliche Matrix m (im Gegensatz zu einem Datenrahmen entgegengesetzt). Dann werden die vier Diagonalen sind:

m <- as.matrix(matrix) 
ix <- ncol(m):1 

paste(diag(m), collapse = "") 
paste(diag(m[ix,]), collapse = "") 
paste(diag(m[,ix]), collapse = "") 
paste(diag(m[ix, ix]), collapse = "")