9

nrow が約 100 万または 2 で、ncol が約 200 のデータ テーブルがあります。

行の各エントリには、座標が関連付けられています。

データのごく一部:

[1,] -2.80331471  -0.8874522 -2.34401863   -3.811584   -2.1292443
[2,]  0.03177716   0.2588624  0.82877467    1.955099    0.6321881
[3,] -1.32954665  -0.5433407 -2.19211837   -2.342554   -2.2142461
[4,] -0.60771429  -0.9758734  0.01558774    1.651459   -0.8137684

最初の 4 行の座標:

9928202 9928251 9928288 9928319

私が望むのは、データとウィンドウサイズを指定すると、各列に平均スライディングウィンドウが適用された同じサイズのデータ​​テーブルを返す関数です。または、言い換えると、行エントリiごとに、coords[i]-windsize と coords[i]+windsize の間の座標を持つエントリを見つけ、初期値をその間隔内の値の平均に置き換えます (列ごとに個別に)。 .

ここでの主な問題は速度です。

これがそのような機能の私の最初のテイクです。

doSlidingWindow <- function(intensities, coords, windsize) {
windHalfSize <- ceiling(windsize/2)
### whole range inds
RANGE <- integer(max(coords)+windsize)
RANGE[coords] <- c(1:length(coords)[1])

### get indeces of rows falling in each window
COORDS <- as.list(coords)
WINDOWINDS <- sapply(COORDS, function(crds){ unique(RANGE[(crds-windHalfSize):
    (crds+windHalfSize)]) })

### do windowing

wind_ints <- intensities
wind_ints[] <- 0
for(i in 1:length(coords)) {
    wind_ints[i,] <- apply(as.matrix(intensities[WINDOWINDS[[i]],]), 2, mean)
}
return(wind_ints)
}

最後の for ループの前のコードは非常に高速で、各エントリに使用する必要があるインデックスのリストを取得します。ただし、for ループを何百万回もグラインドし、データ テーブルのサブセットを取得し、適用内のすべての列を一度に操作できるように複数の行があることを確認する必要があるため、すべてがバラバラになります。

私の 2 番目のアプローチは、実際の値を RANGE リストに貼り付け、ギャップをゼロで埋め、zoo パッケージから rollmean を実行し、列ごとに繰り返すことです。しかし、rollmean はすべてのギャップを通過し、最終的に元の座標の値のみを使用するため、これは冗長です。

Cに行かずに高速化するための助けをいただければ幸いです。

4

2 に答える 2

7

データ生成:

N <- 1e5 # rows
M <- 200 # columns
W <- 10  # window size

set.seed(1)
intensities <- matrix(rnorm(N*M), nrow=N, ncol=M)
coords <- 8000000 + sort(sample(1:(5*N), N))

ベンチマークに使用したマイナーな変更を加えた元の関数:

doSlidingWindow <- function(intensities, coords, windsize) {
  windHalfSize <- ceiling(windsize/2)
  ### whole range inds
  RANGE <- integer(max(coords)+windsize)
  RANGE[coords] <- c(1:length(coords)[1])

  ### get indices of rows falling in each window
  ### NOTE: Each elements of WINDOWINDS holds zero. Not a big problem though.
  WINDOWINDS <- sapply(coords, function(crds) ret <- unique(RANGE[(crds-windHalfSize):(crds+windHalfSize)]))

  ### do windowing
  wind_ints <- intensities
  wind_ints[] <- 0
  for(i in 1:length(coords)) {
    # CORRECTION: When it's only one row in window there was a trouble
    wind_ints[i,] <- apply(matrix(intensities[WINDOWINDS[[i]],], ncol=ncol(intensities)), 2, mean)
  }
  return(wind_ints)
}

可能な解決策:


1) データ.テーブル

data.tableサブセット化で高速であることが知られていますが、このページ(およびその他のスライディング ウィンドウに関連するページ) は、そうではないことを示唆しています。確かに、data.tableコードはエレガントですが、残念ながら非常に遅いです:

require(data.table)
require(plyr)
dt <- data.table(coords, intensities)
setkey(dt, coords)
aaply(1:N, 1, function(i) dt[WINDOWINDS[[i]], sapply(.SD,mean), .SDcols=2:(M+1)])

2) foreach+doSNOW

基本的なルーチンは簡単に並行して実行できるため、次のメリットがあります。

require(doSNOW)
doSlidingWindow2 <- function(intensities, coords, windsize) {
  NC <- 2 # number of nodes in cluster
  cl <- makeCluster(rep("localhost", NC), type="SOCK")
  registerDoSNOW(cl)

  N <- ncol(intensities) # total number of columns
  chunk <- ceiling(N/NC) # number of columns send to the single node

  result <- foreach(i=1:NC, .combine=cbind, .export=c("doSlidingWindow")) %dopar% {
    start <- (i-1)*chunk+1
    end   <- ifelse(i!=NC, i*chunk, N)
    doSlidingWindow(intensities[,start:end], coords, windsize)    
  }

  stopCluster(cl)
  return (result)
}

ベンチマークは、私のデュアルコア プロセッサで顕著なスピードアップを示しています。

system.time(res <- doSlidingWindow(intensities, coords, W))
#    user  system elapsed 
# 306.259   0.204 307.770
system.time(res2 <- doSlidingWindow2(intensities, coords, W))
#  user  system elapsed 
# 1.377   1.364 177.223
all.equal(res, res2, check.attributes=FALSE)
# [1] TRUE

3) Rcpp

はい、あなたが「 Cに行かずに」と尋ねたことは知っています。でも、見てください。このコードはインラインで、かなり単純です。

require(Rcpp)
require(inline)
doSlidingWindow3 <- cxxfunction(signature(intens="matrix", crds="numeric", wsize="numeric"), plugin="Rcpp", body='
  #include <vector>
  Rcpp::NumericMatrix intensities(intens);
  const int N = intensities.nrow();
  const int M = intensities.ncol();
  Rcpp::NumericMatrix wind_ints(N, M);

  std::vector<int> coords = as< std::vector<int> >(crds);
  int windsize = ceil(as<double>(wsize)/2);  

  for(int i=0; i<N; i++){
    // Simple search for window range (begin:end in coords)
    // Assumed that coords are non-decreasing
    int begin = (i-windsize)<0?0:(i-windsize);
    while(coords[begin]<(coords[i]-windsize)) ++begin;
    int end = (i+windsize)>(N-1)?(N-1):(i+windsize);
    while(coords[end]>(coords[i]+windsize)) --end;

    for(int j=0; j<M; j++){
      double result = 0.0;
      for(int k=begin; k<=end; k++){
        result += intensities(k,j);
      }
      wind_ints(i,j) = result/(end-begin+1);
    }
  }

  return wind_ints;
')

基準:

system.time(res <- doSlidingWindow(intensities, coords, W))
#    user  system elapsed 
# 306.259   0.204 307.770
system.time(res3 <- doSlidingWindow3(intensities, coords, W))
#  user  system elapsed 
# 0.328   0.020   0.351
all.equal(res, res3, check.attributes=FALSE)
# [1] TRUE

結果がかなりやる気を起こさせるものであることを願っています。データはメモリに収まりますが、Rcppバージョンはかなり高速です。と言ってN <- 1e6M <-100私は得ました:

   user  system elapsed 
  2.873   0.076   2.951

当然、R が swap の使用を開始すると、すべてが遅くなります。メモリに収まらない非常に大きなデータの場合はsqldfffまたはを検討する必要がありますbigmemory

于 2013-01-20T02:51:53.197 に答える