Accepted to CRAN (first release).
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.