【问题标题】:Calculating weighted polygon centroids in R在R中计算加权多边形质心
【发布时间】:2014-06-30 02:07:28
【问题描述】:

我需要根据单独的人口网格数据集计算一组空间区域的质心。感谢您指导如何通过以下示例实现这一目标。

提前致谢。

require(raster)
require(spdep)
require(maptools)

dat <- raster(volcano)   # simulated population data
polys <- readShapePoly(system.file("etc/shapes/columbus.shp",package="spdep")[1])

# set consistent coordinate ref. systems and bounding boxes
proj4string(dat) <- proj4string(polys) <- CRS("+proj=longlat +datum=NAD27")
extent(dat) <- extent(polys)

# illustration plot
plot(dat, asp = TRUE)
plot(polys, add = TRUE)

【问题讨论】:

  • 因此,对于每个多边形,您想要的坐标是该多边形中单元格的加权(按栅格单元)平均位置,是吗?质心,栅格在哪里?
  • 是的,基本上是每个多边形的人口加权质心,我猜这将是固体物体质心的二维等价物。 closest google match 不能解决它。我假设某个包中已经存在一个函数。
  • 顺便说一句,很好的可重现示例。我会尽量记住在未来链接到这个,作为一个例子,看看它们是什么样子的!
  • 感谢乔希的反馈。它本身也是一个有用的过程 - 我经常解决我计划在 SO 中发布的问题,同时尝试将它们复制为玩具模型。

标签: r gis raster centroid


【解决方案1】:

三个步骤:

首先,找到每个多边形中的所有单元格,返回一个包含单元格编号和值的 2 列矩阵列表:

require(plyr) # for llply, laply in a bit...
cell_value = extract(dat, polys,cellnumbers=TRUE)
head(cell_value[[1]])
     cell value
[1,]   31   108
[2,]   32   108
[3,]   33   110
[4,]   92   110
[5,]   93   110
[6,]   94   111

其次,变成类似矩阵的列表,但添加 x 和 y 坐标:

cell_value_xy = llply(cell_value, function(x)cbind(x,xyFromCell(dat,x[,"cell"])))
head(cell_value_xy[[1]])
     cell value        x        y
[1,]   31   108 8.581164 14.71973
[2,]   32   108 8.669893 14.71973
[3,]   33   110 8.758623 14.71973
[4,]   92   110 8.581164 14.67428
[5,]   93   110 8.669893 14.67428
[6,]   94   111 8.758623 14.67428

第三,计算加权平均坐标。这忽略了任何边缘效应并假设所有网格单元的大小相同:

centr = laply(cell_value_xy, function(m){c(weighted.mean(m[,3],m[,2]), weighted.mean(m[,4],m[,2]))})
head(centr)
            1        2
[1,] 8.816277 14.35309
[2,] 8.327463 14.02354
[3,] 8.993655 13.82518
[4,] 8.467312 13.71929
[5,] 9.011808 13.28719
[6,] 9.745000 13.47444

现在centr 是一个 2 列矩阵。在您的示例中,它非常接近 coordinates(polys),因此我会制作一个带有一些极端权重的人为示例,以确保其按预期工作。

【讨论】:

  • 感谢 Spaced,但我怀疑这是否按预期工作。例如查看点的位置(即points(centr, pch=16, cex=1, col='red'))。如果我们通过使用dat = raster((volcano-92)^1.5/10) 来增加数据的对比度,您会期望看到质心更接近绿色,但在我看来,它们在多边形中的位置仍然有点过于居中。以顶部多边形为例。
  • @geotheory -- 也许可以“手动”检查一些计算。快速浏览每个多边形内的值范围(在许多情况下小于 +/- 5%)使 me 怀疑权重不会对质心位置产生太大影响。我必须更深入地挖掘以确保,但 Spacedman 的代码在我看来确实不错,而且无论如何,这是你的工作,对吧? ;)(当然,+1)。
  • 要进行测试,首先使用平面栅格查看我的点是否等于质心(修改一些边缘效果)。然后制作一个多边形,它是南半部 0 和北半部 1 栅格单元,并检查我的质心在北半部...
  • 是的,检查计算似乎我猜错了——也许不同的数据会成为一个更好的例子。您的两种方法都给出了完全相同的观点,对此我深表感谢。我喜欢 rasterize/zonal 的优雅,但 Spaced 可能会因为“Ronseal”的清晰度而获胜。
