วิธีการซ้อนโพลีกอนทับ SpatialPointsDataFrame และเก็บรักษาข้อมูล SPDF ไว้อย่างไร


17

ฉันมีSpatialPointsDataFrameข้อมูลเพิ่มเติมบางอย่างด้วย ฉันต้องการที่จะดึงจุดเหล่านั้นเข้าไปในรูปหลายเหลี่ยมและในเวลาเดียวกันให้เก็บรักษาSPDFวัตถุและข้อมูลที่เกี่ยวข้อง

จนถึงตอนนี้ฉันมีโชคเล็กน้อยและใช้วิธีจับคู่และรวมกันผ่าน ID ทั่วไป แต่วิธีนี้ใช้ได้เพราะฉันมีข้อมูล gridded กับ IDS แต่ละรายการเท่านั้น

นี่คือตัวอย่างย่อ ๆ ฉันกำลังมองหาจุดที่อยู่ภายในจัตุรัสแดง

library(sp)
set.seed(357)
pts <- data.frame(x = rnorm(100), y = rnorm(100), var1 = runif(100), var2 = sample(letters, 100, replace = TRUE))
coordinates(pts) <- ~ x + y
class(pts)
plot(pts)
axis(1); axis(2)

ply <- matrix(c(-1,-1, 1,-1, 1,1, -1,1, -1,-1), ncol = 2, byrow = TRUE)
ply <- SpatialPolygons(list(Polygons(list(Polygon(ply)), ID = 1)))
ply <- SpatialPolygonsDataFrame(Sr = ply, data = data.frame(polyvar = 357))
plot(ply, add = TRUE, border = "red")

วิธีที่ชัดเจนที่สุดคือการใช้overแต่วิธีนี้จะส่งคืนข้อมูลจากรูปหลายเหลี่ยม

> over(pts, ply)
    polyvar
1        NA
2       357
3       357
4        NA
5       357
6       357

1
ขอบคุณที่ให้ตัวอย่างที่ทำซ้ำได้ ช่วยเหลือเสมอเมื่อพยายามทำความเข้าใจปัญหา!
fdetsch

คำตอบ:


21

จากความsp::overช่วยเหลือ:

 x = "SpatialPoints", y = "SpatialPolygons" returns a numeric
      vector of length equal to the number of points; the number is
      the index (number) of the polygon of ‘y’ in which a point
      falls; NA denotes the point does not fall in a polygon; if a
      point falls in multiple polygons, the last polygon is
      recorded.

ดังนั้นถ้าคุณแปลงเป็นคุณSpatialPolygonsDataFrameจะSpatialPolygonsได้เวกเตอร์ของดัชนีกลับมาและคุณสามารถเซตคะแนนของคุณในNA:

> over(pts,as(ply,"SpatialPolygons"))
  [1] NA  1  1 NA  1  1 NA NA  1  1  1 NA NA  1  1  1  1  1 NA NA NA  1 NA  1 NA
 [26]  1  1  1 NA NA NA NA NA  1  1 NA NA NA  1  1  1 NA  1  1  1 NA NA NA  1  1
 [51]  1 NA NA NA  1 NA  1 NA  1 NA NA  1 NA  1  1 NA  1  1 NA  1 NA  1  1  1  1
 [76]  1  1  1  1  1 NA NA NA  1 NA  1 NA NA NA NA  1  1 NA  1 NA NA  1  1  1 NA

> nrow(pts)
[1] 100
> pts = pts[!is.na(over(pts,as(ply,"SpatialPolygons"))),]
> nrow(pts)
[1] 54
> head(pts@data)
         var1 var2
2  0.04001092    v
3  0.58108350    v
5  0.85682609    q
6  0.13683264    y
9  0.13968804    m
10 0.97144627    o
> 

สำหรับผู้สงสัยสองคนนี่คือหลักฐานว่าค่าใช้จ่ายในการแปลงไม่ใช่ปัญหา:

สองฟังก์ชั่น - วิธีแรกของเจฟฟรีย์อีแวนส์จากนั้นเป็นต้นฉบับของฉันจากนั้นเปลี่ยนการแฮ็กของฉันจากนั้นเป็นเวอร์ชันที่อิงgIntersectsตามคำตอบของ Josh O'Brien:

evans <- function(pts,ply){
  prid <- over(pts,ply)
  ptid <- na.omit(prid) 
  pt.poly <- pts[as.numeric(as.character(row.names(ptid))),]
  return(pt.poly)
}

