Estudios de paisaje, cartografía (R y QGIS), diseño ambiental 3D (Blender) y análisis de datos geográficos en R

Contact Info
Ferrol, Santiago de Compostela, Galicia, Spain, The World

+00(34) not yet...

Privacy

Follow ModlEarth

Estadísticas en raster y vectores grandes + Imágenes de ChatGPT

Estadísticas en raster y vectores grandes + Imágenes de ChatGPT

Si has llegado aquí es porque el thumbnail generado con ChatGPT4 ha funcionado. Quería por una vez ahorrarme los pasos de Canva (seleccionar, hacer copia, editar, exportar, transformar a .webp...)

En la era actual de la informática y el análisis de datos, enfrentamos constantemente el desafío de manejar conjuntos de datos de gran tamaño, especialmente en el campo de la teledetección y el análisis geoespacial. Uno de los problemas más comunes al trabajar con imágenes de raster de gran tamaño es el cálculo de estadísticas descriptivas, como la desviación estándar, que es fundamental para entender la variabilidad de los datos. Sin embargo, el manejo de rasters de gran tamaño puede ser una tarea desalentadora debido a las limitaciones de memoria inherentes a muchos entornos de programación, incluido R, un lenguaje ampliamente utilizado para la estadística y el análisis de datos. En este artículo, exploraremos una metodología robusta para calcular la desviación estándar de un raster extremadamente grande, superando las barreras de la capacidad de memoria de la computadora. Discutiremos cómo la segmentación de rasters en “chunks” más pequeños, seguido de un enfoque de consolidación, puede facilitar este cálculo, permitiéndonos ejecutar operaciones en paralelo mediante la librería parallel en R. Además, abordaremos las limitaciones críticas relacionadas con la memoria y la longitud de los vectores numéricos en R, proporcionando una visión completa de cómo superar estos obstáculos en el análisis de grandes rasters.

Las razones

En primer lugar decir que este post no surge de la nada. Surge de dar respuesta a esta pregunta del foro GIS StackExchange: R: Error Data is too long. Si queréis darme de paso votos de respuesta y apoyo :-)

Cuando trabajamos con rasters de gran tamaño en R, nos enfrentamos a desafíos únicos que requieren enfoques creativos y eficientes para el procesamiento de datos. Uno de estos enfoques es la división del raster en “chunks” o segmentos más pequeños. A continuación, exploraremos las razones detrás de esta estrategia, destacando cómo se relaciona con las limitaciones del sistema y del lenguaje de programación R.

  • Memoria Disponible En cualquier sistema informático, la memoria disponible es un recurso finito que se comparte entre todos los procesos en ejecución. R, al igual que otros entornos de programación, utiliza esta memoria para almacenar y manipular datos. Cuando trabajamos con rasters grandes, el volumen de datos puede ser tan grande que se acerca o incluso supera la cantidad de memoria disponible, lo que puede llevar a un agotamiento de la memoria (conocido coloquialmente como “quemar la RAM”) y provocar que el sistema se ralentice o que la operación en R falle.

  • Límite de Longitud de Vector R impone un límite en la longitud de los vectores, que en sistemas de 32 bits es de aproximadamente 2^31 - 1 elementos. Aunque en teoría este límite es mayor en sistemas de 64 bits, en la práctica sigue estando restringido por la memoria disponible. Un raster, que se almacena internamente como un vector en R, al acercarse a este límite puede causar problemas de rendimiento o errores, especialmente cuando se intenta procesar el raster en su totalidad.

  • Grandes Rasters como Vectores Los rasters grandes se almacenan en R como vectores (o matrices, que son vectores con dimensiones). Esto significa que cada celda del raster se convierte en un elemento del vector. Con rasters extensos, el vector resultante puede ser enorme, llevando al sistema al límite de su capacidad de memoria y al límite de longitud de vector impuesto por R.

  • Estrategia de División en Chunks La solución a estos desafíos es dividir el raster en “chunks” más pequeños. Esta estrategia consiste en procesar segmentos del raster de manera individual y secuencial, en lugar de intentar cargar y procesar todo el raster de una sola vez. Al hacerlo, cada chunk utiliza solo una fracción de la memoria que se necesitaría para el raster completo, manteniendo el uso de la memoria dentro de límites manejables y evitando superar el límite de longitud del vector. Además, esta estrategia facilita la posibilidad de procesar los datos en paralelo, aprovechando múltiples núcleos del procesador para acelerar el análisis.

El proceso

Cargar las librerías:

library(raster)
library(terra)
library(parallel)
library(doParallel)
library(tictoc)

Lee el raster. En este caso se le han asignado los valores 0 para facilitar las cuentas, pero puede tener cualquier valor.

r <- raster(paste0(here(), "/_post_R/data/20240212/test.tif"))

El proceso básicamente consiste en trocear el raster y calcular los estadísticos a partir de esta muestra reducida de valores.

tic()

values  <- list()

# Define chunk size
chunk_size <- 10000  # Number of rows in each chunk

# Calculate the number of chunks needed
n_chunks <- ceiling(nrow(r) / chunk_size)

