ฉันจะสร้างกริดที่ผิดปกติที่มีจุดต่ำสุดได้อย่างไร


20

มีตัวอย่างของจุดกระจายที่ไม่สม่ำเสมอ (~ 1 ล้าน) ขนาดใหญ่ - เป็นไปได้หรือไม่ที่จะสร้างกริดที่ผิดปกติ (ขนาด แต่อาจมีรูปร่างผิดปกติถ้าเป็นไปได้?) ซึ่งจะมีจำนวนจุดnขั้นต่ำที่ระบุไว้

มันมีความสำคัญน้อยกว่าสำหรับฉันหากสร้าง 'เซลล์' ของกริดดังกล่าวมีจำนวนnจำนวนจุดหรืออย่างน้อยnจุด

ฉันรู้วิธีแก้ปัญหาเช่นgenvecgridใน ArcGIS หรือสร้าง Grid Layerใน QGIS / mmgis อย่างไรก็ตามพวกเขาทั้งหมดจะสร้างกริดปกติซึ่งจะส่งผลให้มีเซลล์ว่างเปล่า (ปัญหาเล็ก - ฉันสามารถทิ้งพวกเขา) หรือเซลล์ที่มีคะแนน น้อยกว่าn (ปัญหาใหญ่กว่าเนื่องจากฉันต้องการวิธีแก้ปัญหาเพื่อรวมเซลล์เหล่านั้นอาจใช้เครื่องมือบางอย่างจากที่นี่ใช่ไหม)

ฉันได้รับประโยชน์จากสิ่งต่างๆมากมายและเปิดรับทั้งในเชิงพาณิชย์ (ArcGIS & ส่วนขยาย) หรือฟรี (Python, PostGIS, R) โซลูชั่น


1
กริดต้องการ "ปกติ" อย่างไร ฉันสงสัยว่าคุณสามารถทำการจัดกลุ่มแบบลำดับชั้นได้หรือไม่จากนั้นก็ตัด dendrogram เพื่อให้ตรงกับความต้องการของคุณ เอกสาร CrimeStat มีตัวอย่างที่ดีของการทำคลัสเตอร์ประเภทนี้
Andy W

5
คุณช่วยอธิบายสิ่งที่คุณหมายถึงโดย "ตารางที่ผิดปกติ" ได้ไหม? นั่นฟังดูเหมือน oxymoron :-) ยิ่งไปกว่านั้นจุดประสงค์ของแบบฝึกหัดนี้คืออะไร? โปรดทราบด้วยเช่นกันว่าจำเป็นต้องมีหลักเกณฑ์หรือข้อ จำกัด เพิ่มเติม: หลังจากทั้งหมดหากคุณวาดสี่เหลี่ยมจัตุรัสประมาณ 1 ล้านจุดมันอาจถูกพิจารณาว่าเป็นส่วนหนึ่งของกริดและมันจะมีมากกว่าnของพวกเขา คุณอาจจะไม่สนใจวิธีแก้ปัญหาเล็กน้อยนี้ แต่: แต่ทำไมไม่อย่างนั้นล่ะ?
whuber

@AndyW ขอบคุณ ความคิดที่ดี & คุ้มค่าการสำรวจ จะมีรูปลักษณ์ ขนาด & รูปร่างของ 'กริด' มีความสำคัญรองสำหรับฉัน - ลำดับความสำคัญ (เนื่องจากความเป็นส่วนตัวของข้อมูล) คือการ 'ซ่อน' nคุณสมบัติที่อยู่เบื้องหลัง one
radek

@whuber ขอบคุณเช่นกัน ฉันเห็นด้วย - แต่ไม่แน่ใจว่าฉันจะตั้งชื่อการแบ่งพาร์ติชันดังกล่าวได้อย่างไร แรงจูงใจหลักของฉันคือความเป็นส่วนตัวของข้อมูล มีสถานที่ห้าจุด (ซึ่งฉันไม่สามารถแสดงบนแผนที่สุดท้าย) ฉันต้องการที่จะเป็นตัวแทนพวกเขาโดยพื้นที่ที่ครอบคลุมพวกเขา; และรับค่าเฉลี่ย / มัธยฐาน / ฯลฯ คุณค่าสำหรับสิ่งนั้น ฉันยอมรับว่ามันจะเป็นไปได้ที่จะวาดหนึ่งสี่เหลี่ยมหรือเปลือกนูนเป็นตัวแทนพวกเขาทั้งหมด - นั่นจะเป็นการป้องกันความเป็นส่วนตัวของข้อมูลที่ดีที่สุดที่ฉันเดา? ;] อย่างไรก็ตาม - มันจะมีประโยชน์มากกว่าที่จะเป็นตัวแทนของมันด้วยรูปร่างที่ล้อมรอบสมมติว่ามี 10 คุณสมบัติ จากนั้น - ฉันยังคงสามารถรักษารูปแบบเชิงพื้นที่ได้
radek

