Introducción a la programación funcional
Earth Engine usa un sistema de procesamiento paralelo para realizar cálculos en una gran cantidad de máquinas. Para habilitar este procesamiento, Earth Engine aprovecha las técnicas estándar que suelen utilizar los lenguajes funcionales, como la transparencia referencial y la evaluación diferida, para obtener mejoras significativas en la optimización y la eficiencia.
El concepto principal que diferencia la programación funcional de la programación procedimental es la ausencia de efectos secundarios. Esto significa que las funciones que escribes no dependen de datos externos a la función ni los actualizan. Como verás en los siguientes ejemplos, es posible reestructurar tu problema para que se pueda resolver con funciones sin efectos secundarios, que son mucho más adecuadas para ejecutarse en paralelo.
Bucles for
No se recomienda el uso de bucles for en Earth Engine. Se pueden obtener los mismos resultados con una operación map()
en la que se especifica una función que se puede aplicar de forma independiente a cada elemento. Esto permite que el sistema distribuya el procesamiento en diferentes máquinas.
En el siguiente ejemplo, se ilustra cómo tomarías una lista de números y crearías otra lista con los cuadrados de cada número usando map()
:
Editor de código (JavaScript)
// This generates a list of numbers from 1 to 10. var myList = ee.List.sequence(1, 10); // The map() operation takes a function that works on each element independently // and returns a value. You define a function that can be applied to the input. var computeSquares = function(number) { // We define the operation using the EE API. return ee.Number(number).pow(2); }; // Apply your function to each item in the list by using the map() function. var squares = myList.map(computeSquares); print(squares); // [1, 4, 9, 16, 25, 36, 49, 64, 81]
Condiciones If/Else
Otro problema común que enfrentan los usuarios nuevos que están acostumbrados al paradigma de programación procedimental es el uso adecuado de los operadores condicionales if/else en Earth Engine. Si bien la API proporciona un algoritmo ee.Algorithms.If()
, se desaconseja su uso en favor de un enfoque más funcional con map()
y filtros.
Earth Engine usa la
ejecución diferida, lo que significa que la evaluación de una expresión se retrasa hasta que se requiere su valor realizado. En algunos casos, este tipo de modelo de ejecución evaluará las alternativas verdaderas y falsas de una instrucción ee.Algorithms.If()
. Esto puede generar un uso adicional de memoria y procesamiento, según las expresiones y los recursos necesarios para ejecutarlas.
Supongamos que deseas resolver una variante del ejemplo anterior, en la que la tarea es calcular los cuadrados de solo números impares. A continuación, se muestra un enfoque funcional para resolver este problema sin condiciones if/else:
Editor de código (JavaScript)
// The following function determines if a number is even or odd. The mod(2) // function returns 0 if the number is even and 1 if it is odd (the remainder // after dividing by 2). The input is multiplied by this remainder so even // numbers get set to 0 and odd numbers are left unchanged. var getOddNumbers = function(number) { number = ee.Number(number); // Cast the input to a Number so we can use mod. var remainder = number.mod(2); return number.multiply(remainder); }; var newList = myList.map(getOddNumbers); // Remove the 0 values. var oddNumbers = newList.removeAll([0]); var squares = oddNumbers.map(computeSquares); print(squares); // [1, 9, 25, 49, 81]
Este paradigma es especialmente aplicable cuando se trabaja con colecciones. Si deseas aplicar un algoritmo diferente a la colección según ciertas condiciones, la mejor manera es primero filtrar la colección según la condición y, luego, map()
una función diferente a cada uno de los subconjuntos. Esto permite que el sistema paralelice la operación. Por ejemplo:
Editor de código (JavaScript)
// Import Landsat 8 TOA collection and filter to 2018 images. var collection = ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA') .filterDate('2018-01-01', '2019-01-01'); // Divide the collection into 2 subsets and apply a different algorithm on them. var subset1 = collection.filter(ee.Filter.lt('SUN_ELEVATION', 40)); var subset2 = collection.filter(ee.Filter.gte('SUN_ELEVATION', 40)); // Multiply all images in subset1 collection by 2; // do nothing to subset2 collection. var processed1 = subset1.map(function(image) { return image.multiply(2); }); var processed2 = subset2; // Merge the collections to get a single collection. var final = processed1.merge(processed2); print('Original collection size', collection.size()); print('Processed collection size', final.size());
Iteración acumulativa
Es posible que debas realizar operaciones secuenciales, en las que el resultado de cada iteración se use en la iteración posterior. Earth Engine proporciona un método iterate()
para estas tareas. Recuerda que iterate()
se ejecuta de forma secuencial y, por lo tanto, será lento para operaciones grandes. Úsala solo cuando no puedas usar map()
ni filtros para obtener el resultado deseado.
Una buena demostración de iterate()
es la creación de la secuencia de números de Fibonacci. Aquí, cada número de la serie es la suma de los 2 números anteriores. La función iterate()
toma 2 argumentos: una función (algoritmo) y un valor inicial. La función en sí recibe 2 valores: el valor actual en la iteración y el resultado de la iteración anterior. En el siguiente ejemplo, se muestra cómo implementar una secuencia de Fibonacci en Earth Engine.
Editor de código (JavaScript)
var algorithm = function(current, previous) { previous = ee.List(previous); var n1 = ee.Number(previous.get(-1)); var n2 = ee.Number(previous.get(-2)); return previous.add(n1.add(n2)); }; // Compute 10 iterations. var numIteration = ee.List.repeat(1, 10); var start = [0, 1]; var sequence = numIteration.iterate(algorithm, start); print(sequence); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
Ahora que comprendes bien los conceptos de JavaScript, puedes consultar el instructivo de la API para obtener una introducción a la funcionalidad geoespacial de la API de Earth Engine.