Làm cách nào để phủ một đa giác trên SpatialPointsDataFrame và bảo toàn dữ liệu SPDF?


17

Tôi có một SpatialPointsDataFramevới một số dữ liệu bổ sung. Tôi muốn trích xuất những điểm đó bên trong một đa giác, đồng thời, bảo toàn SPDFđối tượng và dữ liệu tương ứng của nó.

Cho đến nay tôi đã có rất ít may mắn và sử dụng để khớp và hợp nhất thông qua một ID chung, nhưng điều này chỉ hoạt động vì tôi có dữ liệu với các IDS riêng lẻ.

Đây là một ví dụ nhanh, tôi đang tìm kiếm các điểm bên trong hình vuông màu đỏ.

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

Cách tiếp cận rõ ràng nhất sẽ là sử dụng over, nhưng điều này trả về dữ liệu từ đa giác.

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

1
Cảm ơn đã cung cấp một ví dụ tái sản xuất. Luôn luôn giúp đỡ khi cố gắng để hiểu một vấn đề!
fdetsch

Câu trả lời:


21

Từ sự sp::overgiúp đỡ:

 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.

Vì vậy, nếu bạn chuyển đổi của bạn SpatialPolygonsDataFrameđể SpatialPolygonsbạn lấy lại một vector của các chỉ mục và bạn có thể tập hợp con điểm của bạn về 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
> 

Đối với những người nghi ngờ, đây là bằng chứng cho thấy chi phí chuyển đổi không phải là vấn đề:

Hai chức năng - đầu tiên là phương pháp của Jeffrey Evans, sau đó là bản gốc của tôi, sau đó là chuyển đổi bị hack của tôi, sau đó là phiên bản dựa trên gIntersectscâu trả lời của 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,]
}

Bây giờ là một ví dụ trong thế giới thực, tôi đã phân tán một số điểm ngẫu nhiên trên tập columbusdữ liệu:

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

Có vẻ tốt

plot(columbus)
points(pts)

Kiểm tra các chức năng đang làm điều tương tự:

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

Và chạy 500 lần cho điểm chuẩn:

> 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 

Theo trực giác của tôi, nó không phải là một chi phí lớn, trên thực tế, nó có thể ít chi phí hơn là chuyển đổi tất cả các chỉ mục hàng thành ký tự và trở lại hoặc chạy na.omit để nhận các giá trị bị thiếu. Điều này tình cờ dẫn đến một chế độ thất bại khác của evanschức năng ...

Nếu một hàng của khung dữ liệu đa giác là tất cả NA(hoàn toàn hợp lệ), thì lớp phủ với SpatialPolygonsDataFramecác điểm trong đa giác đó sẽ tạo ra một khung dữ liệu đầu ra với tất cả NAs, evans()sau đó sẽ giảm:

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

NHƯNG gIntersectsnhanh hơn, ngay cả khi phải quét ma trận để kiểm tra các giao điểm trong R thay vì trong mã C. Tôi nghi ngờ các prepared geometrykỹ năng của GEOS, tạo ra các chỉ mục không gian - vâng, với prepared=FALSEthời gian lâu hơn một chút, khoảng 5,5 giây.

Tôi ngạc nhiên không có chức năng nào trả thẳng các chỉ số hoặc điểm. Khi tôi viết splancscách đây 20 năm, các hàm đa giác có cả ...


Tuyệt vời, điều này cũng hoạt động cho nhiều đa giác (Tôi đã thêm một ví dụ để chơi với câu trả lời của Joshua).
Roman Luštrik

Với các bộ dữ liệu đa giác lớn ép buộc vào một đối tượng SpatialPolygons là rất nhiều chi phí và không cần thiết. Áp dụng "kết thúc" cho SpatialPolygonsDataFrame trả về chỉ mục hàng có thể được sử dụng để tập hợp các điểm. Xem ví dụ của tôi dưới đây.
Jeffrey Evans

