Utilities for creating a (validated) stratified sample from a GeoDataFrame and preparing it for manual map-accuracy checks.
For each unique value in --groupby-column (a "stratum" — e.g. a land-use class), the script:
- Computes a sample size
nusing the standard formula for estimating a proportion at a given confidence level (--confidence-z) and margin of error (--margin-error), with a finite-population correction for that stratum's sizeN. - Clips
nto[--min-samples, N]:- Floor (
--min-samples): every stratum gets at least this many samples, even if the formula says fewer would do — so rare classes still get a statistically useful sample for accuracy checking. Small strata are therefore sampled at a higher rate than large ones. - Cap (
N): you can never sample more rows than exist — if a stratum has fewer than--min-samplesfeatures, the entire stratum is taken.
- Floor (
- Draws
nfeatures from the stratum, optionally weighted by--bias-column(with an optional--fractional-powertransform of those weights), so e.g. larger polygons are more likely to be picked.
The combined sample is then passed through map_accuracy_prep (adds
mapped/truth/checked/comment columns for review) and sample_group
(splits the output into --n-groups review batches).
Requires Python 3.9+. Once published, install from PyPI:
pip install spatial-stratified-samplingThis installs the stratified-sample command-line tool. To install from a
local clone instead (e.g. for development), run pip install -e . (or
pip install -e ".[dev]" to also pull in test dependencies) from the
project root. Alternatively, install just the runtime dependencies with
pip install -r requirements.txt and run python stratified_sample.py
directly without installing the package.
geopandas depends on GDAL/GEOS/PROJ; if pip install fails to build these,
installing geopandas via conda/mamba (e.g. conda install -c conda-forge geopandas)
is usually easier — install the remaining dependencies afterwards.
Run with named flags, either via the installed command or the script directly (equivalent):
stratified-sample \
--input input.gpkg \
--output output.gpkg \
--groupby-column lu_coden \
--n-groups 3
python stratified_sample.py \
--input input.gpkg \
--output output.gpkg \
--groupby-column lu_coden \
--n-groups 3See stratified-sample --help for full details, or the examples below.
Key options
--input/-i(required): input vector (GeoPackage, Shapefile, etc.)--output/-o(required): output vector path to write sampled features--groupby-column/-g(required): column name to stratify by--n-groups(default1): number of sample groups to assign (for review batching), written to asample_groupcolumn--bias-column/-b(default: none): optional column name to bias sampling by (useareato bias by geometry area)--fractional-power/-f(default: none): power transform applied to bias weights, must be< 1(use0for a log1p transform)--random-state/-r(default: random): random seed for reproducible sampling. If omitted, a random seed is generated and printed so the run can be reproduced later--confidence-z(default1.96): z-score for the desired confidence level--margin-error(default0.05): acceptable margin of error for the sample-size calculation--min-samples(default30): minimum number of samples drawn from each stratum--no-explode: by default, multipart geometries are split into single-part features before sampling; pass this flag to skip that step
Plain stratified sample — default 95% confidence / 5% margin of error, at least 30 features per class, split into 3 review batches:
python stratified_sample.py \
--input input.gpkg \
--output output.gpkg \
--groupby-column lu_coden \
--n-groups 3Reproducible sample — if --random-state is omitted, a random seed is
generated and printed to the console (e.g. pass --random-state 123456789 to reproduce this sample). To get a fresh random sample, just omit
--random-state; to reproduce a previous run exactly, pass the seed it
printed:
python stratified_sample.py \
--input input.gpkg \
--output output.gpkg \
--groupby-column lu_coden \
--n-groups 3 \
--random-state 123456789Area-weighted sample with a fourth-root transform — bigger polygons are
more likely to be picked, but the transform (power 0.25) dampens the bias
so very large polygons don't dominate the sample. Also uses a wider 10%
margin of error:
python stratified_sample.py \
--input input.gpkg \
--output output.gpkg \
--groupby-column lu_coden \
--n-groups 3 \
--bias-column area \
--fractional-power 0.25 \
--margin-error 0.1 \
--min-samples 30Area-weighted sample with a log transform — a milder alternative to a
fractional power; use --fractional-power 0 for log1p(area) weights:
python stratified_sample.py \
--input input.gpkg \
--output output.gpkg \
--groupby-column lu_coden \
--bias-column area \
--fractional-power 0Quick spot-check — a small, single-batch sample for a fast sanity check
rather than a full accuracy assessment (lower --min-samples, no review
batching):
python stratified_sample.py \
--input input.gpkg \
--output output.gpkg \
--groupby-column lu_coden \
--min-samples 10Tighter confidence requirements — a 99% confidence interval (z = 2.576)
with a 3% margin of error, for a more rigorous accuracy assessment (larger
n per stratum):
python stratified_sample.py \
--input input.gpkg \
--output output.gpkg \
--groupby-column lu_coden \
--confidence-z 2.576 \
--margin-error 0.03 \
--min-samples 50In addition to the sampled features' original attributes, the output contains:
mapped: the stratification class the feature was originally mapped astruth: initially a copy ofmapped; intended to be edited during review to record the correct classchecked: review status flag, initialised to-1comment: free-text review comment, initially emptysample_group: review batch index (0to--n-groups - 1)
pip install -e ".[dev]"
pytest