Régression linéaire

Earth Engine propose plusieurs méthodes pour effectuer une régression linéaire à l'aide de réducteurs:

  • ee.Reducer.linearFit()
  • ee.Reducer.linearRegression()
  • ee.Reducer.robustLinearRegression()
  • ee.Reducer.ridgeRegression()

Le réducteur de régression linéaire le plus simple est linearFit(), qui calcule l'estimation des moindres carrés d'une fonction linéaire d'une variable avec un terme constant. Pour une approche plus flexible de la modélisation linéaire, utilisez l'un des réducteurs de régression linéaire, qui permettent d'utiliser un nombre variable de variables indépendantes et dépendantes. linearRegression() implémente la régression des moindres carrés ordinaires(OLS). robustLinearRegression() utilise une fonction de coût basée sur les résidus de régression pour réduire de manière itérative le poids des valeurs aberrantes dans les données (O'Leary, 1990). ridgeRegression() effectue une régression linéaire avec régularisation L2.

L'analyse de régression avec ces méthodes est adaptée à la réduction des objets ee.ImageCollection, ee.Image, ee.FeatureCollection et ee.List. Les exemples suivants illustrent une application pour chacun d'eux. Notez que linearRegression(), robustLinearRegression() et ridgeRegression() ont tous les mêmes structures d'entrée et de sortie, mais linearFit() attend une entrée à deux bandes (X suivi de Y) et ridgeRegression() comporte un paramètre supplémentaire (lambda, facultatif) et une sortie (pValue).

ee.ImageCollection

linearFit()

Les données doivent être configurées en tant qu'image d'entrée à deux bandes, où la première bande est la variable indépendante et la seconde la variable dépendante. L'exemple suivant montre l'estimation de la tendance linéaire des précipitations futures (après 2006 dans les données NEX-DCP30) projetées par les modèles climatiques. La variable dépendante est la prévision de précipitations et la variable indépendante est le temps, ajouté avant d'appeler linearFit():

Éditeur de code (JavaScript)

// This function adds a time band to the image.
var createTimeBand = function(image) {
  // Scale milliseconds by a large constant to avoid very small slopes
  // in the linear regression output.
  return image.addBands(image.metadata('system:time_start').divide(1e18));
};

// Load the input image collection: projected climate data.
var collection = ee.ImageCollection('NASA/NEX-DCP30_ENSEMBLE_STATS')
  .filter(ee.Filter.eq('scenario', 'rcp85'))
  .filterDate(ee.Date('2006-01-01'), ee.Date('2050-01-01'))
  // Map the time band function over the collection.
  .map(createTimeBand);

// Reduce the collection with the linear fit reducer.
// Independent variable are followed by dependent variables.
var linearFit = collection.select(['system:time_start', 'pr_mean'])
  .reduce(ee.Reducer.linearFit());

// Display the results.
Map.setCenter(-100.11, 40.38, 5);
Map.addLayer(linearFit,
  {min: 0, max: [-0.9, 8e-5, 1], bands: ['scale', 'offset', 'scale']}, 'fit');

Notez que la sortie contient deux bandes, le "décalage" (interception) et l'"échelle" (dans ce contexte, "échelle" fait référence à la pente de la ligne et ne doit pas être confondu avec le paramètre d'échelle saisi dans de nombreux réducteurs, qui correspond à l'échelle spatiale). Le résultat, avec les zones de tendance à la hausse en bleu, celles à la baisse en rouge et celles sans tendance en vert, devrait ressembler à la figure 1.


Figure 1. Résultat de linearFit() appliqué aux précipitations projetées. Les zones où les précipitations devraient augmenter sont indiquées en bleu, et celles où elles devraient diminuer en rouge.

linearRegression()

