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

回答:


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.

に変換するSpatialPolygonsDataFrameSpatialPolygons、インデックスのベクトルが返され、ポイントをサブセット化できます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
> 

疑わしい人のために、変換オーバーヘッドが問題ではないという証拠を以下に示します。

2つの機能-最初にJeffrey Evansの方法、次に元の方法、次にハッキングされた変換、次にgIntersectsJosh 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そのポリゴンのポイントのオーバーレイは、すべてNAのs を含む出力データフレームを生成し、それ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、CコードではなくRで交差をチェックするためにマトリックスをスイープする必要がある場合でも、高速です。prepared geometry空間インデックスを作成するGEOS のスキルに疑いがあります-ええ、prepared=FALSEそれは少し長く、約5.5秒かかります。

インデックスまたはポイントを直接返す機能がないことに驚いています。splancs20年前に書いたとき、ポイントインポリゴン関数には両方がありました...


素晴らしいことに、これは複数のポリゴンに対しても機能します(ジョシュアの答えに手を加える例を追加しました)。
ロマンルシュトリック

大きなポリゴンデータセットでは、SpatialPolygonsオブジェクトへの強制は多くのオーバーヘッドであり、必要ありません。SpatialPolygonsDataFrameに「オーバー」を適用すると、ポイントのサブセット化に使用できる行インデックスが返されます。以下の例を参照してください。
ジェフリーエヴァンス

多くのオーバーヘッドの?基本的には、SpatialPolygonsDataFrameオブジェクトから@polygonsスロットを取得するだけです。SpatialPolygonsDataFrameのクラスを「SpatialPolygons」に再割り当てすることで「偽造」することもできます(ただし、これはハッキングであり、推奨されません)。ジオメトリを使用するものはいずれにせよ、いずれかの段階でそのスロットを取得する必要があるため、相対的に言えば、オーバーヘッドはまったくありません。いずれにせよ、ポイントポリゴンテストのロードを実行する実際のアプリケーションでは、とにかく重要ではありません。
Spacedman

オーバーヘッドの計算には、速度以上の考慮事項があります。R名前空間に新しいオブジェクトを作成する際に、必要なRAMを使用しています。これが小さなデータセットで問題にならない場合、大きなデータのパフォーマンスに影響します。Rは、線形性能のダイオフを示します。データが大きくなると、パフォーマンスが低下します。追加のオブジェクトを作成する必要がない場合は、なぜですか?
ジェフリーエヴァンス

1
私たちは今それをテストするまで、それを知りませんでした。
Spacedman

13

sp OPの例に従って、空間交差に基づいてフィーチャを選択するための短い形式を提供します。

pts[ply,]

のように:

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

舞台裏ではこれは

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

注意すべき点は、geometry属性をドロップするメソッドがあることですover。2番目の引数に属性があるかどうかによって動作を変更します(これはOPの混乱でした)。これはのすべてのSpatial *クラスで機能しますが、sp一部のoverメソッドではが必要rgeosになります。詳細については、このビネットを参照してください。たとえば、重複するポリゴンが複数一致する場合です。


知っておきたい!私は幾何学の方法を知りませんでした。
ジェフリーエヴァンス

2
Edzerのサイトへようこそ。ここでお会いできてうれしいです。
whuber

1
ビルに感謝します-stat.ethz.ch/pipermail/r-sig-geoでもっと静かになっている、もっとトラブルを起こすソフトウェアを開発する必要があるかもしれません!;-)
Edzer Pebesma

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

「パッケージ」と「ライブラリ」に関して技術的には正しいのですが、セマンティクスを主張しています。エコロジカルモデリングエディターから、「パッケージ」(実際には私の好み)の使用を「ライブラリ」に変更するように要求されました。私のポイントは、それらが交換可能な用語と好みの問題になっているということです。
ジェフリーエヴァンス

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がある場合、機能ごとに1行のマトリックスを返すため、ひどく失敗します。おそらく、TRUE値の行をスイープできます。
Spacedman

@Spacedman-ビンゴ。する必要がありますapply(gIntersects(pts, ply, byid=TRUE), 2, any)。実際、単一のポリゴンの場合も含まれるため、先に進んでその答えを切り替えます。
ジョシュオブライエン

ああ、any。これは、ベンチマークしたばかりのバージョンよりもわずかに速いかもしれません。
Spacedman

@Spacedman-私の簡単なテストから、首と首が同じように見えobrienrowlings2走り、obrien おそらく 2%速くなるでしょう
ジョシュオブライエン

@ JoshO'Brienどうすればこの答えを多くのポリゴンで使用できますか?つまり、ポイントがどのポリゴンにあるかを示すがpp必要IDです。
-code123

4

rgeosパッケージを使用した可能なアプローチを次に示します。基本的に、gIntersection2つの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?また、返されたdimnamesの解析は、文書化されていない動作に依存しています。
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.