1
IMO ให้คำอธิบายของคุณฉันจะใช้การแก้ไขบางประเภทและแสดงแผนที่แรสเตอร์ (บางทีแบนด์วิดท์ที่ปรับขนาดของ N ที่น้อยที่สุดของคุณจะเพียงพอที่จะทำให้ข้อมูลราบรื่น) เท่าที่ CrimeStat ไฟล์ที่ใหญ่ที่สุดที่ฉันเคยใช้มีประมาณ 100,000 รายที่ฉันเชื่อ (และการรวมกลุ่มจะใช้เวลาอย่างแน่นอน) เป็นไปได้ว่าคุณสามารถทำการสรุปข้อมูลล่วงหน้าเพื่อแสดงว่าเป็นกรณีที่น้อยลงและยังได้ผลลัพธ์ที่ต้องการสำหรับสิ่งที่คุณต้องการ มันเป็นโปรแกรมที่ง่ายจริงๆฉันขอแนะนำให้ใช้เวลาไม่กี่นาทีเพื่อทดลองใช้และดูด้วยตัวคุณเอง
Andy W

คำตอบ:


26

ฉันเห็น MerseyViking ได้แนะนำควอดทรี ฉันจะแนะนำสิ่งเดียวกันและเพื่อที่จะอธิบายมันนี่คือรหัสและตัวอย่าง รหัสนี้เขียนขึ้นRแต่ควรจะย้ายไปที่ Python อย่างง่ายดาย

แนวคิดนี้เรียบง่ายอย่างน่าทึ่ง: แบ่งคะแนนโดยประมาณครึ่งหนึ่งในทิศทาง x จากนั้นแบ่งสองส่วนซ้ำ ๆ กันตามทิศทาง y สลับทิศทางในแต่ละระดับจนกระทั่งไม่ต้องการแยกอีกต่อไป

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

เนื่องจากความตั้งใจคือเพื่อรักษาจำนวนkโหนดขั้นต่ำภายในแต่ละใบไม้ของควอดทรีเราจึงใช้รูปแบบควอดทรีที่ จำกัด มันจะสนับสนุน (1) คะแนนการจัดกลุ่มเป็นกลุ่มที่มีระหว่างkและ 2 * k-1 องค์ประกอบแต่ละและ (2) การทำแผนที่ Quadrants

Rรหัสนี้สร้างแผนผังของโหนดและเทอร์มินัลออกจากพวกเขาแยกแยะตามชั้นเรียน การติดฉลากระดับทำให้การประมวลผลเร็วขึ้นเช่นการลงจุดที่แสดงด้านล่าง รหัสใช้ค่าตัวเลขสำหรับรหัส วิธีนี้ใช้ได้กับความลึกสูงสุด 52 ในทรี (ใช้คู่ผสมหากใช้จำนวนเต็มแบบยาวที่ไม่ได้ลงนามความลึกสูงสุดคือ 32) สำหรับต้นไม้ที่ลึกกว่า (ซึ่งไม่น่าเป็นไปได้สูงในการสมัครใด ๆ เพราะอย่างน้อยk* 2 ^ 52 คะแนนจะเกี่ยวข้องกับ) รหัสจะต้องเป็นสตริง

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. สิ่งนี้มีประโยชน์สำหรับการประเมินเวลาคำนวณ ตัวอย่างเช่นหากใช้เวลา 13 วินาทีในการแบ่งพาร์ติชัน n = 10 ^ 6 คะแนนในเซลล์ 50-99 คะแนน (k = 50), m = 10 ^ 6/50 = 20000 หากคุณต้องการแบ่งพาร์ติชันลงเป็น 5-9 คะแนนต่อเซลล์ (k = 5) m มีขนาดใหญ่กว่า 10 เท่าดังนั้นเวลาจะสูงถึงประมาณ 130 วินาที (เนื่องจากกระบวนการแยกชุดของพิกัดรอบตัว middles ของพวกเขาได้เร็วขึ้นเมื่อเซลล์มีขนาดเล็กลงเวลาที่แท้จริงคือ 90 วินาทีเท่านั้น) หากต้องการไปจนถึง k = 1 จุดต่อเซลล์มันจะใช้เวลานานขึ้นประมาณหกครั้ง ยังหรือเก้านาทีและเราสามารถคาดหวังว่ารหัสจริงจะเร็วกว่านั้นเล็กน้อย

ก่อนที่จะดำเนินการต่อไปเราจะสร้างข้อมูลที่มีระยะห่างที่ไม่สม่ำเสมอและสร้างควอดทรีที่ถูก จำกัด (เวลาที่ผ่านไป 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ลองเขียนเซลล์ quadtree ทั้งหมดเป็นรูปหลายเหลี่ยมรูปร่างโดยใช้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ที่นี่เพื่อหน้าต่างลงในภูมิภาคย่อยหรือเพื่อขยายการแมปไปยังภูมิภาคที่มีขนาดใหญ่กว่าโค้ดนี้มีค่าเริ่มต้นตามขอบเขตของคะแนน)