Par exemple, supposons qu'il existe deux variables dépendantes: les précipitations et la température maximale, et deux variables indépendantes: une constante et le temps. La collection est identique à l'exemple précédent, mais la bande constante doit être ajoutée manuellement avant la réduction. Les deux premières bandes de l'entrée sont les variables "X" (indépendantes) et les deux bandes suivantes sont les variables "Y" (dépendantes). Dans cet exemple, obtenez d'abord les coefficients de régression, puis aplatissez l'image du tableau pour extraire les bandes d'intérêt:

Éditeur de code (JavaScript)

// This function adds a time band to the image.
var createTimeBand = function(image) {
  // Scale milliseconds by a large constant.
  return image.addBands(image.metadata('system:time_start').divide(1e18));
};

// This function adds a constant band to the image.
var createConstantBand = function(image) {
  return ee.Image(1).addBands(image);
};

// Load the input image collection: projected climate data.
var collection = ee.ImageCollection('NASA/NEX-DCP30_ENSEMBLE_STATS')
  .filterDate(ee.Date('2006-01-01'), ee.Date('2099-01-01'))
  .filter(ee.Filter.eq('scenario', 'rcp85'))
  // Map the functions over the collection, to get constant and time bands.
  .map(createTimeBand)
  .map(createConstantBand)
  // Select the predictors and the responses.
  .select(['constant', 'system:time_start', 'pr_mean', 'tasmax_mean']);

// Compute ordinary least squares regression coefficients.
var linearRegression = collection.reduce(
  ee.Reducer.linearRegression({
    numX: 2,
    numY: 2
}));

// Compute robust linear regression coefficients.
var robustLinearRegression = collection.reduce(
  ee.Reducer.robustLinearRegression({
    numX: 2,
    numY: 2
}));

// The results are array images that must be flattened for display.
// These lists label the information along each axis of the arrays.
var bandNames = [['constant', 'time'], // 0-axis variation.
                 ['precip', 'temp']]; // 1-axis variation.

// Flatten the array images to get multi-band images according to the labels.
var lrImage = linearRegression.select(['coefficients']).arrayFlatten(bandNames);
var rlrImage = robustLinearRegression.select(['coefficients']).arrayFlatten(bandNames);

// Display the OLS results.
Map.setCenter(-100.11, 40.38, 5);
Map.addLayer(lrImage,
  {min: 0, max: [-0.9, 8e-5, 1], bands: ['time_precip', 'constant_precip', 'time_precip']}, 'OLS');

// Compare the results at a specific point:
print('OLS estimates:', lrImage.reduceRegion({
  reducer: ee.Reducer.first(),
  geometry: ee.Geometry.Point([-96.0, 41.0]),
  scale: 1000
}));

print('Robust estimates:', rlrImage.reduceRegion({
  reducer: ee.Reducer.first(),
  geometry: ee.Geometry.Point([-96.0, 41.0]),
  scale: 1000
}));

Examinez les résultats pour découvrir que la sortie linearRegression() est équivalente aux coefficients estimés par le réducteur linearFit(), bien que la sortie linearRegression() comporte également des coefficients pour l'autre variable dépendante, tasmax_mean. Les coefficients de régression linéaire robustes sont différents des estimations MCO. L'exemple compare les coefficients des différentes méthodes de régression à un moment donné.

ee.Image

Dans le contexte d'un objet ee.Image, les réducteurs de régression peuvent être utilisés avec reduceRegion ou reduceRegions pour effectuer une régression linéaire sur les pixels de la ou des régions. Les exemples suivants montrent comment calculer les coefficients de régression entre les bandes Landsat dans un polygone arbitraire.

linearFit()

La section du guide décrivant les graphiques de données de tableau présente un graphique en nuage de points de la corrélation entre les bandes SWIR1 et SWIR2 de Landsat 8. Ici, les coefficients de régression linéaire de cette relation sont calculés. Un dictionnaire contenant les propriétés 'offset' (interception y) et 'scale' (pente) est renvoyé.

Éditeur de code (JavaScript)

// Define a rectangle geometry around San Francisco.
var sanFrancisco = ee.Geometry.Rectangle([-122.45, 37.74, -122.4, 37.8]);

