r Значения суммы растрового кирпича в ячейках, определяемые двумя разными растрами, как ускорить вычисления - PullRequest
0 голосов
/ 03 мая 2020

Я работаю с файлами климатических данных с ежедневными данными, поэтому в течение большинства лет 365 растров в кирпиче. Я хочу суммировать значения в файлах для подмножеств дней - скажем, от дня x до дня y. Это можно сделать с помощью stackApply. Ниже я создал некоторый код, который генерирует несколько растров, создает кирпич и применяет stackApply, используя указанные значения c для x и y, 1 и 3.

Однако мне нужно, чтобы x и y были взяты из двух растровых слоев. В приведенном ниже коде они называются raster.start и raster.end. Ниже первого набора кода у меня есть второй набор, который работает, но медленно.

library(raster)
raster.in1 <- raster(nrows=100, ncols=100)
raster.in2 <- raster(nrows=100, ncols=100)
raster.in3 <- raster(nrows=100, ncols=100)
raster.in4 <- raster(nrows=100, ncols=100)
raster.in5 <- raster(nrows=100, ncols=100)
raster.out <- raster(nrows=100, ncols=100)
raster.in1[] <- runif(ncell(raster.in1), min = -10, max = 10)
raster.in2[] <- runif(ncell(raster.in2), min = -20, max = 10)
raster.in3[] <- runif(ncell(raster.in3), min = -30, max = 10)
raster.in4[] <- runif(ncell(raster.in4), min = -40, max = 10)
raster.in5[] <- runif(ncell(raster.in5), min = -50, max = 10)
raster.start <- raster(nrows=100, ncols=100)
raster.end <- raster(nrows=100, ncols=100)
raster.start[] <- runif(ncell(raster.in1), min = 1, max = 2)
raster.start <- round(raster.start, digits = 0)
raster.end <- raster.start + 3

rasterb <- brick(raster.in1, raster.in2, raster.in3, raster.in4, raster.in5)

indices <- format(as.Date(names(rasterb), format = "layer.%d"), format = "%d")
indices <- c(1,1,1,1,1)

datasum.all <- stackApply(rasterb, indices, fun = sum)
datasum.sub1 <- stackApply(rasterb[[c(1:3)]], indices, fun = sum)

Идея состоит в том, чтобы пройти по строкам и столбцам начального и конечного растра, чтобы установить подмножество кирпича и работать с ним. , Вот код, который я разработал для этого.

for (i in 1:nrow(raster.in1)){
  for (j in 1:ncol(raster.in1)){
    start <- raster.start[[1]][i,j] # get the starting day
    end <- raster.end[[1]][i,j] # get the ending day
    raster.out[i,j] <- sum(rasterb[[start:end]][i,j])
  }
}

Однако даже для этого игрушечного примера время вычислений медленное. Это заняло около 1,3 минуты. Я попытался заменить часть кода функциями, как показано ниже, но это не повлияло на время завершения. Любые советы о том, как ускорить этот процесс, очень ценятся.

startEnd <- function(raster.start, raster.end, i,j) {
  start <- raster.start[i,j] # get the starting day
  end <- raster.end[i,j] # get the ending day
  return(c(start,end))
}

rasterOutValue <- function(rasterb, i, j, startEnd){
  return(sum(rasterb[[startEnd]][i,j]))
}

for (i in 1:nrow(raster.in1)){
  for (j in 1:ncol(raster.in1)){
    raster.out[i,j] <-rasterOutValue(rasterb, i, j, startEnd(raster.start, raster.end, i,j))
  }
}

1 Ответ

2 голосов
/ 04 мая 2020

Данные вашего примера (немного более краткие)

library(raster)
b <- brick(nrows=100, ncols=100, nl=5)
r.start <- raster(b)
nc <- ncell(b)
set.seed(88)
values(b) <- cbind(runif(nc, min = -10, max = 10), runif(nc, min = -20, max = 10), 
    runif(ncell(b), min = -30, max = 10), runif(nc, min = -40, max = 10), runif(nc, min = -50, max = 10))
