2016-02-06 20 views
5

So che il miglioramento del ciclo è stato richiesto molte volte prima. Possiamo applicare funzioni familiari per migliorare il ciclo for in R.Modi per migliorare il ciclo per le manipolazioni di matrici in base a un'altra matrice

Tuttavia esiste un modo per migliorare le manipolazioni di una matrice in cui tali manipolazioni dipendono da un'altra matrice? Quello che voglio dire è questo, in cui gli elementi sono impostato su 2 in test sono basati su un'altra matrice index:

for (i in 1:nrow(test)){ 
    test[i,index[i,]] <- 2 
} # where index is predetermined matrix 

Un altro esempio è questo, dove regolano i valori test sulla base l'ordinamento di elementi nella righe di un'altra matrice anyMatrix:

for (i in 1:nrow(test)){ 
    test[i,] <- order(anyMatrix[i,]) 
} 

ho potuto usare lapply o sapply qui ma restituire un elenco e ci vuole stessa quantità di tempo per convertire nuovamente alla matrice.

esempio riproducibile:

test <- matrix(0, nrow = 10, ncol = 10) 
set.seed(1234) 
index <- matrix(sample.int(10, 10*10, TRUE), 10, 10) 
anyMatrix <- matrix(rnorm(10*10), nrow = 10, ncol = 10) 

for (i in 1:nrow(test)){ 
    test[i,index[i,]] <- 2 
} 

for (i in 1:nrow(test)){ 
    test[i,] <- order(anyMatrix[i,]) 
} 

risposta

6

sembrate veramente avere due problemi separati qui.

Problema 1: Data una matrice index, per ciascuna riga e colonna ij si desidera impostare test[i,j] a 2 se j appare in fila i di index. Questo può essere fatto con l'indicizzazione a matrice semplice, passando una matrice a 2 colonne di indici in cui la prima colonna è le righe di tutti gli elementi che si desidera indicizzare e la seconda colonna è le colonne di tutti gli elementi che si desidera indicizzare:

test[cbind(as.vector(row(index)), as.vector(index))] <- 2 
test 
#  [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] 
# [1,] 2 2 0 2 2 2 2 0 2  2 
# [2,] 2 0 2 2 2 2 2 0 2  2 
# [3,] 2 2 2 2 0 0 2 2 0  0 
# [4,] 2 2 0 0 0 2 2 2 0  2 
# [5,] 2 2 2 2 0 0 0 0 2  0 
# [6,] 0 0 0 0 0 2 2 2 2  0 
# [7,] 2 0 2 2 2 2 2 0 0  0 
# [8,] 2 0 2 2 2 2 0 2 0  2 
# [9,] 2 2 2 2 0 0 2 0 2  2 
# [10,] 2 0 2 0 0 2 2 2 2  0 

Poiché esegue tutte le operazioni in una singola operazione vettoriale, dovrebbe essere più veloce di eseguire il ciclo delle righe e gestirle singolarmente. Ecco un esempio con 1 milione di righe e 10 colonne:

OP <- function(test, index) { 
    for (i in 1:nrow(test)){ 
    test[i,index[i,]] <- 2 
    } 
    test 
} 
josliber <- function(test, index) { 
    test[cbind(as.vector(row(index)), as.vector(index))] <- 2 
    test 
} 
test.big <- matrix(0, nrow = 1000000, ncol = 10) 
set.seed(1234) 
index.big <- matrix(sample.int(10, 1000000*10, TRUE), 1000000, 10) 
identical(OP(test.big, index.big), josliber(test.big, index.big)) 
# [1] TRUE 
system.time(OP(test.big, index.big)) 
# user system elapsed 
# 1.564 0.014 1.591 
system.time(josliber(test.big, index.big)) 
# user system elapsed 
# 0.408 0.034 0.444 

qui, l'approccio vettorizzati è 3.5x più veloce.

Problema 2: si desidera impostare fila i di test per order applicata al corrispondente fila di anyMatrix. È possibile farlo con apply:

(test <- t(apply(anyMatrix, 1, order))) 
#  [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] 
# [1,] 1 10 7 8 4 5 3 6 2  9 
# [2,] 8 7 1 6 3 4 9 5 10  2 
# [3,] 4 9 7 1 3 2 6 10 5  8 
# [4,] 1 2 6 4 10 3 9 8 7  5 
# [5,] 9 6 5 1 2 7 10 4 8  3 
# [6,] 9 3 8 6 5 10 1 4 7  2 
# [7,] 3 7 2 5 6 8 9 4 1 10 
# [8,] 9 8 1 3 4 6 7 10 5  2 
# [9,] 8 4 3 6 10 7 9 5 2  1 
# [10,] 4 1 9 3 6 7 8 2 10  5 

non mi aspetto molto di un cambiamento in fase di esecuzione qui, perché apply è in realtà solo scorrendo le righe in modo simile a come siete stati loop nella vostra soluzione. Tuttavia, preferirei questa soluzione perché è molto meno la digitazione e il modo più "R" di fare le cose.

Si noti che entrambe queste applicazioni utilizzavano codice piuttosto diverso, che è piuttosto tipico nella manipolazione dei dati R: ci sono molti operatori specializzati diversi e bisogna scegliere quello giusto per la propria attività. Non penso che esista una singola funzione o addirittura un piccolo insieme di funzioni che saranno in grado di gestire tutte le manipolazioni di matrici in cui tale manipolazione si basa su dati di un'altra matrice.

+0

grazie, ma come è cbind più veloce nel primo? Non ci vorrà più tempo del solito ciclo? Hai un punto di riferimento? – rmania

+0

@rmania Ho aggiornato questa risposta per includere un benchmark che mostra che le operazioni di indicizzazione vettorizzate producono aumenti di velocità rispetto alle alternative di looping. In R, la sostituzione di molte operazioni rapide ripetute con una singola operazione che le esegue tutte insieme comporta spesso massicci aumenti di velocità. – josliber