Kodlamayla İlgili En İyi Uygulamalar

Bu dokümanda, karmaşık veya pahalı Earth Engine hesaplamalarında başarı şansını en üst düzeye çıkarmak için tasarlanmış kodlama uygulamaları açıklanmaktadır. Burada açıklanan yöntemler hem etkileşimli (ör.Kod Düzenleyici) hem de toplu (Export) hesaplamalar için geçerlidir. Ancak genellikle uzun süren hesaplamalar toplu sistemde çalıştırılmalıdır.

İstemci işlevlerini ve nesnelerini sunucu işlevleri ve nesneleriyle karıştırmayın

Earth Engine sunucu nesneleri, ee ile başlayan (ör. ee.Image, ee.Reducer) kurucuları olan nesnelerdir ve bu tür nesnelerdeki tüm yöntemler sunucu işlevleridir. Bu şekilde oluşturulmayan tüm nesneler istemci nesnesidir. İstemci nesneleri Kod Düzenleyici'den (ör. Map, Chart) veya JavaScript dilinden (ör. Date, Math, [], {}) gelebilir.

İstenmeyen davranışları önlemek için komut dosyanızda istemci ve sunucu işlevlerini burada, burada ve burada açıklandığı gibi karıştırmayın. Earth Engine'da istemci ve sunucu arasındaki farklara dair ayrıntılı açıklama için bu sayfaya ve/veya bu eğitime göz atın. Aşağıdaki örnekte, istemci ve sunucu işlevlerinin karıştırılmasının tehlikeleri gösterilmektedir:

Hata: Bu kod çalışmıyor.

var table = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');

// Won't work.
for(var i=0; i<table.size(); i++) {
  print('No!');
}

Hatayı fark edebiliyor musunuz? table.size()'ün bir sunucu nesnesinde sunucu yöntemi olduğunu ve < koşullu gibi istemci tarafı işlevleriyle kullanılamayacağını unutmayın.

