1 셀 / 픽셀의 씨앗에서 래스터에 무작위 모양의 세포 덩어리를 만드는가?


11

제목에서 알 수 있듯이 래스터 내의 씨앗에서 세포 덩어리를 "성장"시키고 싶습니다. 내 기본 래스터는 1과 0으로 가득 차 있으며 1은 육지와 0의 바다 / NA 지역을 나타냅니다. 1에서 60 개의 랜덤 픽셀 / 셀을 시드로 선택하고 미리 정의 된 번호의 연결된 덩어리를 임의로 성장시키고 싶습니다. 픽셀 / 셀의 수는 해당 시드에서 제한됩니다. 이 기술을 '확산 염료'라고 부를 수 있다고 들었지만 그 결과를 많이 찾지 못했습니다. 시드 셀은 2의 값으로 바뀐 다음 주변 1에서 선택된 다음 셀도 2로 변환됩니다. 그러면 2를 나중에 변환 할 수 없습니다.

스레드는 R에서 GIS 데이터를 읽고 조작하는 데 익숙하기 때문에 R 에서이 작업을 수행하려고하므로 약간 도움이됩니다. 그러나 필요한 것은 기존 덩어리를 둘러싼 픽셀을 무작위로 선택하는 규칙 세트입니다. .

누군가 GIS 환경에서 이보다 기본적인 형태의 셀룰러 오토마타를 수행했다면 조언 / 안내를 부탁드립니다.

예:

나는 250 세포의 목표가 있습니다. 나는 1의 값을 가진 하나의 셀을 무작위로 선택합니다. 이것은 2의 값으로 바뀝니다. 그런 다음 시드 셀의 이웃 중 하나 = 1이 2로 바뀝니다. 그런 다음 두 셀의 이웃 중 하나 2 개의 값이 선택된 상태에서 2로 바뀝니다. 250 개 셀의 연속 모양 번호에 도달 할 때까지 계속됩니다.

편집 : 추가 질문

whuber의 훌륭한 답변을 바탕으로 코드에 대한 몇 가지 질문이 있습니다.

  1. 성장한 셀의 값을 생성 된 순서를 나타내는 변수 값이 아닌 '2'에 어떻게 할당합니까?
  2. 내 '1'영역 내에서 60 개의 세포 덩어리를 만들어야합니다. 임의의 시작 위치를 선택하는 방법을 고안했지만 expand친절하게 작성한 기능을 사용하여 루프 내에서 모두 작동하도록 노력하고 있습니다. 서로 충돌하지 않고 동일한 최종 행렬에 포함 된 60 개의 덩어리를 만드는 방법을 제안 할 수 있습니까?

편집 : 문제에 대한 추가 설명

세포의 각 덩어리는 정의 된 크기, 예를 들어 250 세포의 보호 된 영역을 나타냅니다. 각 지역은 육지를 나타내며 값이 1 인 세포로 시작하고 성장해야합니다. 이것은 바다를 나타내므로 0의 세포를 피하십시오. null 모델을 생성하기 위해 각 반복에서 60 개의 보호 된 영역을 사용하여이 1000 번 반복해야합니다. 이러한 영역의 분포가 우연히 표시됩니다. 이러한 이유로, 60 개 영역 모두에 걸쳐 총 셀 수는 1000 개의 반복마다 동일해야하므로 비교할 수 있습니다. 따라서 영역이 닿아도 괜찮지 만 충돌이 있으면 대상의 250에 도달 할 때까지 다른 방향으로 덩어리가 자라는 것이 이상적입니다.

이러한 1000 개의 보호 영역 네트워크가 각각 생성되면 (a) 특정 종 범위와 교차하는지 여부 및 (b) 특정 종의 %가 임의의 네트워크 범위에 속하는지 확인하기 위해 생물 다양성 측정과 같은 다른 래스터 데이터에 대한 마스크로 사용됩니다. 보호 구역 커버.

지금까지 도움을 주신 @whuber에게 감사드립니다. 더 많은 시간을 보내실 것을 기대하지는 않지만 요청하신대로 내 상황을 명확하게 설명하려고 노력했습니다.