Rất nhiều chi phí? Về cơ bản, nó chỉ lấy vị trí @polygons từ đối tượng SpatialPolygonsDataFrame. Bạn thậm chí có thể 'giả mạo' nó bằng cách gán lại lớp của SpatialPolygonsDataFrame thành "SpatialPolygons" (mặc dù điều này là hack và không được khuyến nghị). Bất cứ điều gì sẽ sử dụng hình học sẽ phải có được khe đó ở một số giai đoạn, vì vậy nói một cách tương đối là không có chi phí nào cả. Dù sao nó cũng không đáng kể trong bất kỳ ứng dụng trong thế giới thực nào mà bạn sẽ thực hiện một loạt các bài kiểm tra đa giác điểm.
Spainedman

Có nhiều hơn những cân nhắc về tốc độ trong kế toán cho chi phí. Khi tạo một đối tượng mới trong không gian tên R, bạn đang sử dụng RAM cần thiết. Trong trường hợp đây không phải là vấn đề trong một bộ dữ liệu nhỏ, nó sẽ ảnh hưởng đến hiệu suất với dữ liệu lớn. R không có hiệu suất tuyến tính chết đi. Khi dữ liệu có hiệu suất lớn hơn sẽ mất một ding. Nếu bạn không cần tạo một đối tượng bổ sung, tại sao bạn lại như vậy?
Jeffrey Evans

1
Chúng tôi không biết rằng cho đến khi tôi thử nghiệm nó.
Spainedman

13

sp cung cấp một hình thức ngắn hơn để chọn các tính năng dựa trên giao lộ không gian, theo ví dụ OP:

pts[ply,]

kể từ:

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

Đằng sau hậu trường này là viết tắt của

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

Điều cần lưu ý là có một geometryphương thức loại bỏ các thuộc tính: overthay đổi hành vi nếu đối số thứ hai của nó có thuộc tính hay không (đây là sự nhầm lẫn của OP). Điều này hoạt động trên tất cả các lớp Spatial * sp, mặc dù một số overphương thức yêu cầu rgeos, hãy xem họa tiết này để biết chi tiết, ví dụ như trường hợp nhiều kết quả khớp cho đa giác chồng chéo.


Tốt để biết! Tôi đã không nhận thức được phương pháp hình học.
Jeffrey Evans

2
Chào mừng đến với trang web của chúng tôi, Edzer - thật vui khi gặp bạn ở đây!
whuber

1
Cảm ơn Bill - nó trở nên yên tĩnh hơn trên stat.ethz.ch/pipermail/r-sig-geo hoặc có lẽ chúng ta nên phát triển phần mềm gây ra nhiều rắc rối hơn! ;-)
Edzer Pebesma

6

Bạn đã đi đúng hướng với hơn. Các rownames của đối tượng trả về tương ứng với chỉ số hàng của các điểm. Bạn có thể thực hiện cách tiếp cận chính xác của mình chỉ bằng một vài dòng mã bổ sung.

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) 

Sai - tên gọi của đối tượng được trả về tương ứng với chỉ mục hàng in_this_case - nói chung, tên hàng dường như là tên hàng của các điểm - thậm chí có thể không phải là số. Bạn có thể sửa đổi giải pháp của mình để thực hiện khớp một ký tự có thể làm cho nó mạnh hơn một chút.
Spainedman

@Sapcedman, Đừng quá giáo điều. Giải pháp không sai. Nếu bạn muốn tập hợp các điểm vào một tập hợp đa giác hoặc gán các giá trị đa giác cho các điểm thì hàm over hoạt động mà không ép buộc. Có nhiều cách để thực hiện việc băng qua đường một khi bạn có kết quả trên đối tượng. Giải pháp của bạn về việc ép buộc đối tượng SpatialPolygon tạo ra chi phí cần thiết đáng kể vì thao tác này có thể được thực hiện trực tiếp trên đối tượng SpatialPolygonDataFrame. Nhân tiện trước khi bạn chỉnh sửa một bài đăng hãy chắc chắn rằng bạn đúng. Thuật ngữ thư viện và gói được sử dụng thay thế cho nhau trong R.
Jeffrey Evans

