การสร้างกลุ่มของเซลล์ที่มีรูปร่างแบบสุ่มในแรสเตอร์จาก 1 เซลล์ / พิกเซล?


11

ตามที่ชื่อของฉันบอกว่าฉันต้องการ "เติบโต" กลุ่มของเซลล์จากเมล็ดภายในแรสเตอร์ แรสเตอร์ฐานของฉันเต็มไปด้วย 1 และ 0 ของ 1 ระบุที่ดินและพื้นที่ทะเล / NA ของ 0 จาก 1 ของฉันต้องการเลือก 60 พิกเซลสุ่ม / เซลล์เป็นเมล็ดของฉันแล้วสุ่มกอเชื่อมต่อที่ไม่มีการกำหนดไว้ล่วงหน้า จำนวนพิกเซล / เซลล์ จำกัด จากเมล็ดนั้น ฉันได้ยินมาว่าเทคนิคนี้สามารถเรียกได้ว่า 'การย้อมสีสเปรด' แต่ก็ไม่มีโชคที่ค้นพบมันมาก เซลล์เมล็ดจะถูกเปลี่ยนเป็นค่า 2 จากนั้นเซลล์ถัดไปที่เลือกจากรอบ 1 จะถูกแปลงเป็น 2 เช่นกัน 2 ไม่สามารถแปลงได้ในอนาคต

เธรดนี้จะช่วยได้เล็กน้อยเนื่องจากฉันยินดีที่จะทำเช่นนี้ใน R เนื่องจากฉันคุ้นเคยกับการอ่านและจัดการข้อมูล GIS ใน R อย่างไรก็ตามสิ่งที่ฉันต้องการคือชุดของกฎเพื่อเลือกพิกเซลแบบสุ่มรอบ ๆ กลุ่มที่มีอยู่แบบสุ่ม .

หากใครทำรูปแบบพื้นฐานของออโตมาตาเซลลูล่าร์ในการตั้งค่า GIS ฉันก็จะขอคำแนะนำ / คำแนะนำใด ๆ

ตัวอย่าง:

ฉันมีเป้าหมาย 250 เซลล์ ฉันสุ่มเลือกเซลล์หนึ่งที่มีค่าเป็น 1 นี่จะกลายเป็นค่า 2 จากนั้นหนึ่งในเพื่อนบ้านของเซลล์เมล็ดที่ = 1 หันไปเป็น 2 จากนั้นหนึ่งในเพื่อนบ้านของเซลล์ใดเซลล์หนึ่ง ด้วยค่า 2 ถูกเลือกและเปลี่ยนเป็น 2 สิ่งนี้จะดำเนินต่อไปจนกว่าจะถึงจำนวน 250 เซลล์ที่มีรูปร่างอย่างต่อเนื่อง

แก้ไข: คำถามเพิ่มเติม

จากคำตอบที่ดีของ whuber ฉันมีคำถามสองสามข้อเกี่ยวกับรหัส:

  1. ฉันจะได้รับการจัดสรรค่าของเซลล์ที่ปลูกให้เป็น '2' แทนที่จะเป็นค่าตัวแปรซึ่งแสดงถึงลำดับที่มันถูกสร้างขึ้นได้อย่างไร
  2. ฉันต้องการสร้างเซลล์ 60 กลุ่มภายในพื้นที่ของ '1's ฉันได้คิดค้นวิธีในการเลือกตำแหน่งเริ่มต้นแบบสุ่ม แต่พยายามที่จะทำให้มันทำงานทั้งหมดภายในวงโดยใช้expandฟังก์ชั่นที่คุณเขียน คุณช่วยแนะนำวิธีสร้าง 60 clumps ซึ่งไม่ขัดแย้งกันและอยู่ใน matrix เดียวกันสุดท้ายได้หรือไม่

แก้ไข: คำอธิบายปัญหาเพิ่มเติม

