Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 51 additions & 4 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@
`NA` with a `rejected_reason`) is not read as accepted: `cp` keeps that
fit's nugget and warns, naming the reason, `reliability` is `NA` (it
needs the range), and a fit that did not converge, or whose range is below
the shortest lag fitted, gives neither (the whole refused fit used to be
the shortest lag fitted, gives neither its nugget nor its range (the whole refused fit used to be
used, and a correlation function whose range could be many times the
extent pinned the reliability optimum to the first level, with no R
warning). A range below the shortest lag means the structure cannot be
Expand All @@ -422,8 +422,19 @@
or a character string is refused by name; a `units` object used to be
read as a number in the CRS units, so 1.5 km became a range of 1.5 m and
a floor of 41 million cells. A plain number is taken as the range alone:
it sets the floor, and `cp` and `reliability` are
`NA`. Reliability's domain term is taken over the convex hull the
it sets the floor, and `reliability` is `NA`. Wherever the variogram
gives no nugget (none was fitted, its fit did not converge, its range is
below the shortest lag, or `sac` is a range alone), `cp` takes Mallows'
own noise variance, the residual mean square RSS / (m - L) of the finest
level whose cells hold two scored rows each on average, and the profile
warns; `attr(x, "cp_noise")` records which variance was used, and the
print says so. It counts the structure within those cells as noise too,
so on average it is no smaller than the nugget and errs towards fewer
cells. `cp` used to be `NA` at every level in all four cases, so a
workflow that reads C_p by default (`build_tessellation(approx_n_cells =
<profile>)`, `summary()`) fell through to another criterion with a log
line at most.
Reliability's domain term is taken over the convex hull the
area is measured on, not the bounding box: on a 3000 x 120 strip the
reliability pick is 6 cells whether the strip lies axis-aligned or rotated
by 45 degrees (it was 8 and 2), and 10 either way on a square (it was 10
Expand Down Expand Up @@ -862,8 +873,39 @@
warns once for each model that did not converge. Code that pins the
exact column set of either table needs the new name added.

* `select_resolution()` returns `$seeds`, the centres of the partition the
profile scored at the chosen level, and `get_voronoi_seeds(method =
"kmeans")` and `voronoi_seeds_kmeans()` return those centres when given
the selection or the profile, instead of running a k-means of their own.
A k-means partition is the Voronoi partition of its centres, so these
seeds rebuild the very cells the criteria judged. The fresh k-means the
seeding functions ran (Hartigan-Wong, 10 starts, on whatever cloud they
were given) was a different partition: on 300 points it put 5--16 percent
of them in a different cell from the one they were scored in, and on
2000 points (profiled on a 1500-point subsample) 26--29 percent, so the
count was defended on cells nobody built. The profile keeps every
level's centres in `attr(x, "centres")` (with `attr(x, "centres_crs")`),
the seeds come back in the CRS of the boundary or points they are for,
and `attr(seeds, "kmeans")` assigns `sample_points` to them, each point
to its nearest seed. A count passed as a number (`n = sel$best`) still
runs a fresh k-means, as does a selection made before seeds were kept.
`voronoi_seeds_kmeans()` did not accept a selection or profile as `k`
before, and the advice in `build_tessellation()`'s occupancy warning now
points at the selection rather than the bare count.

## Bug fixes

* **A clipped cell that also touched the boundary from outside was dropped,
and its points left with no cell.** `st_intersection()` returns such a
cell as a GEOMETRYCOLLECTION of its area and a line or point (on an
L-shaped boundary, the cell over the inner corner), and the Voronoi, grid
and Delaunay builders kept only POLYGON and MULTIPOLYGON rows, so the
whole cell went, area and all. On a 4 x 4 L with square cells of side 2,
ten of thirty points had no cell and the cells covered 8 of the
boundary's 10 square units. Each collection is now reduced to its
polygonal part, one row per cell still; a piece with no area is dropped
as before.

* **`coerce_to_points()` crashed R on an empty line feature.** sf's
`st_cast()` turns an empty MULTILINESTRING into one empty LINESTRING, not
zero parts, and `st_line_sample()` on it segfaulted and took the session
Expand Down Expand Up @@ -1708,7 +1750,12 @@
optimistic than the target. `make_folds()` now warns when the floor
leaves more than one point's worth of excess below `phi`, records
`params$n_at_min_train`, and the documentation states the guarantee only
where neither `phi` nor `min_train` binds.
where neither `phi` nor `min_train` binds. The warning names the
statistic that fires it, the largest excess of the realised
nearest-neighbour ECDF over the target at distances up to `phi`, and
gives the two medians only as context: they summarise all the distances,
and the realised median can sit above the target's while the short
distances are over-represented.

* **`make_folds(auto_range = TRUE)` fell back to geometric blocks with only
a log line.** When no range was identified (an unremoved trend, a range
Expand Down
18 changes: 13 additions & 5 deletions R/cross-validation.R
Original file line number Diff line number Diff line change
Expand Up @@ -4701,17 +4701,25 @@ make_folds <- function(points_sf, k,
rp <- rs[rs <= phi]
max(findInterval(rp, rs) / length(rs) - G_target(rp))
} else NA_real_
# The message states the statistic that fired it. It used to lead with
# the two medians, which describe the whole distributions, not the short
# distances the test is about: the realised median could sit above the
# target's while the warning said the folds were "more optimistic".
if (n_at_floor > 0L && is.finite(excess_phi) && excess_phi > 1 / n + 1e-9)
.warn_and_log(paste0(
"make_folds(nndm): min_train = %s stopped the distance matching in %d ",
"of %d folds, which keep a training point closer than the prediction ",
"distances allow, so the realised distances remain more optimistic ",
"than the target (median %.4g against %.4g; largest ECDF excess ",
"%.3f). The prediction locations lie further from the samples than a ",
"distances allow. At distances up to phi (%.4g) the share of folds ",
"whose nearest training point is within a distance runs above the ",
"target's share by as much as %.3f (more than one fold's worth, 1/%d), ",
"so short distances are over-represented and the CV score is ",
"optimistic for the prediction locations. (The medians, %.4g realised ",
"and %.4g target, summarise all distances and need not differ the same ",
"way.) The prediction locations lie further from the samples than a ",
"fold can be made to hold out: read the CV score as an upper bound on ",
"performance there, or lower min_train."),
format(min_train), n_at_floor, n, stats::median(realised[fin]),
stats::median(g_target), excess_phi)
format(min_train), n_at_floor, n, phi, excess_phi, n,
stats::median(realised[fin]), stats::median(g_target))

# Exclusion is not always needed. When prediction locations sit no further
# from the training data than training points sit from each other, plain
Expand Down
Loading
Loading