2015-05-04 17 views
8

Sto tentando di incollare tutti i possibili caratteri disposti in qualsiasi diagonale all'interno di una matrice N * N.Incolla tutte le possibili diagonali di una matrice n * n o dataframe

Ad esempio, si consideri il seguente matrice 3 x 3:

#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]) 
} 

Nella matrice sopra devo incollare le diagonali: "vedere", "fey", "SEO" e "YEF". Posso trovare questi nel dataframe con il seguente codice:

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='') 

Il problema è che voglio automatizzare questo modo che funziona su qualsiasi matrice N x N. (Sto scrivendo una funzione per trovare le diagonali in una matrice N X N). C'è un modo efficace per farlo?

+1

Al fine di creare i dati originali solo fare 'matrice <- data.frame (matrice (c ('s', 't', 'y', 'a',' e ',' l ',' f ',' n ',' e '), ncol = 3), stringsAsFactors = FALSE) ' –

+5

Probabilmente non si vuole chiamarlo' matrix', poiché è anche un nome di funzione. – Frank

risposta

10

Oh, questo è facile se si utilizza matrice invece di data.frame :) Possiamo scegliere elementi di matrice proprio come possiamo prendere elementi vettoriali:

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 

Così ora possiamo usare questa:

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

E se lo vuoi indietro, solo invertire il vettore di indici usando rev funzione:

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

Alcuni parametri:

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

Se vuoi testare la velocità, puoi lasciare la parte incolla: 'bartvec <- function (m) {n <- ncol (m); Lista (m [(1: n-1) * n + 1: n], m [giri ((1: n-1) * n + 1: n)], m [(1: n-1) * n + n: 1], m [giri ((1: n-1) * n + n: 1)])}; bartvec2 <- function (m) {n <- ncol (m); v1 <- m [(1: n-1) * n + 1: n]; v2 <-m [rev ((1: n-1) * n + 1: n)]; Lista (v1, rev (v1), v2, rev (v2))}; bartmat <- function (m) {n <- ncol (m); ix <- 1: n; v1 <- m [cbind (ix, ix)]; v2 <- m [cbind (ix, rev (ix)) ]; lista (v1, rev (v1), v2, rev (v2))}; microbenchmark (bartvec (mat), bartvec2 (mat), bartmat (mat)) 'dove' nc <- 1e4; mat <- matrix (sample (lettere, nc^2, replace = TRUE), ncol = nc) ' – Frank

+1

Vedo che l'underset matrice è un po 'meglio del subset vettoriale, come 10-15%. Qualsiasi risultato può essere passato attraverso 'sapply (res, paste0, collapse =" ")' – Frank

+3

Correlato: http://stackoverflow.com/questions/20489636/the-diag-function-in-r risulta 'diag' è il funzione di base peggiore-ottimizzata che abbia mai visto. – Frank

3

Per una matrice, ciò può essere ottenuto prendendo il diag delle quattro possibili rotazioni. Se si imposta una funzione di rotazione come segue (credit), questo diventa semplice:

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

Come sottolineato da Frank nei commenti, questo potrebbe diventare lento per sufficientemente grandi matrici (sulla mia macchina, rotate inizia a prendere più a lungo di circa un secondo per matrici maggiori di 1000 X 1000). È possibile risparmiare tempo utilizzando rev prima di incollare, ad esempio:

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

La rotazione potrebbe essere costosa. Potresti farlo con due diags e poi invertire i loro risultati, come "yef" == "fey", sai – Frank

+1

@ Frank-fair.A mio avviso, solo richiamare le quattro rotazioni rende più chiaro ciò che il codice dovrebbe fare, che di solito è più importante dell'efficienza ... ma per inversioni molto grandi delle matrici potrebbe risparmiare una notevole quantità di tempo. Aggiornerò di conseguenza. – Joe

3

Un modo è quello di utilizzare diag sulla matrice, chiamato mat qui per evitare la concomitanza con il nome della funzione, e invertire gli ordini di riga e/o colonna per ottenere ogni diagonale e direzione.

È possibile farlo con una funzione supplementare per rendere sistematiche le inversioni in modo da poter utilizzare sapply per il ciclo.

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

Penso che la matrice di inversione sia costosa quanto la rotazione :) – bartektartanus

+0

@bartektartanus Sì, anche se sembra che il tuo benchmark suggerisca che sia dipendente dalle dimensioni. – James

3

Convertire matrix a matrice reale m (al contrario di un frame di dati). Poi i quattro diagonali sono:

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 = "") 
Problemi correlati