sptrends began as infrastructure to reproduce True Significant Trends (TST):
Gutiérrez-Hernández, O., García, L.V. (2025). Uncovering true significant trends in global greening. Remote Sensing Applications: Society and Environment, 37, 101377. https://doi.org/10.1016/j.rsase.2024.101377
It has since grown into a general framework for robust statistical
inference of trends in gridded time series, of which TST is now one of
several implemented workflows – see
vignette("g-workflow-trends").
CRAN-requested fix (submission feedback on 1.5.8): addressed.
classify_moran() and
direction_map() from example text (CRAN flags
unexported-function calls even when commented out).fdr_bh(), fdr_by(),
fdr_bky() and plot_detection_comparison() to
internal.Full audit: functions, docs, website config, NEWS style, cran-comments.
benchmark.R/ validation.R left over from the
1.6 audit integration._pkgdown.yml entries for 5 functions exported in
1.5.3.cran-comments.md to reflect confirmed 1.6
results.MK/Theil-Sen against 5 external packages
(Kendall, trend, modifiedmk, robslopes, zyp); no real discrepancies
found.compute_anomalies() climatology/standardisation
alignment when start_position is not 1.read_ordered_stack() annual dates to use 1
January.evaluation_mask.SPTRENDS_TEST_PARALLEL=true set. No code changes.workflow_trends()’s example again: finds a valid
cell programmatically, keeping real (not synthetic) data.SPTRENDS_TEST_PARALLEL=true to run) to cut check
time.workflow_trends()’s example to a small region
to further cut check time.report = TRUE tests to cut
check time.CRAN-requested fixes (submission feedback on 1.5.2):
.GlobalEnv; uses withr
for seed handling.fdr_bh(), fdr_by(),
fdr_bky(), prepare_cmk_neighbourhood(), and
plot_detection_comparison().:::) calls from all examples.\dontrun{} with
if (interactive()).Found while fixing the above:
@inheritParams/duplicate
@return in fdr_bky() that corrupted the
installed help database.path=-based PNG-saving functions
(trend_histograms(), trend_maps(),
fdr_pvalue_histogram(),
fdr_significance_maps(),
fdr_comparison_barplot(),
fdr_threshold_plot(), fdr_direction_plot(),
prewhiten_histograms(), prewhiten_maps(),
slope_map(), spatial_autocorrelation()’s own
plotting helpers, plot_detection_comparison(), and the
sptrends_simulation/ sptrends_benchmark
plot() methods), which silently produced no file at all on
Debian/Linux (though not on Windows) – the internal helper relied on
grDevices::dev.copy(), which snapshots whatever device
happens to already be active, and produces nothing in headless/batch
environments with no active device. Now opens its own PNG device first
and draws directly into it, which works identically regardless of
platform or interactivity.Resubmission of 1.5 to CRAN, with no code changes – version bump required to avoid a filename collision on CRAN’s own incoming server from an earlier submission attempt.
read_ordered_stack() now supports explicit declaration
of a series’ temporal structure (files, time,
cycle_type, start, end,
time_anchor), as an alternative to automatic detection from
file names – recommended whenever the series is not simply annual. Eight
unambiguous calendar conventions are supported: "annual",
"monthly", "16-day",
"semimonthly", "10-day", "8-day",
"weekly", and "daily", all built with real
calendar arithmetic (leap years included).compute_anomalies()’s own cycle_type now
uses the same vocabulary as read_ordered_stack()’s
("monthly", "16-day",
"semimonthly", "10-day", "8-day",
"weekly"), so a value already used to read a stack can be
reused directly here. "annual" and "daily" are
deliberately not included – see ?compute_anomalies for
why.compute_anomalies() now validates cycle
fully (must be one finite integer >= 2), instead of only
checking cycle < 2.trend_test(method = "CMK") now supports
connectivity = "rook" as a genuine, working option,
alongside the default "queen".ConMK external validation note to
README.md.ConMK: the base statistic matches to
floating-point precision, and the continuity = TRUE option
reproduces ConMK’s own p-values exactly at the specific
edge case (Sm == 0) where the two implementations would
otherwise be expected to diverge.benchmark_methods() now supports scenarios with
different argument sets.sim_trend_stack()
arguments.benchmark_methods()’s slope and
prewhitening stages.Passed a comprehensive external code audit; several improvements resulted.
sim_trend_stack()’s
ar1 and noise_sd arguments.README.md and
DESCRIPTION.fdr_direction_summary() now supports the
"BY" method.NaN/NA consistency in
replicate-aggregated summaries.workflow_tst()/workflow_rta()’s return value
and documented a known limitation with irregular time spacing.cran-comments.md.BY in the FDR
vignette.NEWS.md history
(0.96.4-0.97.1) too: shorter entries.NEWS.md from 1.0 onward: shorter,
improvement-focused entries.workflow_tst()
directly.read_ordered_stack() now shows live reading
progress.summary() on workflow results now leads with the
FDR-corrected result, with uncorrected stats shown as diagnostic
context.summary() table in the package.workflow_tst()/workflow_rta() plots now
show their own title.tst-methods.R,
rta.R and workflow_trends-methods.R.README.md.R CMD check.read_netcdf_stack().fdr_correction() and
refined message formatting.direction_map() gained an optional slope
argument: trend direction can now be derived from the slope estimator’s
own sign. workflow_trends(), workflow_tst()
and workflow_rta() use this automatically when a slope
result is available.README.md: official citation, author
affiliations, reordered examples and modules.vignettes/a-getting-started.Rmd: clarified TST
origin, added references, reworked “Common mistakes”.URL/BugReports to
DESCRIPTION.inst/WORDLIST.Last pre-release (beta) version. sptrends 1.0 is the first stable release.
sim_trend_stack(trend_shape = "block") ignoring
signal_location/signal_size/signal_angle.sim_trend_stack() and
compare_detections() for calibrated spatiotemporal
simulation and known-truth evaluation. Added
simulation_design(), benchmark_methods(), and
benchmark_summary() with unified S3 reporting and
scenario-performance graphics.fdr_correction() example to show BH, BKY
and BY explicitly, and listed additional CRAN packages used as external
validation references.Function type within the parsed description instead of
relying on internal Rd ordering.Function type placement across the
remaining documented helpers.testthat versions.devtools::check() for 0.91.5
itself: 0 errors, 0 warnings, 0 notes.fdr.R/workflow_trends.R broke PDF
manual generation. Replaced with \eqn{\alpha}/plain
text.fdr_correction().smooth_radius) from spatial
intensity (spatial_rho) in sim_trend_stack(),
preserving the established default output.compare_detections() metric in Usage
while keeping FWER opt-in for replicated validation.Typical use paths to every exported
function and distinguished single-run from replicated benchmarking..lintr so
lintr::lint_package() and the lintr stage of
goodpractice::gp() can read the project configuration.terra, while retaining custom regular
expressions for other GDAL-readable formats.q (BH, BKY and BY)
from local FWER control at alpha (permutation maxT) in the
reserved spatial-autocorrelation interface.spatial_autocorrelation(), preserving the global result
while reserving a stable interface for independently validated local
statistics.none, BH, BKY,
BY and permutation-based maxT); global analyses reject
non-none adjustments because they contain only one
test.spatial_autocorrelation() as a general spatial
diagnostic for environmental variables, residuals, coefficients and
inferential fields; FDR-assumption assessment is now presented only as
one qualified application.workflow_trends(); the temporary pre-release
alias and compatibility class were removed.Z = 0, p = 1.n_cores <= 1 while retaining strict positive-integer
validation in exported workflows.NA cases.inst/benchmarks/scalability.R for reproducible
timing and memory-proxy measurements.ties = TRUE:
each cell now contributes its own tie-corrected VarS, and
every cross-covariance uses the corresponding pair of variances. The
default no-ties calculation is unchanged.Sm,
VarSm, and p.spelling::spell_check_package() to
inst/WORDLIST.inst/validation/.rkt comparison for independent
validation of CMK’s regional score aggregation.continuity to CMK: FALSE follows the
published equations; TRUE provides compatibility with
ConMK’s convention.VCTFPW to use the published variance-ratio
correction and 95% lag-1-autocorrelation gate. Cells below the gate
remain unchanged, the first time step is retained, and a
Modified diagnostic is returned.SpatRaster.inspect_ts_cell().inspect_ts_cell() no longer draws a shaded confidence
band; the interval is now text-only in the legend."RM") to
slope_estimator()."RM" validation to
workflow_trends().workflow_trends() for user-defined analytical
workflows."BY") to
fdr_correction().MMK to trend_test().TFPW_Z and VCTFPW to
prewhiten().prewhiten(method = "TFPW_WS").prewhiten(method = "PW").method values to
uppercase.url fields from
inst/CITATION/CITATION.cff, keeping only
doi. Both publisher URLs returned 403 to CRAN’s own
automated checker, a known behaviour toward bots, not a dead link.expect_s4_class() on an S3
object.\donttest{} wrap;
confirmed a false alarm (an earlier grep had missed the closing
brace).trend_test()
and spatial_autocorrelation(). Both previously recomputed
the identical structure independently; now computed once and reusable
via a new precomputed_neighbourhood argument.workflow_tst()/workflow_rta() gain a
shared n_cores, reusing one cluster across their own
internal parallel steps instead of building and tearing down a separate
one for each.compare_detections() gains "fwer"
(family-wise error rate) as a requestable metric, only meaningful with
replicates = TRUE.summary.compare_detections()/..._replicates()
picking the wrong “best” method for FPR/FDR:
both used which.max() unconditionally, but lower is better
for these two error-rate metrics.FDR (a single run’s
realised proportion) and FDR_mean (the actual rate
estimate, averaged across replicates), and added the Benjamini &
Hochberg (1995) reference to compare_detections()’s own
citation list.method = "ols" to trend_test().
Provides the classical parametric alternative to CMK/MK, verified
against stats::lm().slope_estimator()’s own direction map,
histogram, and bar chart via report = TRUE and
plot(which = ...). These reports previously existed only
inside the full workflow_tst()/ workflow_rta()
pipeline, not from slope_estimator() directly.trend_test()’s and
slope_estimator()’s own returned S3 classes
("cmk" to "trend_test", "theil"
to "slope"). Reflects that both functions now cover more
than one method each.fdr_bky()’s variant argument to
implementation, and its "definition6" value to
"original". The old names did not say what they were a
variant/implementation of.inspect_ts_cell()’s type argument
to selection_type. Avoided ambiguity with
graphics::plot()’s own unrelated type argument
used nearby.contextual_mk.R to trend_test.R,
theilsen.R to slope_estimator.R) to match the
generic, multi-method scope their own functions and classes already had
after the renames above.fill_range = TRUE was missing from 5
terra::plot() calls, so the highest-magnitude cells
rendered blank instead of strongly coloured.workflow_tst()/workflow_rta()’s own
progress messages always saying “Contextual Mann-Kendall” regardless of
the actual method used.covr,
including 4 regressions introduced by adding
method = "ols"; the package reached 100% coverage except
one line-group left deliberately untested (unreachable without unsafely
mocking a base R function).lintr::lint_package() for the first time. Of ~985
findings, fixed the 2 genuine issues found and recorded the rest as
deliberate package conventions (short variable names mirroring published
notation, a fixed-width indentation style) in a new .lintr
file, so future runs do not re-surface the same false positives.trend_test(), slope_estimator(),
prewhiten(), fdr_correction(),
spatial_autocorrelation(),
read_ordered_stack(), compute_anomalies(),
compare_detections(), workflow_tst(),
workflow_rta(), inspect_ts_cell(), and the
three shared S3 generics) for a consistent structure and to lead with
general purpose rather than the specific methods available today.trend_test()’s own title (still describing
only two of its three methods) and added a missing citation (Douglas,
Vogel & Kroll, 2000) for the RAMK connection behind CMK’s own
variance correction.?sptrends’s own overview.R CMD check NOTEs from
CITATION.cff living at the package root: excluded it via
.Rbuildignore (it is meant for GitHub/Zenodo, not R’s own
citation() mechanism, and does not need to be part of the
built package).compute_anomalies()’s own example in
\donttest{} (6.17s, over the 5-second threshold).CITATION.cff at the package root, with both
authors, both published references, and the licence.inst/CITATION still referring to the old
tst()/rta() function names after they were
renamed.verbose = TRUE messaging to
inspect_ts_cell(), matching every other function’s own
convention.summarise_replicates() into
compare_detections()’s own replicates = TRUE
argument. One function instead of two for the same task.tst() and rta() renamed to
workflow_tst() and workflow_rta(). Clarifies
these are complete, opinionated workflows, not the only way to run each
analytical step.trend_test(), slope_estimator(), and
prewhiten() now fail fast with an informative error on
invalid input shapes, instead of a cryptic error partway through
computation.spatial_autocorrelation() now errors informatively on
two previously-confusing invalid input cases.sim_trend_stack(), including a
division-by-zero, found by new edge-case tests written for it,
trend_test(), and slope_estimator().devtools::check(remote = TRUE).R CMD check WARNING and a separate ERROR,
both in compute_anomalies()’s own documentation.verbose = addition, found running
test-external-validation.R (a new suite comparing sptrends’
own core statistics against independent reference implementations).@examples block and a malformed
@section title.DESCRIPTION’s own description no longer repeats the package
name.\donttest{}.goodpractice::gp()
and lintr::lint_package(); addressed genuine findings.NEWS.md formatting inconsistency found and
corrected across many earlier entries.covr::zero_coverage(), and removed a small amount of dead
code in prewhiten.R found during that same review.terra::values() matrix-mode usage in two tests, a
genuine bug in a hand-computed Theil-Sen reference implementation, and a
named-vs-unnamed value mismatch in expect_equal().moran_permutation_test() to
spatial_autocorrelation() rename had been left half-done –
neither the old nor the new name was actually callable from outside the
package, and fdr_correction()’s own
moran_check = TRUE silently called the missing old name
internally. Fixed throughout.moran_permutation_test() generalised into
spatial_autocorrelation(), gaining a second method
(method = "getis_ord", global Getis-Ord General G)
alongside the existing Moran’s I. Follows the same method =
pattern already used elsewhere in the package; verified numerically
against the direct definition before being added. Returned field names
changed to be generic ($statistic/ $null_dist,
were $I/$I_null) – breaking change for
code reading the old field names.sim_trend_stack() gains structural-break simulation
(break_type = "mean"/"slope"), composing
independently from the existing monotonic-trend simulation, with its own
ground truth (true_break/break_time) for
validating a future change-point method – none is implemented yet.inspect_ts_cell() gains
slope_method = "ols", matching
slope_estimator()’s own second method; the confidence
band’s “bowtie” shape was checked and confirmed correct (standard
linear-trend theory), not a bug.slope_estimator() gains method = "ols", a
fast closed-form alternative to the default Theil-Sen, not a replacement
for it (not robust to outliers the way Theil-Sen is).prewhiten() gains method = "yue_pilon"
(trend-free prewhitening) alongside the default wang_swail.
Loses one time step (classic lag-1 differencing) and has a different
diagnostics structure. Fixed three real integration bugs found while
adding it –
print()/summary()/plot(),
tst()’s own print(), and
inspect_ts_cell() all hard-assumed the other method’s own
structure and would have errored on a yue_pilon
result.read_ordered_stack() now stores detected years as real
terra::time() metadata; inspect_ts_cell()’s
x-axis uses them automatically. Found and fixed a related bug while
doing this: the prewhitened panel misaligned t by one step
for yue_pilon’s own shorter output, silently, with no
visible symptom.terra::values(x, mat = FALSE)[, 1] errors on a single-layer
raster (already a flat vector, not a matrix) – affected
prewhiten(method = "yue_pilon") entirely (every call
errored) and two "ols" tests.direction_map() made internal – the FDR-corrected and
uncorrected direction are the same computation with an optional filter,
not two things needing separate public entry points. Breaking:
no longer exported; use
plot(x, which = "direction") instead.goodpractice::gp() report; one genuine fix
(a missing add = TRUE on an on.exit() call),
the rest confirmed as tool limitations.contextual_mann_kendall(),
theil_sen_slope(), and wang_swail_prewhiten()
renamed to trend_test(), slope_estimator(),
and prewhiten(), each gaining a method =
argument ahead of a planned second method per function.
Breaking: update any direct calls to the old names, and
neighbourhood = TRUE/FALSE to
method = "cmk"/"mk". Fixed a real naming
collision caught during the rename (tst()’s own
prewhiten flag argument shadowed the new function of the
same name).read_stack.R
gaps confirmed as unreachable defensive branches or an
ncdf4-only test.plot() on "tst"/"rta" results
gains 8 new which views
(slope_map/slope_direction/slope_hist/slope_bar,
pvalue_map/pvalue_significance/pvalue_hist/pvalue_bar),
all uncorrected diagnostics, not a final result. Considered and rejected
a bigger redesign (separate generics) first, since it would not
generalise cleanly across every classed object type this package
returns.workflow_summary()/summary(x, which = "workflow"),
added in the same release, removed again immediately on reflection – it
competed with summary()’s own simpler default rather than
sitting alongside it. No replacement;
x$timing has the same data.inspect_ts_cell() gains show_neighbours: a
small-multiples grid of each aggregated neighbour cell’s own fit, drawn
in a separate window, answering whether the clicked cell’s trend is
representative of its neighbourhood.covr::zero_coverage(); documented two
read_stack.R branches as genuinely unreachable rather than
chased with contrived tests.method_citation() removed entirely – redundant with
each function’s own @references; fixed a real bug this
surfaced along the way (duplicated, driftable citation text in
workflow_summary()). No replacement; see
each function’s own help page.classify_moran() folded into
print()/summary() of a "moran"
object; no longer exported separately.workflow_summary() folded into
summary(x, which = "workflow") for
tst()/rta() results (later removed again in
0.45.1).fdr_direction_map() renamed to
direction_map() and moved out of fdr.R into
its own file – it isn’t itself an FDR method, it combines a trend test’s
direction with an FDR result. Breaking: rename any direct
calls.prepare_cmk_neighbourhood() no longer exported – a
real, acknowledged capability loss for batch-processing many rasters
sharing one grid geometry (no longer precomputable without
:::).classify_moran() no longer exported; its category now
shown automatically in print()/summary() of a
"moran" object.prepare_cmk_neighbourhood()’s batch-processing use case
now demonstrated in examples and a vignette, not only described.test-sptrends-methods.R
checked S3 methods via getNamespaceExports(), which never
reflects S3 methods registered the standard way – fixed to use
getS3method(), the actually-correct check.print.sptrends()/summary.sptrends()/plot.sptrends()
now dispatch by naming convention (.print_<class>()
lookup) instead of a hardcoded switch() – adding a future
class no longer requires editing all three shared generics.print()/summary()/plot() methods
(one per class) consolidated into the 3 shared generics above, mirroring
terra’s own single-entry-point convention.
Breaking: code calling one of the 18 old method names
directly (not via generic dispatch) breaks; ordinary
print(x)/summary(x)/plot(x) usage
is unaffected. Exported function count: 43 to 28.fdr_bh()/fdr_bky() no longer exported (use
fdr_correction(method = ...));
moran_permutation_test() now returns a classed object with
its own print()/summary()/plot(),
folding in two previously separate reporting functions;
compare_detections() likewise gains a class and a genuinely
new summary(). Exported function count: 28 to 23.R CMD check example errors: 15 internal
functions called themselves by bare name in their own
@examples, invisible in devtools::test() but
not under R CMD check’s external-user context. Prefixed
with sptrends:::.trend_maps(), fdr_summary(), and others) no
longer exported – fully covered by the
print()/summary()/plot() methods
added in earlier stages. Breaking: switch to the equivalent S3
method (see each function’s own docs for the exact mapping).
Exported function count: 58 to 43.contextual_mann_kendall()
now returns a classed object
(list(stats = <raster>, neighbourhood = <logical>))
instead of a bare raster, with its own
print()/summary()/plot().
Breaking: update trend$p to
trend$stats$p.wang_swail_prewhiten(),
fdr_correction(), and theil_sen_slope() now
return classed objects with their own
print()/summary()/plot(),
matching tst()/rta()’s own pattern.
theil_sen_slope()’s own change is breaking (was a bare
raster, now list(slope = ..., smoothed = ...)). A
deliberate 5-stage redesign, done before the associated papers are
published rather than after.R/ and all vignettes brought under the 80-character
line-length limit (59 lines fixed), verified line by line, not just
visually diffed – caught a real sprintf() mistake
introduced during the cleanup itself before it reached the codebase.
Test files deliberately left alone (developer-facing only, high
volume).goodpractice::gp() cleanup, low-risk items only:
stop(paste0()) replaced with stop()’s own
concatenation, fixed = TRUE added to plain-string
grepl() calls, several test assertions replaced with more
specific equivalents, one real broken URL fixed in
README.md.wang_swail_prewhiten()/compute_anomalies()
from “Core”/“Support” – documentation/organisation only, no behavioural
change.tst() no longer smooths its
Theil-Sen slope by default, matching rta()’s own default
and removing an unprincipled asymmetry (neither published method
includes this smoothing). Pass
theil_sen_args = list(smooth_neighbourhood = TRUE) to keep
the old behaviour.smooth_neighbourhood = TRUE, and
both plot(which = "slope") methods) used
terra::focal()’s own default
na.policy = "all", letting a non-significant or no-data
cell get painted with a neighbour’s colour/value. Fixed to
"omit" in all three call sites, factored into one shared
helper.expect_s3_class() does not
accept an info argument (unlike
expect_error()); removed it from two new sweep tests.rta() gained
summary.rta()/plot.rta(), closing an asymmetry
with tst().tst()
across 12 argument combinations, rta() across 4).covr::zero_coverage() across several reporting
functions.inspect_ts_cell()’s interactive dispatch,
reversing an earlier “this is the coverage ceiling” conclusion, by
mocking terra::click()/terra::draw() the same
way detectCores() was already mocked elsewhere.R/method_citation.R’s own
string literals (accented names), caught by R CMD check’s
portability check – replaced with Unicode escapes.tst()/rta() called
trend_summary() a second, unconditional time with no way to
suppress its own printed output, so report = FALSE never
achieved full silence and report = TRUE printed the summary
twice. Added verbose = to
trend_summary().No new statistical methodology in this release – architecture, API and documentation work only.
tst()/rta() now record per-step timing in
a new $timing list.workflow_summary(): a Step/Method/Reference/Time
table for a tst()/rta() result (later folded
into summary(), then removed – see later entries).method_citation(): looks up a citation by short
method name (later removed as redundant – see 0.45.1).theil_sen_summary()/theil_sen_map(),
and theil_sen_slope() gained report = TRUE,
closing the one core function still missing companion reporting
functions. Found and fixed a real bug while wiring this in:
inspect_ts_cell()’s own internal call did not set
report = FALSE, so every call would have started
auto-printing a spurious one-cell summary/map.moran_permutation_test() gained
report = TRUE (closing the one remaining function requiring
a separate manual call to see any report) and proper
match.arg() validation on connectivity
(previously failed silently deep inside terra::adjacent()
on a typo).prewhiten_summary() now prints an informative narrative
summary, matching the other *_summary() functions.tst()/rta() results gain a shared
"sptrends" superclass, non-breaking, for future common
methods to dispatch on.inspect_ts_cell() added to README.md for
the first time.rta()’s own documentation to mirror
tst()’s in full; added a new vignette walking through the
complete RTA workflow, closing the one remaining asymmetry.rta()’s introduction:
?sptrends, README.md,
inst/CITATION, and four vignettes updated to present TST
and RTA as two workflows, not TST alone (rta() had been
missing from several places despite already being complete and tested).
theil_sen_slope()/fdr_bh() gained a “used by”
note that both are shared infrastructure, not TST-specific.tst()’s and rta()’s own
@references: both previously cited only their own workflow
paper, not the individual published methods (Wang & Swail, Neeti
& Eastman, Theil, Sen, Benjamini and co-authors) each step builds
on.inspect_ts_cell()’s confidence band was anchored at
t = 0 (typically outside the observed range), widening in
only one direction instead of the standard symmetric “bowtie” shape.
Anchored at the centre of the observed range instead.rta(): a second, complete, published
workflow (Robust Trend Analysis, Gutiérrez-Hernández & García 2024)
alongside tst(), not replacing it – Theil-Sen, Contextual
Mann-Kendall, and standard FDR-BH (not adaptive BKY), deliberately
without prewhitening. Both differences documented at length rather than
left implicit, since prewhitening’s own value before a Mann-Kendall test
is a genuine, unresolved debate in the published literature (cited both
sides). No Moran’s I step and no categorical slope classes, unlike the
original published RTA method – kept consistent with how this package
treats magnitude and diagnostics elsewhere. New
print.rta(). Fixed an inaccurate existing cross-reference:
tst()’s own docs had listed the RTA paper as “the same
workflow”, no longer true now that RTA is separately implemented.helper-sptrends.R, always sourced first.inspect_ts_cell(): a single
neighbourhood argument (default TRUE) replaces
always overlaying two Theil-Sen lines. All aggregation modes now take
the per-time-step median of raw values first, then fit one Theil-Sen
slope to that aggregated series – deliberately different from the
previous “estimate per cell, then median the slopes” order, since only
the “aggregate first” order has a standard confidence-interval formula.
As a consequence, inspect_ts_cell(neighbourhood = TRUE) no
longer numerically matches
theil_sen_slope(..., smooth_neighbourhood = TRUE) at the
same cell – documented as a genuine, intended difference.inspect_ts_cell() gained an optional
prewhitened argument, drawing a second panel using
wang_swail_prewhiten()’s own output side by side with the
raw one. Internally refactored into small, reusable helpers shared by
both panels.inspect_ts_cell()’s own references, and found
and fixed a real gap while doing so:
contextual_mann_kendall() itself had never explicitly cited
Mann (1945)/Kendall (1975), the foundational statistic its own
S/VarS computation rests on.inspect_ts_cell(): interactive
click-to-inspect time series viewer, overlaying the clicked cell’s own
Theil-Sen fit against one that borrows its queen neighbourhood – whether
the two agree is itself the diagnostic. Split into a public interactive
wrapper and an internal, fully testable core (the interactive part
cannot be exercised by automated tests). Verified by direct comparison
to match theil_sen_slope()’s own output exactly for the
same cell.n_cores-capping message safely for
R CMD check, by mocking
parallel::detectCores() rather than actually requesting
more cores than the machine has.plot.tst(which = "slope")’s map title,
dropping smoothing/neighbourhood wording from the displayed label.covr::zero_coverage() line numbers;
confirmed (not forced) that the n_cores-capping message and
two read_stack.R lines remain deliberately/structurally
unreachable.variant argument for fdr_bky()
("multtest"/"definition6", two distinct
published implementations of the same BKY procedure), forwarded through
fdr_correction()/tst(). Confirmed
"multtest"’s threshold is never stricter than
"definition6"’s.d-fdr-correction’s narrative: Moran’s I
check reframed as an optional diagnostic, not part of the main flow; now
shows both FDR-BH and FDR-BKY direction maps side by side.grDevices::hcl.colors()), across every function and
vignette using it. Uncorrected significance stayed red deliberately, to
reinforce it is diagnostic-only.tst()’s own examples now also show
plot(which = "significance"/ "slope"), not only the default
view.expect_equal() on the name alone, not the values. Fixed
with unname().devtools::check(): 7
tests used bare values() instead of
terra::values(); a real portability bug in
moran_null_plot() (Unicode arrow characters failing to
convert on some locales, replaced with plain ASCII); and a
max()-with-no-arguments warning in two all-NA guards,
suppressed.plot.tst(which = "slope") view: the Theil-Sen
slope, masked to FDR-significant cells, smoothed by default at plot time
only. Motivated by real data: comparing CMK’s neighbourhood-averaged
statistic sign against Theil-Sen’s own sign on the bundled dataset
showed 338 disagreeing cells, always pulled towards the locally dominant
trend – this view deliberately uses Theil-Sen’s own sign.smooth_neighbourhood argument for
theil_sen_slope() (default FALSE): queen-3x3
median smoothing of the estimated slope, with an explicit caveat that –
unlike CMK’s own published, validated method – this has no equivalent
literature backing, since blending neighbouring cells’ magnitudes
assumes they share a similar true slope, which does not always
hold.tst()’s own Theil-Sen step now smooths by
default, a deliberate methodological choice despite the caveats
above; calling theil_sen_slope() directly is unaffected.
Override via
theil_sen_args = list(smooth_neighbourhood = FALSE).trend_maps()/ prewhiten_maps() did not fix
their diverging colour range symmetrically around zero, visually
misrepresenting the sign boundary whenever positive and negative values
were not equally extreme.README.md.covr::zero_coverage() results. The single
biggest recurring pattern: the path = (write-to-disk)
argument had never been tested for most reporting functions – added
across 10 of them. Closed several other real per-function gaps.
Deliberately left uncovered: the n_cores-capping message
and two structurally unreachable read_stack.R checks.covr::zero_coverage() line numbers to close
genuine gaps in prewhiten.R (Durbin-Watson out-of-range
messages, several verbose branches, path = untested for two
reporting functions) and read_stack.R
(read_netcdf_stack()’s own messages, never exercised since
every existing test used verbose = FALSE). Noted, not
forced: two checks appear structurally unreachable given existing
upstream guards.wang_swail_prewhiten()’s own code: the clamp only catches
rho at or beyond exactly +-1, not a general
cap at 0.99 – a legitimate 0.995-ish estimate correctly
failed the test’s own wrong assertion. Rewritten to check
abs(rho) < 1, what the code actually guarantees.tst-methods.R, read_stack.R,
prewhiten.R); flagged one added test’s own assertion as
weak, honestly, since execution could not confirm it actually drove the
estimate past the clamping boundary.fdr.R (worst-covered file at 84.65%):
out-of- range input error, multi-layer input message, full
report = TRUE branch, Moran’s I recommendation message,
CSV-writing path.theil_sen_slope()/contextual_mann_kendall():
cut() errors with only a single item to split, which
happens at exactly one valid cell or two time layers. Fixed by switching
to parallel::splitIndices().theilsen.R/contextual_mk.R: verbose messages
previously executed but never asserted, and n_cores
exceeding the number of valid cells without erroring.utils-internal.R’s coverage was
stuck at 58.5%: covr::package_coverage() does not set
NOT_CRAN, so every skip_on_cran()-guarded test
was silently skipped during coverage measurement. Nearly every parallel
test used n_cores of 1-2 (already CRAN-compliant), so the
guard was unnecessary caution – removed from all 9 affected tests.stats::median() of an odd-length integer vector returns an
integer, not a double, unlike the even-length case.contextual_mk.R’s full
report = TRUE branch (previously only its own constituent
functions were tested in isolation); theilsen.R’s
subsampling branch (n*(n-1)/2 > max_pairs), previously
completely untested; utils-internal.R’s remaining
parallel-lapply arguments (seed, packages);
read_stack.R’s NetCDF error path and diagnostic plot
path.R CMD check enforces its own separate hard
limit on parallel workers, so the test errored before the function’s own
logic ever ran.utils-internal.R (31.7%, every internal helper tested
directly), pipeline.R/tst() (71.8%, the
flagship function, largely untouched by earlier rounds),
theilsen.R (76.1%, including a hand-worked exact reference
value, not just a sanity check).terra::values() on a single layer
returns a vector not a matrix; terra::nlyr() returns a
double, not integer), two malformed test fixtures (a file-name
collision, an NetCDF write missing overwrite = TRUE), and
one over-strict expect_silent() where warnings are
legitimately expected.prewhiten.R (gating behaviour,
dw_method, error conditions), fdr.R (raster
input, all reporting functions), and contextual_mk.R
(hand-worked exact S/variance values, isolated- cell
fallback, connectivity comparison).read_ordered_stack() (previously
zero tests, unlike read_netcdf_stack()), plus further
NetCDF tests guarded by
skip_if_not_installed("ncdf4").moran.R (previously
40.8% coverage): hand-worked exact Moran’s I values under both
connectivity types on a checkerboard pattern, confirming they genuinely
differ and are each individually correct.unname() omission in two tests
comparing an extracted raster value against a literal number.compute_anomalies()
(previously 0% coverage), verified against hand-worked expected values,
not just “does it run”.testthat
idiom cleanup: terra::SpatRaster is S4, not S3 – 4
expect_s3_class() calls fixed to
expect_s4_class().goodpractice::gp()’s findings: defensive
on.exit(add = TRUE) fixes, removed placeholder
URL/BugReports fields pointing to a repository
that doesn’t exist yet, fixed a real NA-safety bug introduced while
flattening a nested ifelse() in
fdr_direction_map(), several idiomatic cleanups
(anyDuplicated(), inverted negated conditions), and
confirmed two flagged findings as false positives.goodpractice and lintr to
Suggests, formalising the package’s quality-evaluation
toolkit.alpha’s own multi-value documentation:
0.05 is the conventional standard actually used for the
map/reference message, 0.1/0.01 shown
alongside for context, not as interchangeable defaults.alpha = c(0.1, 0.05, 0.01), reporting
functions used min(alpha) = 0.01 (the strictest) instead of
the conventional 0.05, silently under-reporting
significance in every default run. Fixed via a new
.reference_alpha() helper.covr to Suggests, formalising test
coverage as a quality standard.@examples block across the
package now has at least one explanatory comment, verified
programmatically.sptrends_example() to
example_data() (inconsistent prefix), and two identifiers
to British spelling (summarise_replicates(),
standardise) – done now, before the package reaches
CRAN.tst()’s own examples and all four vignettes switched to
the bundled real temperature dataset instead of
sim_trend_stack(), a more recognisable example for the
package’s flagship function and walkthroughs specifically."accuracy" should error,
left over from before Accuracy became a valid metric.example_data() to
access it, doubling as a realistic read_ordered_stack()
example.compare_detections() gained Accuracy and
MCC (Matthews correlation coefficient, generally preferred
under class imbalance) as two more metrics, both included by
default.compare_detections()’s truth argument
renamed to ground_truth.sim_trend_stack() + compare_detections()
together.summarise_replicates() and
plot_detection_comparison(), completing the validation
toolkit.compare_detections(): a confusion-matrix
comparison of one or more trend-detection results against a known ground
truth, deliberately agnostic to where either side comes from.sim_trend_stack() gained
noise_dist = "gaussian"/"t", generating heavy-tailed noise
– the condition under which rank-based methods’ robustness argument over
a parametric one like OLS actually shows up.sim_trend_stack()’s own
trend-assignment logic: sign flips were applied cell-by-cell at random
(“salt-and-pepper”), silently defeating CMK’s whole rationale (borrowing
strength from a plausibly-trending neighbourhood). Switched to coarse,
spatially contiguous blocks.sim_trend_stack()’s own roxygen block, documenting
the wrong function as exported.sim_trend_stack() gained noise_sd,
trend_fraction (proportion of cells keeping a non-zero true
slope; 0 gives a complete null field for
false-positive-rate checks), and trend_shape
(“radial”/“gradient”/“block”).sim_trend_stack() now
returns list(series = ..., true_slope = ...) instead of the
raster directly – what makes it a simulator with known ground truth,
comparable directly against a fitted slope or a method’s own
significance calls.sim_trend_stack()’s examples now demonstrate
smooth_radius’s own effect directly via Moran’s I (~0.07 vs
~0.65).sim_trend_stack() now genuinely generates spatial
autocorrelation between neighbouring cells, via a new
smooth_radius argument – previously documented but not
actually implemented (noise was independent per cell).moran_permutation_test() documentation now explains why
the permutation approach matters for this package’s own use case
(bounded and/or skewed trend statistics, not normally distributed).sptrends_pipeline() entirely – a deprecated
alias for tst(), never part of a public release.read_netcdf_stack() now issues a real
warning(), not just a documentation note, when the detected
time step looks sub-annual.tst()’s default
fdr_method changed from c("BH", "BKY") to
"BKY" only, the method actually used in the published TST
methodology.fdr_* functions, not only fdr_bh().tst()’s own argument order regrouped for logical
proximity (alpha with moran_check,
fdr_method with q); named-argument code is
unaffected.alpha/q note: applying a
per-test alpha cell by cell across a raster is the
multiple-testing error FDR exists to fix, not a harmless
simplification.Description rewritten twice more for clarity, naming
each workflow component explicitly before citing the TST paper.browseVignettes() (sorts by
title, not file name) by prefixing the getting-started vignette’s own
title.a-/b-/c-/d- prefix
so browseVignettes() lists them in reading order.README.md.@examples block (previously missing
on 21 of 34 functions).README.md and package docs; finalised
Title/Description around a
reproducible-framework framing; added _pkgdown.yml for a
grouped future reference index; fixed a duplicated header in
README.md.Title finalised, deliberately not naming TST or
“monotonic” in the title itself, since TST is meant to be the flagship
workflow, not necessarily the only one going forward.Description rewritten to lead with the
reproducible-framework framing before naming TST.tst() now includes Theil-Sen as a fourth, optional
step, completing the four-component TST workflow as published.tst() now returns a classed object with
print()/summary()/ plot()
methods.tst(), renamed from
sptrends_pipeline() (matching the published workflow’s own
name); the old name kept as a deprecated alias.DESCRIPTION and README.md rewritten to
lead with the general monotonic-trend framing before naming TST by
name.theil_sen_slope(),
fdr_direction_map()/_plot()/_summary(),
compute_anomalies(), and NetCDF support for
read_ordered_stack() plus a new
read_netcdf_stack().inst/CITATION; added the
package-level help page; report = TRUE added as the default
across the core functions; moran_check added to
fdr_correction()/the pipeline; parallel computation
(n_cores) added to the CMK and Moran’s I steps;
documentation overhaul with verified/corrected DOIs throughout.DESCRIPTION rewritten around the three-step
workflow.Initial release, built from four standalone scripts to reproduce the
TST workflow described at the top of this file: selective AR(1)
prewhitening (Wang & Swail, 2001), Contextual Mann-Kendall (Neeti
& Eastman, 2011), pixel-wise FDR correction (Benjamini-Hochberg
1995; Benjamini-Krieger-Yekutieli 2006), and Moran’s I permutation
testing, plus compute_anomalies(), raster/NetCDF reading, a
convenience pipeline wrapper, and a synthetic data generator for
examples and tests.