rowlings <- function(pts,ply){
  return(pts[!is.na(over(pts,as(ply,"SpatialPolygons"))),])
}

rowlings2 <- function(pts,ply){
  class(ply) <- "SpatialPolygons"
  return(pts[!is.na(over(pts,ply)),])
}

obrien <- function(pts,ply){
pts[apply(gIntersects(columbus,pts,byid=TRUE),1,sum)==1,]
}

สำหรับตัวอย่างในโลกแห่งความเป็นจริงฉันได้กระจายจุดสุ่มบางส่วนไว้บนcolumbusชุดข้อมูล:

require(spdep)
example(columbus)
pts=data.frame(
    x=runif(100,5,12),
    y=runif(100,10,15),
    z=sample(letters,100,TRUE))
coordinates(pts)=~x+y

ดูดี

plot(columbus)
points(pts)

ตรวจสอบฟังก์ชั่นที่กำลังทำสิ่งเดียวกัน:

> identical(evans(pts,columbus),rowlings(pts,columbus))
[1] TRUE

และเรียกใช้ 500 ครั้งสำหรับการเปรียบเทียบ:

> system.time({for(i in 1:500){evans(pts,columbus)}})
   user  system elapsed 
  7.661   0.600   8.474 
> system.time({for(i in 1:500){rowlings(pts,columbus)}})
   user  system elapsed 
  6.528   0.284   6.933 
> system.time({for(i in 1:500){rowlings2(pts,columbus)}})
   user  system elapsed 
  5.952   0.600   7.222 
> system.time({for(i in 1:500){obrien(pts,columbus)}})
  user  system elapsed 
  4.752   0.004   4.781 

ตามสัญชาตญาณของฉันมันไม่ได้เป็นค่าใช้จ่ายที่ดีในความเป็นจริงมันอาจจะน้อยกว่าค่าใช้จ่ายกว่าการแปลงดัชนีแถวทั้งหมดให้เป็นอักขระและย้อนกลับหรือใช้ na.omit เพื่อรับค่าที่หายไป ซึ่งบังเอิญนำไปสู่โหมดความล้มเหลวอื่นของevansฟังก์ชัน ...

หากแถวของกรอบข้อมูลรูปหลายเหลี่ยมเป็นทั้งหมดNA(ซึ่งใช้ได้อย่างสมบูรณ์) ดังนั้นการซ้อนทับด้วยSpatialPolygonsDataFrameสำหรับจุดในรูปหลายเหลี่ยมนั้นจะสร้างกรอบข้อมูลผลลัพธ์ด้วยNAs ทั้งหมดซึ่งevans()จะลดลง:

> columbus@data[1,]=rep(NA,20)
> columbus@data[5,]=rep(NA,20)
> columbus@data[17,]=rep(NA,20)
> columbus@data[15,]=rep(NA,20)
> set.seed(123)
> pts=data.frame(x=runif(100,5,12),y=runif(100,10,15),z=sample(letters,100,TRUE))
> coordinates(pts)=~x+y
> identical(evans(pts,columbus),rowlings(pts,columbus))
[1] FALSE
> dim(evans(pts,columbus))
[1] 27  1
> dim(rowlings(pts,columbus))
[1] 28  1
> 

แต่gIntersectsเร็วกว่าแม้ว่าจะต้องกวาดเมทริกซ์เพื่อตรวจสอบจุดตัดใน R แทนที่จะเป็นรหัส C ฉันสงสัยว่ามันเป็นprepared geometryทักษะของ GEOS สร้างดัชนีเชิงพื้นที่ - ใช่ด้วยprepared=FALSEความยาวประมาณ 5.5 วินาที

ฉันประหลาดใจที่ไม่มีฟังก์ชั่นที่จะคืนดัชนีหรือคะแนนโดยตรง เมื่อฉันเขียนsplancs20 ปีที่ผ่านมาฟังก์ชั่นจุดในรูปหลายเหลี่ยมมีทั้ง ...


เยี่ยมมากสิ่งนี้ใช้ได้กับรูปหลายเหลี่ยมหลายรูปแบบ (ฉันได้เพิ่มตัวอย่างเพื่อเล่นลงในคำตอบของ Joshua)
Roman Luštrik

ด้วยการบีบอัดชุดข้อมูลขนาดใหญ่ลงในวัตถุ SpatialPolygons จึงมีค่าใช้จ่ายจำนวนมากและไม่จำเป็น การใช้ "over" กับ SpatialPolygonsDataFrame จะส่งคืนดัชนีแถวซึ่งสามารถใช้ในการย่อยคะแนนได้ ดูตัวอย่างของฉันด้านล่าง
Jeffrey Evans