CodeEditor ui nesneleri ve yöntemleri istemci tarafında olduğundan, for döngülerini kullanmak isteyebileceğiniz bir durum kullanıcı arayüzü kurulumudur. (Earth Engine'da kullanıcı arayüzü oluşturma hakkında daha fazla bilgi edinin). Örneğin:

Kullanıcı arayüzü kurulumu için istemci işlevlerini kullanın.

var panel = ui.Panel();
for(var i=1; i<8; i++) {
  panel.widgets().set(i, ui.Button('button ' + i))
}
print(panel);

Buna karşılık, map() bir sunucu işlevidir ve istemci işlevi, map() işlevine iletilen işlev içinde çalışmaz. Örneğin:

Hata: Bu kod çalışmıyor.

var table = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');

// Error:
var foobar = table.map(function(f) {
  print(f); // Can't use a client function here.
  // Can't Export, either.
});

Koleksiyondaki her öğe için bir işlem yapmak istiyorsanız map() koleksiyon üzerinde bir işlev ve set() bir mülk kullanın:

map() ve set() kullanın.

var table = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
print(table.first());

// Do something to every element of a collection.
var withMoreProperties = table.map(function(f) {
  // Set a property.
  return f.set('area_sq_meters', f.area())
});
print(withMoreProperties.first());

Ayrıca, toplanan verileri hesaplanan veya mevcut özelliklere göre filter() ve sonucu print() de alabilirsiniz. 5.000'den fazla öğe içeren bir koleksiyonu yazdıramayacağınızı unutmayın. "Koleksiyon sorgusu 5.000'den fazla öğe toplandıktan sonra iptal edildi" hatası alırsanız koleksiyonu yazdırmadan önce filter() veya limit().

Gereksiz yere listeye dönüştürmekten kaçının

Earth Engine'daki koleksiyonlar, koleksiyon List veya Array türüne dönüştürülerek bozulan optimizasyonlar kullanılarak işlenir. Koleksiyon öğelerine rastgele erişmeniz gerekmediği sürece (ör.bir koleksiyonun i. öğesini almanız gerekiyorsa) koleksiyondaki filtreleri kullanarak koleksiyon öğelerine tek tek erişebilirsiniz. Aşağıdaki örnekte, bir koleksiyondaki öğeye erişmek için tür dönüşümü (önerilmez) ile filtreleme (önerilir) arasındaki fark gösterilmektedir:

Gereksiz yere listeye dönüştürmeyin.

var table = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');

// Do NOT do this!!
var list = table.toList(table.size());
print(list.get(13)); // User memory limit exceeded.

Bir koleksiyonu gereksiz yere listeye dönüştürerek kolayca hata tetikleyebileceğinizi unutmayın. Daha güvenli olan yöntem filter() kullanmaktır:

filter()'u kullanın.

print(table.filter(ee.Filter.eq('country_na', 'Niger')).first());

Filtreleri analizinizde mümkün olduğunca erken kullanmanız gerektiğini unutmayın.

ee.Algorithms.If() kullanmaktan kaçının

Özellikle eşlenmiş bir işlevde dallanma mantığını uygulamak için ee.Algorithms.If() kullanmayın. Aşağıdaki örnekte gösterildiği gibi, ee.Algorithms.If() bellek yoğun olabilir ve önerilmemektedir:

If() kullanmayın!

var table = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');

// Do NOT do this!
var veryBad = table.map(function(f) {
  return ee.Algorithms.If({
    condition: ee.String(f.get('country_na')).compareTo('Chad').gt(0),
    trueCase: f,      // Do something.
    falseCase: null   // Do something else.
  });
}, true);
print(veryBad); // User memory limit exceeded.

// If() may evaluate both the true and false cases.

map() işlevinin ikinci bağımsız değişkeninin true olduğunu unutmayın. Bu, eşlenen işlevin null döndürebileceği ve bunların sonuç koleksiyonunda bırakılacağı anlamına gelir. Bu, If() olmadan yararlı olabilir ancak burada en kolay çözüm filtre kullanmaktır:

filter()'u kullanın.

print(table.filter(ee.Filter.eq('country_na', 'Chad')));

Bu eğitimde gösterildiği gibi, filtreleri kullanan işlevsel programlama yaklaşımı, bir koleksiyonun bazı öğelerine bir mantığı, koleksiyonun diğer öğelerine ise başka bir mantığı uygulamanın doğru yoludur.

reproject() kullanmaktan kaçının

Yeniden projelendirmeyi kesinlikle gerekli olmadığı sürece kullanmayın. reproject() kullanmanın bir nedeni, sonuçları istediğiniz analiz ölçeğinde inceleyebilmeniz için Code Editor hesaplamalarını belirli bir ölçekte gerçekleşmeye zorlamaktır. Sonraki örnekte, sıcak piksel yamaları hesaplanır ve her yamada piksel sayısı hesaplanır. Örneği çalıştırın ve yamalar arasından birini tıklayın. Yeniden projelendirilen veriler ile yeniden projelendirilmeyen veriler arasındaki piksel sayısının farklı olduğunu unutmayın.

var l8sr = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2');
var sf = ee.Geometry.Point([-122.405, 37.786]);
Map.centerObject(sf, 13);

// A reason to reproject - counting pixels and exploring interactively.
var image = l8sr.filterBounds(sf)
    .filterDate('2019-06-01', '2019-12-31')
    .first();

image = image.multiply(0.00341802).add(149);  // Apply scale factors.
Map.addLayer(image, {bands: ['ST_B10'], min: 280, max: 317}, 'image');

var hotspots = image.select('ST_B10').gt(317)
  .selfMask()
  .rename('hotspots');
var objectSize = hotspots.connectedPixelCount(256);

Map.addLayer(objectSize, {min: 1, max: 256}, 'Size No Reproject', false);

// Beware of reproject!  Don't zoom out on reprojected data.
var reprojected = objectSize.reproject(hotspots.projection());
Map.addLayer(reprojected, {min: 1, max: 256}, 'Size Reproject', false);

Tutarsızlığın nedeni, analiz ölçeğinin Kod Düzenleyici yakınlaştırma seviyesi tarafından ayarlanmasıdır. reproject() işlevini çağırarak Kod Düzenleyici yerine hesaplamanın ölçeğini siz belirlersiniz. reproject() değerini, bu dokümanda açıklanan nedenlerle çok dikkatli kullanın.

Önce filtreleme ve select()

Genel olarak, giriş koleksiyonlarını koleksiyonla başka bir işlem yapmadan önce zamana, konuma ve/veya meta verilere göre filtreleyin. Daha seçici filtreleri daha az seçici filtrelerden önce uygulayın. Mekansal ve/veya zamansal filtreler genellikle daha seçicidir. Örneğin, select() ve filter()'un önce map() uygulandığını unutmayın:

var images = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED');
var sf = ee.Geometry.Point([-122.463, 37.768]);

// Expensive function to reduce the neighborhood of an image.
var reduceFunction = function(image) {
  return image.reduceNeighborhood({
    reducer: ee.Reducer.mean(),
    kernel: ee.Kernel.square(4)
  });
};

var bands = ['B4', 'B3', 'B2'];
// Select and filter first!
var reasonableComputation = images
    .select(bands)
    .filterBounds(sf)
    .filterDate('2018-01-01', '2019-02-01')
    .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 1))
    .aside(print) // Useful for debugging.
    .map(reduceFunction)
    .reduce('mean')
    .rename(bands);
var viz = {bands: bands, min: 0, max: 10000};
Map.addLayer(reasonableComputation, viz, 'reasonableComputation');

mask() yerine updateMask() kullanın