เพียงอย่างเดียวนี้ก็เพียงพอแล้ว:การรวมกันของรูปหลายเหลี่ยมเหล่านี้ไปยังจุดเริ่มต้นจะระบุกลุ่ม เมื่อระบุแล้วการดำเนินการ "สรุป" ฐานข้อมูลจะสร้างสถิติสรุปของคะแนนภายในแต่ละเซลล์


ว้าว! น่าอัศจรรย์ จะให้ช็อตเด็ดกับข้อมูลของฉันอีกครั้งในออฟฟิศ =)
radek

4
คำตอบยอดนิยม @whuber! +1
MerseyViking

1
(1) คุณสามารถอ่าน shapefiles โดยตรงกับ ( อนึ่ง ) เดอะshapefilesแพคเกจหรืออื่น ๆ คุณสามารถส่งออก (x, y) พิกัดในข้อความ ASCII read.tableและอ่านพวกเขาด้วย (2) ฉันขอแนะนำให้เขียนqtในสองรูปแบบ: อันดับแรกเป็นไฟล์รูปร่างจุดxyที่idรวมฟิลด์เป็นตัวระบุคลัสเตอร์ ที่สองที่ส่วนของเส้นที่พล็อตโดยlines.quadtreeถูกเขียนออกมาเป็น polyline shapefile (หรือการประมวลผลแบบอะนาล็อกเขียนเซลล์เป็นรูปหลายเหลี่ยมรูปร่าง) สิ่งนี้ง่ายเหมือนการปรับเปลี่ยนlines.quadtree.leafเอาต์พุตxylimเป็นสี่เหลี่ยม (ดูการแก้ไข)
whuber

1
@ คนที่ขอบคุณมากสำหรับการปรับปรุง ทุกอย่างทำงานได้อย่างราบรื่น ดีกว่า +50 ถึงแม้ว่าตอนนี้ฉันคิดว่ามันสมควร +500!
radek

1
ฉันสงสัยว่ารหัสที่คำนวณได้นั้นไม่ซ้ำกันด้วยเหตุผลบางประการ ทำการเปลี่ยนแปลงเหล่านี้ในคำนิยามของquad: (1) เริ่มต้นid=1; (2) เปลี่ยนid/2เป็นid*2ในlower=บรรทัด; (3) ทำการเปลี่ยนแปลงที่คล้ายกันid*2+1ในupper=บรรทัด (ฉันจะแก้ไขคำตอบของฉันเพื่อสะท้อนให้เห็น) นั่นก็ควรดูแลการคำนวณพื้นที่: ขึ้นอยู่กับ GIS ของคุณพื้นที่ทั้งหมดจะเป็นค่าบวกหรือทั้งหมดจะเป็นค่าลบ หากพวกเขากำลังเชิงลบทั้งหมดกลับรายการสำหรับxและในy cell.quadtree.leaf
whuber

6

ดูว่าอัลกอริทึมนี้ให้ตัวตนที่เพียงพอสำหรับตัวอย่างข้อมูลของคุณหรือไม่:

  1. เริ่มต้นด้วยกริดปกติ
  2. หากรูปหลายเหลี่ยมมีค่าน้อยกว่าขีด จำกัด ให้รวมกับการสลับเพื่อนบ้าน (E, S, W, N) วนวนตามเข็มนาฬิกา
  3. หากรูปหลายเหลี่ยมมีค่าน้อยกว่าขีด จำกัด ให้ไปที่ 2 หรือไปที่รูปหลายเหลี่ยมถัดไป

ตัวอย่างเช่นหากเกณฑ์ขั้นต่ำคือ 3:

ขั้นตอนวิธี


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

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

5

เช่นเดียวกับวิธีแก้ปัญหาที่น่าสนใจของเปาโลการใช้อัลกอริทึมการแบ่งต้นไม้แบบต้นไม้

ตั้งค่าความลึกที่คุณต้องการ quadtree ไป คุณสามารถมีคะแนนขั้นต่ำหรือสูงสุดต่อเซลล์ดังนั้นบางโหนดจะลึก / เล็กกว่าคนอื่น ๆ

แบ่งโลกของคุณโดยกำจัดโหนดว่างเปล่า ล้างและทำซ้ำจนกว่าจะถึงเกณฑ์


ขอบคุณ คุณจะแนะนำซอฟต์แวร์อะไรให้
radek

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

1
ฉันคิดว่ามันเป็นแบบนี้: ลองนึกภาพว่ามีโหนดมากกว่าจุดสูงสุดของคะแนนในนั้น แต่พวกมันจะรวมกันที่ด้านบนซ้ายของโหนด โหนดจะถูกแบ่งย่อย แต่โหนดย่อยด้านล่างขวาจะว่างเปล่าดังนั้นจึงสามารถตัดได้
MerseyViking

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

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