แต่ละกลุ่มของเซลล์จะแสดงพื้นที่คุ้มครองในขนาดที่กำหนดเช่น 250 เซลล์ แต่ละพื้นที่จะต้องเริ่มต้นและเติบโตเป็นเซลล์ที่มีค่า 1 เนื่องจากเป็นที่ดินและหลีกเลี่ยงเซลล์ที่มีค่า 0 เนื่องจากเป็นทะเล ฉันจำเป็นต้องวนซ้ำ 1,000 ครั้งนี้โดยมีพื้นที่คุ้มครอง 60 แห่งในแต่ละการวนซ้ำเพื่อสร้างแบบจำลองที่ว่างซึ่งแสดงว่าการกระจายของพื้นที่เหล่านี้จะเป็นไปโดยบังเอิญ ด้วยเหตุผลนี้จำนวนเซลล์ทั้งหมดใน 60 พื้นที่จะต้องเท่ากันในการวนซ้ำ 1,000 รอบเพื่อให้สามารถเปรียบเทียบได้ ดังนั้นมันก็โอเคถ้าพื้นที่สัมผัส แต่ถ้ามีการปะทะกันแล้วความนึกคิดของกลุ่มจะเติบโตในทิศทางที่มีอยู่อีกจนกว่าเป้าหมายถึง 250 ถึง

เมื่อสร้างเครือข่ายพื้นที่คุ้มครอง 1,000 แห่งเหล่านี้แล้วแต่ละเครือข่ายจะถูกนำมาใช้เป็นหน้ากากป้องกันข้อมูลแรสเตอร์อื่น ๆ เช่นมาตรการความหลากหลายทางชีวภาพเพื่อดู (a) ไม่ว่าพวกเขาจะตัดช่วงพันธุ์เฉพาะหรือ (b)% ชนิดใด ครอบคลุมพื้นที่คุ้มครอง

ขอบคุณ @whuber สำหรับความช่วยเหลือของคุณฉันไม่ได้คาดหวังให้คุณใช้เวลามากขึ้นในการช่วยฉัน แต่คิดว่าฉันจะลองและอธิบายสถานการณ์ของฉันตามที่คุณร้องขอ


นอกจาก R แล้วคุณยังสนใจภาษาการเขียนโปรแกรม / ซอฟต์แวร์อื่น ๆ สำหรับการวิเคราะห์นี้
แอรอน

ฉันก็ยินดีที่จะใช้ ArcGIS หรือ QGIS น่าเสียดายที่ฉันไม่คุ้นเคยกับงูหลาม GDAL ผ่าน bash terminal ก็เป็นไปได้เช่นกัน
JPD

คำตอบ:


12

ฉันจะเสนอRวิธีแก้ไขปัญหาที่เขียนในRวิธีที่ไม่ชัดเจนเพื่อแสดงให้เห็นถึงวิธีการที่อาจเข้าหาบนแพลตฟอร์มอื่น ๆ

ความกังวลในR(เช่นเดียวกับแพลตฟอร์มอื่น ๆ โดยเฉพาะอย่างยิ่งผู้ที่ชื่นชอบสไตล์การเขียนโปรแกรมการทำงาน) คือการปรับปรุงอาร์เรย์ขนาดใหญ่อย่างต่อเนื่องอาจมีราคาแพงมาก แต่อัลกอริทึมนี้จะรักษาโครงสร้างข้อมูลส่วนตัวของตัวเองซึ่ง (a) เซลล์ทั้งหมดที่ถูกเติมจนถึงตอนนี้มีการระบุไว้และ (b) เซลล์ทั้งหมดที่มีให้เลือก (รอบปริมณฑลของเซลล์ที่เติม) มีการระบุไว้ แม้ว่าการจัดการโครงสร้างข้อมูลนี้จะมีประสิทธิภาพน้อยกว่าการจัดทำดัชนีลงในอาร์เรย์โดยตรงโดยการรักษาข้อมูลที่แก้ไขให้มีขนาดเล็ก แต่ก็อาจใช้เวลาในการคำนวณน้อยลง (ไม่มีความพยายามในการปรับให้เหมาะสมRเช่นกันการจัดสรรล่วงหน้าของเวกเตอร์ของรัฐควรประหยัดเวลาดำเนินการบางอย่างหากคุณต้องการทำงานภายในRต่อไป)

รหัสมีความคิดเห็นและควรจะตรงไปตรงมาอ่าน เพื่อทำให้อัลกอริทึมสมบูรณ์ที่สุดเท่าที่จะทำได้จึงไม่ใช้โปรแกรมเสริมใด ๆ ยกเว้นในตอนท้ายเพื่อพล็อตผลลัพธ์ ส่วนที่ยุ่งยากเท่านั้นคือเพื่อประสิทธิภาพและความเรียบง่ายมันต้องการสร้างดัชนีลงในกริด 2D โดยใช้ดัชนี 1D การแปลงเกิดขึ้นในneighborsฟังก์ชั่นซึ่งต้องการการทำดัชนี 2D เพื่อที่จะเข้าใจว่าเพื่อนบ้านที่สามารถเข้าถึงได้ของเซลล์นั้นสามารถแปลงเป็นดัชนี 1D ได้อย่างไร การแปลงนี้เป็นมาตรฐานดังนั้นฉันจะไม่แสดงความคิดเห็นเพิ่มเติมยกเว้นเพื่อชี้ให้เห็นว่าในแพลตฟอร์ม GIS อื่น ๆ คุณอาจต้องการย้อนกลับบทบาทของคอลัมน์และดัชนีแถว (ในRดัชนีแถวเปลี่ยนไปก่อนที่ดัชนีคอลัมน์จะทำ)

เพื่อแสดงให้เห็นว่ารหัสนี้ใช้ตารางที่xเป็นตัวแทนของที่ดินและลักษณะคล้ายแม่น้ำของจุดที่เข้าถึงไม่ได้เริ่มต้นที่ตำแหน่งเฉพาะ (5, 21) ในตารางนั้น (ใกล้กับโค้งล่างของแม่น้ำ) และขยายแบบสุ่มเพื่อครอบคลุม 250 คะแนน . เวลาทั้งหมดคือ 0.03 วินาที (เมื่อขนาดของอาเรย์เพิ่มขึ้นจากระดับ 10,000 ถึง 3,000 แถวโดย 5000 คอลัมน์เวลาจะเพิ่มขึ้นเพียง 0.09 วินาที - ซึ่งเป็นเพียง 3 หรือมากกว่าเท่านั้น - แสดงให้เห็นถึงความสามารถในการปรับขนาดของอัลกอริธึมนี้) แทน เพียงแค่แสดงตารางของ 0, 1, และ 2 ของมันก็จะส่งออกลำดับที่มีการจัดสรรเซลล์ใหม่ ในภาพเซลล์ที่เก่าแก่ที่สุดเป็นสีเขียวจบการศึกษาจากทองคำเป็นสีปลาแซลมอน

แผนที่

มันควรจะเห็นได้ว่ามีการใช้พื้นที่ใกล้เคียงแปดจุดของแต่ละเซลล์ สำหรับละแวกใกล้เคียงอื่น ๆ เพียงแค่ปรับเปลี่ยนnbrhoodค่าใกล้กับจุดเริ่มต้นของexpand: มันเป็นรายการของดัชนีชดเชยที่เกี่ยวข้องกับเซลล์ใด ๆ ที่ได้รับ ยกตัวอย่างเช่นย่าน "D4" matrix(c(-1,0, 1,0, 0,-1, 0,1), nrow=2)อาจจะระบุเป็น

เห็นได้ชัดว่าวิธีการแพร่กระจายนี้มีปัญหา: มันทิ้งไว้ข้างหลัง หากนั่นไม่ใช่สิ่งที่ตั้งใจไว้มีหลายวิธีในการแก้ไขปัญหานี้ ตัวอย่างเช่นเก็บเซลล์ที่มีอยู่ในคิวเพื่อให้เซลล์ที่เก่าที่สุดที่พบนั้นเป็นเซลล์ที่เก่าที่สุด การสุ่มบางอย่างยังสามารถใช้ได้ แต่เซลล์ที่มีอยู่จะไม่ถูกเลือกด้วยความน่าจะเป็นชุด (เท่ากับ) อีกต่อไป อีกวิธีที่ซับซ้อนกว่าคือการเลือกเซลล์ที่มีความน่าจะเป็นซึ่งขึ้นอยู่กับจำนวนเพื่อนบ้านที่มี เมื่อเซลล์ล้อมรอบคุณสามารถสร้างโอกาสในการเลือกสูงจนไม่กี่หลุมที่ไม่ได้บรรจุจนเต็ม

ฉันจะเสร็จสิ้นโดยแสดงความคิดเห็นว่านี่ไม่ใช่หุ่นยนต์อัตโนมัติ (CA) ซึ่งจะไม่ดำเนินการต่อเซลล์โดยเซลล์ แต่จะอัปเดตทั้งเซลล์ของเซลล์ในแต่ละรุ่น ความแตกต่างนั้นบอบบาง: ด้วย 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, ... , ฯลฯ

ประการแรกการเปลี่ยนแปลงexpandที่จะกลับมา (ก) NAในบรรทัดแรกถ้ามีข้อผิดพลาดและ (ข) ค่าในมากกว่าเมทริกซ์indices y(ไม่ต้องเสียเวลาในการสร้างเมทริกซ์ใหม่yด้วยการโทรแต่ละครั้ง) ด้วยการเปลี่ยนแปลงนี้ทำให้การวนซ้ำเป็นเรื่องง่าย: เลือกการเริ่มต้นแบบสุ่มพยายามขยายไปรอบ ๆ มันรวบรวมดัชนีคลัสเตอร์ในindicesกรณีที่สำเร็จแล้วทำซ้ำจนกว่าจะเสร็จ ส่วนสำคัญของห่วงคือการ จำกัด จำนวนของการทำซ้ำในกรณีที่กลุ่มที่อยู่ติดกันจำนวนมากไม่สามารถพบได้: count.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 (ทำให้มีขนาดเล็กและหยาบพอที่จะทำให้กระจุกเกิดขึ้นได้) ใช้เวลาสองวินาทีในการดำเนินการ บนกริด 3100 คูณ 5,000 (ใหญ่กว่า 100 เท่า) ใช้เวลานานกว่า (24 วินาที) แต่ระยะเวลาปรับขยายได้ดีพอสมควร (บนแพลตฟอร์มอื่น ๆ เช่น C ++ ระยะเวลาแทบจะไม่ขึ้นกับขนาดกริด)

60 กลุ่ม


สวัสดี Whuber ขอบคุณมากสำหรับสิ่งนี้ฉันขอขอบคุณจริงๆ ฉันแค่ทำการทดลองเล็กน้อยและอาจจะกลับมาพร้อมคำถามการติดตามบางอย่างในไม่ช้า
JPD

+1 ขอบคุณที่ให้คำตอบอย่างละเอียดกับคำถามที่ซับซ้อนกว่านี้ใน GIS SE
Radar

@whuber ขยายคำถามไปเล็กน้อยตามคำตอบของคุณ ขอบคุณอีกครั้ง!
JPD

1
คำตอบที่ # 1 เป็นเล็กน้อย: แทนที่บรรทัดโดยy[indices] <- 1:length(indices) y[indices] <- 2คำตอบของ # 2 นั้นง่ายมากเพียงแค่วนซ้ำ
whuber

@whuber ขอขอบคุณอีกครั้งสำหรับการอัปเดต size.clustersขณะนี้เป็นปัญหาของการกระจุกปะทะกันและส่งผลให้กลุ่มก้อนขนาดน้อยกว่าจำนวนที่กำหนดไว้ใน ฉันจะแน่ใจได้อย่างไรว่าก้อนเติบโตขึ้นในขนาดที่ถูกต้องในขณะนี้ฉันคิดว่ามันพยายามที่จะเติบโตเป็นกลุ่มที่มีอยู่ล้มเหลว แต่ยังคงลงทะเบียนว่าเป็นการขยายตัวที่ประสบความสำเร็จ จากนั้นฉันก็ตั้งใจที่จะย้ำการผลิตของ 60 clumps 1000 ครั้งสร้างชุดข้อมูลสไตล์โมฆะเฉลี่ย การวางตำแหน่งแบบสุ่มจะแตกต่างกันในแต่ละครั้งภายในforลูปหรือไม่
JPD

3

โดยไม่ต้องทำการดำเนินการของคุณและไม่มีเวลาว่างสำหรับการเล่นฉันสามารถเพิ่มลิงก์ทั้งสองนี้ในรายการของคุณ:

ค้นหาค่า Raster Cell ที่ใกล้ที่สุดโดยอิงจากจุดเวกเตอร์ (คำตอบแรก (ด้วยคะแนนโหวต 4) เป็นสิ่งที่ทำให้ฉันทึ่ง)

เช่นกัน: Hawth's Gridspread จะช่วยด้วย?

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.