私はMerseyVikingが推奨している参照の四分木を。私は同じことを提案しようとしていました、そしてそれを説明するために、ここにコードと例があります。コードは記述されてR
いますが、たとえばPythonに簡単に移植できるはずです。
アイデアは非常に単純です。ポイントをx方向に約半分に分割し、さらに分割が必要なくなるまで、各レベルで方向を交互に、y方向に沿って2つの半分を再帰的に分割します。
実際のポイント位置を偽装することを目的としているため、分割にランダム性を導入すると便利です。これを行うための1つの高速で簡単な方法は、50%から離れた小さなランダム量を変位値セットで分割することです。この方法では、(a)分割値がデータ座標と一致する可能性は非常に低いため、ポイントは分割によって作成された象限に一意に分類され、(b)ポイント座標は四分木から正確に再構築することはできません。
k
各クアッドツリーリーフ内のノードの最小量を維持することを目的としているため、制限された形式のクアッドツリーを実装します。(1)それぞれk
2〜k
-1の要素を持つグループへのクラスター化ポイント、および(2)象限のマッピングをサポートします。
このR
コードは、ノードとターミナルの葉のツリーを作成し、クラスごとに区別します。クラスのラベル付けは、以下に示すように、プロットなどの後処理を促進します。コードはIDに数値を使用します。これは、ツリー内で最大52の深さまで機能します(倍精度を使用します。符号なし長整数が使用される場合、最大の深さは32です)。より深いツリーの場合(少なくともk
* 2 ^ 52ポイントが関係するため、どのアプリケーションでも非常にまれです)、idは文字列でなければなりません。
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)
}
このアルゴリズムの再帰的分割統治設計(およびその結果、ほとんどの後処理アルゴリズムの設計)は、時間要件がO(m)であり、RAM使用量がO(n)でm
あることに注意してください。セルおよびn
ポイントの数です。 セルごとの最小ポイントで割った値にm
比例しn
ます。k
。これは、計算時間の見積もりに役立ちます。たとえば、n = 10 ^ 6ポイントを50-99ポイント(k = 50)のセルに分割するのに13秒かかる場合、m = 10 ^ 6/50 =20000。代わりに5-9に分割する場合セルあたりのポイント(k = 5)、mは10倍大きいため、タイミングは約130秒になります。(セルのサイズが小さくなるにつれて、中心の周りの座標セットを分割するプロセスが速くなるため、実際のタイミングはわずか90秒でした。)セルごとにk = 1ポイントに達するには、約6倍時間がかかります。まだ、または9分で、コードは実際にはそれよりも少し速くなると予想できます。
先に進む前に、興味深い不規則な間隔のデータを生成し、制限されたクワッドツリー(経過時間0.29秒)を作成しましょう。
これらのプロットを生成するコードは次のとおりです。R
のポリモーフィズムを利用します。たとえばpoints.quadtree
、points
関数がquadtree
オブジェクトに適用されるたびに呼び出されます。この機能は、クラスター識別子に応じてポイントに色を付ける機能が非常にシンプルであることから明らかです。
points.quadtree <- function(q, ...) {
points(q$lower, ...); points(q$upper, ...)
}
points.quadtree.leaf <- function(q, ...) {
points(q$value, col=hsv(q$id), ...)
}
グリッド自体のプロットは、4分木分割に使用されるしきい値を繰り返しクリッピングする必要があるため、少し注意が必要ですが、同じ再帰的アプローチはシンプルでエレガントです。必要に応じて、バリアントを使用して象限の多角形表現を作成します。
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!
別の例として、1,000,000個のポイントを生成し、それらをそれぞれ5〜9個のグループに分割しました。タイミングは91.7秒でした。
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=".")
GISと対話する方法の例として、shapefiles
ライブラリを使用して、すべてのquadtreeセルをポリゴンシェープファイルとして書き出しましょう。コードはのクリッピングルーチンをエミュレートしますがlines.quadtree
、今回はセルのベクトル記述を生成する必要があります。これらは、shapefiles
ライブラリで使用するデータフレームとして出力されます。
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]))
}
ポイント自体はread.shp
、(x、y)座標のデータファイルを使用して、またはインポートすることで直接読み取ることができます。
使用例:
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)
(xylim
ここで必要な範囲を使用して、サブ領域にウィンドウ表示するか、マッピングをより大きな領域に拡張します。このコードはデフォルトでポイントの範囲になります。)
これだけで十分です。これらのポリゴンを元のポイントに空間的に結合すると、クラスターが識別されます。識別されると、データベースの「要約」操作により、各セル内のポイントの要約統計が生成されます。