최소 n 개의 점을 포함하는 불규칙한 격자를 생성하려면 어떻게해야합니까?


20

불균일하게 분포 된 점으로 구성된 (약 1 백만 개) 표본이 주어지면, 지정된 최소량의 n 점을 포함하는 불규칙한 격자를 생성 할 수 있습니까 (가능한 경우 모양이 불규칙 할 수 있습니까?) ?

그러한 그리드의 생성 된 '셀'이 정확히 n 개의 포인트 또는 적어도 n 개의 포인트를 포함한다면 그것은 덜 중요합니다 .

ArcGIS의 genvecgrid 또는 QGIS / mmgis의 Create Grid Layer 와 같은 솔루션을 알고 있지만 모두 일반 그리드를 생성하여 빈 셀 (작은 문제-간단히 버릴 수 있음) 또는 포인트 수를 가진 셀로 출력됩니다 n 보다 적습니다 ( 여기 에서 도구를 사용하여 해당 셀을 집계하는 솔루션이 필요하기 때문에 더 큰 문제가 있습니까?).

나는 아무 소용이 없으며 인터넷 (ArcGIS 및 확장) 또는 무료 (Python, PostGIS, R) 솔루션 모두에 개방되어 있습니다.


1
그리드는 얼마나 "정규"이어야합니까? 계층 적 클러스터링을 수행하고 덴드로 그램을 잘라서 요구 사항을 충족시킬 수 있는지 궁금합니다 (정규 공간 구성으로 정의 된 것을 확장하더라도). CrimeStat 문서에는 이러한 유형의 클러스터링에 대한 좋은 예가 있습니다.
Andy W

5
"불규칙한 그리드"가 의미하는 바를 정확히 설명해 주시겠습니까? 그것은 oxymoron처럼 들립니다 :-). 요컨대이 운동의 목적은 무엇입니까? 또한 추가 기준 또는 제약 조건이 필요할 수 있습니다. 결국, 백만 점을 중심으로 사각형을 그리면 그리드의 일부로 간주 될 수 있으며 n 개 이상을 포함 할 수 있습니다 . 당신은 아마이 사소한 해결책을 신경 쓰지 않을 것입니다.
whuber

@AndyW 감사합니다. 좋은 생각과 탐험 할 가치가 있습니다. 보라. (때문에 데이터 프라이버시에) 우선 순위 '숨기기'에있다 - '그리드'의 크기 및 모양은 나를 위해 보조 중요하다 N 일 뒤에 있습니다
라덱

@whuber 감사합니다. 동의합니다. 그러나 다른 파티션의 이름을 어떻게 지정할 수 있는지 확실하지 않았습니다. 위에서 언급했듯이 내 주요 동기는 데이터 개인 정보 보호입니다. 5 개의 포인트 위치 (최종지도에 표시 할 수 없음)가 있으면 해당 위치를 나타내는 영역으로 표시하고 싶습니다. 평균 / 중간 값 등을 얻습니다. 그 가치. 나는 직사각형 또는 볼록 껍질을 모두 나타내는 것이 가능하다는 데 동의합니다-그것이 내가 추측하는 궁극적 인 데이터 개인 정보 보호가 될 것입니까? ;] 그러나 도형 경계로 표현하는 것이 더 유용합니다. 10 가지 기능을 예로 들어 보겠습니다. 그런 다음에도 여전히 공간 패턴을 유지할 수 있습니다.
radek

1
IMO는 귀하의 설명에 따라 일부 유형의 보간을 활용하고 래스터 맵을 표시합니다 (아마도 최소 N 크기의 적응 형 대역폭이 데이터를 부드럽게하기에 충분할 것입니다). CrimeStat까지 내가 사용한 가장 큰 파일은 내가 생각하는 약 10 만 건이었다 (그리고 클러스터링은 확실히 시간이 걸릴 것이다). 적은 수의 데이터로 표현하기 위해 데이터를 미리 생성하여 원하는 결과를 얻을 수 있습니다. 정말 간단한 프로그램입니다. 몇 분 동안 사용해보고 직접 확인하시기 바랍니다.
Andy W

답변:


26

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포인트 수입니다. 셀당 최소 포인트 mn나눈 비율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여기서 원하는 영역을 사용 하여 하위 영역으로 창을 만들거나 더 큰 영역으로 매핑을 확장하십시오.이 코드의 기본값은 점의 범위입니다.)

이것만으로 충분합니다. 이 다각형을 원래 지점에 공간적으로 결합하면 클러스터가 식별됩니다. 식별 된 데이터베이스 "요약"작업은 각 셀 내의 포인트에 대한 요약 통계를 생성합니다.


와우! 환상적인. 사무실에서 다시 한 번 내 데이터를
보여줄 것입니다

4
최고 답변 @whuber! +1
MerseyViking