จำนวนมากของค่าใช้จ่าย? มันเป็นเพียงแค่การใช้ช่อง @polygons จากวัตถุ SpatialPolygonsDataFrame คุณสามารถ 'ปลอม' โดยการกำหนดคลาสของ SpatialPolygonsDataFrame ให้เป็น "SpatialPolygons" อีกครั้ง (แม้ว่านี่จะเป็นแฮ็กและไม่แนะนำก็ตาม) อะไรก็ตามที่จะใช้รูปทรงเรขาคณิตนั้นจะต้องได้รับสล็อตนั้นในบางช่วงดังนั้นค่อนข้างพูดมันไม่มีค่าใช้จ่ายเลย มันไม่มีความสำคัญในแอปพลิเคชันใด ๆ ในโลกแห่งความเป็นจริงที่คุณจะต้องทำการทดสอบจุดหลายจุด
Spacedman

มีข้อควรพิจารณามากกว่าความเร็วในการบัญชีสำหรับค่าใช้จ่าย ในการสร้างวัตถุใหม่ในเนมสเปซ R คุณกำลังใช้ RAM ที่จำเป็น หากนี่ไม่ใช่ปัญหาในชุดข้อมูลขนาดเล็กมันจะส่งผลกระทบต่อประสิทธิภาพการทำงานกับข้อมูลขนาดใหญ่ R มีการแสดงประสิทธิภาพแบบเส้นตรง ในขณะที่ข้อมูลที่ได้รับมีขนาดใหญ่ขึ้นต้องใช้เวลามาก หากคุณไม่จำเป็นต้องสร้างวัตถุเพิ่มเติมทำไมคุณจะ?
Jeffrey Evans

1
เราไม่รู้ว่าจนกว่าฉันจะทำการทดสอบในตอนนี้
Spacedman

13

sp จัดให้มีรูปแบบที่สั้นลงเพื่อเลือกคุณสมบัติตามการแยกเชิงพื้นที่ตามตัวอย่าง OP:

pts[ply,]

ณ วันที่:

points(pts[ply,], col = 'red')

เบื้องหลังสิ่งนี้สั้นมาก

pts[!is.na(over(pts, geometry(ply))),]

สิ่งที่ควรทราบคือมีgeometryวิธีการที่ลดคุณสมบัติ: overเปลี่ยนพฤติกรรมหากอาร์กิวเมนต์ที่สองมีคุณลักษณะหรือไม่ (นี่คือความสับสนของ OP) วิธีนี้ใช้ได้กับคลาส Spatial * ทั้งหมดspถึงแม้ว่าบางoverวิธีจะต้องใช้rgeosดูบทความสั้น ๆนี้เพื่อดูรายละเอียดเช่นกรณีของการแข่งขันหลายรายการสำหรับรูปหลายเหลี่ยมที่ทับซ้อนกัน


ดีแล้วที่รู้! ฉันไม่รู้วิธีเรขาคณิต
Jeffrey Evans

2
ยินดีต้อนรับสู่เว็บไซต์ของเรา Edzer - ยินดีที่ได้พบคุณที่นี่!
whuber

1
ขอบคุณ Bill - มันเริ่มเงียบขึ้นเมื่อstat.ethz.ch/pipermail/r-sig-geoหรือบางทีเราควรพัฒนาซอฟต์แวร์ที่ทำให้เกิดปัญหามากขึ้น! ;-)
Edzer Pebesma

6

คุณมาถูกทางมากกว่า rowname ของวัตถุที่ส่งคืนสอดคล้องกับดัชนีแถวของคะแนน คุณสามารถใช้วิธีการที่แน่นอนด้วยการเพิ่มโค้ดเพียงไม่กี่บรรทัด

library(sp)
set.seed(357)

pts <- data.frame(x=rnorm(100), y=rnorm(100), var1=runif(100), 
                  var2=sample(letters, 100, replace=TRUE))
  coordinates(pts) <- ~ x + y

ply <- matrix(c(-1,-1, 1,-1, 1,1, -1,1, -1,-1), ncol=2, byrow=TRUE)
  ply <- SpatialPolygons(list(Polygons(list(Polygon(ply)), ID=1)))
    ply <- SpatialPolygonsDataFrame(Sr=ply, data=data.frame(polyvar=357))

# Subset points intersecting polygon
prid <- over(pts,ply)
  ptid <- na.omit(prid) 
    pt.poly <- pts[as.numeric(as.character(row.names(ptid))),]  

