簡體   English   中英

對沿任意維度重復的m維數組和(m-1)維數組進行比較

[英]Making comparisons of a m-dimensional array and an (m-1)-dimensional array repeated along an arbitrary dimension

我已經實現了一個基於多維數組的計算,該計算替代了一些循環代碼。 我在此過程中做了一些我認為可以做得更好的事情-但我不確定如何做。

其中之一是將所得的3d陣列與沿三維重復的2d陣列進行比較。

items12 = c(1,2,3,4,5,6)
items3 = c(1,2,3)

m2d = outer(items12, items12, "-")
m3d = outer(items3, m2d, "*")

經過一些操作后,我想比較m2d和m3d,其中m2d沿第三個暗點重復。 我知道兩個選擇,似乎都不是那么優雅,我很好奇是否有更好的方法。

實例化重復的3d數組。 內存沉重但速度很快。

m2d.z.3d = outer(
  m2d, 
  rep(1, length(items3)), "*"
)

m3d - m2d.z.3d

環。 輕但慢。

apply(m3d, 3, function(x) {
    x - m2d
})

有什么建議么? 您會選擇哪一個?

更新示例闡明了任意索引要求。

items12 = c(1,2,3)
items3 = c(1,2)

m2d = outer(items12, items12, "-")
m3d = outer(m2d,items3, "*")

m3d - (m3d - items.3)

# items.3 wrapped along rows
, , 1

     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    1    2    3
[3,]    1    2    3

, , 2

     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    1    2    3
[3,]    1    2    3

m3d.yx = aperm(m3d, c(2,1,3))
aperm(m3d.yx - (m3d.yx - c(items.3)), c(2,1,3)) 

#items.3 wrapped around columns
, , 1

     [,1] [,2] [,3]
[1,]    1    1    1
[2,]    2    2    2
[3,]    3    3    3

, , 2

     [,1] [,2] [,3]
[1,]    1    1    1
[2,]    2    2    2
[3,]    3    3    3

更新

在這種情況下的一些精子基准。

items.3 = rep(c(1,2,3), n)
items.2 = rep(c(1,2), n)

m2d = outer(items.3, items.3, "-")
m3d = outer(m2d, items.2, "*")

funRecycle = function() # items.3 wraps around the columns (index 1, then 2, then 3 etc.)
  m3d - (m3d - c(items.3)) 
funAperm = function() { # temporarily interchange index 1 and 2 to apply along desired index
  m3d.yx = aperm(m3d, c(2,1,3))
  aperm(m3d.yx - (m3d.yx - c(items.3)), c(2,1,3)) 
}
funOuter = function() { # assign the 3d matrix
  m2d.z.3d = outer(
    m2d, 
    rep(1, length(items.2)), "*"
  )
  m3d - m2d.z.3d
}
funArray = function() { # assign the 3d matrix with array
  m2d.z.3d = array(m2d, dim=c(dim(m2d)[1:2], length(items.2)))
  m3d - m2d.z.3d
}
funSweep <- function() sweep(m3d, c(1, 2), m2d, "-")

n = 1

Unit: microseconds
         expr    min      lq     mean  median      uq    max neval   cld
 funRecycle()  1.110  1.3875  1.65388  1.6650  1.9420  2.775   100 a    
   funAperm() 17.200 19.1420 21.23113 20.2520 20.9455 69.077   100    d 
   funOuter() 14.426 15.8130 17.58316 17.2005 18.1710 35.232   100   c  
   funArray()  2.774  3.3300  3.95079  3.8840  4.1610 14.148   100  b   
   funSweep() 31.903 32.7360 34.84129 33.5680 34.4000 62.141   100     e

N = 100

Unit: milliseconds
         expr       min        lq      mean    median        uq       max
 funRecycle()  28.51351  32.35671  37.13257  33.98931  39.94408  85.94085
   funAperm() 232.69297 276.07494 344.70083 352.40273 395.50492 569.54978
   funOuter()  35.25947  43.98674  53.06895  49.72790  55.93677  95.38608
   funArray()  96.78482 110.10501 119.68267 116.50378 120.70943 172.53973
   funSweep() 150.88675 168.90293 193.06270 178.11013 216.79349 291.23719

我對結果感到驚訝,不知何故,在大n處,將所有數乘以1到外部變得比簡單地使用array()復制數組要快。 (在大的情況下,external()看起來可能甚至比循環方法還要快)。

看來,如果我們必須對不同的索引(funAperm)進行比較,則在所有情況下構建帶有external的數組都會更快。

除了aperm之外,還有什么建議可以對任意索引進行比較?

假設您的意思是(我假設這樣做是因為否則m3d - m2d.z.3d無法正常工作):

m3d = outer(m2d, items3, "*") # note how I switched the arguments

然后工作:

m3d - c(m2d)

為了證明這一點:

all.equal(m3d - c(m2d), m3d - m2d.z.3d)
# [1] TRUE

這里我們只是利用向量循環的優勢,因為我們想在最后一個維度上重復。 我們需要執行c()來消除維數,否則R會抱怨數組不兼容(盡管它們實際上在我們想要的特定意義上)。

基於對R源代碼( src/main/arithmetic.c:real_binary() )的src/main/arithmetic.c:real_binary()它看起來像矢量循環不會復制已循環的矢量,因此這應該既快速又有效。

如果要沿任意維度執行此操作,則必須使用aperm重新排列所有維度的數組,以使相關維度最后顯示,然后將結果重新排列為原始維度順序。 這將增加一些開銷。

至於選擇哪種方法,如果您還沒有耗盡內存,請選擇快速方法(即避免使用循環,而應使用完全矢量化的操作)。

另外,一些帶有items12 <- seq(100)items3 <- seq(50)基准測試:

funOuter <- function() {
  m2d.z.3d = outer(
    m2d, 
    rep(1, length(items3)), "*"
  )
  m3d - m2d.z.3d
}
funRecycle <- function() m3d - c(m2d)
funLoop <- function() apply(m3d, 3, "-", m2d)    # this does not appear correct because `apply` doesn't reconstruct dimensions like `sapply`
funSweep <- function() sweep(m3d, c(1, 2), m2d)  # this is the same type of thing but works properly

library(microbenchmark)
microbenchmark(funOuter(), funRecycle(), funLoop(), funSweep())

生產:

Unit: milliseconds
         expr       min        lq      mean    median
   funOuter()  2.297287  2.673768  3.232277  2.835404
 funRecycle()  1.327101  1.485082  2.093252  1.599543
    funLoop() 22.579010 24.586667 27.211804 26.840069
   funSweep() 11.251656 12.012664 13.516147 13.736908       

並檢查結果:

all.equal(funOuter(), funRecycle())
# [1] TRUE
all.equal(funOuter(), funSweep())
# [1] TRUE
all.equal(funOuter(), funLoop())
# Nope, not equal

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM