Rの多数のポリゴンによる切り抜き、マスク、およびラスターの抽出の速度を上げますか?


29

数千のポリゴン境界に基づいて、さまざまな土地利用タイプの面積と被覆率をラスタから抽出しています。個々のポリゴンを繰り返し処理し、特定のポリゴンのサイズまでラスターをマスクして切り取ると、抽出機能がはるかに高速に動作することがわかりました。それにもかかわらず、それはかなり遅いです、そして、誰かが私のコードの効率と速度を改善するための提案を持っているかどうか疑問に思っています。

私はこれに関連した見つけた唯一のものです。この応答使うことを提案ロジャーBivandによるGDAL.open()GDAL.close()だけでなく、getRasterTable()getRasterData()。私はそれらを調べましたが、過去にgdalに問題があり、それを実装する方法を知るのに十分なほどよく知りません。

再現可能な例:

library(maptools)  ## For wrld_simpl
library(raster)

## Example SpatialPolygonsDataFrame
data(wrld_simpl) #polygon of world countries
bound <- wrld_simpl[1:25,] #name it this to subset to 25 countries and because my loop is set up with that variable  

## Example RasterLayer
c <- raster(nrow=2e3, ncol=2e3, crs=proj4string(wrld_simpl), xmn=-180, xmx=180, ymn=-90, ymx=90)
c[] <- 1:length(c)

#plot, so you can see it
plot(c)    
plot(bound, add=TRUE) 

これまでで最速の方法

result <- data.frame() #empty result dataframe 

system.time(
     for (i in 1:nrow(bound)) { #this is the number of polygons to iterate through
      single <- bound[i,] #selects a single polygon
      clip1 <- crop(c, extent(single)) #crops the raster to the extent of the polygon, I do this first because it speeds the mask up
      clip2 <- mask(clip1,single) #crops the raster to the polygon boundary

      ext<-extract(clip2,single) #extracts data from the raster based on the polygon bound
      tab<-lapply(ext,table) #makes a table of the extract output
      s<-sum(tab[[1]])  #sums the table for percentage calculation
      mat<- as.data.frame(tab) 
      mat2<- as.data.frame(tab[[1]]/s) #calculates percent
      final<-cbind(single@data$NAME,mat,mat2$Freq) #combines into single dataframe
      result<-rbind(final,result)
      })

   user  system elapsed 
  39.39    0.11   39.52 

並列処理

並列処理はユーザー時間を半分に削減しましたが、システム時間を2倍にすることで利点を無効にしました。Rasterはこれを抽出機能に使用しますが、残念ながらクロップまたはマスク機能には使用しません。残念ながら、これにより、「IO」による「待機」により、合計経過時間がわずかに長くなります。

beginCluster( detectCores() -1) #use all but one core

複数のコアでコードを実行します:

  user  system elapsed 
  23.31    0.68   42.01 

その後、クラスターを終了します

endCluster()

遅い方法: ラスター関数から直接抽出を行う別の方法は、はるかに長い時間がかかり、データ管理がそれを希望する形式にするかどうかわかりません。

system.time(ext<-extract(c,bound))
   user  system elapsed 
1170.64   14.41 1186.14 

このRコードプロファイラー(marcodvisser.github.io/aprof/Tutorial.html)を試すことができます。ほとんどの場合、どの行に時間がかかるかがわかります。リンクには、Rで処理時間を短縮するためのガイドラインもあります。
Jケリー