// Import a Landsat 8 TOA image for this region.
var img = ee.Image('LANDSAT/LC08/C02/T1_TOA/LC08_044034_20140318');

// Subset the SWIR1 and SWIR2 bands. In the regression reducer, independent
// variables come first followed by the dependent variables. In this case,
// B5 (SWIR1) is the independent variable and B6 (SWIR2) is the dependent
// variable.
var imgRegress = img.select(['B5', 'B6']);

// Calculate regression coefficients for the set of pixels intersecting the
// above defined region using reduceRegion with ee.Reducer.linearFit().
var linearFit = imgRegress.reduceRegion({
  reducer: ee.Reducer.linearFit(),
  geometry: sanFrancisco,
  scale: 30,
});

// Inspect the results.
print('OLS estimates:', linearFit);
print('y-intercept:', linearFit.get('offset'));
print('Slope:', linearFit.get('scale'));

linearRegression()

La même analyse que celle de la section linearFit précédente est appliquée ici, sauf que cette fois, la fonction ee.Reducer.linearRegression est utilisée. Notez qu'une image de régression est construite à partir de trois images distinctes: une image constante et des images représentant les bandes SWIR1 et SWIR2 de la même image Landsat 8. N'oubliez pas que vous pouvez combiner n'importe quel ensemble de bandes pour créer une image d'entrée pour la réduction de région par ee.Reducer.linearRegression. Elles ne doivent pas appartenir à la même image source.

Éditeur de code (JavaScript)

// Define a rectangle geometry around San Francisco.
var sanFrancisco = ee.Geometry.Rectangle([-122.45, 37.74, -122.4, 37.8]);

// Import a Landsat 8 TOA image for this region.
var img = ee.Image('LANDSAT/LC08/C02/T1_TOA/LC08_044034_20140318');

// Create a new image that is the concatenation of three images: a constant,
// the SWIR1 band, and the SWIR2 band.
var constant = ee.Image(1);
var xVar = img.select('B5');
var yVar = img.select('B6');
var imgRegress = ee.Image.cat(constant, xVar, yVar);

// Calculate regression coefficients for the set of pixels intersecting the
// above defined region using reduceRegion. The numX parameter is set as 2
// because the constant and the SWIR1 bands are independent variables and they
// are the first two bands in the stack; numY is set as 1 because there is only
// one dependent variable (SWIR2) and it follows as band three in the stack.
var linearRegression = imgRegress.reduceRegion({
  reducer: ee.Reducer.linearRegression({
    numX: 2,
    numY: 1
  }),
  geometry: sanFrancisco,
  scale: 30,
});

// Convert the coefficients array to a list.
var coefList = ee.Array(linearRegression.get('coefficients')).toList();

// Extract the y-intercept and slope.
var b0 = ee.List(coefList.get(0)).get(0); // y-intercept
var b1 = ee.List(coefList.get(1)).get(0); // slope

// Extract the residuals.
var residuals = ee.Array(linearRegression.get('residuals')).toList().get(0);

// Inspect the results.
print('OLS estimates', linearRegression);
print('y-intercept:', b0);
print('Slope:', b1);
print('Residuals:', residuals);

Un dictionnaire contenant les propriétés 'coefficients' et 'residuals' est renvoyé. La propriété 'coefficients' est un tableau dont les dimensions sont (numX, numY). Chaque colonne contient les coefficients de la variable dépendante correspondante. Dans ce cas, le tableau comporte deux lignes et une colonne. La première ligne, la première colonne correspond à l'ordonnée à l'origine et la deuxième ligne, la première colonne correspond à la pente. La propriété 'residuals' est le vecteur de la racine carrée moyenne des résidus de chaque variable dépendante. Extrayez les coefficients en castant le résultat en tableau, puis en découpant les éléments souhaités ou en convertissant le tableau en liste et en sélectionnant les coefficients par position d'indice.