updateMask() ile mask() arasındaki fark, ilkinin bağımsız değişkenin (yeni maske) ve mevcut resim maskesinin mantıksal and() işlemini yapması, mask()'un ise resim maskesini bağımsız değişkenle değiştirmesidir. İkinci yöntemin tehlikesi, piksellerin maskesini istemeden kaldırabilmenizdir. Bu örnekte amaç, yüksekliği 300 metre veya daha az olan pikselleri maskelemektir. Gördüğünüz gibi (yakınlaştırın), mask() kullanıldığında birçok piksel maskelenmez. Bu pikseller, ilgilenilen resme ait değildir:

var l8sr = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2');
var sf = ee.Geometry.Point([-122.40554461769182, 37.786807309873716]);
var aw3d30 = ee.Image('JAXA/ALOS/AW3D30_V1_1');

Map.centerObject(sf, 7);

var image = l8sr.filterBounds(sf)
    .filterDate('2019-06-01', '2019-12-31')
    .first();

image = image.multiply(0.0000275).subtract(0.2);  // Apply scale factors.
var vis = {bands: ['SR_B4', 'SR_B3', 'SR_B2'], min: 0, max: 0.3};
Map.addLayer(image, vis, 'image', false);

var mask = aw3d30.select('AVE').gt(300);
Map.addLayer(mask, {}, 'mask', false);

// NO!  Don't do this!
var badMask = image.mask(mask);
Map.addLayer(badMask, vis, 'badMask');

var goodMask = image.updateMask(mask);
Map.addLayer(goodMask, vis, 'goodMask', false);

Azaltıcıları birleştirme

Tek bir girişten (ör. bir resim bölgesi) birden fazla istatistik (ör. ortalama ve standart sapma) almanız gerekiyorsa azaltıcıları birleştirmek daha verimlidir. Örneğin, resim istatistiklerini almak için azaltıcıları aşağıdaki gibi birleştirin:

var image = ee.Image(
  'COPERNICUS/S2_HARMONIZED/20150821T111616_20160314T094808_T30UWU');

// Get mean and SD in every band by combining reducers.
var stats = image.reduceRegion({
  reducer: ee.Reducer.mean().combine({
    reducer2: ee.Reducer.stdDev(),
    sharedInputs: true
  }),
  geometry: ee.Geometry.Rectangle([-2.15, 48.55, -1.83, 48.72]),
  scale: 10,
  bestEffort: true // Use maxPixels if you care about scale.
});

print(stats);

// Extract means and SDs to images.
var meansImage = stats.toImage().select('.*_mean');
var sdsImage = stats.toImage().select('.*_stdDev');

Bu örnekte, ortalama azaltıcının standart sapma azaltıcıyla birleştirildiğini ve giriş piksellerinde tek bir geçişi etkinleştirmek için sharedInputs değerinin doğru olduğunu unutmayın. Çıkış sözlüğünde, azaltıcının adı bant adına eklenir. Ortalama ve SD görüntüleri almak için (ör. giriş görüntüsünü normalleştirmek için) değerleri bir resme dönüştürebilir ve örnekte gösterildiği gibi ortalamaları ve SD'leri tek tek ayıklamak için normal ifade kullanabilirsiniz.

Export hareketini kullanın

Kod Düzenleyici'de "Kullanıcı bellek sınırı aşıldı" veya "Hesaplama zaman aşımına uğradı" hatalarıyla sonuçlanan hesaplamalar, Export kullanılarak başarılı olabilir. Bunun nedeni, toplu sistemde (dışa aktarma işlemlerinin yürütüldüğü sistem) zaman aşımlarının daha uzun olması ve izin verilen bellek kullanımının daha fazla olmasıdır. (Hata ayıklama dokümanında ayrıntılı olarak açıklandığı gibi, önce denemek isteyebileceğiniz başka yaklaşımlar da vardır). Önceki örneğe devam edecek olursak sözlüğün hata döndürdüğünü varsayalım. Aşağıdaki gibi bir işlem yaparak sonuçları elde edebilirsiniz:

var link = '86836482971a35a5e735a17e93c23272';
Export.table.toDrive({
  collection: ee.FeatureCollection([ee.Feature(null, stats)]),
  description: 'exported_stats_demo_' + link,
  fileFormat: 'CSV'
});

Bağlantının, yeniden kullanılabilirlik için öğe adına yerleştirildiğini unutmayın. Ayrıca, toAsset öğesini dışa aktarmak istiyorsanız bir geometri sağlamanız gerektiğini unutmayın. Bu geometri, hesaplaması küçük ve ucuz olan resim merkezi gibi herhangi bir şey olabilir. (ör. ihtiyacınız yoksa karmaşık bir geometri kullanmayın).

Hesaplama zaman aşımına uğradı ve Çok fazla eşzamanlı toplama sorunlarını çözmek için Export kullanma örnekleri için hata ayıklama sayfasına bakın. Genel olarak dışa aktarma hakkında ayrıntılı bilgi için bu dokümanı inceleyin.

Klip oluşturmanız gerekmiyorsa clip() kullanmayın

