【问题标题】:Map of bivariate spatial correlation in R (bivariate LISA)R中的二元空间相关图(二元LISA)
【发布时间】:2017-07-18 21:42:05
【问题描述】:

我想创建一张地图,显示两个变量之间的双变量空间相关性。这可以通过制作二元 Moran's I 空间相关性的 LISA 图或使用Lee (2001) 提出的 L 指数来完成。

spdep 库中没有实现双变量 Moran's I,但 L 索引是,所以这是我尝试使用 L 索引但没有成功的方法。一个显示基于莫兰的解决方案的答案我也将非常受欢迎!

正如您从下面可重现的示例中看到的那样,到目前为止,我已经设法计算了本地 L 索引。 我想做的是估计伪p值和create a map of the results like those maps we use in LISA spatial clusters with high-high, high-low, ..., low-low

在此示例中,目标是创建一个在黑人和白人人口之间具有双变量 Lisa 关联的地图。该地图应在 ggplot2 中创建,显示集群:

  • 黑人的高度存在和白人的高度存在
  • 黑人多,白人少
  • 黑人少,白人多
  • 黑人的低存在和白人的低存在哦

可重现的例子

library(UScensus2000tract)
library(ggplot2)
library(spdep)
library(sf)

# load data
  data("oregon.tract")

# plot Census Tract map
  plot(oregon.tract)


# Variables to use in the correlation: white and black population in each census track
  x <- scale(oregon.tract$white)
  y <- scale(oregon.tract$black)


# create  Queen contiguity matrix and Spatial weights matrix
  nb <- poly2nb(oregon.tract)
  lw <- nb2listw(nb)


# Lee index
  Lxy <-lee(x, y, lw, length(x), zero.policy=TRUE)

  # Lee’s L statistic (Global)
    Lxy[1]
    #> -0.1865688811


# 10k permutations to estimate pseudo p-values
  LMCxy <- lee.mc(x, y, nsim=10000, lw, zero.policy=TRUE, alternative="less")

# quik plot of local L
  Lxy[[2]] %>% density() %>% plot() # Lee’s local L statistic  (Local)
  LMCxy[[7]] %>% density() %>% lines(col="red") # plot values simulated 10k times 


# get confidence interval of 95% ( mean +- 2 standard deviations)
  two_sd_above <- mean(LMCxy[[7]]) + 2 * sd(LMCxy[[7]])
  two_sd_below <- mean(LMCxy[[7]]) - 2 * sd(LMCxy[[7]])


# convert spatial object to sf class for easier/faster use
  oregon_sf <- st_as_sf(oregon.tract)


# add L index values to map object
  oregon_sf$Lindex <- Lxy[[2]]

# identify significant local results  
  oregon_sf$sig <- if_else( oregon_sf$Lindex < 2*two_sd_below, 1, if_else( oregon_sf$Lindex > 2*two_sd_above, 1, 0))


# Map of Local L index but only the significant results
  ggplot() + geom_sf(data=oregon_sf, aes(fill=ifelse( sig==T, Lindex, NA)), color=NA)