ee.FeatureCollection

Supposons que vous souhaitiez connaître la relation linéaire entre la réflectance SWIR1 de Sentinel-2 et de Landsat 8. Dans cet exemple, un échantillon aléatoire de pixels au format d'une collection d'éléments géographiques de points est utilisé pour calculer la relation. Un graphique en nuage de points des paires de pixels ainsi que la ligne de meilleure approximation des moindres carrés sont générés (figure 2).

Éditeur de code (JavaScript)

// Import a Sentinel-2 TOA image.
var s2ImgSwir1 = ee.Image('COPERNICUS/S2/20191022T185429_20191022T185427_T10SEH');

// Import a Landsat 8 TOA image from 12 days earlier than the S2 image.
var l8ImgSwir1 = ee.Image('LANDSAT/LC08/C02/T1_TOA/LC08_044033_20191010');

// Get the intersection between the two images - the area of interest (aoi).
var aoi = s2ImgSwir1.geometry().intersection(l8ImgSwir1.geometry());

// Get a set of 1000 random points from within the aoi. A feature collection
// is returned.
var sample = ee.FeatureCollection.randomPoints({
  region: aoi,
  points: 1000
});

// Combine the SWIR1 bands from each image into a single image.
var swir1Bands = s2ImgSwir1.select('B11')
  .addBands(l8ImgSwir1.select('B6'))
  .rename(['s2_swir1', 'l8_swir1']);

// Sample the SWIR1 bands using the sample point feature collection.
var imgSamp = swir1Bands.sampleRegions({
  collection: sample,
  scale: 30
})
// Add a constant property to each feature to be used as an independent variable.
.map(function(feature) {
  return feature.set('constant', 1);
});

// Compute linear regression coefficients. numX is 2 because
// there are two independent variables: 'constant' and 's2_swir1'. numY is 1
// because there is a single dependent variable: 'l8_swir1'. Cast the resulting
// object to an ee.Dictionary for easy access to the properties.
var linearRegression = ee.Dictionary(imgSamp.reduceColumns({
  reducer: ee.Reducer.linearRegression({
    numX: 2,
    numY: 1
  }),
  selectors: ['constant', 's2_swir1', 'l8_swir1']
}));

// Convert the coefficients array to a list.
var coefList = ee.Array(linearRegression.get('coefficients')).toList();

// Extract the y-intercept and slope.
var yInt = ee.List(coefList.get(0)).get(0); // y-intercept
var slope = ee.List(coefList.get(1)).get(0); // slope

// Gather the SWIR1 values from the point sample into a list of lists.
var props = ee.List(['s2_swir1', 'l8_swir1']);
var regressionVarsList = ee.List(imgSamp.reduceColumns({
  reducer: ee.Reducer.toList().repeat(props.size()),
  selectors: props
}).get('list'));

// Convert regression x and y variable lists to an array - used later as input
// to ui.Chart.array.values for generating a scatter plot.
var x = ee.Array(ee.List(regressionVarsList.get(0)));
var y1 = ee.Array(ee.List(regressionVarsList.get(1)));

// Apply the line function defined by the slope and y-intercept of the
// regression to the x variable list to create an array that will represent
// the regression line in the scatter plot.
var y2 = ee.Array(ee.List(regressionVarsList.get(0)).map(function(x) {
  var y = ee.Number(x).multiply(slope).add(yInt);
  return y;
}));

// Concatenate the y variables (Landsat 8 SWIR1 and predicted y) into an array
// for input to ui.Chart.array.values for plotting a scatter plot.
var yArr = ee.Array.cat([y1, y2], 1);

// Make a scatter plot of the two SWIR1 bands for the point sample and include
// the least squares line of best fit through the data.
print(ui.Chart.array.values({
  array: yArr,
  axis: 0,
  xLabels: x})
  .setChartType('ScatterChart')
  .setOptions({
    legend: {position: 'none'},
    hAxis: {'title': 'Sentinel-2 SWIR1'},
    vAxis: {'title': 'Landsat 8 SWIR1'},
    series: {
      0: {
        pointSize: 0.2,
        dataOpacity: 0.5,
      },
      1: {
        pointSize: 0,
        lineWidth: 2,
      }
    }
  })
);


