Skip to content

Commit 7a4403e

Browse files
authored
guidance(#312): coarsening a coverage fraction is a mean over children, not MAX (#350)
Features whose native H3 resolution is coarser than the layer they are overlaid on (ACE/plant-richness at res-8, connectivity at res-9, against the res-10 conserved layer) were over-counted by every shipping model except glm-5.2: they coarsened the conserved weight with MAX over the res-10 children, scoring a whole parent cell as conserved whenever one child was. The guide documented MAX only for the reduction *within* a cell (across overlapping units), which is correct, and said nothing about the second reduction *across child cells* when changing resolution. One keyword, two operations. Adds a gated subsection to Problem 3 stating both reductions in order, with the child-count divisor spelled out (7 per resolution step, 49 for res-10 -> res-8) and a worked res-8 example, plus a one-line pointer from "Joining Different Resolutions" — the section that steers models into coarsening in the first place. Verified against the report gold: the pattern gives 21.17% for ACE BioRank 5 (gold 21.09, MAX rollup 32.68) and 22.30% for channelized connectivity (gold 22.65, MAX rollup 25.58).
1 parent a5d79aa commit 7a4403e

1 file changed

Lines changed: 28 additions & 0 deletions

File tree

h3-guide.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ Units: `'km'`, `'m'`, or `'rads'`.
120120

121121
**Always join by converting the finer (higher-numbered) dataset to the coarser resolution — never look for child columns on the coarser dataset.**
122122

123+
Pick the reducer for that conversion by what the value means: a measured quantity per cell rolls up with `SUM` or `AVG`, but a **coverage fraction** (the share of a cell covered by something) rolls up as the mean over the parent's child cells — see *Feature coarser than the overlay layer* under Problem 3.
124+
123125
### Step 1: Check for pre-computed parent columns (preferred)
124126

125127
Many fine-resolution datasets (e.g. GEBCO h8) already carry pre-computed parent columns (`h7`, `h6`, `h5`, ...). Use these directly — they are faster than calling `h3_cell_to_parent()` on every row. Check the schema first:
@@ -310,6 +312,32 @@ ORDER BY pct_conserved;
310312

311313
Asking about **one** class ("what percent of hardwood woodland is conserved") is the same query with `WHERE f.whr13num = <code>` — keep the `frac × weight` product. Joining to the *distinct conserved cells* instead (a `SEMI JOIN` on `(h10, h0)`) counts every partly-conserved cell as fully conserved and overstates the percentage.
312314

315+
**Feature coarser than the overlay layer — average the child cells.** *(Skip unless the feature's native resolution is coarser than the layer you are overlaying — e.g. a res-8 or res-9 feature against a res-10 coverage layer.)* Two reductions are needed, in this order. Across the overlapping units **within one fine cell**, take `MAX` (or `LEAST(SUM(w), 1)`), as above. Across the **child cells of a coarse parent**, take the mean — `SUM(w) / <children per parent>`, which is `7` for one resolution step (res-10 → res-9) and `49` for two (res-10 → res-8). The parent's weight is the share of the parent that is covered, so `MAX` across children is the wrong reducer there: it scores a whole parent as covered whenever a single child is.
316+
317+
```sql
318+
WITH cell_w AS ( -- one weight per res-10 cell: MAX across overlapping units
319+
SELECT h10, h8, h0, MAX((Final_g1_p + Final_g2_p) / 100.0) AS w
320+
FROM read_parquet('<conserved-areas hex>')
321+
GROUP BY h10, h8, h0
322+
),
323+
parent_w AS ( -- res-10 → res-8: mean over the 49 children
324+
SELECT h8, h0, SUM(w) / 49 AS w8
325+
FROM cell_w
326+
GROUP BY h8, h0
327+
),
328+
feat AS (
329+
SELECT DISTINCT h8, h0
330+
FROM read_parquet('<res-8 feature hex>')
331+
WHERE <feature filter>
332+
)
333+
SELECT 100 * SUM(COALESCE(p.w8, 0) * h3_cell_area(f.h8, 'km^2'))
334+
/ SUM(h3_cell_area(f.h8, 'km^2')) AS pct_conserved
335+
FROM feat f
336+
LEFT JOIN parent_w p USING (h8, h0);
337+
```
338+
339+
Group on `h3_cell_to_parent(h10, 8)` when the fine layer carries no `h8` column. When the coarse feature is itself a `hex-fractions` layer, keep its `frac` in the product as in the same-resolution case: `SUM(f.frac * COALESCE(p.w9, 0)) / SUM(f.frac)`. Matching the coarse feature against the fine layer's *distinct cells* treats a parent as fully covered when any one child is; the inflation grows with the number of children per parent.
340+
313341
**If you plan to mask this result against another hex dataset:** put the
314342
`SEMI JOIN` on the raw `read_parquet(...)` *before* `GROUP BY`, not in a
315343
CTE after it. Aggregation blocks DuckDB's dynamic partition pruning, so a

0 commit comments

Comments
 (0)