clip() özelliğini gereksiz yere kullanmak hesaplama süresini artırır. Analiziniz için gerekli olmadığı sürece clip() kullanmayın. Emin değilseniz klip oluşturmayın. Klibin kötü kullanımına örnek:

Girişleri gereksiz yere kırpmayın.

var table = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
var l8sr = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2');

var belgium = table.filter(ee.Filter.eq('country_na', 'Belgium')).first();

// Do NOT clip unless you need to.
var unnecessaryClip = l8sr
    .select('SR_B4')                          // Good.
    .filterBounds(belgium.geometry())         // Good.
    .filterDate('2019-01-01', '2019-04-01')   // Good.
    .map(function(image) {
      return image.clip(belgium.geometry());  // NO! Bad! Not necessary.
    })
    .median()
    .reduceRegion({
      reducer: ee.Reducer.mean(),
      geometry: belgium.geometry(),
      scale: 30,
      maxPixels: 1e10,
    });
print(unnecessaryClip);

Bölge reduceRegion() çağrısında belirtildiği için giriş resimlerinin kırpılması tamamen atlanabilir:

Çıktı için bölgeyi belirtin.

var noClipNeeded = l8sr
    .select('SR_B4')                           // Good.
    .filterBounds(belgium.geometry())          // Good.
    .filterDate('2019-01-01', '2019-12-31') // Good.
    .median()
    .reduceRegion({
      reducer: ee.Reducer.mean(),
      geometry: belgium.geometry(), // Geometry is specified here.
      scale: 30,
      maxPixels: 1e10,
    });
print(noClipNeeded);

Bu hesaplama zaman aşımına uğrarsa Export bu örnekte gösterildiği gibi.

Karmaşık bir koleksiyonla klip oluşturmanız gerekiyorsa clipToCollection()

Bir şeyi kırpmanız gerekiyorsa ve kırpma için kullanmak istediğiniz geometriler bir koleksiyondaysa clipToCollection() simgesini kullanın:

var ecoregions = ee.FeatureCollection('RESOLVE/ECOREGIONS/2017');
var image = ee.Image('JAXA/ALOS/AW3D30_V1_1');

var complexCollection = ecoregions
    .filter(ee.Filter.eq('BIOME_NAME',
                         'Tropical & Subtropical Moist Broadleaf Forests'));
Map.addLayer(complexCollection, {}, 'complexCollection');

var clippedTheRightWay = image.select('AVE')
    .clipToCollection(complexCollection);
Map.addLayer(clippedTheRightWay, {}, 'clippedTheRightWay', false);

Bellek kullanımı daha yüksek olabilecek büyük ve/veya karmaşık koleksiyonlarda featureCollection.geometry() veya featureCollection.union() kullanmayın.

Bir azaltıcının bölgesi olarak karmaşık bir koleksiyon kullanmayın

Azaltıcı, FeatureCollection içinde birden fazla bölgeden gelen girişleri bir havuzda toplayacak şekilde bir uzamsal azaltma yapmanız gerekiyorsa azaltıcıya geometry girişi olarak featureCollection.geometry() sağlamazsınız. Bunun yerine, clipToCollection() ve koleksiyonun sınırlarını kapsayacak kadar büyük bir bölge kullanın. Örneğin:

var ecoregions = ee.FeatureCollection('RESOLVE/ECOREGIONS/2017');
var image = ee.Image('JAXA/ALOS/AW3D30_V1_1');

var complexCollection = ecoregions
    .filter(ee.Filter.eq('BIOME_NAME', 'Tropical & Subtropical Moist Broadleaf Forests'));

var clippedTheRightWay = image.select('AVE')
    .clipToCollection(complexCollection);
Map.addLayer(clippedTheRightWay, {}, 'clippedTheRightWay');

var reduction = clippedTheRightWay.reduceRegion({
  reducer: ee.Reducer.mean(),
  geometry: ee.Geometry.Rectangle({
    coords: [-179.9, -50, 179.9, 50],  // Almost global.
    geodesic: false
  }),
  scale: 30,
  maxPixels: 1e12
});
print(reduction); // If this times out, export it.

Sıfır olmayan bir errorMargin kullanın

Pahalı olabilecek geometri işlemleri için hesaplamanın gerekli hassasiyeti göz önüne alındığında mümkün olan en büyük hata payını kullanın. Hata payı, geometrilerle ilgili işlemler sırasında (ör. yeniden projeksiyon sırasında) izin verilen maksimum hatayı (metre cinsinden) belirtir. Küçük bir hata payı belirtmek, geometrilerin (koordinatlarla) yoğunlaştırılması gerekmesine neden olabilir. Bu işlem, bellek açısından yoğun olabilir. Hesabınız için mümkün olduğunca büyük bir hata payı belirtmeniz önerilir:

var ecoregions = ee.FeatureCollection('RESOLVE/ECOREGIONS/2017');

