2016-02-06 26 views
5

Wiem, że poprawianie pętli zostało zadane wiele razy wcześniej. Możemy zastosować funkcje rodziny w celu poprawy pętli for w R.Sposoby poprawy pętli dla manipulacji matrycą w zależności od innej macierzy

Czy istnieje jednak sposób na ulepszenie manipulacji matrycą, w której te manipulacje zależą od innej macierzy? Co mam na myśli tutaj jest to, gdzie elementy ustawić do 2 w test oparte są na innej matrycy index:

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

Innym przykładem jest to, gdzie mogę ustawić wartości w test podstawie uporządkowania elementów w wiersze z innej matrycy anyMatrix:

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

mogę użyć lapply lub sapply tu jednak zwrócić listę i zajmuje tyle samo czasu, aby przekształcić go z powrotem do macierzy.

Powtarzalne przykład:

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

Odpowiedz

6

Naprawdę wydaje się mieć dwa oddzielne problemy tutaj.

Problem 1: Biorąc pod uwagę macierz index, dla każdego wiersza i kolumny ij chcesz ustawić test[i,j] do 2 jeśli j pojawia się w wierszu i z index. Można to zrobić za pomocą prostego indeksowania macierzy, przechodząc przez 2-kolumnową macierz indeksów, gdzie pierwsza kolumna jest rzędem wszystkich elementów, które chcesz indeksować, a druga kolumna jest kolumnami wszystkich elementów, które chcesz indeksować:

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 

Ponieważ wykonuje to wszystkie operacje w pojedynczej operacji wektorowej, powinna być szybsza niż przechodzenie między wierszami i obsługa ich pojedynczo. Oto przykład z 1 milionem wierszy i 10 kolumnami:

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 

W tym przypadku podejście wektoryzacji jest 3.5x szybsze.

Problem 2: Chcesz ustawić wiersz i z test do order stosowanej do odpowiedniego rzędu anyMatrix. Można to zrobić z 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 

nie spodziewałbym się wielkich zmian w czasie pracy tutaj, ponieważ apply jest naprawdę zapętlenie poprzez rzędy podobnie do tego, jak zostałeś zapętlenie w roztworze. Mimo to wolałbym to rozwiązanie, ponieważ jest to znacznie mniej pisania i bardziej "R" sposób robienia rzeczy.

Zauważ, że obie te aplikacje używają całkiem innego kodu, co jest dość typowe w manipulowaniu danymi R - istnieje wiele różnych wyspecjalizowanych operatorów i musisz wybrać ten, który jest odpowiedni do Twojego zadania. Nie sądzę, żeby istniała jakaś pojedyncza funkcja, a nawet naprawdę mały zestaw funkcji, które będą w stanie obsłużyć wszystkie manipulacje matrycą, gdzie ta manipulacja opiera się na danych z innej macierzy.

+0

dzięki, ale w jaki sposób cind jest szybszy w pierwszym? Wont cbind zajmuje więcej czasu niż zwykła pętla? Czy masz benchmark? – rmania

+0

@rmania Zaktualizowałem tę odpowiedź, aby uwzględnić wskaźnik pokazujący, że wektoryzowane operacje indeksowania przyniosą przyspieszenia w porównaniu do alternatyw z zapętleniem. W R, zastąpienie wielu powtarzających się szybkich operacji pojedynczą operacją, która wykonuje je wszystkie razem, często powoduje ogromne przyspieszenia. – josliber