避免R中的多个for循环来计算矩阵

Mik*_*ise 15 for-loop r matrix mapply

因此,在生成一些假数据来回答地图问题的过程中,我发现自己写了以下内容:

# 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
Run Code Online (Sandbox Code Playgroud)

现在显然这两个中心for循环并不像我想的那样R'ish.看起来我应该能够得到一个mapply或者什么来做这项工作,但遗憾的是,它返回一个列表,并没有真正做我想要的.其他适用似乎也没有做正确的事情.

我在这里错过了什么?

Raa*_*aad 17

你应该尝试使用矩阵代数.无需使用apply系列中的任何功能:

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


Mam*_*zal 10

您可以使用 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
Run Code Online (Sandbox Code Playgroud)

NBATrends的答案似乎比其他解决方案更快.这里有一些基准

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
Run Code Online (Sandbox Code Playgroud)

  • 现在我有时间仔细考虑一下,这个答案(使用 `outer`)在很多方面都是更好和更通用的答案,因为我们可以为 x 和 y 放置任意函数,而 `crossprod` 实际上只是执行函数那是一种多重产品。`crossprod` 在它适用的地方要快得多,而且对于我的特定问题 `crossprod` 非常适合,所以我不会调整正确的答案,并将其留在本笔记中。 (2认同)

Mat*_*u B 7

线性代数对于您的应用可能更简单,因为您只是将元素两个向量相乘,这可以通过v*u ^ T来完成.在R中,矩阵乘法是%*%.

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  
Run Code Online (Sandbox Code Playgroud)


seb*_*n-c 5

为什么要附加到矩阵结构并在使用矢量时使用apply?

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
  })
Run Code Online (Sandbox Code Playgroud)

您可以随时使用此处的说明进行转换.