bir poligon SpatialPointsDataFrame üzerine nasıl yerleşir ve SPDF verileri korunur?


17

Bir var SpatialPointsDataFramebazı ek bilgilerle. Bu noktaları bir çokgenin içine çıkarmak ve aynı zamanda SPDFnesneyi ve karşılık gelen verilerini korumak istiyorum .

Şimdiye kadar çok az şansım vardı ve ortak bir kimlikle eşleştirme ve birleştirme işlemine başvurdum, ancak bu sadece bireysel IDS ile veri ızgarası yaptığım için çalışıyor.

İşte kısa bir örnek, kırmızı karenin içindeki noktaları arıyorum.

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

En belirgin yaklaşım kullanmak olacaktır over, ancak bu çokgenden verileri döndürür.

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

1
Tekrarlanabilir bir örnek sağladığınız için teşekkür ederiz. Bir sorunu anlamaya çalışırken her zaman yardımcı olur!
fdetsch

Yanıtlar:


21

Yardımdan 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.

İstemiyorsun, mutfakta annene dönüştürmek Yani eğer SpatialPolygonsDataFramehiç SpatialPolygonssizin indeksler bir vektör geri almak ve üzerinde puan subsetine edebilirsiniz 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
> 

Şüpheliler için, dönüşüm yükünün bir sorun olmadığını gösteren kanıtlar:

İki işlev - önce Jeffrey Evans'ın yöntemi, sonra orijinalim, sonra saldırıya uğramış dönüşümüm, ardından gIntersectsJosh O'Brien'ın cevabına dayanan bir sürüm :

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,]
}

Şimdi gerçek dünya örneği için, columbusveri kümesine bazı rastgele noktalar dağıttım :

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

İyi görünüyor

plot(columbus)
points(pts)

İşlevlerin aynı şeyi yaptığını kontrol edin:

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

Kıyaslama için 500 kez çalıştırı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 

Benim sezgiye göre, bu büyük bir yük değil, aslında tüm satır dizinleri karakter ve geri dönüştürmek ya da eksik değerleri almak için na.omit çalıştırmak daha az bir ek yük olabilir. Bu arada evansfonksiyonun başka bir arıza moduna yol açar ...

Çokgenler veri çerçevesinin bir satırı hepsi ise NA(ki bu tamamen geçerlidir), o zaman SpatialPolygonsDataFrameçokgendeki noktalara yönelik bindirme, tüm s'lerle birlikte bir çıkış veri çerçevesi oluşturur ve NAbu evans()da aşağıdakileri düşürür:

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

ANC gIntersects, C kodundan ziyade R'deki kavşakları kontrol etmek için matrisi süpürmek zorunda kalsa bile daha hızlıdır. prepared geometryGEOS'un yeteneklerinden, uzaysal dizinler oluşturduğundan şüpheleniyorum - evet, prepared=FALSEbiraz daha uzun sürüyor, yaklaşık 5.5 saniye.

Endeksleri veya puanları doğrudan geri döndürmek için bir fonksiyon olmadığına şaşırdım. splancs20 yıl önce yazdığımda , çokgen noktası fonksiyonlarının her ikisi de ...


Harika, bu aynı zamanda birden çok çokgen için de çalışıyor (Joshua'nın cevabına bir örnek ekledim).
Roman Luštrik

Büyük poligon veri kümeleri ile bir SpatialPolygons nesnesine zorlama yükü çok fazladır ve gerekli değildir. Bir SpatialPolygonsDataFrame öğesine "over" uygulanması, noktaları alt kümeye koymak için kullanılabilen satır dizinini döndürür. Aşağıdaki örneğime bakın.
Jeffrey Evans

Çok fazla yük? Esasen sadece @polygons yuvasını SpatialPolygonsDataFrame nesnesinden alıyor. Hatta bir SpatialPolygonsDataFrame sınıfını "SpatialPolygons" olacak şekilde yeniden atayarak 'taklit edebilirsiniz' (her ne kadar hacky ve önerilmez). Geometriyi kullanacak herhangi bir şey, o yuvayı yine de bir aşamada almak zorunda kalacak, bu yüzden nispeten ek yükü yok. Daha sonra bir çok nokta-çokgen testi yapacağınız herhangi bir gerçek dünya uygulamasında önemsizdir.
Spacedman

Genel giderlerin muhasebeleştirilmesinde hız hususlarından daha fazlası vardır. R ad alanında yeni bir nesne oluştururken gerekli RAM'i kullanıyorsunuz. Küçük bir veri kümesinde bunun bir sorun olmadığı durumlarda, büyük verilerle performansı etkileyecektir. R, doğrusal bir performans ölümü sergiler. Veri büyüdükçe performans da dingir. Ek bir nesne yaratmanız gerekmiyorsa neden?
Jeffrey Evans

1
Şu ana kadar test edene kadar bunu bilmiyorduk.
Spacedman

13

sp OP örneğini izleyerek, uzamsal kavşağa dayalı özellikleri seçmek için daha kısa bir form sağlar:

pts[ply,]

itibariyle:

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

Perde arkasında bu kısa

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

Unutulmaması gereken şey, geometrynitelikleri düşüren bir yöntem olmasıdır: overikinci argümanında nitelikler varsa veya yoksa (bu OP'nin karışıklığıydı) davranışı değiştirir. Bu sp, bazı overyöntemler gerektirse de tüm Spatial * sınıflarında çalışır , örneğin üst üste gelen çokgenler için birden çok eşleşme olması gibi ayrıntılar için bu vinyete rgeosbakın .


Bunu bildiğim iyi oldu! Geometri yönteminin farkında değildim.
Jeffrey Evans

2
Sitemize hoş geldiniz Edzer - sizi burada görmek çok güzel!
whuber

1
Teşekkürler Bill - stat.ethz.ch/pipermail/r-sig-geo üzerinde daha sessiz oluyor , ya da belki daha fazla soruna neden olan yazılım geliştirmeliyiz! ;-)
Edzer Pebesma

6

Üzerinde doğru yoldaydın. Döndürülen nesnenin rown adları, noktaların satır dizinine karşılık gelir. Kesin yaklaşımınızı sadece birkaç ek kod satırıyla uygulayabilirsiniz.

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) 