for (i in 1:n_chunks) {
    cat("Iteration: ", i, "\n")
    # Calculate row indices for the chunk
    start_row <- (i - 1) * chunk_size + 1
    end_row <- min(i * chunk_size, nrow(r))
    
    # Read the chunk
    chunk <- crop(r, c(1, ncol(r), start_row, end_row))
    
    # Extract values, removing NAs
    vals <- values(chunk)
    valid_vals <- na.omit(vals)
    
    # list of values
    values[[i]] <- c(sum = sum(valid_vals), sum_sq = sum(valid_vals^2), count = length(valid_vals))
}

total_sum <- sum(sapply(values, function(x) x[["sum"]]))
total_sum_sq <- sum(sapply(values, function(x) x[["sum_sq"]]))
total_count <- sum(sapply(values, function(x) x[["count"]]))

mean_value <- total_sum / total_count
variance <- (total_sum_sq - (total_sum^2 / total_count)) / (total_count - 1)
sd_value <- sqrt(variance)
sd_value
toc()
##           used  (Mb) gc trigger  (Mb) max used  (Mb)
## Ncells 2421519 129.4    4644262 248.1  4644262 248.1
## Vcells 3166354  24.2    8388608  64.0  5485610  41.9

El proceso con parallel.

Ojo, aquí la clave para no quemar la ram y joderlo todo es realizar un troceamiento adecuado del raster.

Se ha introducido un pequeño truco que tiene una utilidad increible para acelerar procesos en paralelo y aligerar la ram. Este truco es… la función gc(). Esta función la puedes utilizar cuando tu espacio de trabajo esté realmente cargado. Limpia cosas que no sabiamos ni que estaban ahi.

tic()
# Define chunk size
chunk_size <- 10000  # Number of rows in each chunk

# Calculate the number of chunks needed
n_chunks <- ceiling(raster::nrow(r) / chunk_size)

no_cores <- detectCores() - 1  # Leave one core free
registerDoParallel(cores = no_cores)

values <- foreach(i = 1:n_chunks, .combine = 'rbind') %dopar% {
    # Calculate row indices for the chunk
    start_row <- (i - 1) * chunk_size + 1
    end_row <- min(i * chunk_size, raster::nrow(r))
    
    # Extract values, removing NAs
    vals <- values(crop(r, c(1, raster::ncol(r), start_row, end_row)))
    valid_vals <- na.omit(vals)
    
    # clean memory as much as possible
    gc()
    
    # list of values
    c(sum = sum(valid_vals), sum_sq = sum(valid_vals^2), count = length(valid_vals))
}

total_sum <- sum(values[, 'sum'])
total_sum_sq <- sum(values[, 'sum_sq'])
total_count <- sum(values[, 'count'])

mean_value <- total_sum / total_count
variance <- (total_sum_sq - (total_sum^2 / total_count)) / (total_count - 1)
sd_value <- sqrt(variance)
sd_value
toc()

Conclusión

En resumen, el cálculo de la desviación estándar para rasters de gran tamaño no tiene por qué ser una tarea abrumadora que agote los recursos de memoria de nuestra computadora. Al adoptar un enfoque estratégico que involucra la segmentación del raster en chunks manejables y la implementación de operaciones en paralelo, podemos superar eficazmente las limitaciones de memoria y procesamiento. Este enfoque no solo optimiza el uso de los recursos disponibles, sino que también abre la puerta a análisis más complejos y detallados de datos geoespaciales de gran volumen. La clave está en comprender las restricciones de nuestro entorno de programación, en este caso R, y aplicar soluciones creativas para manejar datos que, de otro modo, serían inmanejables. Esperamos que este blog haya arrojado luz sobre cómo se pueden abordar y superar estos desafíos, permitiéndole aprovechar al máximo sus análisis geoespaciales, sin importar el tamaño de los datos con los que esté trabajando.

Además, la paralelización del proceso te permite hacer esto en un tiempo mucho más pequeño, eso sí, sobrecargando la RAM, así que ten cuidado.

Espero que hayas apreciado el esfuerzo en todo esto. Podrías apoyarme de muchas formas: contratándome, contactándome o símplemente compartiéndolo.


El Anexo ChatGPT4 solo para finalizar en psicodelia

Secuencia de comandos para generar esta chorrada:

  1. genera un thumnail para este post

  2. hazlo de 1280 x 720 y que tenga este texto superpuesto: dont burn your ram

  3. respecto de la imagen anterior, pon el texto dont burn your ram En mayúsculas y en grande y centrado. elimina el resto del texto en la imagen

  4. cruza la imagen con una imagen satelital o algo que represente también un raster, pon el texto en el centro de la imagen

Genera un thumbnail para este post relacionado con la representación visual de datos ráster en R
Thumbnail para post
Haz un thumbnail de 1280x720 con el texto 'Don't burn your RAM' superpuesto
Texto superpuesto: Don't burn your RAM
Cambia la imagen anterior, pon el texto 'DON'T BURN YOUR RAM' en mayúsculas, grande y centrado, eliminando el resto del texto
Texto en mayúsculas: DON'T BURN YOUR RAM
Cruza la imagen con una imagen satelital representativa de un ráster, con el texto centrado en la imagen
Cruza ráster con imagen satelital

También podrían interesarte las siguientes entradas del blog...