var complexCollection = ecoregions.limit(10);
Map.centerObject(complexCollection);
Map.addLayer(complexCollection);

var expensiveOps = complexCollection.map(function(f) {
  return f.buffer(10000, 200).bounds(200);
});
Map.addLayer(expensiveOps, {}, 'expensiveOps');

reduceToVectors() ile çok küçük bir ölçek kullanmayın

Bir rasteri vektöre dönüştürmek istiyorsanız uygun bir ölçek kullanın. Çok küçük bir ölçek belirtmek hesaplama maliyetini önemli ölçüde artırabilir. Ölçeği, gerekli hassasiyeti sağlayacak şekilde mümkün olduğunca yüksek ayarlayın. Örneğin, dünya üzerindeki kara kütlelerini temsil eden poligonları almak için:

var etopo = ee.Image('NOAA/NGDC/ETOPO1');

// Approximate land boundary.
var bounds = etopo.select(0).gt(-100);

// Non-geodesic polygon.
var almostGlobal = ee.Geometry.Polygon({
  coords: [[-180, -80], [180, -80], [180, 80], [-180, 80], [-180, -80]],
  geodesic: false
});
Map.addLayer(almostGlobal, {}, 'almostGlobal');

var vectors = bounds.selfMask().reduceToVectors({
  reducer: ee.Reducer.countEvery(),
  geometry: almostGlobal,
  // Set the scale to the maximum possible given
  // the required precision of the computation.
  scale: 50000,
});
Map.addLayer(vectors, {}, 'vectors');

Önceki örnekte, küresel azaltmalarda kullanılmak üzere jeodezik olmayan bir poligonun kullanıldığına dikkat edin.

reduceToVectors()reduceRegions() ile kullanmayın

reduceToVectors() tarafından döndürülen bir FeatureCollection değerini reduceRegions() için giriş olarak kullanmayın. Bunun yerine, arama yapmadan önce azaltmak istediğiniz bantları ekleyinreduceToVectors():

var etopo = ee.Image('NOAA/NGDC/ETOPO1');
var mod11a1 = ee.ImageCollection('MODIS/006/MOD11A1');

// Approximate land boundary.
var bounds = etopo.select(0).gt(-100);

// Non-geodesic polygon.
var almostGlobal = ee.Geometry.Polygon({
  coords: [[-180, -80], [180, -80], [180, 80], [-180, 80], [-180, -80]],
  geodesic: false
});

var lst = mod11a1.first().select(0);

var means = bounds.selfMask().addBands(lst).reduceToVectors({
  reducer: ee.Reducer.mean(),
  geometry: almostGlobal,
  scale: 1000,
  maxPixels: 1e10
});
print(means.limit(10));

Bir resmin piksellerini başka bir resmin bölgelerinde azaltmanın diğer yolları arasında reduceConnectedCommponents() ve/veya gruplandırıcı azaltıcılar yer alır.

Mahalle işlemleri için fastDistanceTransform()'ü kullanma

Bazı konvolüsyon işlemleri için fastDistanceTransform(), reduceNeighborhood() veya convolve()'den daha verimli olabilir. Örneğin, ikili girişlerde erozyon ve/veya genişleme yapmak için:

var aw3d30 = ee.Image('JAXA/ALOS/AW3D30_V1_1');

// Make a simple binary layer from a threshold on elevation.
var mask = aw3d30.select('AVE').gt(300);
Map.setCenter(-122.0703, 37.3872, 11);
Map.addLayer(mask, {}, 'mask');

// Distance in pixel units.
var distance = mask.fastDistanceTransform().sqrt();
// Threshold on distance (three pixels) for a dilation.
var dilation = distance.lt(3);
Map.addLayer(dilation, {}, 'dilation');

// Do the reverse for an erosion.
var notDistance = mask.not().fastDistanceTransform().sqrt();
var erosion = notDistance.gt(3);
Map.addLayer(erosion, {}, 'erosion');

reduceNeighborhood()'teki optimizasyonları kullanma

Bir topoloji gerçekleştirmeniz gerekiyorsa ve fastDistanceTransform() kullanamıyorsanız reduceNeighborhood()'teki optimizasyonları kullanın.

var l8raw = ee.ImageCollection('LANDSAT/LC08/C02/T1_RT');
var composite = ee.Algorithms.Landsat.simpleComposite(l8raw);

var bands = ['B4', 'B3', 'B2'];

var optimizedConvolution = composite.select(bands).reduceNeighborhood({
  reducer: ee.Reducer.mean(),
  kernel: ee.Kernel.square(3),
  optimization: 'boxcar' // Suitable optimization for mean.
}).rename(bands);

var viz = {bands: bands, min: 0, max: 72};
Map.setCenter(-122.0703, 37.3872, 11);
Map.addLayer(composite, viz, 'composite');
Map.addLayer(optimizedConvolution, viz, 'optimizedConvolution');

İhtiyacınız olandan daha fazla veri örneklemeyin

