این سند، شیوههای کدگذاری را توصیف میکند که هدف آنها به حداکثر رساندن شانس موفقیت برای محاسبات پیچیده یا گران قیمت Earth Engine است. روشهایی که در اینجا توضیح داده میشوند هم برای محاسبات تعاملی (مثلاً ویرایشگر کد) و هم برای محاسبات دستهای ( Export
) قابل اجرا هستند، اگرچه معمولاً محاسبات طولانی مدت باید در سیستم دستهای اجرا شوند.
از مخلوط کردن توابع و اشیاء مشتری با توابع و اشیاء سرور خودداری کنید
اشیاء سرور Earth Engine اشیایی با سازندههایی هستند که با ee
شروع میشوند (مثلا ee.Image
، ee.Reducer
) و هر روشی روی چنین اشیایی، توابع سرور هستند. هر شیئی که به این روش ساخته نشده باشد، یک شی مشتری است. اشیاء مشتری ممکن است از ویرایشگر کد (مانند Map
، Chart
) یا زبان جاوا اسکریپت (مانند Date
، Math
، []
، {}
) آمده باشند.
برای جلوگیری از رفتار ناخواسته، عملکردهای سرویس گیرنده و سرور را در اسکریپت خود با هم ترکیب نکنید، همانطور که در اینجا ، اینجا و اینجا بحث شد. برای توضیح عمیق مشتری در مقابل سرور در Earth Engine به این صفحه و/یا این آموزش مراجعه کنید. مثال زیر خطرات اختلاط عملکرد مشتری و سرور را نشان می دهد:
خطا - این کد کار نمی کند!
var table = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
// Won't work.
for(var i=0; i<table.size(); i++) {
print('No!');
}
آیا می توانید خطا را تشخیص دهید؟ توجه داشته باشید که table.size()
یک متد سرور بر روی یک شی سرور است و نمی تواند با عملکرد سمت کلاینت مانند <
شرطی استفاده شود.
موقعیتی که ممکن است بخواهید از حلقههای for استفاده کنید، تنظیمات رابط کاربری است، زیرا اشیاء و متدهای ui
Code Editor سمت کلاینت هستند. ( در مورد ایجاد رابط کاربری در Earth Engine بیشتر بیاموزید ). به عنوان مثال:
از توابع کلاینت برای راه اندازی رابط کاربری استفاده کنید.
var panel = ui.Panel();
for(var i=1; i<8; i++) {
panel.widgets().set(i, ui.Button('button ' + i))
}
print(panel);
برعکس، map()
یک تابع سرور است و عملکرد مشتری در داخل تابع ارسال شده به map()
کار نخواهد کرد. به عنوان مثال:
خطا - این کد کار نمی کند!
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.
});
برای انجام کاری برای هر عنصر در یک مجموعه، یک تابع را روی مجموعه map()
و یک ویژگی را set()
:
از map()
و set()
استفاده کنید!
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());
همچنین می توانید مجموعه را بر اساس ویژگی های محاسبه شده یا موجود filter()
print()
. توجه داشته باشید که نمی توانید مجموعه ای با 5000 عنصر بیشتر چاپ کنید. اگر خطای «کوئری مجموعه پس از انباشته شدن بیش از 5000 عنصر لغو شد» دریافت کردید، مجموعه را قبل از چاپ filter()
یا limit()
.
از تبدیل غیر ضروری به لیست خودداری کنید
مجموعهها در Earth Engine با استفاده از بهینهسازیهایی پردازش میشوند که با تبدیل مجموعه به نوع List
یا Array
شکسته میشوند. مگر اینکه به دسترسی تصادفی به عناصر مجموعه نیاز داشته باشید (یعنی باید عنصر i'th یک مجموعه را دریافت کنید)، از فیلترهای موجود در مجموعه برای دسترسی به عناصر مجموعه استفاده کنید. مثال زیر تفاوت بین تبدیل نوع (توصیه نمی شود) و فیلتر کردن (توصیه می شود) برای دسترسی به یک عنصر در یک مجموعه را نشان می دهد:
بی جهت به لیست تبدیل نکنید!
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.
توجه داشته باشید که با تبدیل غیر ضروری یک مجموعه به لیست به راحتی می توانید خطاها را راه اندازی کنید. راه امن تر استفاده از filter()
است:
از filter()
استفاده کنید!
print(table.filter(ee.Filter.eq('country_na', 'Niger')).first());
توجه داشته باشید که در تحلیل خود باید هر چه زودتر از فیلترها استفاده کنید .
اجتناب از ee.Algorithms.If()
از ee.Algorithms.If()
برای پیاده سازی منطق انشعاب، به خصوص در یک تابع نقشه برداری شده استفاده نکنید. همانطور که مثال زیر نشان می دهد، ee.Algorithms.If()
می تواند حافظه فشرده داشته باشد و توصیه نمی شود:
If()
استفاده نکنید!
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()
true
است. این بدان معنی است که تابع نگاشت ممکن است null ها را برگرداند و در مجموعه حاصل حذف شوند. این می تواند مفید باشد (بدون If()
)، اما در اینجا ساده ترین راه حل استفاده از فیلتر است:
از filter()
استفاده کنید!
print(table.filter(ee.Filter.eq('country_na', 'Chad')));
همانطور که در این آموزش نشان داده شده است، یک رویکرد برنامه نویسی تابعی با استفاده از فیلترها روش صحیحی برای اعمال یک منطق به برخی از عناصر یک مجموعه و یک منطق دیگر برای سایر عناصر مجموعه است.
اجتناب از reproject()
از reproject استفاده نکنید مگر اینکه کاملاً ضروری باشد. یکی از دلایلی که ممکن است بخواهید از reproject()
استفاده کنید این است که محاسبات ویرایشگر کد را مجبور کنید تا در یک مقیاس خاص اتفاق بیفتند تا بتوانید نتایج را در مقیاس تحلیل مورد نظر خود بررسی کنید. در مثال بعدی، تکه های پیکسل های داغ محاسبه شده و تعداد پیکسل ها در هر پچ محاسبه می شود. مثال را اجرا کنید و روی یکی از پچ ها کلیک کنید. توجه داشته باشید که تعداد پیکسلها بین دادههای بازپخششده با دادههایی که بازپخش نشدهاند متفاوت است.
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);
دلیل این اختلاف این است که مقیاس تجزیه و تحلیل توسط سطح زوم ویرایشگر کد تنظیم می شود. با فراخوانی reproject()
مقیاس محاسبات را به جای ویرایشگر کد تنظیم می کنید. به دلایلی که در این سند توضیح داده شده است، از reproject()
با احتیاط شدید استفاده کنید.
ابتدا فیلتر کنید و select()
به طور کلی، قبل از انجام هر کار دیگری با مجموعه، مجموعه های ورودی را بر اساس زمان، مکان و/یا ابرداده فیلتر کنید. فیلترهای انتخابی بیشتری را قبل از فیلترهای انتخابی کمتر اعمال کنید. فیلترهای مکانی و/یا زمانی اغلب انتخابی تر هستند. به عنوان مثال، توجه داشته باشید که select()
و filter()
قبل از map()
اعمال می شوند:
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 updateMask()
mask()
استفاده کنید
تفاوت بین updateMask()
و mask()
در این است که اولی یک and()
منطقی از آرگومان (ماسک جدید) و ماسک تصویر موجود را انجام می دهد در حالی که mask()
به سادگی ماسک تصویر را با آرگومان جایگزین می کند. خطر دومی این است که می توانید ناخواسته پیکسل ها را از بین ببرید. در این مثال، هدف این است که پیکسلهای کمتر یا مساوی از ارتفاع 300 متری را پنهان کنیم. همانطور که می بینید (کوچک کردن)، استفاده از mask()
باعث می شود بسیاری از پیکسل ها بدون ماسک شوند، پیکسل هایی که به تصویر مورد علاقه تعلق ندارند:
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);
کاهنده ها را ترکیب کنید
اگر به چندین آمار (مثلاً میانگین و انحراف استاندارد) از یک ورودی واحد (مثلاً یک منطقه تصویر) نیاز دارید، ترکیب کاهنده ها کارآمدتر است. به عنوان مثال، برای به دست آوردن آمار تصویر، کاهش دهنده ها را به صورت زیر ترکیب کنید:
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');
در این مثال، توجه داشته باشید که کاهشدهنده میانگین با کاهنده انحراف استاندارد ترکیب شده است و sharedInputs
برای فعال کردن یک عبور واحد از پیکسلهای ورودی درست است. در فرهنگ لغت خروجی، نام کاهش دهنده به نام باند اضافه شده است. برای به دست آوردن تصاویر میانگین و SD (به عنوان مثال برای عادی سازی تصویر ورودی)، می توانید مقادیر را به یک تصویر تبدیل کنید و از regexes برای استخراج میانگین ها و SD ها به صورت جداگانه همانطور که در مثال نشان داده شده است استفاده کنید.
از Export
استفاده کنید
برای محاسباتی که منجر به خطاهای «بیش از حد حافظه کاربر» یا «مدت زمان محاسبات به پایان رسیده» در ویرایشگر کد، ممکن است همین محاسبات با استفاده از Export
موفق شوند. این به این دلیل است که زمانهای زمانی طولانیتر است و ردپای مجاز حافظه در هنگام اجرا در سیستم دستهای (محلی که صادرات اجرا میشود) بزرگتر است. (رویکردهای دیگری وجود دارد که ممکن است بخواهید ابتدا همانطور که در سند اشکال زدایی توضیح داده شده است، آنها را امتحان کنید). در ادامه مثال قبل، فرض کنید که دیکشنری یک خطا را برگرداند. شما می توانید با انجام کارهایی مانند:
var link = '86836482971a35a5e735a17e93c23272';
Export.table.toDrive({
collection: ee.FeatureCollection([ee.Feature(null, stats)]),
description: 'exported_stats_demo_' + link,
fileFormat: 'CSV'
});
توجه داشته باشید که پیوند برای تکرارپذیری در نام دارایی تعبیه شده است. همچنین توجه داشته باشید که اگر میخواهید toAsset
صادر کنید، باید یک هندسه ارائه دهید، که میتواند هر چیزی باشد، به عنوان مثال مرکز تصویر، که محاسبه آن کوچک و ارزان است. (یعنی اگر به آن نیاز ندارید از هندسه پیچیده استفاده نکنید).
صفحه اشکال زدایی را برای نمونه هایی از استفاده از Export
برای حل کردن زمان محاسبات و تعداد زیادی از تجمعات همزمان ببینید. برای جزئیات بیشتر در مورد صادرات به طور کلی به این سند مراجعه کنید.
اگر نیازی به کلیپ ندارید، clip()
استفاده نکنید
استفاده غیرضروری از clip()
زمان محاسبه را افزایش می دهد. از clip()
اجتناب کنید مگر اینکه برای تحلیل شما ضروری باشد. اگر مطمئن نیستید، کلیپ نکنید. نمونه ای از استفاده بد از کلیپ:
ورودی ها را بی جهت کلیپ نکنید!
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);
برش دادن تصاویر ورودی را می توان به طور کامل نادیده گرفت، زیرا منطقه در فراخوانی reduceRegion()
مشخص شده است:
منطقه خروجی را مشخص کنید!
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);
اگر زمان این محاسبه تمام شد، آن را مانند این مثال Export
.
اگر نیاز به کلیپ با یک مجموعه پیچیده دارید، از clipToCollection()
استفاده کنید.
اگر واقعاً نیاز به کلیپ کردن چیزی دارید و هندسه هایی که می خواهید برای برش دادن استفاده کنید در یک مجموعه هستند، از clipToCollection()
استفاده کنید:
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);
از featureCollection.geometry()
یا featureCollection.union()
در مجموعه های بزرگ و/یا پیچیده استفاده نکنید، که می تواند حافظه فشرده تری داشته باشد.
از یک مجموعه پیچیده به عنوان منطقه کاهنده استفاده نکنید
اگر نیاز به کاهش فضایی دارید به طوری که کاهنده ورودیهای چند ناحیه را در یک FeatureCollection
جمع میکند، featureCollection.geometry()
به عنوان ورودی geometry
به کاهنده ارائه نکنید. درعوض، از clipToCollection()
و ناحیه ای به اندازه کافی بزرگ استفاده کنید تا محدوده های مجموعه را در برگیرد. به عنوان مثال:
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.
از یک errorMargin
غیر صفر استفاده کنید
برای عملیات هندسه احتمالاً گران قیمت، با توجه به دقت مورد نیاز محاسبات، از بزرگترین حاشیه خطای ممکن استفاده کنید. حاشیه خطا حداکثر خطای مجاز (بر حسب متر) مجاز در حین عملیات بر روی هندسه ها (مثلاً در هنگام بازپخش) را مشخص می کند. تعیین یک حاشیه خطای کوچک می تواند منجر به نیاز به متراکم کردن هندسه ها (با مختصات) شود که می تواند حافظه فشرده باشد. این تمرین خوب است که تا حد امکان یک حاشیه خطا برای محاسبات خود مشخص کنید:
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()
از یک مقیاس کوچک مسخره استفاده نکنید
اگر می خواهید شطرنجی را به بردار تبدیل کنید، از مقیاس مناسب استفاده کنید. تعیین یک مقیاس بسیار کوچک می تواند هزینه محاسبات را به میزان قابل توجهی افزایش دهد. تنظیم مقیاس تا حد امکان به دقت مورد نیاز است. به عنوان مثال، برای به دست آوردن چند ضلعی که نشان دهنده توده های زمین جهانی است:
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');
در مثال قبلی، به استفاده از یک چند ضلعی غیرژئودزیکی برای استفاده در کاهش های جهانی توجه کنید.
reduceToVectors()
با reduceRegions()
استفاده نکنید
از FeatureCollection
برگردانده شده توسط reduceToVectors()
به عنوان ورودی برای reduceRegions()
استفاده نکنید. در عوض، باندهایی را که می خواهید کاهش دهید، قبل از فراخوانی reduceToVectors()
اضافه کنید:
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));
توجه داشته باشید که روشهای دیگر کاهش پیکسلهای یک تصویر در ناحیههای تصویر دیگر عبارتند از ()reducedConnectedComponents و/یا کاهشدهندههای گروهبندی .
از fastDistanceTransform()
برای عملیات همسایگی استفاده کنید
برای برخی از عملیات کانولوشن، fastDistanceTransform()
ممکن است کارآمدتر از reduceNeighborhood()
یا convolve()
باشد. به عنوان مثال، برای انجام فرسایش و/یا اتساع ورودی های باینری:
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()
استفاده کنید
اگر نیاز به انجام یک کانولوشن دارید و نمیتوانید fastDistanceTransform()
استفاده کنید، از بهینهسازیهای reduceNeighborhood()
استفاده کنید.
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');
داده های بیشتری از آنچه نیاز دارید نمونه برداری نکنید
در برابر اصرار برای افزایش بی مورد اندازه مجموعه داده آموزشی خود مقاومت کنید. اگرچه افزایش مقدار داده های آموزشی در برخی شرایط یک استراتژی یادگیری ماشین موثر است، اما می تواند هزینه محاسباتی را نیز بدون افزایش دقت افزایش دهد. (برای درک زمان افزایش اندازه مجموعه داده آموزشی، به این مرجع مراجعه کنید). مثال زیر نشان می دهد که چگونه درخواست داده های آموزشی بیش از حد می تواند منجر به خطای ترسناک "مقدار محاسبه شده خیلی بزرگ است" شود:
داده های بیش از حد نمونه برداری نکنید!
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
روش بهتر این است که با حجم متوسطی از داده ها شروع کنید و پارامترهای فراپارامترهای طبقه بندی کننده را تنظیم کنید تا مشخص کنید آیا می توانید به دقت مورد نظر خود دست یابید یا خیر:
هیپرپارامترها را تنظیم کنید!
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
}));
در این مثال، طبقه بندی کننده در حال حاضر بسیار دقیق است، بنابراین تنظیم زیادی برای انجام دادن وجود ندارد. ممکن است بخواهید کوچکترین درخت ممکن (یعنی بزرگترین minLeafPopulation
) را انتخاب کنید که هنوز دقت لازم را داشته باشد.
Export
نتایج متوسط
فرض کنید هدف شما نمونه برداری از یک تصویر محاسبه شده نسبتاً پیچیده است. اغلب کارآمدتر است که تصویر را toAsset()
Export
، تصویر صادر شده را بارگیری کنید، سپس نمونه بگیرید. به عنوان مثال:
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...
در این مثال، توجه داشته باشید که تصاویر به صورت شناور صادر می شوند. با دقت مضاعف صادر نکنید مگر اینکه کاملاً ضروری باشد. هنگام انجام این صادرات، توجه داشته باشید که پیوند ویرایشگر کد (که بلافاصله قبل از صادرات به دست آمده است) برای قابلیت تکرار در نام فایل تعبیه شده است.
پس از تکمیل صادرات، دارایی را دوباره بارگیری کنید و نمونه برداری از آن را ادامه دهید. توجه داشته باشید که ابتدا یک نمونه بسیار کوچک در یک منطقه آزمایشی بسیار کوچک برای اشکال زدایی اجرا می شود. وقتی موفقیت آمیز بود، نمونه بزرگتری بگیرید و آن را صادر کنید. چنین نمونه های بزرگی معمولاً باید صادر شوند. انتظار نداشته باشید که چنین نمونه هایی به صورت تعاملی در دسترس باشند (مثلاً از طریق print()
) یا قابل استفاده (مثلاً به عنوان ورودی به طبقه بندی کننده) بدون اینکه ابتدا آنها را صادر کنید.
پیوستن به نقشه-فیلتر
فرض کنید می خواهید به مجموعه ها بر اساس زمان، مکان یا برخی ویژگی های ابرداده بپیوندید. به طور کلی، این کار به بهترین نحو با یک پیوستن انجام می شود. مثال زیر یک اتصال مکانی-زمانی بین مجموعه Landsat 8 و Sentinel-2 انجام می دهد:
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);
اگرچه ابتدا باید یک join را امتحان کنید (در صورت نیاز Export
)، گاهی اوقات یک filter()
در یک map()
نیز می تواند موثر باشد، به ویژه برای مجموعه های بسیار بزرگ.
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()
در مقابل for-loop
فراخوانی reduceRegions()
با یک FeatureCollection
بسیار بزرگ یا پیچیده به عنوان ورودی ممکن است منجر به خطای ترسناک "مقدار محاسبه شده خیلی بزرگ است" شود. یک راه حل بالقوه این است که به جای آن reduceRegion()
روی FeatureCollection
نگاشت کنید. راه حل بالقوه دیگر استفاده از حلقه for (gasp) است. اگرچه این کار در Earth Engine همانطور که در اینجا توضیح داده شده است، ممنوع است، در اینجا و اینجا ، reduceRegion()
می توان در یک حلقه for برای انجام کاهش های بزرگ پیاده سازی کرد.
فرض کنید هدف شما بدست آوردن میانگین پیکسل ها (یا هر آماری) در هر ویژگی در FeatureCollection
برای هر تصویر در ImageCollection
است. مثال زیر سه رویکردی را که قبلا توضیح داده شد مقایسه می کند:
// 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());
توجه داشته باشید که first()
چیزی از هر مجموعه برای اهداف اشکال زدایی چاپ می شود. شما نباید انتظار داشته باشید که نتیجه کامل به صورت تعاملی در دسترس باشد: شما باید Export
. همچنین توجه داشته باشید که حلقههای for-loop باید با احتیاط زیاد و تنها به عنوان آخرین راه حل استفاده شوند. در نهایت، حلقه for نیازمند به دست آوردن اندازه مجموعه ورودی و کدگذاری سخت آن در مکان های مناسب است. اگر هر یک از اینها برای شما مبهم به نظر می رسد، از حلقه for استفاده نکنید.
از تفاوت رو به جلو برای همسایگان در زمان استفاده کنید
فرض کنید یک ImageCollection
مرتب شده زمانی دارید (یعنی یک سری زمانی) و می خواهید هر تصویر را با تصویر قبلی (یا بعدی) مقایسه کنید. به جای استفاده از iterate()
برای این منظور، ممکن است استفاده از تفاوت رو به جلو مبتنی بر آرایه کارآمدتر باشد. مثال زیر از این روش برای حذف کپی مجموعه Sentinel-2 استفاده می کند، که در آن موارد تکراری به عنوان تصاویر با همان روز از سال تعریف می شوند:
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));
مجموعه های چاپ شده را بررسی کنید تا مطمئن شوید که موارد تکراری حذف شده اند.