values(r.start) <- round(runif(nc, min = 1, max = 2))
r.end <- r.start + 3

Теперь вы можете сделать:

s <- stack(r.start, r.end, b)
x <- calc(s, fun=function(x) sum(x[(x[1]:x[2])+2]))

И это, кажется, работает

a <- s[1]
a
#     layer.1.1 layer.2.1 layer.1.2 layer.2.2  layer.3   layer.4   layer.5
#[1,]         2         5 -1.789974  2.640807 4.431439 -23.09203 -5.688119
fun <- function(x) sum(x[(x[1]:x[2])+2])
fun(a)
#[1] -21.70791
x[1]
#[1] -21.70791

calc для растровых объектов то же, что apply для матриц. (именно поэтому он называется app в terra.

Для начала нужно написать функцию, которая делает то, что вы хотите с вектором.

x <- 1:10
test1 <- function(start, end, values) {
    mean(values[start:end]) 
}
test1(2, 5, x)
test1(5, 8, x)

calc принимает только один аргумент, поэтому такая функция

test2 <- function(values) {
    # the +2 to skip the first two elements in the computation
    start <- values[1] + 2
    end <- values[2] + 2
    mean(values[start:end]) 
}

test2(c(2, 5, x))
test2(c(5, 8, x))

И более краткая версия

test3 <- function(v) {
    mean(v[ (v[1]:v[2])+2 ] ) 
}
 test3(c(2, 5, x))
 #[1] 3.5
 test3(c(5, 8, x))
 #[1] 6.5

Второе добавление (и напоминание всегда проверять со значениями NA!). test3 прерывается, когда один из индексов (начало и конец) равен NA (это нормально, если остальные NA)

test3(c(NA, 5, x))
#Error in v[1]:v[2] : NA/NaN argument

Итак, нам нужна функция, которая ловит эти

test4 <- function(v) {
    if (any(is.na(v[1:2]))) {
        NA
    } else {
        mean(v[ (v[1]:v[2])+2 ] ) 
    }
}

test4(c(NA, 5, x))
#[1] NA
test4(c(1, 5, x))
#[1] 3

Обычно "start" и "end" оба будут NA одновременно, поэтому более простая версия, которая также должна работать, может быть

test5 <- function(v) {
    if (is.na(v[1])) {
        NA
    } else {
        mean(v[ (v[1]:v[2])+2 ] ) 
    }
}

. Этот подход с calc может будьте медленнее, поскольку он превращает RasterBrick в RasterStack с 365 + 2 слоями. Это значительно замедляет чтение данных. Таким образом, вы можете попробовать этот подход вместо overlay (здесь снова используя sum)

f <- function(i, v) {
    j <- !is.na(i[,1])
    r <- rep(NA, nrow(i))
    x <- cbind(i[j,,drop=FALSE], v[j,,drop=FALSE])
    r[j] <- apply(x, 1, function(y) sum(y[ (y[1]:y[2])+2 ] )) 
    r
}
cal <-stack(r.start, r.end)
x <- overlay(cal, b, fun= f, recycle=FALSE)

Вы можете ускорить алгоритм, написав его на Rcpp / C ++

library(Rcpp)
cppFunction('std::vector<double> gtemp(NumericMatrix cal, NumericMatrix wth) {
    std::vector<double> out(cal.nrow(), NAN);
    for (int i=0; i<cal.nrow(); i++) {
      if (!std::isnan(cal(i,0))){
         NumericVector v = wth(i,_);
         size_t start = cal(i,0)-1;
         size_t end = cal(i,1);
         out[i] = std::accumulate(v.begin()+start, v.begin()+end, 0.0);
      }  
    }
    return out;
}')

x <- overlay(cal, b, fun=gtemp, recycle=FALSE)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...