2016-02-24 17 views
15

Więc w trakcie generowania pewne fałszywe dane, aby odebrać mapę pytanie, znalazłem się na piśmie następujące:Unikanie wielokrotnego for-pętle w R obliczyć macierz

# Generate some fake data 
lat <- seq(-90, 90, by = 5) 
lon <- seq(-180, 180, by = 10) 
phi <- matrix(0, nrow = length(lat), ncol = length(lon)) 
i <- 1 
for (l1 in lat) { 
    j <- 1 
    for (l2 in lon) { 
     phi[i, j] <- (sin(pi * l1/180) * cos(pi * l2/180))^2 
     j <- j+1 
    } 
    i <- i+1 
} 
phi <- 1500*phi + 4500 # scale it properly 

Teraz oczywisto te dwa centralne ciałami pętle nie są takie, jak bym chciał. Wygląda na to, że powinienem być w stanie uzyskać mapply lub coś w tym rodzaju, ale niestety to zwraca listę i tak naprawdę nie robi tego, co chcę. Inne aplikacje również nie wydają się robić dobrze.

Czego mi tu brakuje?

Odpowiedz

17

Powinieneś spróbować użyć algebry macierzy. Nie ma potrzeby stosowania żadnych funkcji z rodziny zastosowania:

lat <- seq(-90, 90, by = 5) 
lon <- seq(-180, 180, by = 10) 
1500 * tcrossprod(sin(pi * lat/180), cos(pi * lon/180))^2 + 4500 
+0

Wszystko dobrze, podoba mi się ten najlepszy :) –

+0

Oryginał był tutaj: http://stackoverflow.com/questions/35592266/raster-image-on-world-map-in-ggplot/35598289#35598289 - Dałem ci wiersz. –

+0

Pozdrawiam! Muszę powiedzieć, że poniższe testy prędkości były dość zaskakujące. – Raad

10

można użyć outer

x = outer(lat, lon, FUN = function(x,y) {(sin(pi * x/180) * cos(pi * y /180))^2}) 
    identical(x * 1500 + 4500, phi) 
# [1] TRUE 

odpowiedź NBATrends wydaje się być szybszy niż inne rozwiązania. Oto niektóre wartości odniesienia:

library(microbenchmark) 
microbenchmark(within(df, { 
    phi <- (sin(pi * lat/180) * cos(pi * lon/180))^2 
    phi <- 1500*phi + 4500 
}), 1500 * tcrossprod(sin(pi * lat/180), cos(pi * lon/180))^2 + 4500, outer(lat, lon, FUN = function(x,y) {(sin(pi * x/180) * cos(pi * y /180))^2}), 
((as.matrix(l1)%*%t(as.matrix(l2)))^2) * 1500 + 4500) 
Unit: microseconds 
                           expr  min  lq  mean median  uq  max neval 
within(df, {  phi <- (sin(pi * lat/180) * cos(pi * lon/180))^2  phi <- 1500 * phi + 4500 }) 255.670 262.0095 270.50948 266.6880 277.7060 385.467 100 
            1500 * tcrossprod(sin(pi * lat/180), cos(pi * lon/180))^2 + 4500 11.471 12.3770 22.30177 12.9805 13.5850 868.130 100 
       outer(lat, lon, FUN = function(x, y) {  (sin(pi * x/180) * cos(pi * y/180))^2 }) 137.645 139.7590 144.39520 141.5700 145.1925 179.905 100 
              ((as.matrix(l1) %*% t(as.matrix(l2)))^2) * 1500 + 4500 16.301 17.6595 20.20390 19.6215 20.5270 80.294 100 
+0

przez wiele zbyt. Ciekawy. –

+1

Teraz, gdy miałem już czas, aby się nad tym zastanowić, ta odpowiedź (używając "zewnętrznego") jest pod wieloma względami lepszą i bardziej ogólną odpowiedzią, ponieważ możemy ustawić dowolną funkcję dla x i y, a 'crossprod' naprawdę po prostu funkcje, które są produktem wielowymiarowym. 'crossprod' jest o wiele szybszy tam, gdzie ma zastosowanie, a dla mojego szczególnego problemu' crossprod' doskonale pasuje, więc nie poprawię poprawnej odpowiedzi i zostawiam to na tej notatce. –

5

Dlaczego należy dołączyć do struktury macierzy i używać, aby móc wektoryzować?

df <- expand.grid(lat = seq(-90, 90, by = 5), 
       lon = seq(-180, 180, by = 10)) 
df <- within(df, { 
    phi <- (sin(pi * lat/180) * cos(pi * lon/180))^2 
    phi <- 1500*phi + 4500 
    }) 

Zawsze można przekonwertować z powrotem, postępując zgodnie z instrukcjami: here.

7

algebra liniowa może być prostsze do zastosowania, ponieważ są po prostu mnożąc element mądry dwa wektory, które mogą być wykonywane przez V * U^T. W R mnożenie macierzy wynosi %*%.

lat <- seq(-90, 90, by = 5) 
lon <- seq(-180, 180, by = 10) 

l1 <- sin(pi * lat/180) 
l2 <- s(pi * lon/ 180) 

# compute the matrix 
phi <- as.matrix(l1)%*%t(as.matrix(l2)) 
# square each element of the matrix 
phi <- phi^2 
# scale properly 
# square each element of the matrix 
phi <- 1500*phi + 4500 
4

Korzystanie sapply(), ale wolałbym outer() rozwiązanie:

#using sapply 
phi_1 <- 
    t(
    sapply(lat, function(l1) 
     sapply(lon, function(l2)(sin(pi * l1/180) * cos(pi * l2/180))^2)) 
) * 1500 + 4500 

#compare result 
identical(phi_1, phi) 
# [1] TRUE 
Powiązane problemy