
Temporal category of every row of a break-class summary
Source:R/dft_break_category.R
dft_break_category.RdCompose dft_rast_break_class()'s threshold-free measurements into the
labelled split the package and its consumers actually report: whether a
transition is a switch that held, a switch that turns on a single endpoint
observation, or a sequence that never settled — and, for the last of those,
whether the endpoints differ at all.
Arguments
- x
Either the list returned by
dft_rast_break_class()(its$summaryand$yearsare used) or a$summary-shaped data frame, in which caseyearsis required. Columnsfrom_class,to_class,statusandbreak_yearmust be present.- years
Integer vector of the observation years in the series. Ignored with a warning when
xis adft_rast_break_class()result, which carries its own.- rule
Character. The labelling rule to apply. Only
"v1"exists; it is recorded in the returnedrulecolumn so old output identifies the rule that produced it.
Value
x's summary with three columns appended, its class and row
order preserved ($summary arrives sorted by n_cells descending):
category— a factor with the five levels below, in that orderstrength—dft_break_strength(),NAoff a clean switchrule— the rule that producedcategory
Details
dft_rast_break_class() deliberately applies no threshold. Composing the
label is a judgement, so it lives here, in one place, under a version — see
rule. The numbers it rests on stay where they were measured.
NA propagates: a row whose pixels carry an NA in any interior year cannot
be scanned, arrives from dft_rast_break_class() with status NA, and is
labelled NA rather than refused. It is the only NA this function
produces, which is why an unlabelable class code is an error instead. The
bundled example series has no such row — mask a year, or classify a series
with a real NA in it, to see one.
A status value outside stable / break / flicker, or a break row
carrying no break_year, is an error rather than an NA: both mean the
frame did not come from dft_rast_break_class().
The "v1" rule
| 0 | stable | status == "stable" |
| 1 | break_sustained | a clean switch, strength >= 2 |
| 2 | break_endpoint | a clean switch, strength == 1 |
| 3 | unsettled | never settles, and the endpoints differ |
| 4 | stable_flicker | never settles, and the endpoints agree |
Levels 3 and 4 are the reason this function exists. A two-epoch comparison reports level 3 as change and cannot see level 4 at all, so they are used differently and must never be added together: on the Bulkley floodplain that is 2,032.9 ha against 3,186.5 ha, and summing them gives 7,811.5 ha of "changed" area where the published layer says 4,625.0 — a 69% overstatement.
strength is thresholded at 2 and the threshold is not an argument: a
free threshold would mean rule = "v1" no longer identifies the labelling,
which is the composition problem this function exists to end. Use
dft_break_strength() directly for another cut.
n_flips is not part of this function's surface. It stays on $breaks,
where it is measured.
See also
dft_rast_break_category() for the same rule at pixel grain;
dft_break_strength() for the number it thresholds;
dft_rast_break_class() for the measurements.
Examples
years <- 2017:2023
rasters <- lapply(years, function(yr) {
terra::rast(system.file("extdata", paste0("example_", yr, ".tif"),
package = "drift"))
})
names(rasters) <- years
res <- dft_rast_break_class(dft_rast_classify(rasters, source = "io-lulc"))
cat_tbl <- dft_break_category(res)
head(cat_tbl[c("from_class", "to_class", "category", "strength", "rule")])
#> # A tibble: 6 × 5
#> from_class to_class category strength rule
#> <chr> <chr> <fct> <int> <chr>
#> 1 Trees Trees stable NA v1
#> 2 Rangeland Rangeland stable NA v1
#> 3 Trees Trees stable_flicker NA v1
#> 4 Rangeland Rangeland stable_flicker NA v1
#> 5 Water Water stable NA v1
#> 6 Trees Rangeland unsettled NA v1
# the two populations a four-level vocabulary pools, kept apart
tapply(cat_tbl$area, cat_tbl$category, sum)
#> stable break_sustained break_endpoint unsettled stable_flicker
#> 61.17 10.98 10.40 12.65 27.91
# a summary read back from CSV needs the series stated
dft_break_category(as.data.frame(res$summary), years = years)$category[1:5]
#> [1] stable stable stable_flicker stable_flicker stable
#> Levels: stable break_sustained break_endpoint unsettled stable_flicker