如何在SpatialPointsDataFrame上覆盖多边形并保留SPDF数据?


17

我有SpatialPointsDataFrame一些其他数据。我想提取多边形内的那些点,同时保留SPDF对象及其对应的数据。

到目前为止,我运气不佳,只能通过一个通用ID进行匹配和合并,但这只能工作,因为我已经使用单个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 2013年

Answers:


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
> 

对于怀疑者,以下证据表明转换开销不是问题:

有两个功能-首先是Jeffrey Evans的方法,然后是我的原始方法,然后是被黑的转换,然后是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多边形中带有for点的覆盖将产生一个输出数据帧all NA,然后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代码中的交点,BUT 还是更快。我怀疑它的prepared geometryGEOS技能,可以创建空间索引-是的,prepared=FALSE它需要更长的时间,大约5.5秒。

我很惊讶,没有一个函数可以直接返回索引或点。当我splancs20年前写的时候,多边形点函数同时具有...


太好了,它也适用于多个多边形(我在Joshua的答案中添加了一个示例来使用)。
RomanLuštrik13年

对于大型的多边形数据集,强制转换为SpatialPolygons对象会产生大量开销,并且不是必需的。对SpatialPolygonsDataFrame应用“ over”将返回行索引,该索引可用于子集点。请参阅下面的示例。
Jeffrey Evans

一个很大的开销?它实际上只是从SpatialPolygonsDataFrame对象中获取@polygons插槽。您甚至可以通过将SpatialPolygonsDataFrame的类重新分配为“ SpatialPolygons”来“伪造”它(尽管这很hacky,不建议这样做)。任何要使用几何图形的东西无论如何都必须在某个阶段获得该插槽,因此相对而言根本没有任何开销。无论如何,在任何实际应用程序中,它都是微不足道的,您将在其中进行点多边形测试。
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的困惑)。该工程进行的跨所有空间*类sp,虽然有些over方法需要rgeos,看到这个小插曲的详细信息,如重叠的多边形多场比赛的情况下。


很高兴知道!我不知道几何方法。
杰弗里·埃文斯

2
欢迎来到我们的网站Edzer,很高兴在这里见到您!
豪伯

1
谢谢Bill-stat.ethz.ch/pipermail/r-sig-geo上它变得越来越安静,或者我们应该开发引起更多麻烦的软件!;-)
Edzer Pebesma 2015年

6

您的选择正确了。返回对象的行名与点的行索引相对应。您只需添加几行代码即可实现您的确切方法。

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.
杰弗里埃文斯

我在帖子中添加了一些基准,并发现了您的功能的另一个问题。另外,“软件包是R函数,数据和已编译代码的良好定义的集合。存储软件包的目录称为库”
Spacedman

尽管您在技术上对“包”和“库”是正确的,但是您在争论语义。我刚刚收到一个生态模型编辑器的请求,要求我们将“包”的使用(实际上是我的偏好)更改为“库”。我的观点是,它们正在成为可互换的术语和优先事项。
Jeffrey Evans

1
Sheldon Cooper博士曾经说过“技术上正确”是“最好的正确”。该编辑器在技术上是错误的,这是最严重的错误。
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

@Spacedman -从我的快速测试,它看起来像obrienrowlings2并驾齐驱,具有obrien 也许 2%的速度。
Josh O'Brien

@ JoshO'Brien如何在多个多边形上使用此答案?那pp应该有一个a 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
应该tmppts.intersect?另外,像这样解析返回的暗名取决于未记录的行为。
Spacedman

@Spacedman,您是对的tmp,在完成代码后忘记将其删除。另外,解析正确dimnames。这是一种快速的解决方案,可以为提问者提供快速的答案,并且肯定会有更好(且更通用)的方法,例如您的方法:-)
fdetsch 2013年

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.