【问题讨论】:

    标签: r geospatial spatial spdep


    【解决方案1】:

    这个呢?

    我使用的是常规的 Moran's I,而不是您建议的 Lee Index。但我认为基本的推理几乎是一样的。

    正如您在下面看到的那样——以这种方式产生的结果与来自 GeoDA 的结果非常相似

    library(dplyr)
    library(ggplot2)
    library(sf)
    library(spdep)
    library(rgdal)
    library(stringr)
    library(UScensus2000tract)
    
    #======================================================
    # load data
    data("oregon.tract")
    
    # Variables to use in the correlation: white and black population in each census track
    x <- oregon.tract$white
    y <- oregon.tract$black
    
    #======================================================
    # Programming some functions
    
    # Bivariate Moran's I
    moran_I <- function(x, y = NULL, W){
            if(is.null(y)) y = x
    
            xp <- (x - mean(x, na.rm=T))/sd(x, na.rm=T)
            yp <- (y - mean(y, na.rm=T))/sd(y, na.rm=T)
            W[which(is.na(W))] <- 0
            n <- nrow(W)
    
            global <- (xp%*%W%*%yp)/(n - 1)
            local  <- (xp*W%*%yp)
    
            list(global = global, local  = as.numeric(local))
    }
    
    
    # Permutations for the Bivariate Moran's I
    simula_moran <- function(x, y = NULL, W, nsims = 1000){
    
            if(is.null(y)) y = x
    
            n   = nrow(W)
            IDs = 1:n
    
            xp <- (x - mean(x, na.rm=T))/sd(x, na.rm=T)
            W[which(is.na(W))] <- 0
    
            global_sims = NULL
            local_sims  = matrix(NA, nrow = n, ncol=nsims)
    
            ID_sample = sample(IDs, size = n*nsims, replace = T)
    
            y_s = y[ID_sample]
            y_s = matrix(y_s, nrow = n, ncol = nsims)
            y_s <- (y_s - apply(y_s, 1, mean))/apply(y_s, 1, sd)
    
            global_sims  <- as.numeric( (xp%*%W%*%y_s)/(n - 1) )
            local_sims  <- (xp*W%*%y_s)
    
            list(global_sims = global_sims,
                 local_sims  = local_sims)
    }
    
    
    #======================================================
    # Adjacency Matrix (Queen)
    
    nb <- poly2nb(oregon.tract)
    lw <- nb2listw(nb, style = "B", zero.policy = T)
    W  <- as(lw, "symmetricMatrix")
    W  <- as.matrix(W/rowSums(W))
    W[which(is.na(W))] <- 0
    
    #======================================================
    # Calculating the index and its simulated distribution
    # for global and local values
    
    m   <- moran_I(x, y, W)
    m[[1]] # global value
    
    m_i <- m[[2]]  # local values
    
    local_sims <- simula_moran(x, y, W)$local_sims
    
    # Identifying the significant values 
    alpha <- .05  # for a 95% confidence interval
    probs <- c(alpha/2, 1-alpha/2)
    intervals <- t( apply(local_sims, 1, function(x) quantile(x, probs=probs)))
    sig        <- ( m_i < intervals[,1] )  | ( m_i > intervals[,2] )
    
    #======================================================
    # Preparing for plotting
    
    oregon.tract     <- st_as_sf(oregon.tract)
    oregon.tract$sig <- sig
    
    
    # Identifying the LISA patterns
    xp <- (x-mean(x))/sd(x)
    yp <- (y-mean(y))/sd(y)
    
    patterns <- as.character( interaction(xp > 0, W%*%yp > 0) ) 
    patterns <- patterns %>% 
            str_replace_all("TRUE","High") %>% 
            str_replace_all("FALSE","Low")
    patterns[oregon.tract$sig==0] <- "Not significant"
    oregon.tract$patterns <- patterns
    
    
    # Plotting
    ggplot() + geom_sf(data=oregon.tract, aes(fill=patterns), color="NA") +
            scale_fill_manual(values = c("red", "pink", "light blue", "dark blue", "grey95")) + 
            theme_minimal()
    

    您可以通过更改置信区间(例如,使用 90% 而不是 95%)获得与 GeoDa 更接近(但不相同)的结果。

    我想剩余的差异来自于计算莫兰 I 的一种稍微不同的方法。我的版本给出了与包 spdep 中可用的函数 moran 相同的值。但 GeoDa 可能使用了另一种方法。

    【讨论】:

    • 非常感谢 Rogerio,这看起来很有希望。 y_s 是空间滞后的 y 吗?
    • @rafa.pereira, y_s 代表“y 模拟”。滞后 y 乘以邻接矩阵后得到:W%*%y_s
    • 嗨@RogerioJB,你如何在你的函数中获得Moran's I的全局p值?
    • 该函数还为全局索引提供 sim 值:simula_moran(x, y, W)$global_sims。您可以通过计算绝对值高于实际指数的abs值的sims的比例来获得p值。但是,一旦相关指数随着观察次数的增加而非常快速地达到显着性,这个比例可能会为零或非常接近于零。您可以通过增加模拟次数来获得更精确的结果 - 但由于 RAM 内存不足可能会导致 R 崩溃(如果您执行的次数超过一百万,则可能会发生)
    • 还有另一种方法。从视觉上看,模拟值的分布似乎是对称的并且以零为中心。如果它是正常的,并且如果增加 sims 的数量会使这种相似性变得更好(您必须对其进行测试),那么您可以使用通常的程序将观察到的统计数据除以其标准差(并玩一个伪渐近游戏)
    【解决方案2】:

    我想现在添加到线程中已经很晚了,但是 Lee 的 L 与您在这里所做的完全不同,这是 Wartenberg (1985) 的创新。这有一些潜在的缺点。主要是,它测试了 xy 的滞后 之间的关系,正如@RogerioJB 通过解释说通过将模拟的 y 乘以邻接矩阵来计算空间滞后 y。 Lee (2001) 的创新完全不同,它涉及 Pearson 的 r 和空间平滑标量 (SSS) 的集成,而是比较了 xy 而不是 y 的滞后。 @RogerioJB 采用的方法可以通过从 lee.mc 函数生成可能的本地 l 的分布来复制。反过来,结果可以以类似于 GeoDa 的高-高...低-低显着性聚类图的样式绘制。

    【讨论】:

      【解决方案3】:

      根据@justin-k 的建议,我修改了@rogeriojb 的二元LISA 代码来计算Lee 的L 统计量。这种方法从spdep 包中创建了一个修改过的lee.mc() 函数来模拟本地L 值。我在GitHub gist 中提供了另一个带有点级数据的示例。

      library(boot)
      library(dplyr)
      library(ggplot2)
      library(sf)
      library(spdep)
      library(rgdal)
      library(stringr)
      library(UScensus2000tract)
      
      #======================================================
      # load data
      data("oregon.tract")
      
      # Variables to use in the correlation: white and black population in each census track
      x <- oregon.tract$white
      y <- oregon.tract$black
      
      # ----------------------------------------------------- #
      # Program a function
      ## Permutations for Lee's L statistic
      ## Modification of the lee.mc() function within the {spdep} package
      ## Saves 'localL' output instead of 'L' output
      simula_lee <- function(x, y, listw, nsim = nsim, zero.policy = NULL, na.action = na.fail) {
        
        if (deparse(substitute(na.action)) == "na.pass") 
          stop ("na.pass not permitted")
        na.act <- attr(na.action(cbind(x, y)), "na.action")
        x[na.act] <- NA
        y[na.act] <- NA
        x <- na.action(x)
        y <- na.action(y)
        if (!is.null(na.act)) {
          subset <- !(1:length(listw$neighbours) %in% na.act)
          listw <- subset(listw, subset, zero.policy = zero.policy)
        }
        n <- length(listw$neighbours)
        if ((n != length(x)) | (n != length(y))) 
          stop ("objects of different length")
        gamres <- suppressWarnings(nsim > gamma(n + 1))
        if (gamres) 
          stop ("nsim too large for this number of observations")
        if (nsim < 1) 
          stop ("nsim too small")
        xy <- data.frame(x, y)
        S2 <- sum((unlist(lapply(listw$weights, sum)))^2)
        
        lee_boot <- function(var, i, ...) {
          return(lee(x = var[i, 1], y = var[i, 2], ...)$localL)
        }
        
        res <- boot(xy, statistic = lee_boot, R = nsim, sim = "permutation", 
                    listw = listw, n = n, S2 = S2, zero.policy = zero.policy)
      }
      
      # ----------------------------------------------------- #
      # Adjacency Matrix
      nb <- poly2nb(oregon.tract)
      lw <- nb2listw(nb, style = "B", zero.policy = T)
      W  <- as(lw, "symmetricMatrix")
      W  <- as.matrix(W / rowSums(W))
      W[which(is.na(W))] <- 0
      
      # ----------------------------------------------------- #
      # Calculate the index and its simulated distribution
      # for global and local values
      
      # Global Lee's L
      lee.test(x = x, y = y, listw = lw, zero.policy = TRUE,
               alternative = "two.sided", na.action = na.omit)
      
      # Local Lee's L values
      m <- lee(x = x, y = y, listw = lw, n = length(x), 
               zero.policy = TRUE, NAOK = TRUE)
      
      # Local Lee's L simulations
      local_sims <- simula_lee(x = x, y = y, listw = lw, nsim = 10000,
                               zero.policy = TRUE, na.action = na.omit)
      
      m_i <- m[[2]]  # local values
      
      # Identify the significant values 
      alpha <- 0.05  # for a 95% confidence interval
      probs <- c(alpha/2, 1-alpha/2)
      intervals <- t(apply(t(local_sims[[2]]), 1, function(x) quantile(x, probs = probs)))
      sig <- (m_i < intervals[ , 1] ) | ( m_i > intervals[ , 2])
      
      #======================================================
      # Preparing for plotting
      oregon.tract <- st_as_sf(oregon.tract)
      oregon.tract$sig <- sig
      
      # Identifying the Lee's L patterns
      xp <- scale(x)
      yp <- scale(y)
      
      patterns <- as.character(interaction(xp > 0, W%*%yp > 0)) 
      patterns <- patterns %>% 
        str_replace_all("TRUE","High") %>% 
        str_replace_all("FALSE","Low")
      patterns[oregon.tract$sig == 0] <- "Not significant"
      oregon.tract$patterns <- patterns
      
      # Plotting
      ggplot() +
        geom_sf(data = oregon.tract, aes(fill = patterns), color = "NA") +
        scale_fill_manual(values = c("red", "pink", "light blue", "dark blue", "grey95")) + 
        guides(fill = guide_legend(title = "Lee's L clusters")) +
        theme_minimal()
      

      Lee's L clusters for oregon.tract data

      【讨论】:

        猜你喜欢
        • 2016-11-25
        • 2016-10-06
        • 1970-01-01
        • 1970-01-01
        • 2018-05-25
        • 1970-01-01
        • 2018-04-22
        • 2011-01-27
        • 1970-01-01
        相关资源
        最近更新 更多