Figure 2. Courbe de régression linéaire des moindres carrés et nuage de points pour un échantillon de pixels représentant la réflectance TOA de Sentinel-2 et de Landsat 8 SWIR1.

ee.List

Les colonnes d'objets ee.List 2D peuvent être des entrées pour les réducteurs de régression. Les exemples suivants fournissent des preuves simples. La variable indépendante est une copie de la variable dépendante, ce qui produit un intercepteur y égal à 0 et une pente égale à 1.

linearFit()

Éditeur de code (JavaScript)

// Define a list of lists, where columns represent variables. The first column
// is the independent variable and the second is the dependent variable.
var listsVarColumns = ee.List([
  [1, 1],
  [2, 2],
  [3, 3],
  [4, 4],
  [5, 5]
]);

// Compute the least squares estimate of a linear function. Note that an
// object is returned; cast it as an ee.Dictionary to make accessing the
// coefficients easier.
var linearFit = ee.Dictionary(listsVarColumns.reduce(ee.Reducer.linearFit()));

// Inspect the result.
print(linearFit);
print('y-intercept:', linearFit.get('offset'));
print('Slope:', linearFit.get('scale'));

Transposez la liste si les variables sont représentées par des lignes en la convertissant en ee.Array, en la transposant, puis en la convertissant à nouveau en ee.List.

Éditeur de code (JavaScript)

// If variables in the list are arranged as rows, you'll need to transpose it.
// Define a list of lists where rows represent variables. The first row is the
// independent variable and the second is the dependent variable.
var listsVarRows = ee.List([
  [1, 2, 3, 4, 5],
  [1, 2, 3, 4, 5]
]);

// Cast the ee.List as an ee.Array, transpose it, and cast back to ee.List.
var listsVarColumns = ee.Array(listsVarRows).transpose().toList();

// Compute the least squares estimate of a linear function. Note that an
// object is returned; cast it as an ee.Dictionary to make accessing the
// coefficients easier.
var linearFit = ee.Dictionary(listsVarColumns.reduce(ee.Reducer.linearFit()));

// Inspect the result.
print(linearFit);
print('y-intercept:', linearFit.get('offset'));
print('Slope:', linearFit.get('scale'));

linearRegression()

L'application de ee.Reducer.linearRegression() est semblable à l'exemple linearFit() ci-dessus, à l'exception d'une variable indépendante constante incluse.

Éditeur de code (JavaScript)

// Define a list of lists where columns represent variables. The first column
// represents a constant term, the second an independent variable, and the third
// a dependent variable.
var listsVarColumns = ee.List([
  [1, 1, 1],
  [1, 2, 2],
  [1, 3, 3],
  [1, 4, 4],
  [1, 5, 5]
]);

// Compute ordinary least squares regression coefficients. numX is 2 because
// there is one constant term and an additional independent variable. numY is 1
// because there is only a single dependent variable. Cast the resulting
// object to an ee.Dictionary for easy access to the properties.
var linearRegression = ee.Dictionary(
  listsVarColumns.reduce(ee.Reducer.linearRegression({
    numX: 2,
    numY: 1
})));

// Convert the coefficients array to a list.
var coefList = ee.Array(linearRegression.get('coefficients')).toList();

// Extract the y-intercept and slope.
var b0 = ee.List(coefList.get(0)).get(0); // y-intercept
var b1 = ee.List(coefList.get(1)).get(0); // slope

// Extract the residuals.
var residuals = ee.Array(linearRegression.get('residuals')).toList().get(0);

// Inspect the results.
print('OLS estimates', linearRegression);
print('y-intercept:', b0);
print('Slope:', b1);
print('Residuals:', residuals);