Minimum n nokta içeren düzensiz ızgarayı nasıl oluşturabilirim?


20

Eşit dağılmamış noktalardan oluşan büyük bir (~ 1 milyon) örnek verildiğinde, belirtilen minimum miktarda n noktası içeren düzensiz ızgara oluşturmak (boyut olarak, ancak mümkünse şekil olarak düzensiz olabilir mi?) Mümkün mü ?

Bu tür bir ızgaranın jenerik 'hücrelerinin' tam olarak n sayıda veya en az n noktası içermesi benim için daha az önemlidir .

ArcGIS'te genvecgrid veya QGIS / mmgis'de Izgara Katmanı Oluştur gibi çözümlerin farkındayım, ancak hepsi boş hücrelerle (daha küçük sorun - bunları atabilirim) veya puan sayısına sahip hücrelerle sonuçlanacak düzenli ızgaralar oluşturacak n'den daha az (muhtemelen buradaki bazı araçları kullanarak bu hücreleri toplamak için bir çözüme ihtiyacım olduğu için daha büyük bir sorun mu var ?).

Boşuna dolaşıyorum ve hem ticari (ArcGIS ve uzantılar) hem de ücretsiz (Python, PostGIS, R) çözümlere açıkım.


1
Şebekenin ne kadar "düzenli" olması gerekir? Biraz hiyerarşik kümeleme yapıp yapamayacağınızı merak ediyorum ve sonra sadece ihtiyaçlarınızı karşılamak için dendrogramı kesip çıkarabilirsiniz (bu muhtemelen düzenli bir mekansal konfigürasyon olarak tanımlanan şeyi uzatsa da). CrimeStat belgelerinde bu tür kümelemeye ilişkin bazı iyi örnekler bulunmaktadır .
Andy W

5
"Düzensiz ızgara" ile ne demek istediğinizi tam olarak açıklar mısınız? Bu bir oksimoron gibi geliyor :-). Daha da önemlisi, bu egzersizin amacı ne olurdu? Ayrıca, ek ölçütlere veya kısıtlamalara ihtiyaç duyulduğuna dikkat edin: sonuçta, 1 milyon noktanın etrafına bir kare çizdiyseniz, bir ızgaranın parçası olarak kabul edilebilir ve n'den daha fazlasını içerecektir . Muhtemelen bu önemsiz çözümü umursamazsınız: ama neden olmasın, tam olarak?
whuber

@AndyW Teşekkürler. İyi fikir ve keşfetmeye değer. Bir bakacak. '
Izgara'nın

@whuber Ayrıca teşekkürler. Katılıyorum - ama böyle bir bölümlemeyi nasıl isimlendirebileceğimden emin değildim. Yukarıda belirtildiği gibi - ana motivasyonum veri gizliliği. (Nihai haritada gösteremediğim) beş noktaya sahip olmak onları onları kapsayan alanla temsil etmek istiyorum; ve ortalama / medyan / vb. değeri. Hepsini temsil eden bir dikdörtgen veya dışbükey gövde çizmenin mümkün olacağını kabul ediyorum - sanırım nihai veri gizliliği koruması bu olacak mı? ;] Ancak - bunu sınırlayıcı şekillerle temsil etmek daha yararlı olur, diyelim ki 10 özellik. O zaman - Hala mekansal kalıbı koruyabilirim.
radek

