Context
#62 ran dft_rast_break_class() on the published seven-year IO LULC series of every group that has one (bulk_co_ff04, necr_ch_ff04, lnth_ch_ff04, kotl_bt_ff04). The #9 body named a "corrected series" — a per-pixel mode filter that keeps a sustained break and overwrites the flicker years — as worth building only if flicker turned out large everywhere. It did.
The numbers (data-raw/logs/break_class_groups/summary_groups.csv, one row per group)
| group |
changed_ha |
pct_sustained |
pct_endpoint |
pct_flicker |
stable_flicker_ha |
pct_stable_flicker_of_valid |
| bulk |
4625.0 |
19.7 |
36.4 |
44.0 |
3186.5 |
7.76 |
| necr |
5779.4 |
31.0 |
29.4 |
39.6 |
3828.3 |
9.15 |
| lnth |
1629.7 |
20.6 |
31.0 |
48.5 |
1576.7 |
9.85 |
| kotl |
3537.8 |
24.7 |
33.1 |
42.3 |
2235.5 |
3.22 |
Flicker is the largest category of the 2017 -> 2023 changed area in every group (39.6-48.5%), and a further 3.2-9.9% of every floodplain flickers while reading stable on the endpoints — 0.63 to 0.97 times the changed area itself (stable_flicker_over_changed), invisible to the two-epoch layer. Full reading in inst/notes/temporal-qa-groups.md.
What to build
A function that takes the same named list of classified years dft_rast_break_class() takes and returns a corrected series (a named list, same grid) in which:
- a pixel with
n_flips == 0 is unchanged;
- a pixel with
n_flips == 1 is unchanged — a clean switch is real by this leg's evidence, whatever its n_before / n_after (the caller thresholds, as everywhere else in the package);
- a pixel with
n_flips >= 2 is overwritten year by year with a mode that respects a sustained switch: not the whole-series mode (dft_rast_consensus() already does that and votes a real mid-window switch back to its old class), but a run-length rule — a run of at least run_min consecutive years of one class is kept, and any run shorter than that is relabelled to the neighbouring long run (the earlier one on a tie, or by a documented rule).
The two-epoch transition of the corrected series is then the deliverable a report can quote as change: dft_rast_transition(corrected, from = "2017", to = "2023"). Its changed area should land near the pct_sustained + pct_endpoint share of today's, and the note's Q1 table is the acceptance check — run the corrected series through dft_rast_break_class() again and the flicker row should be near zero.
Open decisions for the plan gate
Relates to #9, #62. Scale-test on the BULK pair before the PR per CLAUDE.md; the four groups' COGs are one data-raw/break_class_groups.R <group> away.
Context
#62 ran
dft_rast_break_class()on the published seven-year IO LULC series of every group that has one (bulk_co_ff04,necr_ch_ff04,lnth_ch_ff04,kotl_bt_ff04). The #9 body named a "corrected series" — a per-pixel mode filter that keeps a sustained break and overwrites the flicker years — as worth building only if flicker turned out large everywhere. It did.The numbers (
data-raw/logs/break_class_groups/summary_groups.csv, one row per group)Flicker is the largest category of the 2017 -> 2023 changed area in every group (39.6-48.5%), and a further 3.2-9.9% of every floodplain flickers while reading stable on the endpoints — 0.63 to 0.97 times the changed area itself (
stable_flicker_over_changed), invisible to the two-epoch layer. Full reading ininst/notes/temporal-qa-groups.md.What to build
A function that takes the same named list of classified years
dft_rast_break_class()takes and returns a corrected series (a named list, same grid) in which:n_flips == 0is unchanged;n_flips == 1is unchanged — a clean switch is real by this leg's evidence, whatever itsn_before/n_after(the caller thresholds, as everywhere else in the package);n_flips >= 2is overwritten year by year with a mode that respects a sustained switch: not the whole-series mode (dft_rast_consensus()already does that and votes a real mid-window switch back to its old class), but a run-length rule — a run of at leastrun_minconsecutive years of one class is kept, and any run shorter than that is relabelled to the neighbouring long run (the earlier one on a tie, or by a documented rule).The two-epoch transition of the corrected series is then the deliverable a report can quote as change:
dft_rast_transition(corrected, from = "2017", to = "2023"). Its changed area should land near thepct_sustained + pct_endpointshare of today's, and the note's Q1 table is the acceptance check — run the corrected series throughdft_rast_break_class()again and the flicker row should be near zero.Open decisions for the plan gate
run_mindefault: 2 matches the "sustained two years each side" reading used throughout Categorical breakpoint detection: sustained switch vs flicker across the annual class series (the temporal leg of change QA) #9 / Temporal QA across five watershed groups from the published annual series: does the BULK split generalise? #62.run_minat all (e.g.ABABABA): leave as is and flag, or mode. Either is defensible; the count of such pixels is the number to report.n_before == 1orn_after == 1, 29-36% of changed area) are touched. Temporal QA across five watershed groups from the published annual series: does the BULK split generalise? #62's Q2 says 2017 is the odd endpoint somewhat more often than 2023 in every group (ratio 1.09-1.25) and argues against moving the baseline; this function should leave clean switches alone and let the caller decide.Relates to #9, #62. Scale-test on the BULK pair before the PR per
CLAUDE.md; the four groups' COGs are onedata-raw/break_class_groups.R <group>away.