Tôi đã thêm một số điểm chuẩn vào bài đăng của mình và phát hiện ra một vấn đề khác với chức năng của bạn. Ngoài ra "Các gói là tập hợp các hàm R, dữ liệu và mã được biên dịch theo định dạng được xác định rõ. Thư mục nơi các gói được lưu trữ được gọi là thư viện"
Spacesman

Trong khi bạn đúng về mặt kỹ thuật liên quan đến "gói" so với "thư viện" thì ngữ nghĩa của bạn đang tranh cãi. Tôi vừa có một yêu cầu biên tập Mô hình sinh thái rằng chúng tôi thay đổi việc sử dụng "gói" (thực sự là sở thích của tôi) thành "thư viện". Quan điểm của tôi là chúng đang trở thành các thuật ngữ có thể hoán đổi cho nhau và là vấn đề ưu tiên.
Jeffrey Evans

1
"đúng về mặt kỹ thuật" như Tiến sĩ Sheldon Cooper từng nhận xét, "là loại chính xác nhất". Biên tập viên đó là sai về mặt kỹ thuật, đó là loại sai lầm tồi tệ nhất.
Spainedman

4

Đây có phải là những gì bạn đang theo đuổi?

Một lưu ý, khi chỉnh sửa: Cần có lệnh gọi apply()để thực hiện công việc này với các SpatialPolygonsđối tượng tùy ý , có thể chứa nhiều hơn một tính năng đa giác. Cảm ơn @Spacesman đã khuyến khích tôi trình bày cách áp dụng điều này cho trường hợp tổng quát hơn.

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)

Thất bại khủng khiếp nếu plycó nhiều hơn một tính năng, bởi vì gIntersectstrả về một ma trận với một hàng cho mỗi tính năng. Bạn có thể có thể quét các hàng cho giá trị TRUE.
Spainedman

@Spacesman - Bingo. Cần phải làm apply(gIntersects(pts, ply, byid=TRUE), 2, any). Trên thực tế, tôi sẽ tiếp tục và chuyển câu trả lời cho câu hỏi đó, vì nó cũng bao gồm cả trường hợp của một đa giác.
Josh O'Brien

Ah, any. Đó có thể là nhanh hơn một chút so với phiên bản tôi vừa điểm chuẩn.
Spainedman

@Spacesman - Từ các thử nghiệm nhanh của tôi, nó trông giống như obrienrowlings2chạy cổ và cổ, có obrien thể nhanh hơn 2%.
Josh O'Brien

@ JoshO'Brien làm thế nào người ta có thể sử dụng câu trả lời này trên nhiều đa giác? Đó là ppnên có một IDchỉ ra trong đó đa giác các điểm được đặt.
mã123

4

Đây là một cách tiếp cận có thể sử dụng rgeosgói. Về cơ bản, nó sử dụng gIntersectionchức năng cho phép bạn giao nhau hai spđối tượng. Bằng cách trích xuất ID của những điểm nằm trong đa giác, sau đó bạn có thể đặt lại bản gốc của mình SpatialPointsDataFrame, giữ tất cả dữ liệu tương ứng. Mã này gần như tự giải thích, nhưng nếu có bất kỳ câu hỏi nào, xin vui lòng hỏi!

# 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
Nên tmpđược pts.intersect? Ngoài ra, phân tích các tên miền được trả lại như thế tùy thuộc vào hành vi không có giấy tờ.
Spainedman

@Spacesman, bạn nói đúng tmp, quên xóa nó khi hoàn thành mã. Ngoài ra, bạn đúng về phân tích cú pháp dimnames. Đây là một giải pháp nhanh chóng để cung cấp cho người hỏi câu trả lời nhanh và chắc chắn có những cách tiếp cận tốt hơn (và phổ quát hơn), ví dụ như của bạn :-)
fdetsch

1

Có một giải pháp cực kỳ đơn giản sử dụng spatialEcothư viện.

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)

Kiểm tra kết quả:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.