【问题标题】:Updating raster values in foreach loops in R? (raster time-series NA imputation)在 R 的 foreach 循环中更新栅格值? (栅格时间序列 NA 插补)
【发布时间】:2019-11-15 03:37:57
【问题描述】:

我正在尝试在时间序列栅格中估算 NA 值。这是我的数据的可重现示例:

library(raster)
library(rgdal)
library(doParallel)
library(foreach)

r1 <- r2 <- r3 <- r4 <- r5 <- raster(nrow=100, ncol=100)
values(r1) <- runif(ncell(r1))
values(r2) <- runif(ncell(r2))
values(r3) <- runif(ncell(r3))
values(r4) <- runif(ncell(r4))
values(r5) <- runif(ncell(r5))

s <- stack(r1, r2, r3, r4, r5)
time_series <- brick(s)
time_series[1, 30][2] <- NA
time_series[3, 20][3] <- NA
time_series[5, 10][5] <- NA
time_series[8, 40][4] <- NA

有诸如 gapfill 之类的软件包,但我发现它们对于我的任务来说太慢了。我在这里找到了另一种方法,如答案: https://gis.stackexchange.com/questions/279354/ndvi-time-series-with-missing-values

作者:https://gis.stackexchange.com/users/8520/jeffrey-evans

我想将 for 循环转换为 foreach,这样我就可以为更大的图像计算它。这是带有 for 循环的代码:

impute.loess <- function(y, x.length = NULL, s = 0.80, 
                         smooth.data = FALSE, ...) {
  if(is.null(x.length)) { x.length = length(y) }
  options(warn = -1)
  x <- 1:x.length
  if (all(is.na(y))) {
    return(y)
  } else {
    p <- loess(y ~ x, span = s, data.frame(x = x, y = y))
    if(smooth.data == TRUE) {
      y <- predict(p, x)
    } else {
      na.idx <- which( is.na(y) )
      if( length(na.idx) > 1 ) {
        y[na.idx] <- predict(p, data.frame(x = na.idx))
      }
    }   
    return(y)
  }
}

time_series_new <- time_series

time_series_new[] <- NA

for (rl in 1:nrow(time_series)) {
  v <- getValues(time_series, rl, 1)
  time_series_new[rl,] <- as.matrix(t(apply(v, MARGIN=1, FUN=impute.loess)))
}

我尝试过的 Foreach 替代方案是这样的:

time_series_new2 <- time_series

time_series_new2[] <- NA

cl <- parallel::makeCluster(detectCores()-1)
doParallel::registerDoParallel(cl)

time_series_new2 <- foreach (rl = 1:nrow(time_series),
                             .packages = "raster",
                             .combine = 'rbind') %dopar% {
                        v <- getValues(time_series, rl, 1)
                        time_series_new[rl,] <- as.matrix(t(apply(v,
                                                MARGIN=1, FUN=impute.loess)))
}

parallel::stopCluster(cl)

但是,这里有区别:

> class(time_series_new)
[1] "RasterBrick"
attr(,"package")
[1] "raster"

> class(time_series_new2)
[1] "matrix"

如果我不将 foreach 循环分配给对象,它只会导出结果。最后我想要一个更新的栅格对象,但找不到解决我的问题的方法。

我找不到如何设置矩阵值栅格对象 - 设置值不起作用可能是因为尺寸不同:

> dim(time_series_new)
[1] 100 100   5

> dim(time_series_new2)
[1] 10000     5

我知道 foreach 循环的工作方式不同。有没有办法在 foreach 循环中更新 time_series_new2 对象,以便我可以在最后获得更新的栅格对象?

编辑:

setValues() 确实有效!如:

time_series_new3 <- time_series

time_series_new3[] <- NA #empty raster object

time_series_new3 <- setValues(time_series_new3, time_series_new2) #filled with matrix rendered from foreach loop

> time_series_new3
class      : RasterBrick 
dimensions : 100, 100, 10000, 5  (nrow, ncol, ncell, nlayers)
resolution : 3.6, 1.8  (x, y)
extent     : -180, 180, -90, 90  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0 
source     : memory
names      :      layer.1,      layer.2,      layer.3,      layer.4,      layer.5 
min values : 1.468023e-04, 3.525158e-04, 9.689084e-05, 5.349121e-05, 4.214607e-05 
max values :    0.9999564,    0.9999854,    0.9997795,    0.9999780,    0.9997880 

> time_series_new2
class      : RasterBrick 
dimensions : 100, 100, 10000, 5  (nrow, ncol, ncell, nlayers)
resolution : 3.6, 1.8  (x, y)
extent     : -180, 180, -90, 90  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0 
source     : memory
names      :      layer.1,      layer.2,      layer.3,      layer.4,      layer.5 
min values : 1.468023e-04, 3.525158e-04, 9.689084e-05, 5.349121e-05, 4.214607e-05 
max values :    0.9999564,    0.9999854,    0.9997795,    0.9999780,    0.9997880

> all.equal(time_series_new2, time_series_new3)
[1] TRUE

不过,我想知道在 foreach 中的更新。

【问题讨论】:

    标签: r foreach parallel-processing gis parallel-foreach


    【解决方案1】:

    foreach 循环中,您没有更新rasterBrick time_series_new 的副作用。也就是说,time_series_new 知道它是什么——raster 类型的对象。 rbind 组合将强制 non-data.frames 进入 matrices。这就是 100 x 100 x 5 变成 10000 x 5 的方式。

    由于for 循环的缓慢,我假设您将使用parallel 计算。如果是这种情况,我建议以不同的方式解决问题,尤其是在缺失值不多的情况下。

    我们可以先看看有多少行实际上有缺失数据:

    missing_dat_rows <- which(is.na(getValues(time_series)) == T, arr.ind = T)[, 1]
    missing_dat_rows <- unique(missing_dat_rows)
    
    missing_dat_rows
    #[1]  30 220 740 410
    

    因此,我们现在可以专注于这 4 个结果,而不是循环遍历 10,000 个结果。

    time_series3 <- time_series
    for (mis_row in missing_dat_rows) {
      values(time_series3)[mis_row, ] <- impute.loess(getValues(time_series3)[mis_row, ])
    }
    

    不幸的是,我无法让impute.loess() 函数为我返回值。如果你想继续你的循环方法,我做了几个小改动可能会有所帮助:

    impute.loess <- function(y, x.length = NULL, s = 0.80, 
                             smooth.data = FALSE, ...) {
      if(is.null(x.length)) { x.length = length(y) }
      options(warn = -1)
    
      x <- 1:x.length
      if (all(is.na(y))| all(!is.na(y))) { #added the or statement - I don't think we want to do this if there are no missing values.
        return(y)
      } else {
        p <- loess(y ~ x, span = s, data.frame(x = x, y = y))
        if(smooth.data == TRUE) {
          y <- predict(p, x)
        } else {
          na.idx <- which( is.na(y) )
          # if( length(na.idx) > 1 ) { #commented out - I feel as though we should be replacing all NAs
            y[na.idx] <- predict(p, data.frame(x = na.idx))
          # }
        }   
        return(y)
      }
    }
    

    【讨论】:

    • 您好,感谢您的详尽回答!不幸的是,我使用大型卫星图像,它们会有很大的差距,因此需要并行方法。我会尝试在更方便的时候将您的方法转换为并行方法。
    猜你喜欢
    • 2015-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多