source(here::here("R", "setup.R"))
paths <- sih_paths()
amsr <- load_sic_stack(paths$amsr_dir)
nsidc <- load_sic_stack(paths$nsidc_dir)5 Aligning and pairing the two products
To learn how the coarse product relates to the fine one, we need to compare them cell for cell on the same days. That requires three things: the two stacks on one grid, restricted to the dates they share, and then flattened into a table with one row per pixel per day. This chapter does all three and ends with a picture of the relationship the model will learn.
5.1 Putting both products on one grid
Both products already share a projection (EPSG:3976), so there is no reprojection to do. What differs is the grid: AMSR is 12.5 km and the Sea Ice Index is 25 km, and their extents differ slightly. We resample the coarse product onto the fine grid with bilinear interpolation, which is appropriate for a smoothly varying field like concentration. The fine AMSR grid is the target, so AMSR itself is never altered.
nsidc_aligned <- align_products(fine = amsr, coarse = nsidc)
rbind(
amsr = dim(amsr),
nsidc_raw = dim(nsidc),
nsidc_aligned = dim(nsidc_aligned)
) [,1] [,2] [,3]
amsr 175 128 91
nsidc_raw 91 68 91
nsidc_aligned 175 128 91
The aligned coarse stack now has the same number of rows, columns, and layers as AMSR, so the two line up cell for cell.
This step is worth a comment, because it is the one place where this tutorial departs from the published research notebook. In that notebook the line that resamples the coarse product to 12.5 km was commented out, so the network was trained and applied on the native 25 km grid and the “12.5 km” in the output name described alignment with the AMSR extent rather than the actual cell size. We make the resample explicit, so the harmonized product genuinely sits on the 12.5 km grid and the label is accurate. The transfer function the network learns is a per-pixel value mapping either way; resampling first simply means the output grid is the fine one.
5.3 Pairing pixel by pixel
Now flatten both stacks into a single table. build_pairs() reads the two stacks in the same cell-and-layer order, drops any pixel that is NA in either (land and missing data), and returns a data frame with one row per ocean pixel per shared day.
pairs <- build_pairs(fine = amsr_ov, coarse_aligned = nsidc_ov)
nrow(pairs)[1] 1790771
head(pairs) nsidc amsr
1 0 0
2 0 0
3 0 0
4 0 0
5 0 0
6 0 0
That is a large training table from a modest amount of data: even this single year of every-fourth-day sampling yields well over a million paired observations, because each day contributes thousands of ocean pixels.
5.4 The relationship to be learned
Plotting the paired values against each other shows why harmonization is needed and what the model has to do. If the two products agreed perfectly, every point would fall on the dashed one-to-one line. They do not: the coarse Sea Ice Index and the fine AMSR systematically disagree, especially at intermediate concentrations near the ice edge, where the coarser footprint mixes ice and open water differently.
ggplot(pairs, aes(nsidc, amsr)) +
geom_bin2d(bins = 80) +
scale_fill_gradientn(colours = c("#deebf7", "#6baed6", "#2171b5", "#08306b"),
trans = "log10", name = "pixels") +
geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = COLORS$accent_red) +
coord_equal(xlim = c(0, 1), ylim = c(0, 1)) +
labs(title = "Sea Ice Index vs AMSR, paired pixels",
x = "Sea Ice Index (25 km), SIC", y = "AMSR (12.5 km), SIC") +
theme_bri()
A quick numerical summary confirms the eye: the two products are strongly correlated but not identical, and the coarse product carries a small mean offset relative to the fine one.
c(correlation = cor(pairs$nsidc, pairs$amsr),
mean_bias = mean(pairs$nsidc - pairs$amsr),
rmse_raw = sqrt(mean((pairs$nsidc - pairs$amsr)^2)))correlation mean_bias rmse_raw
0.96820496 -0.06927442 0.14655607
The next chapter fits a model to this relationship and asks whether a neural network does better than a straight line.