R 외에도이 분석에 사용하려는 다른 프로그래밍 언어 / 소프트웨어는 무엇입니까?
Aaron

ArcGIS 또는 QGIS를 사용하게되어 기쁩니다. 불행히도 나는 파이썬에 익숙하지 않습니다. bash 터미널을 통한 GDAL도 가능합니다.
JPD

답변:


12

다른 플랫폼에서 어떻게 접근 할 수 있는지 설명하기 위해 R약간의 R방식으로 코딩 된 솔루션을 제공 할 것입니다 .

R(다른 프로그래밍 플랫폼뿐만 아니라 특히 함수형 프로그래밍 스타일을 선호하는 플랫폼)에 대한 관심은 큰 배열을 지속적으로 업데이트하는 것은 매우 비쌀 수 있다는 것입니다. 대신,이 알고리즘은 (a) 지금까지 채워진 모든 셀이 나열되고 (b) 선택 가능한 모든 셀 (채워진 셀의 둘레)이있는 자체 개인 데이터 구조를 유지합니다. 나열되어 있습니다. 이 데이터 구조를 조작하는 것은 배열로 직접 인덱싱하는 것보다 비효율적이지만 수정 된 데이터를 작은 크기로 유지하면 계산 시간이 훨씬 덜 소요됩니다. (내에서 최적화하기 위해 아무 노력도하지 않았습니다 R. 상태 벡터의 사전 할당은에서 작업을 계속하려면 실행 시간을 절약해야합니다 R.)

코드는 주석 처리되어 있으며 읽기 편해야합니다. 알고리즘을 가능한 한 완벽하게 만들기 위해 결과를 플롯하기 위해 끝을 제외하고 추가 기능을 사용하지 않습니다. 유일한 까다로운 부분은 효율성과 단순성을 위해 1D 인덱스를 사용하여 2D 그리드에 인덱스하는 것을 선호한다는 것입니다. neighbors함수 에서 변환이 발생하는데, 셀의 액세스 가능한 이웃이 무엇인지 파악한 다음 1D 인덱스로 변환하려면 2D 색인이 필요합니다. 이 변환은 표준이므로 다른 GIS 플랫폼에서는 열 및 행 인덱스의 역할을 되돌릴 수 있다는 점을 제외하고는 더 이상 언급하지 않습니다. ( R에서 행 인덱스는 열 인덱스보다 먼저 변경됩니다.)

예를 들어,이 코드는 x토지와 접근 할 수없는 지점의 강과 같은 특징을 나타내는 그리드를 취하고 해당 그리드 의 특정 위치 (5, 21)에서 시작하여 (하천의 낮은 굴곡 근처) 250 포인트를 포함하도록 임의로 확장합니다. . 총 타이밍은 0.03 초입니다. (배열의 크기가 5000 행 x 10,000 행에서 3000 행으로 증가하면 타이밍은이 알고리즘의 확장 성을 나타내는 0.09 초 (3 정도 정도)까지 증가합니다.) 대신 0, 1, 2의 그리드 만 출력하면 새 셀이 할당 된 순서를 출력합니다. 그림에서 가장 오래된 세포는 녹색이며 금을 통해 연어 색으로 졸업합니다.

지도

각 셀의 8 포인트 이웃이 사용되고 있음이 분명해야합니다. 다른 이웃의 경우 간단히 nbrhood시작 부분 근처 의 값을 수정하십시오 expand. 주어진 셀과 관련된 인덱스 오프셋 목록입니다. 예를 들어 "D4"이웃은로 지정할 수 있습니다 matrix(c(-1,0, 1,0, 0,-1, 0,1), nrow=2).

