Google Earth Engine – Export Multiband Statistics as CSV Using ee.Reducer.mean().group()

google-earth-enginegoogle-earth-engine-javascript-api

I am working with a multiband raster obtained from MODIS images and calculations between different periods. Every band contains the mean value of EVI index grouped by zones (using ee.reducer.mean().group()). I was able to export them as a single CSV, but I want to obtain a single CSV file where each column represents the grouped stats of every band. Is there any way to achieve this?

resultsStack is the multiband raster; filterZones is the grouping filter

for(var i = 0; i < 6; i++) {
      
      var stats = resultsStack.select(i).addBands(filterZones);
      
      var means = stats.reduceRegion({
      reducer: ee.Reducer.mean().group({
        groupField: 1,
        groupName: 'band_' + (i + 1).toString(),
      }),
      geometry: extent,
      scale: 500,
      maxPixels: 1e8
      });
        
      print(means);
      
      var asList = ee.List(means.get('groups')).map(function (pair) {
        return ee.Feature(null, pair);
      });
      
      var rawCollection = ee.FeatureCollection(asList);
      
      Export.table.toDrive({
        collection: ee.FeatureCollection(rawCollection),
        description: 'evi_band' + (i + 1).toString() + '_' + idate + '_' + fdate,
        folder: 'evi_stats_' + idate + '_' + fdate,
        fileFormat: 'CSV',
        selectors: ['band_' + (i + 1).toString(), 'mean']
      });
    }

Best Answer

There are plenty of options on how to do this. Unfortunately, as far as I know, they all require quite a bit of fiddling to get your data in the correct shape. Here's one way, creating a feature collection where every feature represent a band/group/mean. This collection is joined by itself on the group property, and a new collection where every feature contains all band means for a group.

var flatCollection = ee.FeatureCollection(
  resultsStack.bandNames()
    .map(function (band) {
      return ee.FeatureCollection(ee.List(
        resultsStack.select(ee.String(band))
          .addBands(filterZones)
          .reduceRegion({
            reducer: ee.Reducer.mean().group(1),
            geometry: extent,
            scale: 500,
            maxPixels: 1e8
          })
          .get('groups')
      )
      .map(function (group) {
        return ee.Feature(null, ee.Dictionary(group)).set('band', band)
      }))
    })
).flatten()


var stats = ee.Join.saveAll('features')
  .apply({
    primary: flatCollection.distinct('group'), 
    secondary: flatCollection, 
    condition: ee.Filter.equals({leftField: 'group', rightField: 'group'})
  })
  .map(function (groupFeature) {
    return ee.Feature(ee.List(groupFeature.get('features'))
      .iterate(
        function (feature, acc) {
          feature = ee.Feature(feature)
          return ee.Feature(acc)
            .set(feature.getString('band'), feature.getNumber('mean'))
        },
        ee.Feature(null, {group: groupFeature.getNumber('group')})
      )
    )
  })
  .sort('group')

https://code.earthengine.google.com/8e4b60811fd8864bce876bacfe2dcd70

You can implement this with a single reduceRegion() call, using repeat() on the reducer. I believe that would require you to use an unweighted mean reducer, but that might not a big problem. I'm not sure you'd see any performance improvements though. It could be worth to test if your performance isn't good enough when doing multiple reduceRegion() calls.

https://code.earthengine.google.com/9388e923cf5ab54943102c1692fc892d

Related Question