【解决方案2】:

另一种选择。

我喜欢它的紧凑性,但它可能只有在您相当熟悉整个 raster 函数系列的情况下才有意义:

## Convert polygons to a raster layer
z <- rasterize(polys, dat)

## Compute weighted x and y coordinates within each rasterized region
xx <- zonal(init(dat, v="x")*dat, z) / zonal(dat,z)
yy <- zonal(init(dat, v="y")*dat, z) / zonal(dat,z)

## Combine results in a matrix
res <- cbind(xx[,2],yy[,2])
head(res)
#          [,1]     [,2]
# [1,] 8.816277 14.35309
# [2,] 8.327463 14.02354
# [3,] 8.993655 13.82518
# [4,] 8.467312 13.71929
# [5,] 9.011808 13.28719
# [6,] 9.745000 13.47444

【讨论】:

  • 越来越熟悉raster 包函数只能是一件好事。我查看了raster 中的函数列表,并没有看到任何明显的东西——我们需要一个更好的备忘单!
  • @Spacedman -- 同意。我是在 Etienne Racine 的 Visual Raster Cheat Sheet 中遇到 init 时才发现它的。它(备忘单)很漂亮,但它仍然遗漏了该包中的许多好东西——尤其是它用于处理空间矢量数据类型的功能套件。我猜很少有人知道它的aggregate()cover()bind()symdif()union()intersect()erase() 功能。 (最后三个也可以通过重载的"+""*""-" 运算符调用,这很有趣。)
【解决方案3】:

Spacedman 和 Josh 的答案真的很棒,但我想分享另外两个相对快速和简单的替代方案。

library(data.table)
library(spatialEco)
library(raster)
library(rgdal)

使用data.table 方法:

# get centroids of raster data
  data_points <- rasterToPoints(dat, spatial=TRUE)

# intersect with polygons
  grid_centroids <- point.in.poly(data_points, polys)

# calculate weighted centroids
  grid_centroids <- as.data.frame(grid_centroids)
  w.centroids <- setDT(grid_centroids)[, lapply(.SD, weighted.mean, w=layer), by=POLYID, .SDcols=c('x','y')]

使用wt.centroid{spatialEco}

  # get a list of the ids from each polygon
    poly_ids <- unique(grid_centroids@data$POLYID)

  # use lapply to calculate the weighted centroids of each individual polygon
    w.centroids.list <- lapply(poly_ids, function(i){wt.centroid( subset(grid_centroids, grid_centroids@data$POLYID ==i)
                                                                  , 'layer', sp = TRUE)} )

【讨论】:

    【解决方案4】:

    下面是我自己不太优雅的解决方案。给出与 Spacedman 和 Josh 完全相同的结果。

    # raster to pixels
    p = rasterToPoints(dat) %>% as.data.frame()
    coordinates(p) = ~ x + y
    crs(p) = crs(polys)
    
    # overlay pixels on polygons
    ol = over(p, polys) %>% mutate(pop = p$layer) %>% cbind(coordinates(p)) %>% 
      filter(COLUMBUS_ %in% polys$COLUMBUS_) %>%     # i.e. a unique identifier
      dplyr::select(x, y, pop, COLUMBUS_) %>% as_data_frame()
    
    # weighted means of x/y values, by pop
    pwcs = split(ol, ol$COLUMBUS_) %>% lapply(function(g){
      data.frame(x = weighted.mean(g$x, g$pop), y = weighted.mean(g$y, g$pop))
    }) %>% bind_rows() %>% as_data_frame()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-11-14
      • 2014-06-28
      • 2011-01-22
      • 1970-01-01
      • 2021-01-30
      • 2019-03-02
      • 1970-01-01
      相关资源
      最近更新 更多