이 살포 방법에는 문제가 있음이 명백합니다. 이것이 의도 한 것이 아닌 경우이 문제를 해결하는 다양한 방법이 있습니다. 예를 들어 사용 가능한 셀을 대기열에 보관하여 가장 오래된 셀도 가장 빠른 셀이되도록합니다. 일부 무작위 화는 여전히 적용 할 수 있지만 사용 가능한 셀은 더 이상 균일 한 (같은) 확률로 선택되지 않습니다. 더 복잡한 또 다른 방법은 채워진 이웃 수에 따라 확률이있는 사용 가능한 셀을 선택하는 것입니다. 셀이 둘러싸인 후에는 선택 가능성이 너무 높아서 홀이 채워지지 않을 수 있습니다.

나는 이것이 셀 단위로 진행하지는 않지만 각 세대에서 셀의 전체 엉킴을 업데이트 할 것입니다. 차이점은 미묘합니다. CA의 경우 셀의 선택 확률이 균일하지 않습니다.

#
# Expand a patch randomly within indicator array `x` (1=unoccupied) by
# `n.size` cells beginning at index `start`.
#
expand <- function(x, n.size, start) {
  if (x[start] != 1) stop("Attempting to begin on an unoccupied cell")
  n.rows <- dim(x)[1]
  n.cols <- dim(x)[2]
  nbrhood <- matrix(c(-1,-1, -1,0, -1,1, 0,-1, 0,1, 1,-1, 1,0, 1,1), nrow=2)
  #
  # Adjoin one more random cell and update `state`, which records
  # (1) the immediately available cells and (2) already occupied cells.
  #
  grow <- function(state) {
    #
    # Find all available neighbors that lie within the extent of `x` and
    # are unoccupied.
    #
    neighbors <- function(i) {
      n <- c((i-1)%%n.rows+1, floor((i-1)/n.rows+1)) + nbrhood
      n <- n[, n[1,] >= 1 & n[2,] >= 1 & n[1,] <= n.rows & n[2,] <= n.cols, 
             drop=FALSE]             # Remain inside the extent of `x`.
      n <- n[1,] + (n[2,]-1)*n.rows  # Convert to *vector* indexes into `x`.
      n <- n[x[n]==1]                # Stick to valid cells in `x`.
      n <- setdiff(n, state$occupied)# Remove any occupied cells.
      return (n)
    }
    #
    # Select one available cell uniformly at random.
    # Return an updated state.
    #
    j <- ceiling(runif(1) * length(state$available))
    i <- state$available[j]
    return(list(index=i,
                available = union(state$available[-j], neighbors(i)),
                occupied = c(state$occupied, i)))
  }
  #
  # Initialize the state.
  # (If `start` is missing, choose a value at random.)
  #
  if(missing(start)) {
    indexes <- 1:(n.rows * n.cols)
    indexes <- indexes[x[indexes]==1]
    start <- sample(indexes, 1)
  }
  if(length(start)==2) start <- start[1] + (start[2]-1)*n.rows
  state <- list(available=start, occupied=c())
  #
  # Grow for as long as possible and as long as needed.
  #
  i <- 1
  indices <- c(NA, n.size)
  while(length(state$available) > 0 && i <= n.size) {
    state <- grow(state)
    indices[i] <- state$index
    i <- i+1
  }
  #
  # Return a grid of generation numbers from 1, 2, ... through n.size.
  #
  indices <- indices[!is.na(indices)]
  y <- matrix(NA, n.rows, n.cols)
  y[indices] <- 1:length(indices)
  return(y)
}
#
# Create an interesting grid `x`.
#
n.rows <- 3000
n.cols <- 5000
x <- matrix(1, n.rows, n.cols)
ij <- sapply(1:n.cols, function(i) 
      c(ceiling(n.rows * 0.5 * (1 + exp(-0.5*i/n.cols) * sin(8*i/n.cols))), i))
x[t(ij)] <- 0; x[t(ij - c(1,0))] <- 0; x[t(ij + c(1,0))] <- 0
#
# Expand around a specified location in a random but reproducible way.
#
set.seed(17)
system.time(y <- expand(x, 250, matrix(c(5, 21), 1)))
#
# Plot `y` over `x`.
#
library(raster)
plot(raster(x[n.rows:1,], xmx=n.cols, ymx=n.rows), col=c("#2020a0", "#f0f0f0"))
plot(raster(y[n.rows:1,] , xmx=n.cols, ymx=n.rows), 
     col=terrain.colors(255), alpha=.8, add=TRUE)