1
(1) 직접 (와 shape 파일을 읽을 수 있습니다 특히 )를 shapefiles패키지 또는 다른 당신은 ASCII 텍스트 (X, Y) 좌표를 내보내고 그들을 읽을 수 있습니다 read.table. (2) qt두 가지 형식으로 작성하는 것이 좋습니다 . 먼저, 필드가 클러스터 식별자로 포함 xy되는 지점 모양 파일로 id; 둘째,로 그려진 선분 lines.quadtree이 폴리 라인 쉐이프 파일로 기록됩니다 (또는 유사한 처리로 셀을 폴리곤 쉐이프 파일로 기록합니다). 사각형 lines.quadtree.leaf으로 출력 하도록 수정 하는 것만 큼 ​​간단합니다 xylim. (편집 참조)
whuber

1
@whubber 업데이트 해 주셔서 감사합니다. 모든 것이 원활하게 작동했습니다. +50의 가치가 있지만 지금은 +500의 가치가 있다고 생각합니다!
radek

1
어떤 이유로 계산 된 ID가 고유하지 않은 것으로 생각됩니다. 다음의 정의에서 다음과 같이 변경하십시오 quad. (1) initialize id=1; (2) 변경 id/2id*2에서 lower=라인; (3)과 유사한 변경 id*2+1upper=라인. (나는 그것을 반영하기 위해 답장을 편집 할 것입니다.) 그것은 또한 면적 계산에주의를 기울여야합니다. GIS에 따라 모든 지역은 긍정적이거나 모두 부정적인 것입니다. 그들은 모두 음성 인 경우, 대한 목록을 반대 x하고 ycell.quadtree.leaf.
whuber

6

이 알고리즘이 데이터 샘플에 충분한 익명 성을 제공하는지 확인하십시오.

  1. 정규 그리드로 시작
  2. 다각형이 임계 값보다 작은 경우 시계 방향으로 나선형으로 이웃하는 교차 (E, S, W, N)와 병합합니다.
  3. 다각형의 임계 값보다 작은 경우 2로 이동하고 그렇지 않으면 다음 다각형으로 이동

예를 들어 최소 임계 값이 3 인 경우

연산


1
악마는 세부 사항에 있습니다.이 접근법 (또는 거의 모든 응집 클러스터링 알고리즘)은 작은 "고아"포인트를 사방에 흩어져서 처리 할 수없는 것으로 보입니다. 나는이 접근법이 불가능하다고 말하지는 않지만, 실제 알고리즘과 현실적인 포인트 데이터 세트에 대한 적용 예가 없으면 건전한 회의론을 유지할 것입니다.
whuber

실제로이 방법은 문제가 될 수 있습니다. 내가 생각한이 방법의 한 가지 적용은 점을 주거용 건물의 표현으로 사용합니다. 이 방법은 인구 밀집 지역에서 잘 작동한다고 생각합니다. 그러나 문자 그대로 '어두운 곳의 중간'에 하나 또는 두 개의 건물이 있고 여전히 많은 반복이 필요하며 실제로 가장 큰 영역이 최소 임계 값에 도달하는 경우가 있습니다.
radek

5

Paulo의 흥미로운 솔루션과 마찬가지로 쿼드 트리 세분화 알고리즘을 사용하는 방법은 무엇입니까?

쿼드 트리로 가고 싶은 깊이를 설정하십시오. 셀당 최소 또는 최대 포인트 수를 가질 수 있으므로 일부 노드는 다른 노드보다 깊거나 작습니다.

빈 노드를 버리고 세계를 세분화하십시오. 기준을 충족 할 때까지 헹구고 반복하십시오.


감사. 어떤 소프트웨어를 추천 하시겠습니까?
radek

1
원칙적으로 이것은 좋은 생각입니다. 그러나 셀당 양의 최소 포인트 수 미만을 허용하지 않으면 빈 노드가 어떻게 발생합니까? (많은 종류의 쿼드 트리가 있으므로 비어있는 노드의 가능성은 데이터에 적합하지 않은 노드를 염두에두고 있음을 나타내므로 의도 된 작업에 대한 유용성에 대한 우려가 제기됩니다.)
whuber

1
노드는 노드의 최대 임계 값을 초과하지만 노드의 왼쪽 상단으로 클러스터되어 있다고 상상해보십시오. 노드는 세분화되지만 오른쪽 아래 하위 노드는 비어 있으므로 정리할 수 있습니다.
MerseyViking

1
당신이하고있는 일을 봅니다 (+1). 트릭은 좌표 (예 : 중앙값)에 의해 결정된 지점에서 세분화하여 빈 셀을 보장하지 않는 것입니다. 그렇지 않으면 쿼드 트리는 주로 점 자체가 아니라 점이 차지하는 공간에 의해 결정됩니다. 귀하의 접근 방식은 @Paulo가 제안한 일반적인 아이디어를 수행하는 효과적인 방법이됩니다.
whuber

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.