0

I am comparing (maximum & minimum values) yearly composites (both day & night) of Terra MODIS Daily LST 1 km. I calculated using same images but different approaches - just to see if they have any discrepencies. I used 2021 as test year and temperature unit used in degree Celcius.

At first, (after filtering, clipping, and unit conversion) I calculated daytime and nighttime yearly mean composites using all images available, i.e. 364, for the year 2021.

Then, I calculated monthly composites using the same images, i.e. 354 scenes. After that I calculated yearly mean composite using these 12 images (stored in an ImageCollection). I extracted both maximum and minimum values from the composites - yearly composite from all images directly; yearly composite from monthly composites.

To my understanding, these composites should be identical and have same max and min values (of scene). I found same maximum values , however, the minimum values do not match.

max values min values

Although I used both day and nighttime LST, to keep the question simple, I am going to show code only for nighttime data. In the code below, the min value from yearly composite (calculated from monthly composite) is -18.368 degree Celcius, whereas, yearly composite (from 364 images) returns -16.071 degree Celcius.

Why are maximum value remains same (screenshot 1) but minimum values differ (screenshot 2, and code given below)?

TIA

Edit 1: To explore more on the issue, I wanted to map the differences; I subtracted the composites and added the yielded difference image to display. The composites are found with differences in significant amount of pixels. Maybe GEE limits the precision, i.e. decimal places, for calculation of mean, hence underestimation in yearly composite which is based on monthly composites? On the other hand, it it's true then MAX values should mismatch as well, which are not.

difference map

It is also interesting that the number of pixels with differences increase as latitude increases (north/south).

Link to GEE code

GEE code after Edit 1

// ******************** DATA IMPORT & FILTER *********************** //
var bound = ee.FeatureCollection('users/salitchakma/SE_Asia_boundary');
var terra = ee.ImageCollection('MODIS/061/MOD11A1').filter(ee.Filter.date('2021-01-01', '2021-12-31'));

// ******************************** FUNCTIONS ***************************** //
// Quality mask; code adopted from https://spatialthoughts.com/2021/08/19/qa-bands-bitmasks-gee/
var bitwiseExtract = function(input, fromBit, toBit) {
  var maskSize = ee.Number(1).add(toBit).subtract(fromBit);
  var mask = ee.Number(1).leftShift(maskSize).subtract(1);
  return input.rightShift(fromBit).bitwiseAnd(mask);
};

// Quality filter for nighttime data
var quality_night = function(img) {
  var lstNight = img.select('LST_Night_1km');
  var qcNight = img.select('QC_Night');
  var qaMask = bitwiseExtract(qcNight, 0, 1).lte(1);
  var dataQualityMask = bitwiseExtract(qcNight, 2, 3).eq(0);  // Only good quality data (flag:0)
  var emissivityMask = bitwiseExtract(qcNight, 4, 5).lte(1);    // No more than 0.02 emissivity error
  var lstErrorMask = bitwiseExtract(qcNight, 6, 7).lte(1);    // No more than 2K LST error
  var mask = qaMask.and(dataQualityMask).and(lstErrorMask).and(emissivityMask);
  return lstNight.updateMask(mask);
};

// Function to clip each image; adding pixel counts to a property, 'pixel_count'
var clipped = function (img) {
   img = img.clip(bound);
   return img
    .copyProperties(img,['system:time_start','system:time_end']);
  };

// Function to convert kelvin to degree celcius
var kelvin_celcius = function(img){
  return img
    .multiply(0.02)
    .subtract(273.15)
    .copyProperties(img,['system:time_start','system:time_end']);
};

// To get monthly mean composite
var months = ee.List.sequence(1, 12);
var monthly = function (m) {
        var avg_img = imgCol.filter(ee.Filter.calendarRange(m, m, 'month')).mean();
        return avg_img;
};

// Quality filter application
var terra_night = terra.map(quality_night);

// Clips to study area 
var terra_night_clip = terra_night.map(clipped);

// converts kelvin to celcius
var terra_night_celcius = terra_night_clip.map(kelvin_celcius);

// Monthly mean composite image collection
var imgCol = terra_night_celcius;   // Sets up image collection on which monthly function will be mapped
var terra_night_monthly = ee.ImageCollection.fromImages(
  months.map(monthly));

// Yearly mean composite from months' mean composites
var mon_yearly_mean = terra_night_monthly.mean();

//Yearly mean composite from 2021's imgCol
var yearly_mean = terra_night_celcius.mean();

// ************** FINDING MIN *************
var mon_yearly_min = mon_yearly_mean.reduceRegion({
  reducer: ee.Reducer.min(),
  geometry: bound,
  scale: 1000,
  maxPixels: 1e9,
  bestEffort: true
});

var yearly_min = yearly_mean.reduceRegion({
  reducer: ee.Reducer.min(),
  geometry: bound,
  scale: 1000,
  maxPixels: 1e9,
  bestEffort: true
});
print("MINIMUM:",
      "Monthly to Yearly composite min value:", mon_yearly_min.values(['LST_Night_1km']).get(0), // Returns -18.368374999999975
      "Yearly composite min value:", yearly_min.values(['LST_Night_1km']).get(0)  // Returns -16.071111111111083, therefore the values do not match
);

// ********** EDIT 1  *****************
var diff = yearly_mean.subtract(mon_yearly_mean);
var params = {min: -2, max: 2, palette:['red','white','green']};
Map.addLayer(diff, params, "Difference");

1 Answer 1

2

The mean of seasonal means is only equivalent to a yearly mean if all the seasons have exactly the same number of images in them. Otherwise, you're weighting some of the seasons more than others.
For instance, if there's only 1 image in winter, but 100 in each of the other three seasons, the weight of the 1 winter image in the mean of means is 1/4, whereas it'd be 1/301 in the annual mean.

5
  • 1
    +1 but to be more accurate, it is not the number of images that counts but the number of valid pixels (=non cloudy etc)
    – radouxju
    Sep 12 at 12:21
  • Thanks for feedbacks! @Noel I also thought like radouxju, that in GEE, mean() gets counted at pixel level, not at image level. Anyway, even so, if weight of a winter pixel reduces to 1/301 in yearly composite from 1/4 (monthly composite, there are other images (in other months' composites) adding up weights in yearly composite, therefore making the final two composites, one from all pixels of a year, another from 12 monthly composite pixels, identical. E.g., (((1+2+3)÷3)+(2+3+4)÷3)÷2 = 2.5; (1+2+3+2+3+4)÷6=2.5.
    – Salit
    Sep 13 at 4:33
  • Okay, got it now. for small samples, the mean and mean of means remain identical but as sample size increases the differences get introduced. I tested it by generating 301 random numbers and calculating 3 means from 100 each, and 1 mean from remining 1; another mean from all 301 values. The mean and mean of means differ slightly.
    – Salit
    Sep 13 at 5:10
  • The spatial pattern of the pixels with differences are interesting though. They increase with distance from equator.
    – Salit
    Sep 13 at 5:17
  • Update: After few tests, I found, the mean and mean of means would only be identical if the sample and sub-samples are of normal distribution. The difference has nothing to do with the sample size but data distribution. Since the LST data are not with normal distribution, the 12 months' means get skewed.
    – Salit
    Sep 14 at 10:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.