Context
dft_rast_consensus() (#8) computes per-pixel mode across classified rasters. This works well for filtering single-year misclassification noise, but has a fundamental limitation: it can't distinguish noise from real change.
The problem
A pixel that transitions Trees → Rangeland in 2022 looks like this across a 2021-2023 window:
| Year |
Class |
| 2021 |
Trees |
| 2022 |
Rangeland |
| 2023 |
Rangeland |
Mode = Trees (2/3 if tied, or Rangeland 2/3 depending on direction). The consensus either misses the real change or requires the post-change class to dominate the window before it registers.
A 3-year window needs 2+ years of the new class. A 5-year window needs 3+. Real change is slow to detect; noise filtering improves.
Possible approaches
Weighted mode
Give recent years more weight (e.g. 2023 counts 2x). Faster to detect real change while still smoothing older noise. Simple extension of current function.
Breakpoint / trajectory detection
Detect when a pixel changed, not just what it became. Trees/Trees/Trees/Rangeland/Rangeland = real change at year 4 with high confidence. Trees/Rangeland/Trees/Rangeland/Trees = noise.
Established algorithms for this exist but work on continuous spectral values, not categorical classes:
- BFAST (Breaks For Additive Season and Trend) — decomposes pixel time series into trend + seasonal + remainder, detects breakpoints. R package
bfast. Designed for deforestation monitoring.
- LandTrendr — fits piecewise linear segments to spectral history. Available in GEE + R.
Both require raw spectral bands (NDVI, NBR), not pre-classified maps. Using them in drift would mean a fundamentally different pipeline: fetch raw bands → build per-pixel time series → detect breakpoints → classify segments.
Categorical breakpoint detection
A simpler approach that works with classified inputs: scan each pixel's class sequence for sustained transitions. If a pixel holds class A for N consecutive years then switches to class B for M consecutive years, that's a real change with confidence proportional to min(N, M). This doesn't exist as a package and could be drift's niche — breakpoint detection for categorical time series.
Recommendation
Start with weighted mode as a parameter on dft_rast_consensus(). File categorical breakpoint detection as a separate future exploration if the weighted approach isn't sufficient.
Context
dft_rast_consensus()(#8) computes per-pixel mode across classified rasters. This works well for filtering single-year misclassification noise, but has a fundamental limitation: it can't distinguish noise from real change.The problem
A pixel that transitions Trees → Rangeland in 2022 looks like this across a 2021-2023 window:
Mode = Trees (2/3 if tied, or Rangeland 2/3 depending on direction). The consensus either misses the real change or requires the post-change class to dominate the window before it registers.
A 3-year window needs 2+ years of the new class. A 5-year window needs 3+. Real change is slow to detect; noise filtering improves.
Possible approaches
Weighted mode
Give recent years more weight (e.g. 2023 counts 2x). Faster to detect real change while still smoothing older noise. Simple extension of current function.
Breakpoint / trajectory detection
Detect when a pixel changed, not just what it became. Trees/Trees/Trees/Rangeland/Rangeland = real change at year 4 with high confidence. Trees/Rangeland/Trees/Rangeland/Trees = noise.
Established algorithms for this exist but work on continuous spectral values, not categorical classes:
bfast. Designed for deforestation monitoring.Both require raw spectral bands (NDVI, NBR), not pre-classified maps. Using them in drift would mean a fundamentally different pipeline: fetch raw bands → build per-pixel time series → detect breakpoints → classify segments.
Categorical breakpoint detection
A simpler approach that works with classified inputs: scan each pixel's class sequence for sustained transitions. If a pixel holds class A for N consecutive years then switches to class B for M consecutive years, that's a real change with confidence proportional to min(N, M). This doesn't exist as a package and could be drift's niche — breakpoint detection for categorical time series.
Recommendation
Start with weighted mode as a parameter on
dft_rast_consensus(). File categorical breakpoint detection as a separate future exploration if the weighted approach isn't sufficient.