plot(pts)
  axis(1); axis(2)
    plot(ply, add=TRUE, border="red")
      plot(pt.poly,pch=19,add=TRUE) 

ผิด - ชื่อแถวของวัตถุที่ถูกส่งคืนนั้นสอดคล้องกับดัชนีแถวin_this_caseโดยทั่วไปชื่อแถวดูเหมือนจะเป็นชื่อแถวของคะแนนซึ่งอาจไม่ได้เป็นตัวเลข คุณสามารถแก้ไขโซลูชันของคุณเพื่อทำการจับคู่อักขระซึ่งอาจทำให้มีประสิทธิภาพมากขึ้น
Spacedman

@Sapcedman อย่าเชื่อฟัง วิธีแก้ไขไม่ถูกต้อง หากคุณต้องการเซตย่อยคะแนนให้กับชุดรูปหลายเหลี่ยมหรือกำหนดค่ารูปหลายเหลี่ยมเพื่อให้คะแนนฟังก์ชัน over ทำงานได้โดยไม่ต้องใช้การบีบบังคับ มีหลายอย่างที่ต้องดำเนินการทางม้าลายเมื่อคุณมีวัตถุที่เกิดขึ้น คุณแก้ปัญหาของการบีบบังคับกับวัตถุ SpatialPolygon สร้างค่าใช้จ่ายที่จำเป็นมากเพราะการดำเนินการนี้สามารถทำได้โดยตรงบนวัตถุ SpatialPolygonDataFrame โดยก่อนที่คุณจะแก้ไขโพสต์ให้แน่ใจว่าคุณถูกต้อง คำว่า
เจฟฟรีย์อีแวนส์

ฉันได้เพิ่มมาตรฐานในโพสต์ของฉันและพบปัญหาอื่นเกี่ยวกับการทำงานของคุณ นอกจากนี้ "แพคเกจคือชุดของฟังก์ชั่น R ข้อมูลและรหัสที่รวบรวมในรูปแบบที่กำหนดไว้อย่างดีไดเรกทอรีที่จัดเก็บแพคเกจจะถูกเรียกว่าห้องสมุด"
Spacedman

ในขณะที่คุณถูกต้องทางเทคนิคเกี่ยวกับ "แพคเกจ" กับ "ห้องสมุด" ของคุณกำลังโต้เถียงความหมาย ฉันเพิ่งขอตัวแก้ไขแบบจำลองเชิงนิเวศวิทยาว่าเราเปลี่ยนการใช้ "แพ็คเกจ" (ซึ่งอันที่จริงฉันชอบ) เป็น "ไลบรารี่" ประเด็นของฉันคือพวกเขากลายเป็นคำที่ใช้แทนกันได้และเรื่องของความชอบ
Jeffrey Evans

1
"ถูกต้องทางเทคนิค" ตามที่ดร. เชลดอนคูเปอร์เคยกล่าวไว้ว่า "ถูกต้องที่สุด" การแก้ไขนั้นเป็นความผิดทางเทคนิคซึ่งเป็นความผิดที่เลวร้ายที่สุด
Spacedman

4

นี่คือสิ่งที่คุณเป็นหรือไม่

หมายเหตุหนึ่งเมื่อแก้ไข: apply()จำเป็นต้องใช้การโทรเพื่อตัดเพื่อทำให้งานนี้กับSpatialPolygonsวัตถุที่กำหนดเองซึ่งอาจมีคุณลักษณะรูปหลายเหลี่ยมมากกว่าหนึ่งรายการ ขอขอบคุณ @Spacedman ที่ให้ฉันสาธิตวิธีการใช้สิ่งนี้กับกรณีทั่วไปมากขึ้น

library(rgeos)
pp <- pts[apply(gIntersects(pts, ply, byid=TRUE), 2, any),]


## Confirm that it works
pp[1:5,]
#              coordinates       var1 var2
# 2 (-0.583205, -0.877737) 0.04001092    v
# 3   (0.394747, 0.702048) 0.58108350    v
# 5    (0.7668, -0.946504) 0.85682609    q
# 6    (0.31746, 0.641628) 0.13683264    y
# 9   (-0.469015, 0.44135) 0.13968804    m

plot(pts)
plot(ply, border="red", add=TRUE)
plot(pp, col="red", add=TRUE)

ล้มเหลวอย่างน่ากลัวหากplyมีคุณลักษณะมากกว่าหนึ่งเพราะgIntersectsส่งคืนเมทริกซ์ที่มีหนึ่งแถวสำหรับแต่ละคุณลักษณะ คุณอาจกวาดแถวเป็นค่า TRUE ได้
Spacedman

