如何在R中使用外推法来外推栅格数据

12

我正在尝试使用this文章中的方法,利用R软件对气候条件进行降尺度处理。我已经接近成功,但还缺少一些步骤。

所需软件包和数据

为了进行这个例子,我上传了一些数据到archive.org网站,使用以下代码加载所需的软件包和数据:

library(raster)
library(rgdal)

download.file("https://archive.org/download/Downscaling/BatPatagonia.rds", "Bat.rds")
download.file("https://archive.org/download/Downscaling/TempMinPatNow.rds", "Tmin.rds")

BatPatagonia <- readRDS("Bat.rds")
TempMinPatNow <- readRDS("Tmin.rds")

BatPatagonia是从GEBCO数据集中提取和转换的区域海底地形和高度信息的光栅文件,而TempMinPatNow则是从WorldClim中提取的同一区域1月份的最低温度。下面是这些数据集的图表:

enter image description here

这个问题的目标

为了缩小过去冰川时期的数据,我需要模拟当前气候如果海平面与过去相同会是什么样子。为了做到这一点,我使用GEBCO数据,并找出海岸线的大致位置。根据上述文章中的方法,以下是要遵循的前三个步骤:

  1. 创建一个高于海平面20米的陆地DEM
  2. 在移动窗口中计算多元线性回归
  3. 将系数外推到海洋

第3点是我一直在努力解决的问题,我将展示我如何完成前两个步骤,并展示我一直在寻找的解决第3点的方法。

1. 创建一个高于海平面20米的陆地DEM

为了做到这一点,我使用了栅格图像,并使用以下代码将所有低于20米的值替换为NA值:

Elev20 <- BatPatagonia

values(Elev20) <- ifelse(values(Elev20) <= 20, NA, values(Elev20))

生成的光栅图像如下所示

enter image description here

2. 在移动窗口中计算多元线性回归

根据第2591页的手稿,下一步是使用以下公式对海拔高度超过20米的数据进行移动窗口内的多元线性回归:

enter image description here

我们已经有了高程数据,但我们还需要纬度和经度的栅格图像,为此我们使用以下代码,首先创建纬度和经度栅格图像:

Latitud <- BatPatagonia
Longitud <- BatPatagonia

data_matrix <- raster::xyFromCell(BatPatagonia, 1:ncell(BatPatagonia))

values(Latitud) <- data_matrix[, 2]
values(Longitud) <- data_matrix[, 1]

我们将把这个值乘以一个栅格掩模,该掩模仅包含高程超过20米的区域,以便我们只获取所需的值:

Elev20Mask <- BatPatagonia

values(Elev20Mask) <- ifelse(values(Elev20Mask) <= 20, NA, 1)

Longitud <- Elev20Mask*Longitud

Latitud <- Elev20Mask*Latitud

现在我将使用响应变量和预测变量构建一个堆栈:
Preds <- stack(Elev20, Longitud, Latitud, TempMinPatNow)

names(Preds) <- c("Elev", "Longitud", "Latitud", "Tmin")

下图显示了生成的堆栈:

enter image description here

如论文所述,移动窗口应为25x25个单元格,共计625个单元格,但是如果移动窗口中的数据少于170个单元格,则不应进行回归,并且最多应有624个单元格,以确保我们只对靠近海岸的区域进行建模。

使用移动窗口的多元回归的结果应该是一个堆栈,其中包括本地截距和方程中每个Beta的本地估计值。我使用以下代码使用getValuesFocal函数找到了如何实现这一点(此循环需要一段时间):

# First we establish the 25 by 25 window

w <- c(25, 25)

# Then we create the empty layers for the resulting stack

intercept <- Preds[[1]]
intercept[] <- NA

elevationEst <- intercept

latitudeEst <- intercept

longitudeEst <- intercept

现在我们开始编码:

for (rl in 1:nrow(Preds)) {
  v <- getValuesFocal(Preds[[1:4]], row = rl, nrows = 1, ngb = w, array = FALSE)
  int <- rep(NA, nrow(v[[1]]))
  x1 <- rep(NA, nrow(v[[1]]))
  x2 <- rep(NA, nrow(v[[1]]))
  x3 <- rep(NA, nrow(v[[1]]))
  x4 <- rep(NA, nrow(v[[1]]))
  for (i in 1:nrow(v[[1]])) {
    xy <- na.omit(data.frame(x1 = v[[1]][i, ], x2 = v[[2]][i, ], x3 = v[[3]][i, 
                                                                         ], y = v[[4]][i, ]))
    
    if (nrow(xy) > 170 & nrow(xy) <= 624) {
      coefs <- coefficients(lm(as.numeric(xy$y) ~ as.numeric(xy$x1) + 
                             as.numeric(xy$x2) + as.numeric(xy$x3)))
      int[i] <- coefs[1]
      x1[i] <- coefs[2]
      x2[i] <- coefs[3]
      x3[i] <- coefs[4]
    } else {
      int[i] <- NA
      x1[i] <- NA
      x2[i] <- NA
      x3[i] <- NA
    }
  }

  intercept[rl, ] <- int
  elevationEst[rl, ] <- x1
  longitudeEst[rl, ] <- x2
  latitudeEst[rl, ] <- x3

  message(paste(rl, "of", nrow(Preds), "ready"))
}

