Kırpma, maske ve rasterin R hızını birçok poligon tarafından arttırmak?


29

Binlerce poligon sınırına dayanan bir rasterden alanı ve farklı arazi kullanım türlerinin yüzde kapağını çıkarıyorum. Her bir poligonda yinelemeliyim ve kırparsam, ardından rasterini belirli poligonun boyutuna kadar maskelersem, ekstrakt fonksiyonunun çok daha hızlı çalıştığını gördüm. Bununla birlikte, oldukça yavaş ve kodumun verimliliğini ve hızını artırmak için herhangi bir öneriniz olup olmadığını merak ediyorum.

Ben bununla ilgili bulduk tek şey bu yanıtı kullanarak önerdi Roger Bivand tarafından GDAL.open()ve GDAL.close()hem de getRasterTable()ve getRasterData(). Bunlara baktım, ancak geçmişte gdal ile ilgili sorun yaşadım ve nasıl uygulanacağını bilecek kadar iyi tanımıyorum.

Tekrarlanabilir Örnek:

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) 

Şimdiye kadarki en hızlı yöntem

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 

Paralel işleme

Paralel işleme kullanıcı süresini yarı yarıya azaltır, ancak sistem süresini iki katına çıkartarak avantajı reddeder. Raster bunu ekstre işlevi için kullanır, ancak ne yazık ki kırpma veya maske işlevi için kullanılamaz. Maalesef, bu , “IO” tarafından “beklemekten” ötürü, belli miktarda daha büyük toplam geçen zaman bırakır .

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

kodunu birden çok çekirdeğe kodla:

  user  system elapsed 
  23.31    0.68   42.01 

sonra kümeyi sonlandır

endCluster()

Yavaş Yöntem: Doğrudan raster işlevinden özütleme yapmanın alternatif yöntemi çok daha uzun sürer ve veri yönetiminden istediğim forma sokmak konusunda emin değilim:

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

Bu R kodu profilleyicisini deneyebilirsiniz ( marcodvisser.github.io/aprof/Tutorial.html ). Hangi satırların çoğu zaman alacağını size söyleyebilir. Bağlantı ayrıca R'deki işleme süresini
J Kelly

Burada sadece iki kuruş. . . ancak kırpma / piksel değerleri, kırpma içindeki piksel sayısı çok düşük olduğunda çalışmaz. Limitin nerede olduğundan emin değilim, ancak yalnızca 1-5 piksel olan ekinler konusunda sorun yaşadım (nedenini tam olarak belirleyemedim (uzamsal paketler için biraz yeni) ancak ekin işlevinin bağlı olduğuna bahse girerim piksel sınırları, bu nedenle herhangi bir pikseli kırpmak için mücadele eder). Tarama paketinden elde edilen ekstrenin böyle bir sorunu yoktur, ancak kullanıcı zamanının iki katından fazla ve sistem zamanının iki katından fazla olduğu konusunda anlaşmaya varılmıştır. Düşük çözünürlüklü rasterleri olanlara bir uyarı (ve bir
Neal Barsch,

2
Özü, Rcpp paketi üzerinden C'ye taşınan yeni bir paket velox var. Poligon kullanarak ekstrakt işlemlerinde hızda ~ 10 kat artış sağlıyor.
Jeffrey Evans

@JeffreyEvans. Bu sorunun cevabını şimdi velox kullanarak test etmek. Bununla birlikte, onunla son derece geniş vektörlerin tahsisi konusunda sorun yaşamak.
SeldomSeenSlim

Yanıtlar:


23

Sonunda bu fonksiyonu geliştirmek için uğraştım. Amaçlarım için rasterize()ilk önce poligonun en hızlı olduğunu ve kullanım getValues()yerine buldum extract(). Rasterleştirme, küçük çokgenlerde raster değerlerini sıralamak için kullanılan orijinal koddan çok daha hızlı değildir, ancak kırpılacak büyük rasterleri olan büyük poligon alanlarına gelince ve değerleri çıkarıldığında parlar. Ayrıca getValues(), extract()fonksiyondan çok daha hızlı olduğunu da buldum .

Ben de kullanarak çok çekirdekli işlemeyi çözdüm foreach().

Umarım bu, çokgen bir kaplamadan raster değerleri almak için bir R çözümü isteyenler için faydalıdır. Bu, benim için iyi çalışmayan ve bu kullanıcı gibi saatler süren işlemlerden sonra boş çıktılar veren ArcGIS’in “Tablo Kesişimi” ne benzer.

#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)

İşte fonksiyon:

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. 
  }
}

Bu nedenle, kullanmak için, single@data$OWNERtanımlayıcı poligonunuzun sütun ismine uyması için ayarlayın (işleve yerleştirilmiş olabilir ...) ve şunu ekleyin:

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

3
Öneri getValuesçok daha hızlı oldu extractkullanmak çünkü eğer geçerli görünmüyor extractyapmanız gerekmez cropve rasterize(veya mask). Orijinal sorudaki kod her ikisini de yapar ve bu işlem iki kat işlem süresi hakkında olmalıdır.
Robert Hijmans,

bilmek tek yolu test etmektir.
djas,

