Generating Target-Group Background Points in Python

Target-group background (TGB) is the most defensible way to correct observer-effort bias in presence-only species distribution models when a same-methodology survey set exists. It is a specific technique within Presence-Only Data Preparation, the stage of the Species Distribution Modeling with MaxEnt workflow that curates occurrence input. The idea is simple and powerful: instead of drawing background points uniformly across the study area, draw them from the pooled localities of a target group — all taxa collected with the same method and detectability as your focal species (for example, every vascular-plant record from the same herbarium programme). Because those localities encode where surveyors actually went, background sampled from them carries the same effort bias as the presences, and MaxEnt cancels the bias out. This page builds a kernel-density (KDE) effort surface from the target group and samples background weighted by it.

When to Use Target-Group Background

TGB requires a pooled set of same-methodology records. When you have one, it is lower-effort and better-founded than modelling an explicit effort layer. Use the table to confirm it fits before building it.

Approach Requires Corrects When to prefer
Target-group background A same-methodology multi-taxon record set Observer effort, implicitly A target group exists (herbarium, trap grid, eBird checklists)
Kernel-density bias file Only the focal presences + an access proxy Access-driven clustering No target group; effort tracks roads/trails
Uniform random background Nothing Nothing (assumes even effort) Effort is genuinely uniform (rare)
Spatial thinning Only the focal presences Autocorrelation between points Any predictive model, alongside the above

TGB and a road-based bias file are alternative effort corrections — pick the one your data supports; do not stack them. Both, however, pair naturally with spatial thinning of occurrence records, which addresses a different artifact. The effort surface you build here must align cell-for-cell with the environmental predictor stack so background points sample valid feature vectors.

Minimal Reproducible Example

The pipeline has three parts: rasterise the pooled target-group localities into an effort surface with a Gaussian KDE, normalise it to a sampling-probability grid, then draw background points with probability proportional to that grid. The effort raster is built on the predictor stack’s transform so the grids match exactly.

import numpy as np
import geopandas as gpd
import rasterio
from rasterio.features import rasterize
from scipy.ndimage import gaussian_filter

# Pooled localities of ALL taxa surveyed with the same method as the focal species.
target = gpd.read_file("target_group_localities.gpkg")

# Match the predictor stack grid exactly (CRS, transform, shape).
with rasterio.open("predictor_stack.tif") as stack:
    transform = stack.transform
    height, width = stack.height, stack.width
    crs = stack.crs

target = target.to_crs(crs)

# 1. Rasterise target-group point density (count of records per cell).
shapes = ((geom, 1) for geom in target.geometry)
counts = rasterize(
    shapes,
    out_shape=(height, width),
    transform=transform,
    fill=0,
    merge_alg=rasterio.enums.MergeAlg.add,   # accumulate overlapping points
    dtype="float32",
)

# 2. Smooth into a continuous effort (KDE) surface; sigma in cells.
effort = gaussian_filter(counts, sigma=2.0)

# 3. Normalise to a probability surface that sums to 1 over sampled cells.
prob = effort / effort.sum()

# 4. Draw background points weighted by survey effort.
rng = np.random.default_rng(42)
n_background = 10_000
flat_idx = rng.choice(prob.size, size=n_background, replace=True, p=prob.ravel())
rows, cols = np.unravel_index(flat_idx, prob.shape)

# Convert (row, col) cell indices to map coordinates at cell centres.
xs, ys = rasterio.transform.xy(transform, rows, cols, offset="center")
background = gpd.GeoDataFrame(
    geometry=gpd.points_from_xy(xs, ys), crs=crs
)
background.to_file("tgb_background.gpkg", driver="GPKG")
print(f"Drew {len(background)} target-group background points")

The result is a background sample dense where the target group was surveyed intensively and sparse where it was not — the same spatial footprint as the focal presences, which is exactly what lets MaxEnt separate habitat from access.

Parameter Reference

Parameter Type Default Recommended range Ecological rationale
sigma (KDE) float, cells none 1–3 cells Bandwidth of the effort smoothing; approximate the survey’s detection neighbourhood, not the whole landscape.
n_background int none 10,000–100,000 Enough background to characterise the accessible environment; scale up for large or heterogeneous regions.
merge_alg enum replace MergeAlg.add Accumulates coincident records so heavily visited cells get higher effort weight.
replace (rng.choice) bool True True Sampling with replacement lets high-effort cells contribute multiple background points, matching effort intensity.
target group definition set none same method + detectability The single most important choice; too broad a group dilutes the bias signal, too narrow leaves it noisy.

Expected Output and Verification

A correct TGB sample is a set of points whose spatial density tracks the target-group effort surface, all falling on valid (non-nodata) predictor cells. Verify two invariants: the probability surface is a proper distribution, and background density correlates with target-group density.

import numpy as np
from scipy.stats import spearmanr

# 1. prob is a valid probability surface.
assert np.isfinite(prob).all(), "Effort surface contains NaN"
assert abs(prob.sum() - 1.0) < 1e-6, "Probability surface does not sum to 1"
assert (prob >= 0).all(), "Negative probabilities"

# 2. Background density should correlate with target-group effort.
bg_counts = np.zeros(prob.shape, dtype=float)
np.add.at(bg_counts, (rows, cols), 1)
mask = effort.ravel() > 0
rho, _ = spearmanr(effort.ravel()[mask], bg_counts.ravel()[mask])
assert rho > 0.5, f"Background does not follow effort (rho={rho:.2f})"
print(f"TGB verified: prob sums to 1, effort-density correlation rho={rho:.2f}")

A Spearman correlation above roughly 0.5 confirms the background inherited the survey footprint. Overlaying the background on the target-group points should show them occupying the same regions. The corrected presence/background pair then feeds MaxEnt model training, and the payoff is checked in Model Validation & AUC Metrics with spatially blocked folds.

Common Pitfalls

  • Choosing the wrong target group. The group must share collection method and detectability with the focal species — vascular plants for a plant, ground beetles for a beetle. Pooling incompatible taxa injects a different effort pattern and can bias the model worse than no correction. When in doubt, keep the group tight around comparable methodology.
  • Background that does not reach the predictor grid. If the effort raster’s transform or CRS differs from the predictor stack, sampled points land on the wrong cells or off-grid. Build the effort raster directly from the stack’s transform, height, and width, as above.
  • Too few background points. Undersampling the accessible environment leaves MaxEnt’s background distribution noisy and destabilises the fitted response curves. Draw at least 10,000 points for a regional model and more for large heterogeneous extents.
  • Sampling nodata or ocean cells. If the target group has records over water or beyond the predictor extent, mask the effort surface to valid predictor cells before normalising, or those background points carry no feature vector and are silently dropped.

Frequently Asked Questions

How is target-group background different from a bias file?

They are two implementations of the same principle — make the background carry the presences’ effort bias. A bias file weights uniform background by a modelled sampling surface (often a road-proximity KDE). Target-group background instead draws the background directly from the localities of a same-methodology taxon group, so no separate effort proxy is needed. Prefer TGB when a credible target group exists; use a bias file when it does not.

Can I combine target-group background with spatial thinning?

Yes, and you usually should. Thinning acts on the focal presences to break autocorrelation; TGB shapes the background to match effort. They are orthogonal, so a typical pipeline thins the presences first, then samples target-group background — see spatial thinning of occurrence records.

Up one level: Presence-Only Data Preparation · Species Distribution Modeling with MaxEnt