ここ2セントだけです。。。ただし、トリミングのピクセル数が非常に少ない場合、トリミング/ getvaluesメソッドは機能しません。制限がどこにあるのかはわかりませんが、1〜5ピクセルしかなかった作物に問題がありました(空間パッケージにはまだ少し新しい理由があります)が、作物の機能はピクセル境界なので、個々のピクセルをトリミングするのに苦労します)。ラスターパッケージからの抽出にはこのような問題はありませんが、ユーザー時間の2倍以上であり、システム時間の2倍以上であることに同意しています。ただ、低解像度のラスタを持っている人に警告(および中
ニールBarsch

2
Rcppパッケージを介して抽出物をCに移動した、やや新しいパッケージveloxがあります。ポリゴンを使用した抽出操作の速度が最大10倍になります。
ジェフリーエヴァンス

@JeffreyEvans veloxを使用して、この質問に対する答えをテストします。ただし、非常に大きなベクトルの割り当てに問題があります。
SeldomSeenSlim

回答:


23

私はついにこの機能を改善することに成功しました。私の目的ではrasterize()、最初にポリゴンが最速であり、のgetValues()代わりに使用することがわかりましたextract()。ラスタライズは、小さなポリゴンのラスタ値を集計するための元のコードほど高速ではありませんが、トリミングする大きなラスタがあり、値を抽出する大きなポリゴン領域に到達したときに光ります。またgetValues()extract()関数よりもはるかに高速であることがわかりました。

また、を使用してマルチコア処理を計算しましたforeach()

これが、ポリゴンオーバーレイからラスター値を抽出するためのRソリューションを必要とする他の人々に役立つことを願っています。これは、ArcGISの「Tabulate Intersection」に似ていますが、このユーザーのように、何時間も処理した後に空の出力を返す、私にとってはうまくいきませんでした。

#initiate multicore cluster and load packages
library(foreach)
library(doParallel)
library(tcltk)
library(sp)
library(raster)

cores<- 7
cl <- makeCluster(cores, output="") #output should make it spit errors
registerDoParallel(cl)

関数は次のとおりです。

multicore.tabulate.intersect<- function(cores, polygonlist, rasterlayer){ 
  foreach(i=1:cores, .packages= c("raster","tcltk","foreach"), .combine = rbind) %dopar% {

    mypb <- tkProgressBar(title = "R progress bar", label = "", min = 0, max = length(polygonlist[[i]]), initial = 0, width = 300) 

    foreach(j = 1:length(polygonlist[[i]]), .combine = rbind) %do% {
      final<-data.frame()
      tryCatch({ #not sure if this is necessary now that I'm using foreach, but it is useful for loops.

        single <- polygonlist[[i]][j,] #pull out individual polygon to be tabulated

        dir.create (file.path("c:/rtemp",i,j,single@data$OWNER), showWarnings = FALSE) #creates unique filepath for temp directory
        rasterOptions(tmpdir=file.path("c:/rtemp",i,j, single@data$OWNER))  #sets temp directory - this is important b/c it can fill up a hard drive if you're doing a lot of polygons

        clip1 <- crop(rasterlayer, extent(single)) #crop to extent of polygon
        clip2 <- rasterize(single, clip1, mask=TRUE) #crops to polygon edge & converts to raster
        ext <- getValues(clip2) #much faster than extract
        tab<-table(ext) #tabulates the values of the raster in the polygon

        mat<- as.data.frame(tab)
        final<-cbind(single@data$OWNER,mat) #combines it with the name of the polygon
        unlink(file.path("c:/rtemp",i,j,single@data$OWNER), recursive = TRUE,force = TRUE) #delete temporary files
        setTkProgressBar(mypb, j, title = "number complete", label = j)

      }, error=function(e){cat("ERROR :",conditionMessage(e), "\n")}) #trycatch error so it doesn't kill the loop

      return(final)
    }  
    #close(mypb) #not sure why but closing the pb while operating causes it to return an empty final dataset... dunno why. 
  }
}

そのため、それを使用するにsingle@data$OWNERは、識別ポリゴン(関数に組み込まれている可能性のある推測)の列名に合うように調整し、以下を入力します。

myoutput <- multicore.tabulate.intersect(cores, polygonlist, rasterlayer)

3
を使用する場合は、および(または)を行う必要がないためgetValues、よりもはるかに速い提案extractは有効ではないようです。元の質問のコードは両方を行いますが、処理時間は約2倍になります。extractcroprasterizemask
ロバートハイマンズ

知る唯一の方法はテストすることです。
ジャス

ここのpolygonlistはどのクラスであり、polygonlist [[i]] [、j]はここで何をすべきでしょうか(ELI5、お願いします)。私は並列処理の初心者なので、あまりよくわかりません。私はpolygonlist [[i]] [、j]からpolygonlist [、j]に変更するまで、何も返す関数を取得できませんでした。正しいクラスは何ですか?それを変更した後、プロセスを実行していくつかの出力を取得しましたが、まだ何か間違いがあります。(n個の小さなポリゴン内の中央値を抽出しようとするため、コードも少し変更しました)。
レイマ

@RobertH私の場合、トリミング(およびマスキング)により、実行が約3倍速くなります。私は1億エーカーのラスターを使用していますが、ポリゴンはそのほんの一部です。ポリゴンにトリミングしないと、プロセスの実行速度がはるかに遅くなります。ここに私の結果があります:clip1 <-crop(rasterlayer、extent(single))> system.time(ext <-extract(clip1、single))#トリミングされたラスターユーザーシステムからの抽出65.94 0.37 67.22> system.time(ext < -extract(rasterlayer、single))#1億エーカーのラスターユーザーシステムからの抽出175.00 4.92 181.10
ルークマコーレー

4

ポイント、XYまたはポリゴンからのラスター(ラスタースタック)の抽出を高速化

すばらしい答え、ルーク。あなたはRウィザードでなければなりません!コードを簡素化するための非常に小さな調整を以下に示します(場合によってはパフォーマンスがわずかに向上する可能性があります)。cellFromPolygon(またはポイントの場合はcellFromXY)を使用して、クリップとgetValuesを使用することにより、一部の操作を回避できます。

ラスタスタックからポリゴンまたはポイントデータを抽出する------------------------

 library(raster)  
 library(sp)   

  # create polygon for extraction
  xys= c(76.27797,28.39791,
        76.30543,28.39761,
        76.30548,28.40236,
        76.27668,28.40489)
  pt <- matrix(xys, ncol=2, byrow=TRUE)
  pt <- SpatialPolygons(list(Polygons(list(Polygon(pt)), ID="a")));
  proj4string(pt) <-"+proj=longlat +datum=WGS84 +ellps=WGS84"
  pt <- spTransform(pt, CRS("+proj=sinu +a=6371007.181 +b=6371007.181 +units=m"))
  ## Create a matrix with random data & use image()
  xy <- matrix(rnorm(4448*4448),4448,4448)
  plot(xy)

  # Turn the matrix into a raster
  NDVI_stack_h24v06 <- raster(xy)
  # Give it lat/lon coords for 36-37°E, 3-2°S
  extent(NDVI_stack_h24v06) <- c(6671703,7783703,2223852,3335852)
  # ... and assign a projection
  projection(NDVI_stack_h24v06) <- CRS("+proj=sinu +a=6371007.181 +b=6371007.181 +units=m")
  plot(NDVI_stack_h24v06)
  # create a stack of the same raster
  NDVI_stack_h24v06 = stack( mget( rep( "NDVI_stack_h24v06" , 500 ) ) )


  # Run functions on list of points
  registerDoParallel(16)
  ptm <- proc.time()
  # grab cell number
  cell = cellFromPolygon(NDVI_stack_h24v06, pt, weights=FALSE)
  # create a raster with only those cells
  r = rasterFromCells(NDVI_stack_h24v06, cell[[1]],values=F)
  result = foreach(i = 1:dim(NDVI_stack_h24v06)[3],.packages='raster',.combine=rbind,.inorder=T) %dopar% {
     #get value and store
     getValues(crop(NDVI_stack_h24v06[[i]],r))
  }
  proc.time() - ptm
  endCluster()

ユーザーシステム経過16.682 2.610 2.530

  registerDoParallel(16)
  ptm <- proc.time()
  result = foreach(i = 1:dim(NDVI_stack_h24v06)[3],.packages='raster',.inorder=T,.combine=rbind) %dopar% {
        clip1 <- crop(NDVI_stack_h24v06[[i]], extent(pt)) #crop to extent of polygon
        clip2 <- rasterize(pt, clip1, mask=TRUE) #crops to polygon edge & converts to raster
         getValues(clip2) #much faster than extract
  }
  proc.time() - ptm
  endCluster()

ユーザーシステム経過33.038 3.511 3.288


私は2つのアプローチを実行しましたが、私のユースケースではあなたの方法は少し遅くなりました。
ルークマコーレー

2

オーバーレイの精度の低下がそれほど重要ではない場合(最初から正確であると仮定して)、通常、最初にポリゴンをラスターに変換することにより、ゾーンの計算速度を大幅に向上させることができます。rasterパッケージには含まれていzonal()意図タスクのために働く必要がある機能を、。ただし、標準のインデックスを使用して、同じ次元の2つの行列をいつでもサブセット化できます。ポリゴンを維持する必要があり、GISソフトウェアを気にしない場合、QGISは実際には、ArcGISまたはENVI-IDLよりもゾーン統計で高速でなければなりません。


2

また、私はこれにしばらく苦労し、〜1kmx1kmグリッドの〜300mx300mグリッドマップの土地被覆クラスの面積シェアを計算しようとしました。後者はポリゴンファイルでした。マルチコアソリューションを試してみましたが、グリッドセルの数に対してはまだ遅すぎました。代わりに私:

  1. すべてのグリッドセルに一意の番号を与える1kmx1kmグリッドをラスタライズ
  2. gdalUtilsパッケージのallign_rasters(またはgdalwarpを直接)でr = "near"オプションを使用して、1kmx1kmグリッドの解像度を300mx300m、同じ投影などに上げました。
  3. stack_file <-stack(lc、grid)のラスターパッケージを使用して、ステップ2の300mx300m土地被覆マップと300mx300mグリッドをスタックします。
  4. マップを結合するdata.frameを作成します。df<-as.data.frame(rasterToPoints(stack_file))。1kmx1kmマップのグリッドセル番号を300mx300m土地被覆マップにマップします。
  5. dplyrを使用して、1kmx1kmセルの土地被覆クラスセルのシェアを計算します。
  6. 元の1kmx1kmグリッドにリンクすることにより、手順5に基づいて新しいラスターを作成します。

この手順は、300mx300mで15ミリ以上のグリッドセルを持つ土地被覆マップで試したときに、PCでメモリの問題なしに非常に迅速に実行されます。

不規則な形状のポリゴンファイルをラスターデータと組み合わせたい場合にも、上記のアプローチが機能すると思います。おそらく、rasterize(ラスタはおそらく遅い)またはgdal_rasterizeを使用して、ポリゴンファイルを300mx300グリッドに直接ラスタライズすることにより、ステップ1と2を組み合わせることができます。私の場合、同様に再投影する必要があったため、gdalwarpを使用して再投影と分解の両方を同時に行いました。


0

大きなモザイク(50k x 50k)からポリゴン内の値を抽出するには、この同じ問題に直面する必要があります。ポリゴンには4つのポイントしかありません。私が見つけた最速の方法はcrop、多角形の境界にモザイクし、多角形を2つの三角形に三角形化し、三角形内のポイントを確認することです(私が見つけた最速のアルゴリズム)。extract関数と比較すると、実行時間は20秒から0.5秒に短縮されます。ただし、この関数でcropは各ポリゴンごとに約2秒が必要です。

申し訳ありませんが、再現可能な完全な例を提供することはできません。以下のRコードには入力ファイルは含まれていません。

この方法は、単純なポリゴンでのみ機能します。

par_dsm <- function(i, image_tif_name, field_plys2) {
    library(raster)
    image_tif <- raster(image_tif_name)
    coor <- field_plys2@polygons[[i]]@Polygons[[1]]@coords
    ext <- extent(c(min(coor[,1]), max(coor[,1]), min(coor[,2]), max(coor[,2])))

    extract2 <- function(u, v, us, vs) {
        u1 <- us[2]  - us[1]
        u2 <- us[3]  - us[2]
        u3 <- us[1]  - us[3]
        v1 <- vs[1]  - vs[2]
        v2 <- vs[2]  - vs[3]
        v3 <- vs[3]  - vs[1]
        uv1 <- vs[2] * us[1] - vs[1] * us[2]
        uv2 <- vs[3] * us[2] - vs[2] * us[3]
        uv3 <- vs[1] * us[3] - vs[3] * us[1]

        s1 <- v * u1 + u * v1 + uv1
        s2 <- v * u2 + u * v2 + uv2
        s3 <- v * u3 + u * v3 + uv3
        pos <- s1 * s2 > 0 & s2 * s3 > 0
        pos 
    }

    system.time({
        plot_rect <- crop(image_tif, ext, snap ='out')
        system.time({
        cell_idx <- cellFromXY(plot_rect, coor[seq(1,4),])
        row_idx <- rowFromCell(plot_rect, cell_idx)
        col_idx <- colFromCell(plot_rect, cell_idx)

        rect_idx <- expand.grid(lapply(rev(dim(plot_rect)[1:2]), function(x) seq(length.out = x)))

        pixel_idx1 <- extract2(
            rect_idx[,2], rect_idx[,1], 
            row_idx[c(1,2,3)], col_idx[c(1,2,3)])
        pixel_idx2 <- extract2(
            rect_idx[,2], rect_idx[,1], 
            row_idx[c(1,4,3)], col_idx[c(1,4,3)])
        pixel_idx <- pixel_idx1 | pixel_idx2
        })
    })
    mean(values(plot_rect)[pixel_idx])
}

# field_plys2: An object of polygons
# image_taf_name: file name of mosaic file
library(snowfall)
sfInit(cpus = 14, parallel = TRUE)
system.time(plot_dsm <- sfLapply(
    seq(along = field_plys2), par_dsm, image_tif_name, field_plys2))
sfStop()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.