เพิ่มความเร็วของการครอบตัดหน้ากากและแยกแรสเตอร์โดยรูปหลายเหลี่ยมใน 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 

การประมวลผลแบบขนาน

การประมวลผลแบบขนานจะลดเวลาของผู้ใช้ลงครึ่งหนึ่ง แต่ไม่ได้รับประโยชน์โดยทำให้เวลาของระบบเพิ่มขึ้นสองเท่า 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 Kelly

แค่สองเซ็นต์ของฉันที่นี่ . . แต่วิธีครอบตัด / getvalues ​​ไม่ทำงานเมื่อจำนวนพิกเซลในการครอบตัดเหลือน้อยมาก ฉันไม่แน่ใจว่าขีด จำกัด อยู่ที่ใด แต่ฉันมีปัญหาเกี่ยวกับการครอบตัดที่มีเพียง 1-5 พิกเซล (ฉันไม่ได้ระบุเหตุผลที่แน่ชัดว่าทำไม (บิตใหม่ยังคงเป็นแพ็คเกจอวกาศ) แต่ฉันเดิมพันฟังก์ชันการครอบตัดขึ้นอยู่กับ ขอบเขตของพิกเซลดังนั้นจึงต่อสู้เพื่อครอบตัดพิกเซลแต่ละพิกเซล) แยกออกจากแพ็คเกจ raster ไม่มีปัญหาดังกล่าว แต่ตกลงมากกว่าเวลาผู้ใช้สองเท่าและมากกว่าสองเท่าในเวลาระบบ เพียงแค่เตือนให้กับผู้ที่มี rasters ความละเอียดต่ำ (และใน
Neal Barsch

2
มีแพ็คเกจใหม่คือ velox ซึ่งย้ายสารสกัดไปยัง C ผ่านแพ็คเกจ Rcpp มันให้ความเร็วเพิ่มขึ้น 10 เท่าในการสกัดโดยใช้รูปหลายเหลี่ยม
Jeffrey Evans

@JeffreyEvans ทดสอบคำตอบสำหรับคำถามนี้โดยใช้ velox ทันที มีปัญหากับมันจัดสรรเวกเตอร์ที่มีขนาดใหญ่มากอย่างไรก็ตาม
SeldomSeenSlim

คำตอบ:


23

ในที่สุดฉันก็ได้รับรอบเพื่อปรับปรุงฟังก์ชั่นนี้ ฉันพบว่าสำหรับวัตถุประสงค์ของฉันมันเป็นที่เร็วที่สุดในrasterize()รูปหลายเหลี่ยมครั้งแรกและใช้แทนgetValues() extract()การแรสเตอร์นั้นไม่เร็วกว่ารหัสต้นฉบับสำหรับการทำตารางค่าแรสเตอร์ในรูปหลายเหลี่ยมขนาดเล็ก แต่มันจะส่องแสงเมื่อมันมาถึงพื้นที่รูปหลายเหลี่ยมขนาดใหญ่ที่มีแรสเตอร์ขนาดใหญ่ที่ถูกครอบตัดและค่าที่แยก ฉันก็พบว่าgetValues()เร็วกว่าextract()ฟังก์ชั่น

ฉันยังคิดว่าการประมวลผลแบบ multi-core foreach()ใช้

ฉันหวังว่านี่จะมีประโยชน์สำหรับคนอื่น ๆ ที่ต้องการโซลูชัน R สำหรับการแยกค่า raster จากการซ้อนทับรูปหลายเหลี่ยม สิ่งนี้คล้ายกับ "Tabulate Intersection" ของ ArcGIS ซึ่งทำงานได้ไม่ดีสำหรับฉันส่งคืนเอาต์พุตว่างหลังจากประมวลผลเป็นชั่วโมงเช่นผู้ใช้รายนี้

#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ว่าดูไม่ถูกต้องเพราะถ้าคุณใช้extractคุณไม่จำเป็นต้องทำcropและrasterize(หรือmask) รหัสในคำถามเดิมทำทั้งสองอย่างและควรใช้เวลาประมวลผลสองเท่า
Robert Hijmans

วิธีเดียวที่จะรู้คือโดยการทดสอบ
djas

รูปหลายเหลี่ยมที่นี่คือรูปหลายเหลี่ยมและรูปหลายเหลี่ยม [[i]] [, j] ทำที่นี่ (ELI5 ได้โปรด) ฉันเป็นมือใหม่ในการขนานสิ่งดังนั้นฉันไม่เข้าใจที่ดีมาก ฉันไม่สามารถรับฟังก์ชั่นเพื่อส่งคืนสิ่งใดได้จนกว่าฉันจะเปลี่ยนเป็นรูปหลายเหลี่ยม [[i]] [, j] เป็นรูปหลายเหลี่ยม [, j] ซึ่งดูเหมือนว่าตรรกะเพราะ [[j]] เป็นองค์ประกอบ jth ของ SpatialPolygonsDataframe คลาสที่ถูกต้องคืออะไร? หลังจากการเปลี่ยนแปลงที่ฉันได้กระบวนการทำงานและเอาท์พุทบางอย่าง แต่ก็ยังมีบางอย่างผิดปกติ (ฉันพยายามที่จะแยกค่ามัธยฐานใน n รูปหลายเหลี่ยมขนาดเล็กดังนั้นฉันจึงเปลี่ยนรหัสเล็กน้อยเช่นกัน)
reima

@ Robert ในกรณีของฉันการครอบตัด (และการปิดบัง) ทำให้มันทำงานได้เร็วขึ้นประมาณ 3 เท่า ฉันใช้แรสเตอร์ 100 ล้านเอเคอร์และรูปหลายเหลี่ยมเป็นเศษส่วนเล็ก ๆ ของนั้น หากฉันไม่ครอบตัดรูปหลายเหลี่ยมกระบวนการจะช้าลงมาก นี่คือผลลัพธ์ของฉัน: clip1 <- การครอบตัด (rasterlayer, ขอบเขต (single))> system.time (ext <-extract (clip1, single)) #extracting จากระบบผู้ใช้แรสเตอร์ที่ถูกครอบตัดที่ผ่านมา 65.94 0.37 67.22> system.time (ext < -extract (rasterlayer, single)) #extracting จากระบบผู้ใช้แรสเตอร์ 100 ล้านเอเคอร์ที่ผ่านไป 175.00 4.92 181.10
Luke Macaulay

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


ฉันรันทั้งสองวิธีและวิธีการของคุณออกมาช้าลงเล็กน้อยในกรณีที่ใช้งาน
Luke Macaulay

2

หากการสูญเสียความแม่นยำของการซ้อนทับนั้นไม่สำคัญอย่างยิ่ง - สมมติว่ามันแม่นยำในการเริ่มต้น - โดยทั่วไปเราสามารถบรรลุความเร็วในการคำนวณเป็นรูปวงรีที่มากขึ้นโดยการแปลงรูปหลายเหลี่ยมเป็นแรสเตอร์ rasterแพคเกจที่มีzonal()ฟังก์ชั่นซึ่งจะทำงานได้ดีสำหรับงานที่ตั้งใจไว้ อย่างไรก็ตามคุณสามารถเซตย่อยสองเมทริกซ์ของมิติเดียวกันโดยใช้การทำดัชนีมาตรฐานเสมอ หากคุณต้องดูแลรูปหลายเหลี่ยมและคุณไม่สนใจซอฟต์แวร์ GIS จริง ๆ แล้ว QGIS นั้นจะต้องเร็วกว่าสถิติแบบเลขฐานสองมากกว่า ArcGIS หรือ ENVI-IDL


2

ฉันยังต้องดิ้นรนกับสิ่งนี้เป็นระยะเวลาหนึ่งโดยพยายามคำนวณส่วนแบ่งพื้นที่ของชั้นดินปกคลุมของแผนที่กริด 300 ~ 300 ตารางเมตรในตาราง ~ 1kmx1km ด้านหลังเป็นไฟล์รูปหลายเหลี่ยม ฉันลองใช้มัลติคอร์โซลูชั่น แต่นี่ก็ช้าเกินไปสำหรับจำนวนของเซลล์กริดที่ฉันมี แต่ฉัน:

  1. ปรับตาราง 1kmx1km ให้เป็นตารางเฉพาะจำนวนเซลล์ทั้งหมด
  2. ใช้ allign_rasters (หรือ gdalwarp โดยตรง) จากแพ็คเกจ gdalUtils พร้อมตัวเลือก r = "ใกล้" เพื่อเพิ่มความละเอียดของตาราง 1kmx1km เป็น 300mx300m, การฉายเดียวกัน
  3. สแต็กแผนที่พื้นที่คลุมดินขนาด 300 มม. 300 ม. และกริด 300 มม. 300 ม. จากขั้นตอนที่ 2 โดยใช้แพ็คเกจแรสเตอร์: stack_file <- สแต็ก (lc, กริด)
  4. สร้าง data.frame เพื่อรวมแผนที่: df <- as.data.frame (rasterToPoints (stack_file)) ซึ่งแมปหมายเลขเซลล์กริดของแผนที่ 1kmx1km ไปยังแผนที่ครอบคลุมพื้นที่ 300mx300m
  5. ใช้ dplyr เพื่อคำนวณส่วนแบ่งของเซลล์ชั้นดินปกคลุมในเซลล์ 1kmx1km
  6. สร้างแรสเตอร์ใหม่บนพื้นฐานของขั้นตอนที่ 5 โดยเชื่อมโยงกับตาราง 1kmx1km ดั้งเดิม

ขั้นตอนนี้ทำงานค่อนข้างเร็วและไม่มีปัญหาเรื่องหน่วยความจำในพีซีของฉันเมื่อฉันลองทำในแผนที่ครอบคลุมพื้นที่ที่มีเซลล์กริด>> 15 เซลล์กริดที่ 300 มม.

ฉันคิดว่าวิธีการข้างต้นจะใช้งานได้หากต้องการรวมไฟล์รูปหลายเหลี่ยมกับรูปร่างที่ผิดปกติกับข้อมูลแรสเตอร์ บางทีเราสามารถรวมขั้นตอนที่ 1 & 2 ได้โดยการ rasterizing ไฟล์รูปหลายเหลี่ยมโดยตรงกับตารางขนาด 300mx300 โดยใช้ rasterize (raster อาจช้า) หรือ gdal_rasterize ในกรณีของฉันฉันต้องการที่จะปฏิเสธเช่นกันดังนั้นฉันจึงใช้ 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.