약간 수정하면 expand여러 클러스터를 만들기 위해 반복 할 수 있습니다 . 여기에서 2, 3, ... 등을 실행하는 식별자로 클러스터를 구별하는 것이 좋습니다.

먼저 (a) 오류가 있으면 첫 번째 줄에서 (b) 행렬이 아닌 값 expand을 반환하도록 변경 NA하십시오 . ( 매번 호출 할 때마다 새 행렬 을 만드는 데 시간을 낭비하지 마십시오 .)이 변경으로 인해 반복이 쉬워집니다. 임의의 시작을 선택하고 주위를 확장하고 성공 하면 클러스터 인덱스를 누적 한 다음 완료 될 때까지 반복하십시오. 루프의 핵심 부분은 많은 연속 클러스터를 찾을 수없는 경우 반복 횟수를 제한하는 것입니다 .indicesyyindicescount.max

다음은 60 개의 클러스터 중심이 무작위로 균일하게 선택된 예입니다.

size.clusters <- 250
n.clusters <- 60
count.max <- 200
set.seed(17)
system.time({
  n <- n.rows * n.cols
  cells.left <- 1:n
  cells.left[x!=1] <- -1 # Indicates occupancy of cells
  i <- 0
  indices <- c()
  ids <- c()
  while(i < n.clusters && length(cells.left) >= size.clusters && count.max > 0) {
    count.max <- count.max-1
    xy <- sample(cells.left[cells.left > 0], 1)
    cluster <- expand(x, size.clusters, xy)
    if (!is.na(cluster[1]) && length(cluster)==size.clusters) {
      i <- i+1
      ids <- c(ids, rep(i, size.clusters))
      indices <- c(indices, cluster)
      cells.left[indices] <- -1
    }                
  }
  y <- matrix(NA, n.rows, n.cols)
  y[indices] <- ids
})
cat(paste(i, "cluster(s) created.", sep=" "))

310 x 500 그리드에 적용했을 때의 결과는 다음과 같습니다 (클러스터가 명확하게 보이도록 충분히 작고 굵게 표시됨). 실행하는 데 2 ​​초가 걸립니다. 3100 x 5000 그리드 (100 배 더 큼)에서는 시간이 더 오래 걸리지 만 (24 초) 타이밍이 적절하게 조정됩니다. C ++과 같은 다른 플랫폼에서는 타이밍이 그리드 크기에 거의 의존하지 않아야합니다.

60 클러스터


안녕하세요 후버 정말 감사합니다. 정말 감사합니다. 나는 약간의 실험을하고 있으며 곧 몇 가지 후속 질문으로 돌아올 수 있습니다.
JPD

+1 GIS SE에 대한 좀 더 복잡한 질문에 대한 철저한 답변을 해주셔서 감사합니다.
레이더

@whuber. 답변을 바탕으로 질문을 조금 확장했습니다. 다시 감사합니다!
JPD

1
# 1에 대한 답은 간단 y[indices] <- 1:length(indices)합니다 y[indices] <- 2. # 2에 대한 대답은 거의 간단합니다. 그냥 루프입니다.
whuber

@whuber. 업데이트 해 주셔서 다시 한 번 감사드립니다. 이제 덩어리 충돌 문제가 발생하여 덩어리 크기가에 정의 된 수보다 작습니다 size.clusters. 현재 덩어리가 기존 덩어리로 자라려고 시도하지만 실패하지만 여전히 성공적인 확장으로 등록되므로 덩어리가 올바른 크기로 커지는 방법은 무엇입니까? 그런 다음 평균 null 모델 스타일 데이터 집합을 생성하여 60 번의 덩어리를 1000 번 반복 생성하려고합니다. for루프 내에서 임의의 위치가 매번 달라질 수 있습니까?
JPD

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