@Spacedman - บิงโก apply(gIntersects(pts, ply, byid=TRUE), 2, any)ต้องทำ ในความเป็นจริงฉันจะไปข้างหน้าและสลับคำตอบไปที่นั้นเพราะมันครอบคลุมกรณีของรูปหลายเหลี่ยมเดียวเช่นกัน
Josh O'Brien

anyอา อาจเร็วกว่ารุ่นที่ฉันเพิ่งเทียบเคียง
Spacedman

@ Spacerman - จากการทดสอบอย่างรวดเร็วของฉันดูเหมือนobrienและrowlings2เรียกใช้คอและลำคอด้วยobrien อาจจะเร็วขึ้น 2%
Josh O'Brien

@ JoshO'Brien เราจะใช้คำตอบนี้กับรูปหลายเหลี่ยมได้อย่างไร นั่นคือppควรจะมีสิ่งIDที่บ่งบอกถึงจุดที่มีรูปหลายเหลี่ยมอยู่
code123

4

นี่เป็นวิธีที่เป็นไปได้โดยใช้rgeosแพ็คเกจ โดยพื้นฐานแล้วจะใช้ประโยชน์จากgIntersectionฟังก์ชั่นที่ช่วยให้คุณสามารถตัดกันสองspวัตถุ ด้วยการแยก ID ของจุดเหล่านั้นที่อยู่ในรูปหลายเหลี่ยมคุณจะสามารถเซตย่อยต้นฉบับของคุณSpatialPointsDataFrameและรักษาข้อมูลที่เกี่ยวข้องทั้งหมดได้ รหัสนี้เกือบจะอธิบายด้วยตนเอง แต่ถ้ามีคำถามใด ๆ โปรดอย่าลังเล!

# Required package
library(rgeos)

# Intersect polygons and points, keeping point IDs
pts.intersect <- gIntersection(ply, pts, byid = TRUE)

# Extract point IDs from intersected data
pts.intersect.strsplit <- strsplit(dimnames(pts.intersect@coords)[[1]], " ")
pts.intersect.id <- as.numeric(sapply(pts.intersect.strsplit, "[[", 2))

# Subset original SpatialPointsDataFrame by extracted point IDs
pts.extract <- pts[pts.intersect.id, ]

head(coordinates(pts.extract))
              x          y
[1,] -0.5832050 -0.8777367
[2,]  0.3947471  0.7020481
[3,]  0.7667997 -0.9465043
[4,]  0.3174604  0.6416281
[5,] -0.4690151  0.4413502
[6,]  0.4765213  0.6068021

head(pts.extract)
         var1 var2
2  0.04001092    v
3  0.58108350    v
5  0.85682609    q
6  0.13683264    y
9  0.13968804    m
10 0.97144627    o

1
ควรtmpเป็นpts.intersectอย่างไร นอกจากนี้การแยกวิเคราะห์ dimname ที่ส่งคืนเช่นนั้นขึ้นอยู่กับพฤติกรรมที่ไม่มีเอกสาร
Spacedman

@Spacedman คุณถูกต้องtmpลืมลบออกเมื่อทำรหัสเสร็จ dimnamesนอกจากนี้คุณถูกต้องเกี่ยวกับการแยก นี่เป็นวิธีแก้ปัญหาที่รวดเร็วในการให้คำตอบที่รวดเร็วแก่ผู้ถามและมีแนวทางที่ดีขึ้น (และเป็นสากลมากขึ้น) ตัวอย่างเช่นคุณ :-)
fdetsch

1

มีวิธีแก้ปัญหาที่ง่ายมากในการใช้spatialEcoห้องสมุด

library(spatialEco)

# intersect points in polygon
  pts <- point.in.poly(pts, ply)

# check plot
  plot(ply)
  plot(a, add=T)

# convert to data frame, keeping your data
  pts<- as.data.frame(pts)

ตรวจสอบผลลัพธ์:

pts

>             x          y       var1 var2 polyvar
> 2  -0.5832050 -0.8777367 0.04001092    v     357
> 3   0.3947471  0.7020481 0.58108350    v     357
> 5   0.7667997 -0.9465043 0.85682609    q     357
> 6   0.3174604  0.6416281 0.13683264    y     357
> 9  -0.4690151  0.4413502 0.13968804    m     357
> 10  0.4765213  0.6068021 0.97144627    o     357
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.