Poligonlist hangi sınıfa burada ve poligonlist [[i]] [, j] burada ne yapmalı (ELI5, lütfen)? Paralel şeylere yeniyim, o yüzden bunu çok iyi anlamıyorum. Çokgen [[i]] [, j] ile çokgen [[j] 'e dönene kadar hiçbir şey döndürme fonksiyonunu bulamadım, [. doğru sınıf mı? Bunu değiştirdikten sonra süreci çalıştırdım ve bazı çıktılar aldım, ama kesinlikle hala yanlış bir şeyler var. (Medyan değerini n küçük poligonun içine çıkarmaya çalışıyorum, bu yüzden kodun bir kısmını da değiştirdim).
reima,

@RobertH Benim durumumda, kırpma (ve maskeleme) yaklaşık 3 kat daha hızlı çalışmasını sağlar. 100 milyon dönümlük raster kullanıyorum ve çokgenler bunun küçük bir bölümü. Çokgene kırpmazsam, işlem çok daha yavaş gerçekleşir. İşte sonuçlarım: klip1 <- kırpma (rasterlayer, kapsam (tek))> system.time (ext <-extract (clip1, single)) # kırpılmış raster kullanıcı sisteminden çıkarma 65.94 0.37 67.22> system.time (ext < ekstrakt (rasterlayer, single)) # 100 milyon dönümlük raster kullanıcı sisteminden ekstrakte edilen 175.00 4.92 181.10
Luke Macaulay

4

Raster (raster yığını), XY veya Poligon noktasından çıkarılmasını hızlandırır

Harika cevap Luke. Sen bir R sihirbazı olmalısın! İşte kodunuzu basitleştirmek için çok küçük bir değişiklik (bazı durumlarda performansı biraz artırabilir). CellFromPolygon (veya noktalar için cellFromXY) kullanarak ve ardından clip and getValues ​​kullanarak bazı işlemleri önleyebilirsiniz.

Çokgen veya nokta verilerini raster yığınlarından çıkarın ------------------------

 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()

kullanıcı sistemi geçti 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()

kullanıcı sistemi geçen 33.038 3.511 3.288


İki yaklaşımı koştum ve kullanım durumumda yönteminiz biraz yavaşladı.
Luke Macaulay

2

Yer paylaşımının hassasiyetindeki bir kayıp son derece önemli değilse - ile başlamanın kesin olduğu varsayılırsa - genellikle çokgenleri bir raster haline dönüştürerek çok daha yüksek bölgesel hesaplama hızları elde edilebilir. rasterPaket içeriği zonal()amaçlanan görev için iyi çalışması gerekir fonksiyonunu. Bununla birlikte, standart endekslemeyi kullanarak her zaman aynı boyuttaki iki matrisi alt kümesine ekleyebilirsiniz. Çokgenleri korumanız gerekiyorsa ve GIS yazılımına aldırış etmiyorsanız, QGIS bölgesel istatistiklerde ArcGIS veya ENVI-IDL'den daha hızlı olması gerekir.


2

Ben de ~ 300mx300m grid haritasının arazi örtüsü sınıflarının alan payını ~ 1kmx1km bir şebekede hesaplamaya çalışarak bir süre bunun için mücadele ettim. Sonuncusu çokgen bir dosyaydı. Çok çekirdekli çözümü denedim ama bu hala sahip olduğum ızgara hücrelerinin sayısı için çok yavaştı. Bunun yerine ben:

  1. Tüm ızgara hücrelerine benzersiz bir sayı veren 1km x 1km ızgarayı rasterleştirdi
  2. 1kmx1km ağın çözünürlüğünü 300mx300m'ye, aynı projeksiyona, vb. Kadar arttırmak için, gdalUtils paketinden allign_rasters (veya doğrudan gdalwarp) r = "near" seçeneğiyle kullanıldı.
  3. Raster paketini kullanarak, adım 2'deki 300mx300m arazi örtüsü haritasını ve 300mx300m ızgarasını istifleyin: stack_file <- stack (lc, grid).
  4. Haritaları birleştirmek için bir data.frame oluşturun: df <- as.data.frame (rasterToPoints (stack_file)), 1kmx1km haritanın ızgara hücre numaralarını 300mx300m arazi örtüsü haritasına eşler
  5. Toprak örtüsü sınıf hücrelerinin 1kmx1km hücrelerde payını hesaplamak için dplyr kullanın.
  6. Orijinal 1km x 1km ızgaraya bağlayarak 5. adımda yeni bir raster oluşturun.

Bu işlem oldukça hızlı ve bilgisayarımda bellek sorunları olmadan çalışıyor, 300mx300m'de> 15 millik ızgara hücreleri olan bir arazi örtüsü haritasında denediğimde.

Yukarıdaki yaklaşımın, bir çokgen dosyayı düzensiz şekillerle raster verilerle birleştirmek istediğinde de işe yarayacağını tahmin ediyorum. Belki, bir tanesi adım 1 ve 2'yi çokgen dosyasını rasterleştir (muhtemelen yavaş raster) ya da gdal_rasterize kullanarak bir 300mx300 ızgaraya doğrudan rasterleştirerek birleştirebilir. Benim durumumda, aynı zamanda yeniden programlamam gerekti, ben de aynı anda hem tekrar hem de ayrıştırmak için gdalwarp kullandım.


0

Çokgen içindeki değerleri büyük bir mozaikten (50k x 50k) çıkarmak için de aynı problemle yüzleşmeliyim. Poligonumda sadece 4 puan var. Bulduğum en hızlı yöntem crop, poligonun sınırına mozaik yapmak, poligonu üçgen şeklinde 2 üçgene mozaiklemek, sonra üçgen içindeki noktaların olup olmadığını kontrol etmektir (Bulduğum en hızlı algoritma). İşlevle karşılaştırdığınızda extract, çalışma süresi 20 saniyeden 0,5 saniyeye düşürülür. Bununla birlikte, fonksiyon crophala her poligon için yaklaşık 2 sn gerektirir.

Maalesef, yeniden üretilebilir tam örneği sağlayamıyorum. Aşağıdaki R kodları giriş dosyalarını içermez.

Bu yöntem yalnızca basit çokgenler için çalışıyor.

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()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.