9 votos

¿Lectura y reclasificación eficiente de muchos rasters en R?

Me han encargado que cree un análisis de idoneidad de las condiciones de las olas en el Golfo de México. Tengo unos 2.000 archivos ráster de unos 8 MB cada uno (2.438 columnas, 1.749 filas, tamaño de celda de 1 km). El parámetro de los archivos ráster es el período de las olas y me gustaría reclasificar todos los rásteres de manera que si 4<= wave period <=9 then make cell = 1 A continuación, la suma de todos los raster en un raster final y dividir por el número total de raster para dar un porcentaje total de las observaciones adecuadas y el resultado de la exportación en algún formato compatible con ESRI ... tal vez algo que puede apoyar flotadores si es necesario. No he trabajado mucho con Python o R, pero después de buscar en línea parece tener sentido para llevar a cabo este proceso en una de esas lenguas. He llegado con un poco de código hasta ahora en R, pero estoy confundido acerca de cómo hacer este trabajo.

library(rgdal)
raster_data <- list.files(path=getwd())    #promt user for dir containing raster files
num_files <- length(raster_data)
for (i in raster_data) {                   #read in rasters
   my_data <- readGDAL(raster_data[i])

En este punto estoy confundido sobre si debo también reclasificar y empezar a sumar los datos dentro de este bucle o no. Mi opinión es que sí, ya que de lo contrario creo que posiblemente me quedaría sin memoria en mi ordenador, pero no estoy seguro. Tampoco estoy seguro de cómo reclasificar los datos.

Buscando en Internet creo que utilizaría reclass(my_data, c(-Inf,3,0, 4,9,1, 10,Inf,0)) pero, ¿se ve bien?

Y para la suma utilizaría sum(stack(my_data)) y de alguna manera la salida que. Además... si esto se puede realizar de forma más eficiente o escribir en Python, también estaría abierto a ello. Realmente soy un principiante cuando se trata de la programación.

0 votos

Sólo tienes que utilizar python-gdal. Será mucho más eficiente en su caso.

0 votos

Gracias, Rebelious. Tengo curiosidad por saber por qué python-gdal es más eficiente en esta situación. ¿También sería posible ver algunos de los pasos en código que serían necesarios para hacer esto? Tratando de averiguar la mejor manera de hacer esto, mientras que la utilización de la menor cantidad de memoria, y la cpu, como sea posible ... es confuso para averiguar cómo escribir el código de tal manera que va a leer en los datos, el proceso, sacarlo de la memoria y luego pasar a la siguiente trama.

0 votos

No puedo decirte exactamente por qué, pero la causa general es que R fue diseñado para otros propósitos y se sabe que tiene un rendimiento lento con los ciclos. En cuanto al ejemplo de código, si nadie lo proporciona, compartiré uno contigo en unas 10 horas cuando tenga acceso a la máquina donde se almacena el script correspondiente.

10voto

SteveBurkett Puntos 960

Esta es una forma concisa de hacerlo en R --- aquí sin archivos intermedios:

library(raster)
raster_data <- list.files(path=getwd())    #promt user for dir containing raster files
s <- stack(raster_data)
f <- function(x) { rowSums(x >= 4 & x <= 9) }
x <- calc(s, f, progress='text', filename='output.tif')

1 votos

+1 Esto es bueno para los problemas pequeños, pero hagamos los cálculos para éste: 2438 columnas por 1749 filas por 8 bytes/valor por 2 mil cuadrículas = 63,6 GB, todo lo cual R debe mantener en la RAM para crear s . (Es probable que se necesite el doble de RAM porque s probablemente no reemplazará raster_data .) Espero que tengas mucha memoria RAM. Tu solución puede ser factible dividiendo las 2000 cuadrículas en grupos más pequeños, realizando el cálculo para cada grupo y luego combinando esos cálculos.

3 votos

@whuber: 's' es un objeto pequeño, sólo un montón de punteros a los archivos. La función calc, al igual que otras funciones del paquete raster, no cargará todos los valores en la memoria; los procesará en trozos. Es decir, la división en grupos como sugieres se hace automáticamente, entre bastidores. El tamaño de los trozos puede ser optimizado para la cantidad de RAM disponible a través de rasterOptions().

1 votos

Gracias por explicarlo. Había asumido, sin comprobarlo, que stack y calc funcionaba como la mayoría de los R funciones cargando primero todos los datos en la RAM.

5voto

crstamps2 Puntos 233

Como he notado en los comentarios, generalmente se debe evitar el uso de R para fines no estadísticos debido a problemas de rendimiento en ciertos aspectos (trabajar con ciclos es un ejemplo). Aquí tienes un ejemplo de código en Pyhton (gracias a este artículo ) para la reclasificación de un solo archivo con una sola banda. Podrá modificarlo fácilmente para el procesamiento por lotes si conoce cómo obtener todos los archivos del directorio . Tenga en cuenta que los rásteres se representan como arrays, por lo que puede utilizar métodos de arrays para mejorar el rendimiento cuando sea aplicable. Para trabajar con arrays en Python vea Documentación sobre Numpy .

UPD: el código que publiqué inicialmente era una versión truncada de un filtro personalizado que necesitaba un procesamiento por píxel. Pero para esta pregunta el uso de Numpy aumentará el rendimiento (ver código).

from osgeo import gdal
import sys
import numpy

gdalData = gdal.Open("path_to_file")
if gdalData is None:
  sys.exit("ERROR: can't open raster")

#print "Driver short name", gdalData.GetDriver().ShortName
#print "Driver long name", gdalData.GetDriver().LongName
#print "Raster size", gdalData.RasterXSize, "x", gdalData.RasterYSize
#print "Number of bands", gdalData.RasterCount
#print "Projection", gdalData.GetProjection()
#print "Geo transform", gdalData.GetGeoTransform()

raster = gdalData.ReadAsArray()
xsize = gdalData.RasterXSize
ysize = gdalData.RasterYSize

#print xsize, 'x', ysize

## per pixel processing
#for col in range(xsize):
#  for row in range(ysize):
#    # if pixel value is 16 - change it to 7
#    if raster[row, col] == 16:
#      raster[row, col] = 7

#reclassify raster values equal 16 to 7 using Numpy
temp = numpy.equal(raster, 16)
numpy.putmask(raster, temp, 7)

# write results to file (but at first check if we are able to write this format)
format = "GTiff"
driver = gdal.GetDriverByName(format)
metadata = driver.GetMetadata()
if metadata.has_key(gdal.DCAP_CREATE) and metadata[gdal.DCAP_CREATE] == "YES":
  pass
else:
  print "Driver %s does not support Create() method." % format
  sys.exit(1)
if metadata.has_key(gdal.DCAP_CREATECOPY) and metadata[gdal.DCAP_CREATECOPY] == "YES":
  pass
else:
  print "Driver %s does not support CreateCopy() method." % format
  sys.exit(1)

# we already have the raster with exact parameters that we need
# so we use CreateCopy() method instead of Create() to save our time
outData = driver.CreateCopy("path_to_new_file", gdalData, 0)
outData.GetRasterBand(1).WriteArray(raster)

5 votos

El "R es lento cuando se hacen ciclos (bucles)" es a menudo mal utilizado como una razón para evitar R. Sí, si se hace un bucle sobre las celdas de un raster en R sería lento, pero el paquete raster trabaja en raster enteros a la vez, y tiene un montón de código C y por lo tanto se ejecuta a velocidad C. Para un raster de ese tamaño, la mayor parte del trabajo sería a velocidad C, la sobrecarga de los bucles sería insignificante.

0 votos

@Spacedman, sí 'raster' es un paquete útil (y me gusta), pero nunca estuve satisfecho con su rendimiento, incluso cuando los bucles no estaban involucrados.

2 votos

Bien, pues compara el tiempo que se tarda en R con el que se tarda en Python. No se puede operar sobre un array numpy completo en lugar de hacer un bucle?

3voto

Jay Bazuzi Puntos 194
  1. No utilice readGDAL. Lee en un objeto Spatial* lo que podría no ser una buena idea..

  2. Utilice el raster paquete. Puede leer cosas GDAL en objetos Raster. Esto es algo bueno. r = raster("/path/to/rasterfile.tif") lo leerá en r .

  3. Su clasificación es entonces t = r > 4 & r <= 9

  4. La gran pregunta es si hay que imprimirlos en nuevos archivos de trama y luego hacer el paso de resumen en otro bucle. Si tienes capacidad de almacenamiento, yo escribiría en nuevos archivos porque si tu bucle falla (porque uno de esos 2000 archivos es basura) tendrás que empezar de nuevo. Así que usa writeRaster para crear rásteres con umbral si decide hacerlo.

  5. Su bucle es entonces algo así como

esto.

count = raster(0,size of your raster)
for(i in 1:number of rasters){
  r = raster(path to binary raster file 'i')
  count = count + r
}
return(count)

La gestión de la memoria de R podría golpear aquí - cuando se hace count=count+r R bien podría hacer una nueva copia de count . Así que eso es potencialmente 2000 copias de ella - pero la recolección de basura se y limpiar, y es aquí donde el bucle de R solía ser muy malo.

En términos de tiempo, en mi PC de 4 años, la operación de umbral tarda aproximadamente 1,5s en una trama de ese tamaño de números aleatorios entre cero y veinte. Por 2000 = haz las cuentas. Como siempre, haz un pequeño conjunto de pruebas para desarrollar el código, luego lánzalo sobre tus datos reales y vete a tomar un café.

0 votos

Tengo curiosidad por saber a qué te refieres con "el umbral de la operación". En mi sistema (corriendo R 2.15.0), la lectura de una cuadrícula de 1,6 megapíxeles (en formato nativo de punto flotante ESRI) tarda 0,19 segundos y la realización de las cinco operaciones del bucle -dos comparaciones, una conjunción, una suma y un almacenamiento- tarda otros 0,09 segundos, lo que supone 0,28 segundos por iteración. En cambio, si el bucle se realiza almacenando en caché 100 cuadrículas a la vez en una matriz y utilizando rowSums para hacer los totales, el uso de la RAM, por supuesto, aumenta: pero, igualmente, no hay recolección de basura. El tiempo cae sólo a 0,26 segundos por iteración.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X