Eğitim veri kümenizin boyutunu gereksiz yere artırma dürtüsüne karşı koymalısınız. Eğitim verilerinin miktarını artırmak bazı durumlarda etkili bir makine öğrenimi stratejisi olsa da doğrulukta herhangi bir artış olmadan bilgi işlem maliyetini de artırabilir. (Eğitim veri kümesi boyutunun ne zaman artırılması gerektiğini öğrenmek için bu referans bölümüne bakın). Aşağıdaki örnekte, çok fazla eğitim verisi istemenin korkunç "Hesaplanan değer çok büyük" hatasına nasıl yol açabileceği gösterilmektedir:

Çok fazla veri örneklemeyin.

var l8raw = ee.ImageCollection('LANDSAT/LC08/C02/T1_RT');
var composite = ee.Algorithms.Landsat.simpleComposite(l8raw);
var labels = ee.FeatureCollection('projects/google/demo_landcover_labels');

// No!  Not necessary.  Don't do this:
labels = labels.map(function(f) { return f.buffer(100000, 1000); });

var bands = ['B2', 'B3', 'B4', 'B5', 'B6', 'B7'];

var training = composite.select(bands).sampleRegions({
  collection: labels,
  properties: ['landcover'],
  scale: 30
});

var classifier = ee.Classifier.smileCart().train({
  features: training,
  classProperty: 'landcover',
  inputProperties: bands
});
print(classifier.explain()); // Computed value is too large

Daha iyi bir yaklaşım, orta miktarda veri ile başlamak ve istenen doğruluğa ulaşıp ulaşamayacağınızı belirlemek için sınıflandırıcının hiper parametrelerini ayarlamaktır:

Hiperparametreleri ayarlayın.

var l8raw = ee.ImageCollection('LANDSAT/LC08/C02/T1_RT');
var composite = ee.Algorithms.Landsat.simpleComposite(l8raw);
var labels = ee.FeatureCollection('projects/google/demo_landcover_labels');

// Increase the data a little bit, possibly introducing noise.
labels = labels.map(function(f) { return f.buffer(100, 10); });

var bands = ['B2', 'B3', 'B4', 'B5', 'B6', 'B7'];

var data = composite.select(bands).sampleRegions({
  collection: labels,
  properties: ['landcover'],
  scale: 30
});

// Add a column of uniform random numbers called 'random'.
data = data.randomColumn();

// Partition into training and testing.
var training = data.filter(ee.Filter.lt('random', 0.5));
var testing = data.filter(ee.Filter.gte('random', 0.5));

// Tune the minLeafPopulation parameter.
var minLeafPops = ee.List.sequence(1, 10);

var accuracies = minLeafPops.map(function(p) {
  var classifier = ee.Classifier.smileCart({minLeafPopulation: p})
      .train({
        features: training,
        classProperty: 'landcover',
        inputProperties: bands
      });

  return testing
    .classify(classifier)
    .errorMatrix('landcover', 'classification')
    .accuracy();
});

print(ui.Chart.array.values({
  array: ee.Array(accuracies),
  axis: 0,
  xLabels: minLeafPops
}));

Bu örnekte sınıflandırıcı zaten çok doğru olduğundan çok fazla ayarlama yapmanız gerekmez. Gerekli doğruluğu sağlayan mümkün olan en küçük ağacı (yani en büyük minLeafPopulation) seçebilirsiniz.

Export ara sonuçlar

Hedefinizin, nispeten karmaşık bir hesaplanmış görüntüden örnek almak olduğunu varsayalım. Genellikle resmi ExporttoAsset(), dışa aktarılan resmi yükleyip örneklemek daha verimlidir. Örneğin:

var image = ee.Image('UMD/hansen/global_forest_change_2018_v1_6');
var geometry = ee.Geometry.Polygon(
    [[[-76.64069800085349, 5.511777325802095],
      [-76.64069800085349, -20.483938229362376],
      [-35.15632300085349, -20.483938229362376],
      [-35.15632300085349, 5.511777325802095]]], null, false);
var testRegion = ee.Geometry.Polygon(
    [[[-48.86726050085349, -3.0475996402515717],
      [-48.86726050085349, -3.9248707849303295],
      [-47.46101050085349, -3.9248707849303295],
      [-47.46101050085349, -3.0475996402515717]]], null, false);

// Forest loss in 2016, to stratify a sample.
var loss = image.select('lossyear');
var loss16 = loss.eq(16).rename('loss16');

// Scales and masks Landsat 8 surface reflectance images.
function prepSrL8(image) {
  var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0);
  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
  var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0);
  return image.addBands(opticalBands, null, true)
      .addBands(thermalBands, null, true)
      .updateMask(qaMask);
}

var collection = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
    .map(prepSrL8);

// Create two annual cloud-free composites.
var composite1 = collection.filterDate('2015-01-01', '2015-12-31').median();
var composite2 = collection.filterDate('2017-01-01', '2017-12-31').median();

