Earth Engine unterstützt Arraytransformationen wie Transponieren, Inversieren und Pseudoinvers. Betrachten Sie als Beispiel eine OLS-Regression (Ordinary Least Squares, Methode der kleinsten Quadrate) einer Zeitreihe von Bildern. Im folgenden Beispiel wird ein Bild mit Bändern für Prädiktoren und eine Antwort in ein Arraybild umgewandelt und dann auf drei Arten „aufgelöst“, um Schätzwerte für die kleinsten Quadrate zu erhalten. Stellen Sie zuerst die Bilddaten zusammen und konvertieren Sie sie in Arrays:
// Scales and masks Landsat 8 surface reflectance images. function prepSrL8(image) { // Develop masks for unwanted pixels (fill, cloud, cloud shadow). var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0); var saturationMask = image.select('QA_RADSAT').eq(0); // Apply the scaling factors to the appropriate bands. var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2); var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0); // Replace the original bands with the scaled ones and apply the masks. return image.addBands(opticalBands, null, true) .addBands(thermalBands, null, true) .updateMask(qaMask) .updateMask(saturationMask); } // Load a Landsat 8 surface reflectance image collection. var collection = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2') // Filter to get only two years of data. .filterDate('2019-04-01', '2021-04-01') // Filter to get only imagery at a point of interest. .filterBounds(ee.Geometry.Point(-122.08709, 36.9732)) // Prepare images by mapping the prepSrL8 function over the collection. .map(prepSrL8) // Select NIR and red bands only. .select(['SR_B5', 'SR_B4']) // Sort the collection in chronological order. .sort('system:time_start', true); // This function computes the predictors and the response from the input. var makeVariables = function(image) { // Compute time of the image in fractional years relative to the Epoch. var year = ee.Image(image.date().difference(ee.Date('1970-01-01'), 'year')); // Compute the season in radians, one cycle per year. var season = year.multiply(2 * Math.PI); // Return an image of the predictors followed by the response. return image.select() .addBands(ee.Image(1)) // 0. constant .addBands(year.rename('t')) // 1. linear trend .addBands(season.sin().rename('sin')) // 2. seasonal .addBands(season.cos().rename('cos')) // 3. seasonal .addBands(image.normalizedDifference().rename('NDVI')) // 4. response .toFloat(); }; // Define the axes of variation in the collection array. var imageAxis = 0; var bandAxis = 1; // Convert the collection to an array. var array = collection.map(makeVariables).toArray(); // Check the length of the image axis (number of images). var arrayLength = array.arrayLength(imageAxis); // Update the mask to ensure that the number of images is greater than or // equal to the number of predictors (the linear model is solvable). array = array.updateMask(arrayLength.gt(4)); // Get slices of the array according to positions along the band axis. var predictors = array.arraySlice(bandAxis, 0, 4); var response = array.arraySlice(bandAxis, 4);
import ee import geemap.core as geemap
import math # Scales and masks Landsat 8 surface reflectance images. def prep_sr_l8(image): # Develop masks for unwanted pixels (fill, cloud, cloud shadow). qa_mask = image.select('QA_PIXEL').bitwiseAnd(int('11111', 2)).eq(0) saturation_mask = image.select('QA_RADSAT').eq(0) # Apply the scaling factors to the appropriate bands. optical_bands = image.select('SR_B.').multiply(0.0000275).add(-0.2) thermal_bands = image.select('ST_B.*').multiply(0.00341802).add(149.0) # Replace the original bands with the scaled ones and apply the masks. return ( image.addBands(optical_bands, None, True) .addBands(thermal_bands, None, True) .updateMask(qa_mask) .updateMask(saturation_mask) ) # Load a Landsat 8 surface reflectance image collection. collection = ( ee.ImageCollection('LANDSAT/LC08/C02/T1_L2') # Filter to get only two years of data. .filterDate('2019-04-01', '2021-04-01') # Filter to get only imagery at a point of interest. .filterBounds(ee.Geometry.Point(-122.08709, 36.9732)) # Prepare images by mapping the prep_sr_l8 function over the collection. .map(prep_sr_l8) # Select NIR and red bands only. .select(['SR_B5', 'SR_B4']) # Sort the collection in chronological order. .sort('system:time_start', True) ) # This function computes the predictors and the response from the input. def make_variables(image): # Compute time of the image in fractional years relative to the Epoch. year = ee.Image(image.date().difference(ee.Date('1970-01-01'), 'year')) # Compute the season in radians, one cycle per year. season = year.multiply(2 * math.pi) # Return an image of the predictors followed by the response. return ( image.select() .addBands(ee.Image(1)) # 0. constant .addBands(year.rename('t')) # 1. linear trend .addBands(season.sin().rename('sin')) # 2. seasonal .addBands(season.cos().rename('cos')) # 3. seasonal .addBands(image.normalizedDifference().rename('NDVI')) # 4. response .toFloat() ) # Define the axes of variation in the collection array. image_axis = 0 band_axis = 1 # Convert the collection to an array. array = collection.map(make_variables).toArray() # Check the length of the image axis (number of images). array_length = array.arrayLength(image_axis) # Update the mask to ensure that the number of images is greater than or # equal to the number of predictors (the linear model is solvable). array = array.updateMask(array_length.gt(4)) # Get slices of the array according to positions along the band axis. predictors = array.arraySlice(band_axis, 0, 4) response = array.arraySlice(band_axis, 4)
Hinweis: Mit arraySlice()
werden alle Bilder in der Zeitreihe für den Indexbereich zurückgegeben, der entlang der bandAxis
(der 1-Achse) angegeben ist. An dieser Stelle kann Matrixalgebra verwendet werden, um die OLS-Koeffizienten zu berechnen:
// Compute coefficients the hard way. var coefficients1 = predictors.arrayTranspose().matrixMultiply(predictors) .matrixInverse().matrixMultiply(predictors.arrayTranspose()) .matrixMultiply(response);
import ee import geemap.core as geemap
# Compute coefficients the hard way. coefficients_1 = ( predictors.arrayTranspose() .matrixMultiply(predictors) .matrixInverse() .matrixMultiply(predictors.arrayTranspose()) .matrixMultiply(response) )
Diese Methode funktioniert zwar, ist aber ineffizient und führt zu schwer lesbarem Code. Besser ist es, die Methode pseudoInverse()
(matrixPseudoInverse()
für ein Array-Bild) zu verwenden:
// Compute coefficients the easy way. var coefficients2 = predictors.matrixPseudoInverse() .matrixMultiply(response);
import ee import geemap.core as geemap
# Compute coefficients the easy way. coefficients_2 = predictors.matrixPseudoInverse().matrixMultiply(response)
Aus Sicht der Lesbarkeit und der Berechnungseffizienz ist solve()
(matrixSolve()
für ein Array-Bild) die beste Methode, um die OLS-Koeffizienten zu erhalten. Die Funktion solve()
bestimmt anhand der Eigenschaften der Eingaben, wie das System am besten gelöst werden kann. Dabei wird die Pseudoinverse für überbestimmte Systeme, die Inverse für quadratische Matrizen und spezielle Verfahren für nahezu singuläre Matrizen verwendet:
// Compute coefficients the easiest way. var coefficients3 = predictors.matrixSolve(response);
import ee import geemap.core as geemap
# Compute coefficients the easiest way. coefficients_3 = predictors.matrixSolve(response)
Um ein mehrbandiges Bild zu erhalten, projizieren Sie das Arraybild in einen weniger dimensionalen Raum und flachen Sie es dann ab:
// Turn the results into a multi-band image. var coefficientsImage = coefficients3 // Get rid of the extra dimensions. .arrayProject([0]) .arrayFlatten([ ['constant', 'trend', 'sin', 'cos'] ]);
import ee import geemap.core as geemap
# Turn the results into a multi-band image. coefficients_image = ( coefficients_3 # Get rid of the extra dimensions. .arrayProject([0]).arrayFlatten([['constant', 'trend', 'sin', 'cos']]) )
Sehen Sie sich die Ausgaben der drei Methoden an. Die resultierende Matrix der Koeffizienten ist unabhängig vom Solver gleich. Da solve()
flexibel und effizient ist, eignet es sich gut für allgemeine lineare Modelle.