Cokriging
CoKriging estimates a primary variable with help from one or more related
secondary variables. For example, you might combine sparse yield samples with
NDVI, elevation, or soil EC. The workflow has three steps: screen candidate
variables, fit and interpolate with the ones that pass, and cross-validate to
check whether cokriging actually improves the result.
Co-kriging is an advanced workflow. It is not part of the default
apbase.Map selection today. Use it when you have meaningful secondary
variables and want to test whether they reduce error.
from apbase.cross_variogram import screen_secondary_variables
from apbase.cokriging import CoKriging
from apbase.cross_validate import cross_validate_cokriging, select_best_model
# 1. Screen candidates -- keep only the admissible ones
screened = screen_secondary_variables(x0, y0, z0, {"ndvi": (xn, yn, zn), "ec": (xe, ye, ze)})
screened.report # per-candidate rho, p-value, admissible_fraction, decision
secondaries = screened.selected # e.g. {"ndvi": (xn, yn, zn)}
# 2. Fit and interpolate
ck = CoKriging(method="icm", max_neighbors=40, min_neighbors=3)
ck.fit(x0, y0, z0, secondaries)
z_targets = ck.interpolate(targets) # targets: (n, 2) array
# 3. Confirm it's actually better than plain kriging via LOO-CV
cv = cross_validate_cokriging(x0, y0, z0, secondaries)
best = select_best_model(cv.by_method)
best.method, best.significant
Do not skip the first step. Passing an unrelated secondary variable directly
to CoKriging may not raise an error, but it can make the estimate worse.
Always screen candidates first.
Core inputs
x0, y0 : array_like
: Primary source coordinates.
z0 : array_like
: Primary values. Must have the same length as x0 and y0.
candidates : dict[str, tuple[array_like, array_like, array_like]]
: Candidate secondary variables passed to screen_secondary_variables, for
example {"ndvi": (xn, yn, zn)}.
secondaries : dict or list
: Screened secondary variables passed to CoKriging.fit and
cross_validate_cokriging. screened.selected already has the right shape.
targets : array_like or Grid
: Target coordinates passed to CoKriging.interpolate.
Step 1: screening
screen_secondary_variables checks whether each candidate has a meaningful
correlation with the primary (Fisher -test) and an admissible cross-
variogram (Cauchy-Schwarz check). Only candidates that pass both checks are
kept. See Covariate screening for the statistics.
Screening parameters
alpha : float, default 0.05
: Significance level for the Fisher z-test.
correction : {"bonferroni", "none"}, default "bonferroni"
: Multiple-testing correction used when screening several candidates.
min_abs_rho : float, default 0.3
: Minimum practical correlation. A statistically significant but weak
candidate is still rejected.
max_violation_fraction : float, default 0.1
: Maximum fraction of evaluated lags allowed to violate the cross-variogram
admissibility bound.
max_colocation_dist : float, optional
: Maximum distance used to pair primary and secondary samples. None requires
exact coordinate coincidence.
screened = screen_secondary_variables(
x0, y0, z0,
{"ndvi": (xn, yn, zn), "ec": (xe, ye, ze), "random_noise": (xr, yr, zr)},
alpha=0.05,
min_abs_rho=0.3,
)
screened.selected already has the shape expected by CoKriging.fit, so pass
it directly as secondaries. screened.report includes every candidate,
including rejected ones, and explains each decision. It is useful when you
need to record why a variable was dropped.
Step 2: fitting and interpolating
ck = CoKriging(method="icm", radius=None, max_neighbors=40, min_neighbors=3)
ck.fit(x0, y0, z0, secondaries)
estimates = ck.interpolate(targets)
Fitting parameters
method : {"collocated", "icm", "lmc"}, default "collocated"
: Co-kriging model. "icm" is a reasonable default when unsure: no Markov
screening assumption and no per-secondary shape choice to tune.
radius : float, optional
: Local search radius. If omitted, APbase derives it from the primary
variogram.
max_neighbors, min_neighbors : int
: Neighbor limits. For "icm" and "lmc", these bound every variable's own
neighbor search.
n_structures : int, default 2
: Number of nested LMC structures. Used only by "lmc".
model_values : array_like, optional
: Precomputed model values. Currently supported for "collocated".
ck.model_params gives readable fitted parameters (per-method shape
differs -- see the class docstring); ck.model_values gives the flat
native vector, useful for reusing a fit without re-running it.
Collocated cokriging needs the secondary value at every interpolation target, not only at its source points (see the class docstring). If needed, pre-interpolate a sparse secondary onto a sufficiently dense grid first. ICM/LMC do not have this requirement.
Step 3: cross-validating
Fitting cokriging is only part of the job. Cross-validation shows whether it actually beats plain kriging on this dataset.
Cross-validation parameters
max_points : int, default 500
: Maximum primary samples evaluated by leave-one-out validation.
seed : int, optional
: Seed used when validation samples a subset of primary points.
radius, max_neighbors, min_neighbors, n_structures
: Same meaning as in CoKriging.
from apbase.cross_validate import cross_validate_cokriging, select_best_model
from apbase.kriging import Kriging
cv = cross_validate_cokriging(x0, y0, z0, secondaries, max_neighbors=40, min_neighbors=3)
cv.by_method["icm"].rmse, cv.by_method["lmc"].rmse, cv.by_method["collocated"].rmse
best = select_best_model(cv.by_method)
best.method # e.g. "icm"
best.significant # True if the margin over the runner-up is unlikely due to chance
cross_validate_cokriging runs leave-one-out on the primary for all
three methods in one pass, sharing one primary spatial index; see
Cross-validation for what stays fixed (the
coregionalization models) versus what is genuinely held out (the
primary). Compare cv.by_method[...] against a plain
apbase.cross_validate.cross_validate(x0, y0, z0) kriging-only baseline
to check whether adding secondaries helped at all, not just which
cokriging method is best among themselves.