// We want a strtatified sample of this stack.
var stack = composite1.addBands(composite2)
    .float(); // Export the smallest size possible.

// Export the image.  This block is commented because the export is complete.
/*
var link = '0b8023b0af6c1b0ac7b5be649b54db06'
var desc = 'Logistic_regression_stack_' + link;
Export.image.toAsset({
  image: stack,
  description: desc,
  assetId: desc,
  region: geometry,
  scale: 30,
  maxPixels: 1e10
})
*/

// Load the exported image.
var exportedStack = ee.Image(
  'projects/google/Logistic_regression_stack_0b8023b0af6c1b0ac7b5be649b54db06');

// Take a very small sample first, to debug.
var testSample = exportedStack.addBands(loss16).stratifiedSample({
  numPoints: 1,
  classBand: 'loss16',
  region: testRegion,
  scale: 30,
  geometries: true
});
print(testSample); // Check this in the console.

// Take a large sample.
var sample = exportedStack.addBands(loss16).stratifiedSample({
  numPoints: 10000,
  classBand: 'loss16',
  region: geometry,
  scale: 30,
});

// Export the large sample...

Bu örnekte, görüntülerin float olarak dışa aktarıldığını unutmayın. Kesinlikle gerekli olmadığı sürece verileri çift hassasiyetle dışa aktarmayın. Bu dışa aktarma işlemini yaparken, yeniden üretilebilirlik için dosya adına bir Code Editor bağlantısının (dışa aktarma işleminden hemen önce elde edilir) yerleştirildiğini unutmayın.

Dışa aktarma işlemi tamamlandıktan sonra öğeyi yeniden yükleyin ve öğeden örneklemeye devam edin. Hata ayıklama için önce çok küçük bir test alanında çok küçük bir örnek çalıştırıldığını unutmayın. Bu işlemin başarılı olduğu gösterildiğinde daha büyük bir örnek alın ve dışa aktarın. Bu tür büyük örneklerin genellikle dışa aktarılması gerekir. Bu tür örneklerin, önce dışa aktarılmadan etkileşimli olarak (örneğin, print() aracılığıyla) veya kullanılabilir (örneğin, sınıflandırıcıya giriş olarak) olmasını bekleyemezsiniz.

Birleştirme ve harita filtresi

Koleksiyonları zamana, konuma veya bazı meta veri özelliklerine göre birleştirmek istediğinizi varsayalım. Genellikle bu işlem en verimli şekilde bir birleştirme ile gerçekleştirilir. Aşağıdaki örnekte, Landsat 8 ve Sentinel-2 koleksiyonları arasında uzaysal-zamansal bir birleştirme yapılmaktadır:

var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED')
    .filterBounds(ee.Geometry.Point([-2.0205, 48.647]));

var l8 = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2');

var joined = ee.Join.saveAll('landsat').apply({
  primary: s2,
  secondary: l8,
  condition: ee.Filter.and(
    ee.Filter.maxDifference({
      difference: 1000 * 60 * 60 * 24, // One day in milliseconds
      leftField: 'system:time_start',
      rightField: 'system:time_start',
    }),
    ee.Filter.intersects({
      leftField: '.geo',
      rightField: '.geo',
    })
  )
});
print(joined);

Önce bir birleştirme denemeniz (gerekirse Export) gerekir. Ancak özellikle çok büyük koleksiyonlar söz konusu olduğunda, bazen bir map() içinde filter() de etkili olabilir.

var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED')
    .filterBounds(ee.Geometry.Point([-2.0205, 48.647]));

var l8 = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2');

var mappedFilter = s2.map(function(image) {
  var date = image.date();
  var landsat = l8
      .filterBounds(image.geometry())
      .filterDate(date.advance(-1, 'day'), date.advance(1, 'day'));
  // Return the input image with matching scenes in a property.
  return image.set({
    landsat: landsat,
    size: landsat.size()
  });
}).filter(ee.Filter.gt('size', 0));
print(mappedFilter);

reduceRegion(), reduceRegions() ve for döngüsü karşılaştırması

Giriş olarak çok büyük veya karmaşık bir FeatureCollection ile reduceRegions() çağrılması, korkulan "Hesaplanan değer çok büyük" hatasına neden olabilir. Olası çözümlerden biri, reduceRegion() yerine FeatureCollection üzerinde reduceRegion() eşlemektir. Başka bir olası çözüm de (korkmayın) for döngüsü kullanmaktır. Burada, burada ve burada açıklandığı gibi, Earth Engine'da bu işlemin yapılması kesinlikle önerilmez. Bununla birlikte, reduceRegion() büyük azaltmalar yapmak için bir for döngüsünde uygulanabilir.

