Самый быстрый способ выбрать допустимый диапазон для растровых данных

Используя R, мне нужно выбрать допустимый диапазон для данного растра (из пакета raster) самым быстрым способом. Я пробовал это:

library(raster)
library(microbenchmark)
library(ggplot2)
library(compiler)

r <- raster(ncol=100, nrow=100)
r[] <- runif(ncell(r))

#Let's see if precompiling helps speed...
f <- function(x, min, max) reclassify(x, c(-Inf, min, NA, max, Inf, NA))
g <- cmpfun(f)

#Benchmark!
compare <- microbenchmark(
    calc(r, fun=function(x){ x[x < 0.2] <- NA; x[x > 0.8] <- NA; return(x)}), 
    reclassify(r, c(-Inf, 0.2, NA, 0.8, Inf, NA)),
    g(r, 0.2, 0.8),
    times=100)
autoplot(compare) #Reclassify is much faster, precompiling doesn't help much.

#Check they are the same...
identical(
          calc(r, fun=function(x){ x[x < 0.2] <- NA; x[x > 0.8] <- NA; return(x)}),
          reclassify(r, c(-Inf, 0.2, NA, 0.8, Inf, NA))
) #TRUE
identical(
          reclassify(r, c(-Inf, 0.2, NA, 0.8, Inf, NA)),
          g(r, 0.2, 0.8),
) #TRUE

Эталон

Метод реклассификации намного быстрее, но я уверен, что его можно ускорить еще больше. Как я могу это сделать?


person AF7    schedule 03.12.2015    source источник


Ответы (3)


Вот еще один способ:

h <- function(r, min, max) {
  rr <- r[]
  rr[rr < min | rr > max] <- NA
  r[] <- rr
  r
}

i <- cmpfun(h)

identical(
  i(r, 0.2, 0.8),
  g(r, 0.2, 0.8)
)



#Benchmark!
compare <- microbenchmark(
  calc(r, fun=function(x){ x[x < 0.2] <- NA; x[x > 0.8] <- NA; return(x)}), 
  reclassify(r, c(-Inf, 0.2, NA, 0.8, Inf, NA)),
  g(r, 0.2, 0.8),
  h(r, 0.2, 0.8),
  i(r, 0.2, 0.8),
  times=100)
autoplot(compare) 

Компиляция в данном случае мало помогает.

введите здесь описание изображения

Вы даже можете получить дополнительное ускорение, обращаясь к слотам растрового объекта напрямую, используя @ (хотя обычно это не рекомендуется).

j <- function(r, min, max) {
  v <- r@data@values
  v[v < min | v > max] <- NA
  r@data@values <- v
  r
}

k <- cmpfun(j)

identical(
  j(r, 0.2, 0.8)[],
  g(r, 0.2, 0.8)[]
)

введите здесь описание изображения

person johannes    schedule 03.12.2015
comment
Это блестящий ответ. Спасибо! - person AF7; 03.12.2015
comment
функция j действительно не рекомендуется, так как во многих случаях она не работает. К сожалению, вы разглашаете такую ​​​​плохую практику - person Robert Hijmans; 07.12.2015

Хотя принятый ответ на этот вопрос верен для примера растра, важно отметить, что самая быстрая безопасная функция сильно зависит от размера растра: функции h и i, представленные @rengis, работают быстрее только с относительно небольшими растрами (и относительно простая реклассификация). Простое увеличение размера растра r в примере OP на величину в десять делает reclassify быстрее:

# Code from OP @AF7
library(raster)
library(microbenchmark)
library(ggplot2)
library(compiler)

#Let's see if precompiling helps speed...
f <- function(x, min, max) reclassify(x, c(-Inf, min, NA, max, Inf, NA))
g <- cmpfun(f)

# Funcions from @rengis
h <- function(r, min, max) {
  rr <- r[]
  rr[rr < min | rr > max] <- NA
  r[] <- rr
  r
}

i <- cmpfun(h)

# Benchmark with larger raster (100k cells, vs 10k originally)
r <- raster(ncol = 1000, nrow = 100)
r[] <- runif(ncell(r))

compare <- microbenchmark(
  calc(r, fun=function(x){ x[x < 0.2] <- NA; x[x > 0.8] <- NA; return(x)}), 
  reclassify(r, c(-Inf, 0.2, NA, 0.8, Inf, NA)),
  g(r, 0.2, 0.8),
  h(r, 0.2, 0.8),
  i(r, 0.2, 0.8),
  times=100)
autoplot(compare) 

Сравнить результаты с растром из 100 тыс. ячеек

Точная точка, когда reclassify становится быстрее, зависит как от количества ячеек в растре, так и от сложности реклассификации, но в этом случае точка пересечения находится примерно на 50 000 ячеек (см. ниже).

По мере того, как растр становится еще больше (или вычисления усложняются), другим способом ускорить реклассификацию является использование многопоточности, например. с пакетом snow:

# Reclassify, using clusterR to split into two threads
library(snow)
tryCatch({
      beginCluster(n = 2)
      clusterR(r, reclassify, args = list(rcl = c(-Inf, 0.2, NA, 0.8, Inf, NA)))
    }, finally = endCluster())

Многопоточность влечет за собой еще больше накладных расходов на настройку, и поэтому имеет смысл только с очень большими растрами и/или более сложными вычислениями (на самом деле, я был удивлен, заметив, что это не оказалось лучшим вариантом ни для одной из условия, которые я тестировал ниже — возможно, с более сложной реклассификацией?).

Чтобы проиллюстрировать, я нанес результаты микробенчмарка с использованием настройки OP с интервалами до 10 миллионов ячеек (по 10 прогонов каждой) ниже:

Результаты сравнительного анализа, время выполнения и размер растра

И последнее замечание: компиляция не повлияла ни на один из протестированных размеров.

person nelliott    schedule 28.03.2016

В растровом пакете для этого есть функция: clamp. Это быстрее, чем g, но медленнее, чем h и i, потому что в него встроены некоторые накладные расходы (безопасность).

compare <- microbenchmark(
  h(r, 0.2, 0.8),
  i(r, 0.2, 0.8),
  clamp(r, 0.2, 0.8),
  g(r, 0.2, 0.8),
  times=100)
autoplot(compare) 
person Robert Hijmans    schedule 07.12.2015
comment
Я знал, что у raster есть что-то подобное, и просто забыл об этом. Черт бы меня побрал. Можете ли вы привести пример того, почему вышеупомянутая функция j не рекомендуется? - person AF7; 08.12.2015
comment
Функция j не будет работать, если объект Raster* создан из файла на диске, так как в @data@values не будет никаких значений. Установка новых значений напрямую искажает внутренне сохраненные минимальные и максимальные значения (но это не обязательно ломает вещи) - person Robert Hijmans; 08.12.2015
comment
отлично, спасибо за разъяснение. Честно говоря, я не уверен, какой ответ принять сейчас: ваш более точен, но @rengis предоставил h, которая является самой быстрой функцией (при этом сохраняя приличную безопасность). - person AF7; 09.12.2015