Coeffs <- stack(intercept, elevationEst, latitudeEst, longitudeEst, (intercept + Preds$Elev * elevationEst + Preds$Longitud * longitudeEst + Preds$Latitud *latitudeEst), Preds$Tmin)

names(Coeffs) <- c("intercept", "elevationEst", "longitudeEst", "latitudeEst", "fitted", "Observed")

这个循环的结果是下面展示的 coeffs 栈:

enter image description here

我卡在这里了

将系数外推到海洋

现在的目标是将系数堆栈的前4个栅格(截距,高程估计,经度估计和纬度估计)外推到上次冰河期时海岸线应该在的位置,当时海平面下降了120米。

MaxGlacier <- BatPatagonia

values(MaxGlacier) <- ifelse(values(MaxGlacier) < -120, NA,1)

以下地图显示了预计海岸线:

enter image description here

作者将系数投影到海岸线上的方法是使用NCL语言中的poisson_grid_fill函数通过求解Poisson方程来填补间隙。但我想保持简单,尝试在同一种语言中完成所有操作。我还在Python中找到了类似的函数。
我会满意任何有效的外推过程,我不限制搜索算法。
我发现了几个R包,如Gapfill包,并找到了一个review of methods来填补间隙,但它们大多用于插值,主要用于基于其他图层的NDVI图层,其中填充了间隙。
我该如何继续?

1
请注意,您已经可以简化代码并立即获得 Elev20 = mask(BatPatagonia, BatPatagonia <= 20, maskvalue = 1)Longitud = mask(init(BatPatagonia, 'x'), Elev20)Latitud = mask(init(BatPatagonia, 'y'), Elev20)。;-) - ztl
谢谢@ztl,我稍后会编辑代码,使用你更高效的代码进行改进。 - Derek Corcoran
2
给定一个空间场(比如只有一个图层的栅格),可以使用Kriging方法预测任何空间位置的值。请参见R软件包“gstat”或“fields”。 - Nairolf
1
https://gis.stackexchange.com/ 可能是这个问题更好的提问地点。 - Nairolf
@Florian 我曾经在考虑是否将它放在那里,但由于我在这个论坛上的声望更高,所以我可以提供悬赏,但在gis.stackexchange上我不能这样做。 - Derek Corcoran
显示剩余4条评论
1个回答

4
回顾几十年前我在本科物理时期,我们使用拉普拉斯松弛法来解决这类泊松方程问题。 我不确定,但我猜这也可能是 poisson_grid_fill 的工作方式。该过程很简单。 松弛法是一个迭代过程,在其中我们将每个单元格(除了形成边界条件的单元格)计算为水平或垂直相邻单元格的平均值,然后重复此过程,直到结果接近稳定解决方案。
在你的情况下,已经有值的单元格提供你的边界条件,我们可以对其他单元格进行迭代。 例如,像这样(此处演示截距系数 - 您可以以同样的方式完成其他系数):
gaps = which(is.na(intercept)[])
intercept.ext = intercept
w=matrix(c(0,0.25,0,0.25,0,0.25,0,0.25,0), nc=3, nr=3)
max.it = 1000
for (i in 1:max.it) intercept.ext[gaps] = focal(intercept.ext, w=w, na.rm=TRUE)[gaps]
intercept.ext = mask(intercept.ext, MaxGlacier)

编辑

以下是嵌入函数的相同过程,演示如何使用 while 循环直到达到所需的容差(或超出最大迭代次数)。请注意,此功能旨在演示原理,并未针对速度进行优化。

gap.fill = function(r, max.it = 1e4, tol = 1e-2, verbose=FALSE) {
  gaps = which(is.na(r)[])
  r.filled = r
  w = matrix(c(0,0.25,0,0.25,0,0.25,0,0.25,0), nc=3, nr=3)
  i = 0
  while(i < max.it) {
    i = i + 1
    new.vals = focal(r.filled, w=w, na.rm=TRUE)[gaps]
    max.residual = suppressWarnings(max(abs(r.filled[gaps] - new.vals), na.rm = TRUE))
    if (verbose) print(paste('Iteration', i, ': residual = ', max.residual))
    r.filled[gaps] = new.vals
    if (is.finite(max.residual) & max.residual <= tol) break
  }
  return(r.filled)
}

intercept.ext = gap.fill(intercept)
intercept.ext = mask(intercept.ext, MaxGlacier)
plot(stack(intercept, intercept.ext))

enter image description here


1
不错。顺便提一下,poisson_grid_fill 的帮助页面显示该函数接受一个“松弛系数”作为参数之一,这与您对其使用方法的猜测是一致的。 - Josh O'Brien
不错 @dww,我会更仔细地检查答案。 - Derek Corcoran
@dww 一切看起来都很不错。不过我有一个问题,你的代码中好像没有使用max.it。我错了吗? - Derek Corcoran
1
嗨 - 我只是展示了一个简单的版本。我只使用max.it来确定循环运行的次数。更全面的函数可能会使用阈值来无限期地运行循环,直到单元格值的变化小于阈值。如果循环时间过长,可以使用max.it来跳出循环。 - dww
再次感谢@dww,那么使用while循环,您能否在代码中添加一个工作示例?谢谢。 - Derek Corcoran
1
谢谢@dww,我会等一个小时左右,但我真的怀疑是否有人能给出更好的答案,那时我会给你奖励。 - Derek Corcoran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接