Amacınızın, bir ImageCollection içindeki her resim için FeatureCollection içindeki her özellikteki piksellerin ortalamasını (veya herhangi bir istatistiği) elde etmek olduğunu varsayalım. Aşağıdaki örnekte, daha önce açıklanan üç yaklaşım karşılaştırılmıştır:

// Table of countries.
var countriesTable = ee.FeatureCollection("USDOS/LSIB_SIMPLE/2017");
// Time series of images.
var mod13a1 = ee.ImageCollection("MODIS/006/MOD13A1");

// MODIS vegetation indices (always use the most recent version).
var band = 'NDVI';
var imagery = mod13a1.select(band);

// Option 1: reduceRegions()
var testTable = countriesTable.limit(1); // Do this outside map()s and loops.
var data = imagery.map(function(image) {
  return image.reduceRegions({
    collection: testTable,
    reducer: ee.Reducer.mean(),
    scale: 500
  }).map(function(f) {
    return f.set({
      time: image.date().millis(),
      date: image.date().format()
    });
  });
}).flatten();
print(data.first());

// Option 2: mapped reduceRegion()
var data = countriesTable.map(function(feature) {
  return imagery.map(function(image) {
    return ee.Feature(feature.geometry().centroid(100),
        image.reduceRegion({
          reducer: ee.Reducer.mean(),
          geometry: feature.geometry(),
          scale: 500
        })).set({
          time: image.date().millis(),
          date: image.date().format()
        }).copyProperties(feature);
  });
}).flatten();
print(data.first());

// Option 3: for-loop (WATCH OUT!)
var size = countriesTable.size();
// print(size); // 312
var countriesList = countriesTable.toList(1); // Adjust size.
var data = ee.FeatureCollection([]); // Empty table.
for (var j=0; j<1; j++) { // Adjust size.
  var feature = ee.Feature(countriesList.get(j));
  // Convert ImageCollection > FeatureCollection
  var fc = ee.FeatureCollection(imagery.map(function(image) {
    return ee.Feature(feature.geometry().centroid(100),
        image.reduceRegion({
          reducer: ee.Reducer.mean(),
          geometry: feature.geometry(),
          scale: 500
        })).set({
          time: image.date().millis(),
          date: image.date().format()
        }).copyProperties(feature);
  }));
  data = data.merge(fc);
}
print(data.first());

Hata ayıklama amacıyla her koleksiyondaki first() öğesinin yazdırıldığını unutmayın. Sonuçların tamamının etkileşimli olarak sunulmasını beklememelisiniz. Export Ayrıca for döngülerinin son derece dikkatli bir şekilde ve yalnızca son çare olarak kullanılmasını öneririz. Son olarak, for döngüsü için giriş koleksiyonunun boyutunun manuel olarak alınması ve uygun konumlara sabit kodlanması gerekir. Bu konulardan herhangi biri size net gelmiyorsa for döngüsü kullanmayın.

Zaman içindeki komşular için ileri farkını kullanma

Zamansal olarak sıralanmış bir ImageCollection'iniz (ör. zaman serisi) olduğunu ve her resmi önceki (veya sonraki) resimle karşılaştırmak istediğinizi varsayalım. Bu amaç için iterate() kullanmak yerine dizi tabanlı ileri diferansiyel kullanmak daha verimli olabilir. Aşağıdaki örnekte, Sentinel-2 koleksiyonundaki kopyaları kaldırmak için bu yöntem kullanılır. Kopya, aynı yılın aynı gününe sahip görüntüler olarak tanımlanır:

var sentinel2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
var sf = ee.Geometry.Point([-122.47555371521855, 37.76884708376152]);
var s2 = sentinel2
    .filterBounds(sf)
    .filterDate('2018-01-01', '2019-12-31');

var withDoys = s2.map(function(image) {
  var ndvi = image.normalizedDifference(['B4', 'B8']).rename('ndvi');
  var date = image.date();
  var doy = date.getRelative('day', 'year');
  var time = image.metadata('system:time_start');
  var doyImage = ee.Image(doy)
      .rename('doy')
      .int();
  return ndvi.addBands(doyImage).addBands(time)
      .clip(image.geometry()); // Appropriate use of clip.
});

var array = withDoys.toArray();
var timeAxis = 0;
var bandAxis = 1;

var dedupe = function(array) {
  var time = array.arraySlice(bandAxis, -1);
  var sorted = array.arraySort(time);
  var doy = sorted.arraySlice(bandAxis, -2, -1);
  var left = doy.arraySlice(timeAxis, 1);
  var right = doy.arraySlice(timeAxis, 0, -1);
  var mask = ee.Image(ee.Array([[1]]))
      .arrayCat(left.neq(right), timeAxis);
  return array.arrayMask(mask);
};

var deduped = dedupe(array);

// Inspect these outputs to confirm that duplicates have been removed.
print(array.reduceRegion('first', sf, 10));
print(deduped.reduceRegion('first', sf, 10));

Yinelenen öğelerin kaldırıldığını doğrulamak için basılı koleksiyonları inceleyin.