1
IMO açıklamanız göz önüne alındığında, bir tür enterpolasyon kullanacağım ve bir raster haritası göstereceğim (belki de minimum N'nizin boyutu, verileri düzeltmek için yeterli olacaktır). CrimeStat'a gelince, kullandığım en büyük dosyalar inandığım yaklaşık 100.000 vaka idi (ve kümelenme kesinlikle zaman alacaktı). Verilerinizi daha az vaka olarak temsil etmek ve yine de istediğiniz herhangi bir şey için istenen sonuçları elde etmek için verilerinizin genelleştirilmesini yapabilirsiniz. Gerçekten basit bir program, denemek ve kendiniz görmek için sadece birkaç dakikanızı ayırmanızı öneririm.
Andy W

Yanıtlar:


26

Görüyorum MerseyViking bir dörtlü tavsiye etti . Aynı şeyi önerecektim ve açıklamak için kod ve bir örnek. Kod yazılmıştır, Rancak Python'a kolayca bağlanmalıdır.

Fikir son derece basittir: noktaları x yönünde yaklaşık ikiye bölün, sonra iki yarıyı y-yönü boyunca, her seviyede alternatif yönler olacak şekilde, daha fazla bölme istenmeyene kadar bölün.

Amaç, gerçek nokta konumlarını gizlemek olduğundan, bölünmelere biraz rastgelelik kazandırmak yararlıdır . Bunu yapmanın hızlı basit bir yolu,% 50'den küçük bir rastgele miktar ayarlamak için bir kantil olarak bölmektir. Bu şekilde (a) bölme değerlerinin veri koordinatlarına denk gelmesi pek olası değildir, böylece noktalar, bölümleme tarafından oluşturulan çeyreklere ayrılacaktır ve (b) nokta koordinatlarının, dördüncülden tam olarak yeniden yapılandırılması imkansız olacaktır.

Amaç, kher dörtlü yaprak içinde minimum miktarda düğüm sağlamak olduğundan, sınırlı bir dörtlü form uygularız. (1) kümelenme noktalarını, her biri ve k2 * k-1 elemente sahip gruplara ve (2) kadranları eşleştirmeye destekleyecektir.

Bu Rkod, düğümleri ve terminal yapraklarını sınıflarına göre ayıran bir ağaç oluşturur. Sınıf etiketlemesi, aşağıda gösterilen çizim gibi işlem sonrası işlemleri hızlandırır. Kod, kimlikler için sayısal değerler kullanır. Bu ağaçta 52 derinliğe kadar çalışır (çiftler kullanarak; işaretsiz uzun tamsayılar kullanılıyorsa, maksimum derinlik 32'dir). Daha derin ağaçlar için (herhangi bir uygulamada olması pek olası değildir, çünkü en az k* 2 ^ 52 puan söz konusu olacaktır), kimliklerin dizeler olması gerekir.

quadtree <- function(xy, k=1) {
  d = dim(xy)[2]
  quad <- function(xy, i, id=1) {
    if (length(xy) < 2*k*d) {
      rv = list(id=id, value=xy)
      class(rv) <- "quadtree.leaf"
    }
    else {
      q0 <- (1 + runif(1,min=-1/2,max=1/2)/dim(xy)[1])/2 # Random quantile near the median
      x0 <- quantile(xy[,i], q0)
      j <- i %% d + 1 # (Works for octrees, too...)
      rv <- list(index=i, threshold=x0, 
                 lower=quad(xy[xy[,i] <= x0, ], j, id*2), 
                 upper=quad(xy[xy[,i] > x0, ], j, id*2+1))
      class(rv) <- "quadtree"
    }
    return(rv)
  }
  quad(xy, 1)
}

Bu algoritmanın özyinelemeli bölme ve fethetme tasarımının (ve sonuç olarak işlem sonrası algoritmaların çoğunun) zaman gereksiniminin O (m) ve RAM kullanımının O (n) olduğu anlamına gelir m. ve nnokta sayısıdır. hücre başına minimum noktaya bölünmesiyle morantılıdır n,k. Bu, hesaplama sürelerini tahmin etmek için kullanışlıdır. Örneğin, n = 10 ^ 6 noktasını 50-99 noktalı hücrelere (k = 50) bölmek 13 saniye alırsa, m = 10 ^ 6/50 = 20000. Bunun yerine 5-9'a kadar bölmek istiyorsanız hücre başına nokta (k = 5), m 10 kat daha büyüktür, bu nedenle zamanlama yaklaşık 130 saniyeye kadar çıkar. (Hücreler küçüldükçe bir dizi koordinatı aracı bölme işlemi hızlandığından, gerçek zamanlama sadece 90 saniyeydi.) Hücre başına k = 1 noktaya kadar gitmek için yaklaşık altı kat daha uzun sürecek Yine de dokuz dakika, ve kodun bundan biraz daha hızlı olmasını bekleyebiliriz.

Daha ileri gitmeden önce, bazı ilginç düzensiz aralıklı veriler üretelim ve sınırlı dörtlü (0,29 saniye geçen süre) oluşturalım:

Dörtlü ağaç

İşte bu grafikleri üretmek için kod. R'S polimorfizminden yararlanır : points.quadtreeörneğin, pointsişlev bir quadtreenesneye her uygulandığında çağrılır . Bunun gücü, noktaları küme tanımlayıcılarına göre renklendirme fonksiyonunun aşırı basitliğinde belirgindir:

points.quadtree <- function(q, ...) {
  points(q$lower, ...); points(q$upper, ...)
}
points.quadtree.leaf <- function(q, ...) {
  points(q$value, col=hsv(q$id), ...)
}

Izgarayı çizmek biraz daha zordur, çünkü dörtlü bölümleme için kullanılan eşiklerin tekrarlı olarak kesilmesini gerektirir, ancak aynı özyinelemeli yaklaşım basit ve zariftir. İsterseniz kadranların çokgen temsillerini oluşturmak için bir varyant kullanın.

lines.quadtree <- function(q, xylim, ...) {
  i <- q$index
  j <- 3 - q$index
  clip <- function(xylim.clip, i, upper) {
    if (upper) xylim.clip[1, i] <- max(q$threshold, xylim.clip[1,i]) else 
      xylim.clip[2,i] <- min(q$threshold, xylim.clip[2,i])
    xylim.clip
  } 
  if(q$threshold > xylim[1,i]) lines(q$lower, clip(xylim, i, FALSE), ...)
  if(q$threshold < xylim[2,i]) lines(q$upper, clip(xylim, i, TRUE), ...)
  xlim <- xylim[, j]
  xy <- cbind(c(q$threshold, q$threshold), xlim)
  lines(xy[, order(i:j)],  ...)
}
lines.quadtree.leaf <- function(q, xylim, ...) {} # Nothing to do at leaves!

Başka bir örnek olarak, 1.000.000 puan oluşturdum ve bunları her biri 5-9'luk gruplara ayırdım. Zamanlama 91.7 saniyeydi.

n <- 25000       # Points per cluster
n.centers <- 40  # Number of cluster centers
sd <- 1/2        # Standard deviation of each cluster
set.seed(17)
centers <- matrix(runif(n.centers*2, min=c(-90, 30), max=c(-75, 40)), ncol=2, byrow=TRUE)
xy <- matrix(apply(centers, 1, function(x) rnorm(n*2, mean=x, sd=sd)), ncol=2, byrow=TRUE)
k <- 5
system.time(qt <- quadtree(xy, k))
#
# Set up to map the full extent of the quadtree.
#
xylim <- cbind(x=c(min(xy[,1]), max(xy[,1])), y=c(min(xy[,2]), max(xy[,2])))
plot(xylim, type="n", xlab="x", ylab="y", main="Quadtree")
#
# This is all the code needed for the plot!
#
lines(qt, xylim, col="Gray")
points(qt, pch=".")

resim açıklamasını buraya girin


Bir CBS ile nasıl etkileşime girileceğine bir örnek olarak , tüm dört hücreli hücreleri shapefileskitaplığı kullanarak bir çokgen şekil dosyası olarak yazalım . Kod, kırpma rutinlerini taklit eder lines.quadtree, ancak bu kez hücrelerin vektör tanımlarını üretmesi gerekir. Bunlar shapefileskütüphane ile kullanım için veri çerçeveleri olarak çıkar .

cell <- function(q, xylim, ...) {
  if (class(q)=="quadtree") f <- cell.quadtree else f <- cell.quadtree.leaf
  f(q, xylim, ...)
}
cell.quadtree <- function(q, xylim, ...) {
  i <- q$index
  j <- 3 - q$index
  clip <- function(xylim.clip, i, upper) {
    if (upper) xylim.clip[1, i] <- max(q$threshold, xylim.clip[1,i]) else 
      xylim.clip[2,i] <- min(q$threshold, xylim.clip[2,i])
    xylim.clip
  } 
  d <- data.frame(id=NULL, x=NULL, y=NULL)
  if(q$threshold > xylim[1,i]) d <- cell(q$lower, clip(xylim, i, FALSE), ...)
  if(q$threshold < xylim[2,i]) d <- rbind(d, cell(q$upper, clip(xylim, i, TRUE), ...))
  d
}
cell.quadtree.leaf <- function(q, xylim) {
  data.frame(id = q$id, 
             x = c(xylim[1,1], xylim[2,1], xylim[2,1], xylim[1,1], xylim[1,1]),
             y = c(xylim[1,2], xylim[1,2], xylim[2,2], xylim[2,2], xylim[1,2]))
}

Noktaların kendileri read.shp(x, y) koordinatlarının bir veri dosyası kullanılarak veya içe aktarılarak doğrudan okunabilir .

Kullanım örneği:

qt <- quadtree(xy, k)
xylim <- cbind(x=c(min(xy[,1]), max(xy[,1])), y=c(min(xy[,2]), max(xy[,2])))
polys <- cell(qt, xylim)
polys.attr <- data.frame(id=unique(polys$id))
library(shapefiles)
polys.shapefile <- convert.to.shapefile(polys, polys.attr, "id", 5)
write.shapefile(polys.shapefile, "f:/temp/quadtree", arcgis=TRUE)

( xylimBurada bir alt bölgeye pencere oluşturmak veya eşlemeyi daha büyük bir bölgeye genişletmek için istediğiniz herhangi bir kapsamı kullanın ; bu kod varsayılan olarak noktaların boyutuna göre değişir.)

Bu tek başına yeterlidir: bu çokgenlerin orijinal noktalara uzamsal birleşimi kümeleri tanımlayacaktır. Bir kez tanımlandığında, veritabanı "özetleme" işlemleri, her bir hücre içindeki noktaların özet istatistiklerini üretecektir.


Vaov! Fantastik. Bir kez ofiste verilerimle bir şans verecek =)
radek

4
Top cevap @whuber! +1
MerseyViking

1
(1) Şekil dosyalarını doğrudan paketle ( diğerlerinin yanı sıra ) okuyabilir shapefilesveya ASCII metninde (x, y) koordinatlarını dışa aktarabilir ve birlikte okuyabilirsiniz read.table. (2) dışarı yazma tavsiye qtiki şekilde: birincisi, bir nokta shape olarak xynerede idalanları küme tanımlayıcılar olarak dahil edilir; ikincisi, çizilen çizgi parçalarının bir poli çizgi lines.quadtreeşekil dosyası olarak yazılması (veya benzer işlem hücrelerin bir poligon şekil dosyası olarak yazılması). Bu, bir dikdörtgen olarak lines.quadtree.leafçıktıyı değiştirmek kadar basittir xylim. (Bkz. Düzenlemeler.)
whuber

1
@whubber Güncelleme için çok teşekkürler. Her şey sorunsuz çalıştı. İyi +50 hak etti, ancak şimdi +500 hak ettiğini düşünüyorum!
radek

1
Hesaplanan kimliklerin bir nedenle benzersiz olmadığını düşünüyorum. Bu değişiklikleri şu tanımda yapın quad: (1) initialize id=1; (2) bir değişiklik id/2için id*2de lower=hat; (3) benzer bir değişiklik id*2+1olarak upper=hat. (Cevabımı bunu yansıtacak şekilde düzenleyeceğim.) Bu aynı zamanda alan hesaplamasına da dikkat etmelidir: CBS'nize bağlı olarak, tüm alanlar pozitif veya hepsi negatif olacaktır. Hepsi negatifse, için xve yiçindeki listeleri tersine çevirin cell.quadtree.leaf.
whuber

6

Bu algoritmanın veri örneğiniz için yeterli anonimlik sağlayıp sağlamadığını görün:

  1. düzenli bir ızgara ile başla
  2. poligon eşik değerinden daha azsa, saat yönünde spiral olarak değişen komşu (E, S, W, N) ile birleştirin.
  3. çokgenin eşiği azsa 2'ye, bir sonraki çokgene git

Örneğin, minimum eşik 3 ise:

algoritması


1
Şeytan ayrıntılarda gizlidir: Bu yaklaşım (veya neredeyse tüm aglomerasyon kümeleme algoritmaları), her yere dağılmış küçük "yetim" noktaları bırakmakla tehdit ediyor gibi görünüyor, bu da işlenemiyor. Bu yaklaşımın imkansız olduğunu söylemiyorum , ancak gerçek bir algoritma ve bunun gerçekçi bir nokta veri kümesine uygulanması örneği olmadan sağlıklı bir şüpheciliği koruyacağım.
whuber

Aslında bu yaklaşım sorunlu olabilir. Düşündüğüm bu yöntemin bir uygulaması, puanları konut binalarının temsili olarak kullanır. Bu yöntemin daha yoğun nüfuslu alanlarda iyi çalışacağını düşünüyorum. Bununla birlikte, kelimenin tam anlamıyla 'hiçbir yerin ortasında' bir veya iki bina olduğu ve çok fazla yineleme alacağı ve gerçekten de geniş alanların minimum eşiğe ulaşmasıyla sonuçlanacak durumlar olacaktır.
radek

5

Paulo'nun ilginç çözümüne benzer şekilde, dörtlü ağaç altbölüm algoritması kullanmaya ne dersiniz?

Dörtlü ağacın gitmesini istediğiniz derinliği ayarlayın. Ayrıca, hücre başına minimum veya maksimum sayıda noktaya sahip olabilirsiniz, böylece bazı düğümler diğerlerinden daha derin / küçük olur.

Boş düğümleri atarak dünyanızı alt bölümlere ayırın. Durulayın ve kriterler karşılanana kadar tekrarlayın.


Teşekkürler. Bunun için hangi yazılımı önerirsiniz?
radek

1
Prensipte bu iyi bir fikirdir. Ancak hücre başına pozitif minimum nokta sayısından daha azına asla izin vermezseniz boş düğümler nasıl ortaya çıkar? (Dört çeşit dörtlü vardır, bu nedenle boş düğüm olasılığı, verilere adapte edilmemiş olan ve amaçlanan görev için yararlılığıyla ilgili endişeleri artıran bir
aklınız olduğunu gösterir

1
Bunu şöyle hayal ediyorum: Bir düğümün maksimum nokta eşiğinden daha fazlasına sahip olduğunu hayal edin, ancak düğümün sol üst tarafına doğru kümelenmişlerdir. Düğüm alt bölümlere ayrılacak, ancak sağ alt alt düğüm boş olacak, böylece budanabilir.
MerseyViking

1
Ne yaptığınızı görüyorum (+1). Hile, koordinatlar (medyanları gibi) tarafından belirlenen bir noktada alt bölümlere ayrılmak ve böylece boş hücrelerin garanti edilmemesidir. Aksi takdirde, dörtlü esas olarak noktaların kendileri tarafından değil, noktaların işgal ettiği alan tarafından belirlenir; yaklaşımınız daha sonra @Paulo tarafından önerilen genel fikri gerçekleştirmenin etkili bir yolu haline gelir.
whuber

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.