MerseyViking이 quadtree 를 추천했습니다 . 나는 똑같은 것을 제안하고 설명하기 위해 코드와 예제가 있습니다. 코드는 작성 R
되었지만 파이썬으로 쉽게 이식해야합니다.
아이디어는 놀랍도록 간단합니다. x 방향으로 점을 약 절반으로 분할 한 다음 더 이상 분할이 필요하지 않을 때까지 각 레벨에서 방향을 번갈아 가면서 y 방향을 따라 두 개의 반쪽을 반복적으로 분할합니다.
의도는 실제 포인트 위치를 위장하는 것이기 때문에 분할에 임의성을 도입하는 것이 좋습니다. 이를 수행하는 가장 빠른 간단한 방법 중 하나는 50 %에서 작은 임의의 양을 Quantile로 분할하는 것입니다. 이 방식에서 (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), ...)
}
그리드 자체를 플로팅하는 것은 쿼드 트리 파티셔닝에 사용 된 임계 값을 반복적으로 클리핑해야하기 때문에 조금 까다 롭지 만 동일한 재귀 접근 방식은 간단하고 우아합니다. 원하는 경우 변형을 사용하여 사분면의 다각형 표현을 구성하십시오.
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
라이브러리를 사용하여 모든 쿼드 트리 셀을 다각형 모양 파일로 작성해 보겠습니다 . 이 코드는의 클리핑 루틴을 에뮬레이트 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
여기서 원하는 영역을 사용 하여 하위 영역으로 창을 만들거나 더 큰 영역으로 매핑을 확장하십시오.이 코드의 기본값은 점의 범위입니다.)
이것만으로 충분합니다. 이 다각형을 원래 지점에 공간적으로 결합하면 클러스터가 식별됩니다. 식별 된 데이터베이스 "요약"작업은 각 셀 내의 포인트에 대한 요약 통계를 생성합니다.