Yanlış - döndürülen nesnenin rown adları in_this_case satır dizinine karşılık gelir - genel olarak satır adları, noktaların satır adları gibi görünür - hatta sayısal bile olmayabilir. Bir karakter eşleşmesi yapmak için çözümünüzü biraz daha sağlam hale getirecek şekilde değiştirebilirsiniz.
Spacedman

@Sapcedman, Çok dogmatik olma. Çözüm yanlış değil. Noktaları bir çokgen kümesine alt kümeye ayarlamak veya noktalara çokgen değerleri atamak istiyorsanız, over işlevi zorlama olmadan çalışır. Ortaya çıkan nesne üzerinde bir kez yaya geçidi gerçekleştirmek için birden fazla oldu. Bir SpatialPolygon nesnesine zorlama çözümü, bu işlem doğrudan SpatialPolygonDataFrame nesnesi üzerinde gerçekleştirilebileceği için önemli ölçüde gerekli ek yükü oluşturur. Bu arada bir yayını düzenlemeden önce haklı olduğunuzdan emin olun. Kütüphane ve paket terimi R'de birbirinin yerine kullanılmaktadır.
Jeffrey Evans

Yazıma bazı ölçütler ekledim ve işlevinizle ilgili başka bir sorun tespit ettim. Ayrıca "Paketler iyi tanımlanmış bir biçimde R fonksiyonları, veriler ve derlenmiş kod koleksiyonlarıdır. Paketlerin saklandığı dizine kütüphane denir"
Spacedman

Eğer "paket" ve "kütüphane" ile ilgili teknik olarak doğru olsa da, anlambilim tartışıyor. Ekolojik Modelleme editörüne "paket" (aslında benim tercihim olan) kullanımımızı "kütüphane" olarak değiştirme isteğimiz vardı. Demek istediğim, birbirlerinin yerine geçebilecek terimler ve tercih meselesi haline gelmeleri.
Jeffrey Evans

1
"teknik olarak doğru" Dr Sheldon Cooper bir keresinde belirttiği gibi, "doğru olanı en iyi türüdür". Bu editör teknik olarak yanlış, bu da en kötüsü.
Spacedman

4

Arkanda olan bu mu?

Düzenlemede bir not: Kaydırma çağrısı , muhtemelen birden fazla çokgen özelliği içeren apply()rastgele SpatialPolygonsnesnelerle çalışabilmesi için gereklidir . @Spacedman'a bunu daha genel duruma nasıl uygulayacağımı göstermem için teşekkürler.

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)

plyBirden fazla özelliği varsa korkunç bir şekilde başarısız olur , çünkü gIntersectsher özellik için bir satır içeren bir matris döndürür. Muhtemelen satırları DOĞRU bir değer için tarayabilirsiniz.
Spacedman

@ Spacedman - Bingo. Yapmam gerek apply(gIntersects(pts, ply, byid=TRUE), 2, any). Aslında, devam edeceğim ve cevabı buna geçireceğim, çünkü tek bir çokgen örneğini de kapsıyor.
Josh O'Brien

Ah any,. Bu, kıyasladığım versiyondan biraz daha hızlı olabilir.
Spacedman

@Spacedman - Hızlı testlerimden, belki % 2 daha hızlı , boyun ve boyun gibi görünüyor obrienve rowlings2çalışıyor . obrien
Josh O'Brien

@ JoshO'Brien bu cevabı birçok çokgende nasıl kullanabilir? Bu, noktaların hangi poligonda bulunduğunu gösteren ppbir olmalıdır ID.
kod123

4

İşte rgeospaketi kullanarak olası bir yaklaşım . Temel olarak, gIntersectioniki spnesneyle kesişmenize izin veren işlevi kullanır . Çokgen içinde bulunan bu noktaların kimliklerini çıkararak, daha sonra orijinalinizi alt kümeye ayırarak SpatialPointsDataFramekarşılık gelen tüm verileri saklayabilirsiniz. Kod neredeyse kendi kendini açıklıyor, ancak herhangi bir sorunuz varsa, lütfen sormaya çekinmeyin!

# 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
Meli tmpolmak pts.intersect? Ayrıca, döndürülen dimnames'in bu şekilde ayrıştırılması belgelenmemiş davranışa bağlıdır.
Spacedman

@Spacedman, haklısın tmp, kodu bitirirken kaldırmayı unuttum. Ayrıca dimnames,. Bu, sorgulayıcıya hızlı bir cevap vermek için hızlı bir çözümdü ve elbette daha iyi (ve daha evrensel) yaklaşımlar var, örneğin sizinki :-)
fdetsch

1

Bir yoktur son derece basit bir çözüm kullanarak spatialEcokütüphane.

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)

Sonucu kontrol edin:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.