spatialsample 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- spatialsample-0.1.0/CHANGELOG.md +36 -0
- spatialsample-0.1.0/CITATION.cff +21 -0
- spatialsample-0.1.0/LICENSE +21 -0
- spatialsample-0.1.0/MANIFEST.in +8 -0
- spatialsample-0.1.0/PKG-INFO +309 -0
- spatialsample-0.1.0/README.md +262 -0
- spatialsample-0.1.0/pyproject.toml +127 -0
- spatialsample-0.1.0/setup.cfg +4 -0
- spatialsample-0.1.0/src/spatialsample/__init__.py +89 -0
- spatialsample-0.1.0/src/spatialsample/_frame.py +91 -0
- spatialsample-0.1.0/src/spatialsample/_types.py +36 -0
- spatialsample-0.1.0/src/spatialsample/accuracy.py +232 -0
- spatialsample-0.1.0/src/spatialsample/balance.py +144 -0
- spatialsample-0.1.0/src/spatialsample/bas.py +96 -0
- spatialsample-0.1.0/src/spatialsample/cli.py +184 -0
- spatialsample-0.1.0/src/spatialsample/designs.py +562 -0
- spatialsample-0.1.0/src/spatialsample/estimators.py +222 -0
- spatialsample-0.1.0/src/spatialsample/geometry.py +253 -0
- spatialsample-0.1.0/src/spatialsample/grts.py +175 -0
- spatialsample-0.1.0/src/spatialsample/io.py +161 -0
- spatialsample-0.1.0/src/spatialsample/lpm.py +133 -0
- spatialsample-0.1.0/src/spatialsample/probabilities.py +202 -0
- spatialsample-0.1.0/src/spatialsample/py.typed +0 -0
- spatialsample-0.1.0/src/spatialsample/raster.py +170 -0
- spatialsample-0.1.0/src/spatialsample/sample.py +213 -0
- spatialsample-0.1.0/src/spatialsample.egg-info/PKG-INFO +309 -0
- spatialsample-0.1.0/src/spatialsample.egg-info/SOURCES.txt +36 -0
- spatialsample-0.1.0/src/spatialsample.egg-info/dependency_links.txt +1 -0
- spatialsample-0.1.0/src/spatialsample.egg-info/entry_points.txt +2 -0
- spatialsample-0.1.0/src/spatialsample.egg-info/requires.txt +22 -0
- spatialsample-0.1.0/src/spatialsample.egg-info/top_level.txt +1 -0
- spatialsample-0.1.0/tests/conftest.py +46 -0
- spatialsample-0.1.0/tests/test_accuracy.py +78 -0
- spatialsample-0.1.0/tests/test_designs.py +312 -0
- spatialsample-0.1.0/tests/test_estimators_balance.py +127 -0
- spatialsample-0.1.0/tests/test_geometry.py +112 -0
- spatialsample-0.1.0/tests/test_io_cli.py +274 -0
- spatialsample-0.1.0/tests/test_statistics.py +123 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project
|
|
5
|
+
uses [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-11-08
|
|
10
|
+
### Added
|
|
11
|
+
- Sampling domains: `Region` (polygons and multipolygons with holes, built from GeoJSON,
|
|
12
|
+
Shapely/GeoPandas `__geo_interface__` or coordinates) and `RasterMask` (classified rasters
|
|
13
|
+
with a north-up geotransform). Pure NumPy point-in-polygon tests.
|
|
14
|
+
- Designs: simple random (`srs`), systematic square/hexagonal grid with random start and
|
|
15
|
+
optional random rotation (`systematic`), stratified by polygons or raster classes with
|
|
16
|
+
proportional, equal or Neyman allocation (`stratified`), stratified pixel sampling for map
|
|
17
|
+
accuracy assessment (`stratified_pixels`), GRTS with hierarchical address randomisation,
|
|
18
|
+
reverse hierarchical ordering and oversample sites (`grts`), Balanced Acceptance Sampling
|
|
19
|
+
with a random-start Halton sequence (`bas`), the local pivotal method LPM2 with unequal
|
|
20
|
+
inclusion probabilities (`lpm`), Poisson-disk / minimum-distance sampling (`poisson_disk`),
|
|
21
|
+
cluster plots (`cluster`), two-stage PPS sampling (`two_stage`), systematic points along
|
|
22
|
+
lines (`points_along_lines`) and random transects (`random_transects`).
|
|
23
|
+
- Every design returns a `Sample` with inclusion probabilities (or densities), design
|
|
24
|
+
weights, site type (base/over) and visiting order, exportable to GeoJSON, CSV and
|
|
25
|
+
GeoPandas.
|
|
26
|
+
- Estimators: Horvitz-Thompson total and mean, Hajek ratio mean, SRS (with-replacement)
|
|
27
|
+
variance and the local neighbourhood variance estimator of Stevens & Olsen (2003).
|
|
28
|
+
- Voronoi spatial-balance metric (Stevens & Olsen 2004) and `compare_designs`.
|
|
29
|
+
- Map accuracy assessment after Olofsson et al. (2014): area, user's, producer's and overall
|
|
30
|
+
accuracy with standard errors and confidence intervals, sample-size formula and
|
|
31
|
+
allocation helper. Reproduces the published worked example.
|
|
32
|
+
- Command-line interface `spatialsample` with `srs`, `systematic`, `grts`, `bas`, `lpm`,
|
|
33
|
+
`poisson`, `stratified`, `balance`, `compare`, `olofsson` and `samplesize` commands.
|
|
34
|
+
|
|
35
|
+
[Unreleased]: https://github.com/YusufEminoglu/spatialsample_sdk/compare/v0.1.0...HEAD
|
|
36
|
+
[0.1.0]: https://github.com/YusufEminoglu/spatialsample_sdk/releases/tag/v0.1.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite it as below."
|
|
3
|
+
authors:
|
|
4
|
+
- family-names: "Eminoğlu"
|
|
5
|
+
given-names: "Yusuf"
|
|
6
|
+
email: "yusufeminoglu@gmail.com"
|
|
7
|
+
orcid: "https://orcid.org/0009-0005-6000-2934"
|
|
8
|
+
title: "spatialsample"
|
|
9
|
+
version: "0.1.0"
|
|
10
|
+
date-released: "2026-11-08"
|
|
11
|
+
license: "MIT"
|
|
12
|
+
repository-code: "https://github.com/YusufEminoglu/spatialsample_sdk"
|
|
13
|
+
url: "https://pypi.org/project/spatialsample/"
|
|
14
|
+
abstract: "Design-based spatial sampling in Python: GRTS, Balanced Acceptance Sampling, the local pivotal method, stratified, systematic, cluster, two-stage and transect designs, Horvitz-Thompson estimation with local-neighbourhood variance, the Voronoi spatial-balance metric and Olofsson et al. (2014) map accuracy assessment."
|
|
15
|
+
keywords:
|
|
16
|
+
- spatial sampling
|
|
17
|
+
- survey design
|
|
18
|
+
- GRTS
|
|
19
|
+
- balanced acceptance sampling
|
|
20
|
+
- local pivotal method
|
|
21
|
+
- accuracy assessment
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yusuf Eminoğlu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: spatialsample
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Design-based spatial sampling in Python: GRTS, BAS, LPM, stratified and systematic designs, Horvitz-Thompson estimation, spatial balance and Olofsson map accuracy assessment.
|
|
5
|
+
Author-email: Yusuf Eminoğlu <yusufeminoglu@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/YusufEminoglu/spatialsample_sdk
|
|
8
|
+
Project-URL: Repository, https://github.com/YusufEminoglu/spatialsample_sdk
|
|
9
|
+
Project-URL: Issues, https://github.com/YusufEminoglu/spatialsample_sdk/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/YusufEminoglu/spatialsample_sdk/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: spatial-sampling,survey-design,grts,balanced-acceptance-sampling,local-pivotal-method,spatially-balanced,horvitz-thompson,stratified-sampling,accuracy-assessment,land-cover,ecology,field-survey,gis
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
23
|
+
Classifier: Intended Audience :: Science/Research
|
|
24
|
+
Classifier: Intended Audience :: Developers
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: numpy>=1.20.0
|
|
30
|
+
Provides-Extra: scipy
|
|
31
|
+
Requires-Dist: scipy>=1.7.0; extra == "scipy"
|
|
32
|
+
Provides-Extra: shapely
|
|
33
|
+
Requires-Dist: shapely>=2.0.0; extra == "shapely"
|
|
34
|
+
Provides-Extra: geo
|
|
35
|
+
Requires-Dist: shapely>=2.0.0; extra == "geo"
|
|
36
|
+
Requires-Dist: geopandas>=0.12.0; extra == "geo"
|
|
37
|
+
Provides-Extra: all
|
|
38
|
+
Requires-Dist: scipy>=1.7.0; extra == "all"
|
|
39
|
+
Requires-Dist: shapely>=2.0.0; extra == "all"
|
|
40
|
+
Requires-Dist: geopandas>=0.12.0; extra == "all"
|
|
41
|
+
Provides-Extra: dev
|
|
42
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
43
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
44
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
45
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
46
|
+
Dynamic: license-file
|
|
47
|
+
|
|
48
|
+
# spatialsample
|
|
49
|
+
|
|
50
|
+
[](https://pypi.org/project/spatialsample/)
|
|
51
|
+
[](https://pypi.org/project/spatialsample/)
|
|
52
|
+
[](https://github.com/YusufEminoglu/spatialsample_sdk/blob/main/LICENSE)
|
|
53
|
+
[](https://docs.astral.sh/ruff/)
|
|
54
|
+
|
|
55
|
+
**Design-based spatial sampling in Python.** Decide where to put field plots, household
|
|
56
|
+
survey points or validation samples for a land-cover map, with spatially balanced designs
|
|
57
|
+
(GRTS, BAS, LPM), classical designs, exact inclusion probabilities and design weights,
|
|
58
|
+
Horvitz-Thompson estimation, a spatial-balance metric to compare designs, and the
|
|
59
|
+
Olofsson et al. (2014) good-practice workflow for map accuracy and area estimation.
|
|
60
|
+
|
|
61
|
+
The core needs only NumPy. SciPy (faster nearest-neighbour search), Shapely and GeoPandas
|
|
62
|
+
are optional.
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install spatialsample # NumPy only
|
|
68
|
+
pip install "spatialsample[scipy]" # + SciPy KD-tree for large balance/frame computations
|
|
69
|
+
pip install "spatialsample[geo]" # + Shapely and GeoPandas interoperability
|
|
70
|
+
pip install "spatialsample[all]" # everything
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Quick start
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
import spatialsample as ss
|
|
77
|
+
|
|
78
|
+
# Study area in a projected CRS (metres): an L-shaped block with a lake (hole).
|
|
79
|
+
area = ss.Region.from_coords(
|
|
80
|
+
[(0, 0), (1000, 0), (1000, 600), (600, 600), (600, 1000), (0, 1000)],
|
|
81
|
+
holes=[[(200, 200), (400, 200), (400, 400), (200, 400)]],
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
sites = ss.grts(area, n=50, n_over=10, seed=1) # 50 base + 10 replacement sites
|
|
85
|
+
sites.to_geojson("sites.geojson") # or .to_csv(), .to_geodataframe()
|
|
86
|
+
|
|
87
|
+
print(sites.base().weight.sum()) # 800000.0 (= area; weights in m^2)
|
|
88
|
+
print(ss.spatial_balance(sites, domain=area).value) # Voronoi balance, lower is better
|
|
89
|
+
|
|
90
|
+
# After the field work: estimate the mean of a variable y observed at the base sites
|
|
91
|
+
base = sites.base()
|
|
92
|
+
y = observe(base.xy) # your measurements
|
|
93
|
+
est = ss.estimate(base, y) # HT total, ratio mean, local variance
|
|
94
|
+
print(est.mean, est.mean_se)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Inputs can be a `Region`, a `RasterMask`, any GeoJSON mapping (`Polygon`, `MultiPolygon`,
|
|
98
|
+
`Feature`, `FeatureCollection`) or any object with `__geo_interface__` (Shapely, GeoPandas).
|
|
99
|
+
GRTS and LPM also accept an `(N, 2)` array of candidate sites (a finite frame, such as
|
|
100
|
+
households, lakes or pixels), optionally with a size measure `prob=` for
|
|
101
|
+
unequal-probability sampling.
|
|
102
|
+
|
|
103
|
+
> Coordinates are treated as planar. Reproject longitude/latitude data to a projected or
|
|
104
|
+
> equal-area CRS before sampling; distance-based designs and the balance metric assume
|
|
105
|
+
> linear units.
|
|
106
|
+
|
|
107
|
+
## Designs
|
|
108
|
+
|
|
109
|
+
| Function | Design | Inclusion probabilities | Reference |
|
|
110
|
+
|---|---|---|---|
|
|
111
|
+
| `srs` | Simple random sampling of locations | exact, `n / area` | |
|
|
112
|
+
| `systematic` | Square or hexagonal grid, uniformly random start, optional random rotation | exact, `1 / cell area` (random `n`) | |
|
|
113
|
+
| `stratified` | Stratified by polygons or raster classes; proportional, equal or Neyman allocation; SRS, BAS, GRTS or LPM within strata | exact, `n_h / area_h` | Cochran (1977) |
|
|
114
|
+
| `stratified_pixels` | Stratified random sampling of raster pixels without replacement | exact, `n_h / N_h` | Olofsson et al. (2014) |
|
|
115
|
+
| `grts` | Generalized Random Tessellation Stratified: randomised quadrant-recursive addresses, systematic selection on the resulting line, reverse hierarchical ordering, oversample sites | exact (finite frames), equal or unequal | Stevens & Olsen (2004) |
|
|
116
|
+
| `bas` | Balanced Acceptance Sampling from a random-start Halton sequence (bases 2, 3); oversample continues the sequence | exact, `n / area` | Robertson et al. (2013) |
|
|
117
|
+
| `lpm` | Local pivotal method (LPM2), equal or unequal probabilities | exact | Grafström et al. (2012) |
|
|
118
|
+
| `poisson_disk` | Minimum-distance (simple sequential inhibition) sampling | approximate, `n / area` | |
|
|
119
|
+
| `cluster` | Cluster plots (FIA-style centre + 3 at 120 degrees, 2 x 2, line, cross or custom) around SRS/BAS/GRTS centres | `n_clusters / area` per subplot | |
|
|
120
|
+
| `two_stage` | PPS-by-area (or equal) selection of primary units, then SRS inside each | exact; self-weighting with PPS | |
|
|
121
|
+
| `points_along_lines` | Systematic points along roads/rivers/hedgerows with random start | exact, `1 / spacing` per unit length | |
|
|
122
|
+
| `random_transects` | Straight transects of fixed length and random (or fixed) bearing | `n / area` | |
|
|
123
|
+
|
|
124
|
+
Every design returns a `Sample` with `xy`, `pi` (inclusion probability for finite frames,
|
|
125
|
+
inclusion density per unit area for continuous designs, following Cordy 1993), `weight =
|
|
126
|
+
1/pi`, `site_type` (`base` / `over`), visiting `order`, optional `stratum`, `index` and
|
|
127
|
+
extra columns (`cluster`, `psu`, `chainage`, pixel `row`/`col`, ...).
|
|
128
|
+
|
|
129
|
+
GRTS and LPM on a continuous area use a dense, randomly offset grid frame (default
|
|
130
|
+
`max(4000, 100 (n + n_over))` cells for GRTS) and move each selected cell centre to a
|
|
131
|
+
uniform location within its cell, as recent versions of the R package spsurvey do.
|
|
132
|
+
|
|
133
|
+
### Which design is most spread out?
|
|
134
|
+
|
|
135
|
+
Voronoi spatial balance (Stevens & Olsen 2004): the variance of the summed inclusion
|
|
136
|
+
probability inside each site's Voronoi polygon, `mean((v_i - 1)^2)`. Zero is perfectly
|
|
137
|
+
balanced. Measured with `ss.compare_designs` on the L-shaped area above (800,000 m^2, with
|
|
138
|
+
a hole), `n = 50`, 100 independent draws per design, 20,000-point evaluation grid:
|
|
139
|
+
|
|
140
|
+
| Design | Voronoi balance, mean | sd |
|
|
141
|
+
|---|---:|---:|
|
|
142
|
+
| systematic, square (random rotation) | 0.041 | 0.011 |
|
|
143
|
+
| systematic, hexagonal (random rotation) | 0.044 | 0.008 |
|
|
144
|
+
| LPM | 0.074 | 0.017 |
|
|
145
|
+
| BAS | 0.087 | 0.021 |
|
|
146
|
+
| GRTS | 0.105 | 0.024 |
|
|
147
|
+
| Poisson-disk (default distance) | 0.118 | 0.033 |
|
|
148
|
+
| simple random | 0.352 | 0.106 |
|
|
149
|
+
|
|
150
|
+
Systematic grids are the most even but have a random sample size, no valid design-based
|
|
151
|
+
variance estimator and are vulnerable to periodic patterns; GRTS, BAS and LPM give fixed
|
|
152
|
+
sample sizes, exact inclusion probabilities and three to five times better balance than
|
|
153
|
+
simple random sampling. Reproduce with `spatialsample compare area.geojson --n 50 --reps 100`.
|
|
154
|
+
|
|
155
|
+
### Does balance pay off?
|
|
156
|
+
|
|
157
|
+
Estimating the mean of the smooth surface `10 + 0.01 x + 5 sin(y / 150)` over the same area
|
|
158
|
+
with `n = 30` (400 draws per design, ratio estimator, `ss.estimate` defaults):
|
|
159
|
+
|
|
160
|
+
| Design | bias | true SD of the estimate | mean estimated SE |
|
|
161
|
+
|---|---:|---:|---:|
|
|
162
|
+
| simple random | +0.026 | 0.905 | 0.880 (SRS variance) |
|
|
163
|
+
| GRTS | -0.015 | 0.331 | 0.399 (local variance) |
|
|
164
|
+
| LPM | -0.026 | 0.282 | 0.423 (local variance) |
|
|
165
|
+
| BAS | +0.011 | 0.180 | 0.426 (local variance) |
|
|
166
|
+
| systematic, hexagonal | -0.003 | 0.185 | 0.456 (local variance) |
|
|
167
|
+
|
|
168
|
+
All biases are within Monte Carlo error. Spatially balanced designs cut the standard error
|
|
169
|
+
by a factor of 2.7 (GRTS) to 5 (BAS) here; the local neighbourhood variance estimator captures much of that
|
|
170
|
+
gain while staying on the conservative side for smooth surfaces.
|
|
171
|
+
|
|
172
|
+
## Estimation
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
ss.ht_total(y, pi) # Horvitz-Thompson total (integral for continuous designs)
|
|
176
|
+
ss.ht_mean(y, pi, size=area) # HT mean; without size: Hajek ratio mean
|
|
177
|
+
ss.srs_variance(y, pi) # with-replacement variance of the HT total
|
|
178
|
+
ss.local_variance(y, xy, pi, k=4) # Stevens & Olsen (2003) neighbourhood variance
|
|
179
|
+
ss.estimate(sample, y) # all of the above with a CI, design-aware defaults
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`local_variance` uses neighbourhoods of each site and its three nearest sample neighbours,
|
|
183
|
+
initial weights proportional to `1/pi` balanced to a doubly stochastic matrix by
|
|
184
|
+
iterative proportional fitting. By default each neighbourhood term is divided by
|
|
185
|
+
`1 - sum_j w_ij^2`, the usual small-sample correction for a weighted variance, which makes
|
|
186
|
+
the estimator unbiased (relative to the with-replacement variance) when the variable has no
|
|
187
|
+
spatial structure; pass `correction=False` for the uncorrected form, which is about 25 %
|
|
188
|
+
smaller with `k = 4`. Tests check both properties by simulation.
|
|
189
|
+
|
|
190
|
+
## Map accuracy and area estimation (Olofsson et al. 2014)
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
import spatialsample as ss
|
|
194
|
+
|
|
195
|
+
# Section 5 worked example of Olofsson et al. (2014): sample counts, rows = map class,
|
|
196
|
+
# columns = reference class; mapped areas in 30 m pixels (0.09 ha each).
|
|
197
|
+
matrix = [[66, 0, 5, 4],
|
|
198
|
+
[0, 55, 8, 12],
|
|
199
|
+
[1, 0, 153, 11],
|
|
200
|
+
[2, 1, 9, 313]]
|
|
201
|
+
pixels = [200_000, 150_000, 3_200_000, 6_450_000]
|
|
202
|
+
labels = ["Deforestation", "Forest gain", "Stable forest", "Stable non-forest"]
|
|
203
|
+
|
|
204
|
+
res = ss.olofsson(matrix, [p * 0.09 for p in pixels], labels=labels)
|
|
205
|
+
print(res.summary())
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
class mapped area est. area +/- 95% CI user +/- producer +/-
|
|
210
|
+
-------------------------------------------------------------------------------------------------
|
|
211
|
+
Deforestation 18,000 21,158 6,158 0.880 0.074 0.749 0.213
|
|
212
|
+
Forest gain 13,500 11,686 3,756 0.733 0.101 0.847 0.254
|
|
213
|
+
Stable forest 288,000 285,770 15,510 0.927 0.040 0.935 0.034
|
|
214
|
+
Stable non-forest 580,500 581,386 16,281 0.963 0.021 0.962 0.018
|
|
215
|
+
overall accuracy 0.947 +/- 0.018 (95% CI), n = 640
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
These reproduce the published estimates (for example a deforestation area of
|
|
219
|
+
21,158 ha +/- 6,158 ha against 18,000 ha mapped; user's accuracy 0.88, producer's accuracy
|
|
220
|
+
0.75 +/- 0.21; overall accuracy 0.95). The paper's sample-size example (conjectured user's
|
|
221
|
+
accuracies 0.7, 0.6, 0.9, 0.95 and a target standard error of 0.01 for overall accuracy) is
|
|
222
|
+
`ss.sample_size(pixels, [0.7, 0.6, 0.9, 0.95], 0.01) == 641`.
|
|
223
|
+
|
|
224
|
+
To draw that sample from a classified raster:
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
mask = ss.RasterMask(landcover_array, origin=(x_min, y_max), cell_size=30, nodata=0)
|
|
228
|
+
n_h = ss.allocate_accuracy_sample(641, pixels, rare={0: 75, 1: 75}) # fix rare classes
|
|
229
|
+
validation = ss.stratified_pixels(mask, dict(zip(mask.class_counts(), n_h)), seed=7)
|
|
230
|
+
validation.to_csv("validation_points.csv") # x, y, row, col, stratum, pi, weight
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Command line
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
# 50 GRTS sites plus 10 replacements
|
|
237
|
+
spatialsample grts area.geojson --n 50 --over 10 --seed 1 -o sites.geojson
|
|
238
|
+
|
|
239
|
+
# other designs: srs, systematic (--shape hex --random-rotation), bas, lpm, poisson,
|
|
240
|
+
# stratified (--field zone --allocation proportional --method grts)
|
|
241
|
+
spatialsample bas area.geojson --n 40 --over 5 --seed 3 -o sites.csv
|
|
242
|
+
|
|
243
|
+
# spatial balance of any set of sites (GeoJSON points or CSV with x,y)
|
|
244
|
+
spatialsample balance sites.geojson --frame area.geojson
|
|
245
|
+
|
|
246
|
+
# compare designs on your own study area
|
|
247
|
+
spatialsample compare area.geojson --n 50 --reps 50
|
|
248
|
+
|
|
249
|
+
# Olofsson accuracy assessment from an error matrix CSV (header row and label column optional)
|
|
250
|
+
spatialsample olofsson matrix.csv --areas 200000,150000,3200000,6450000 --pixel-area 0.09
|
|
251
|
+
spatialsample samplesize --areas 200000,150000,3200000,6450000 --users 0.7,0.6,0.9,0.95 --target-se 0.01
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Output is GeoJSON (default, also to stdout when `-o` is omitted) or CSV by file extension.
|
|
255
|
+
Each site carries `site_id`, `design`, `site_type`, `order`, `pi` and `weight`.
|
|
256
|
+
|
|
257
|
+
## API overview
|
|
258
|
+
|
|
259
|
+
| Area | Names |
|
|
260
|
+
|---|---|
|
|
261
|
+
| Domains | `Region`, `Polygon`, `RasterMask` |
|
|
262
|
+
| Designs | `srs`, `systematic`, `stratified`, `stratified_pixels`, `grts`, `bas`, `lpm`, `poisson_disk`, `cluster`, `two_stage`, `points_along_lines`, `random_transects` |
|
|
263
|
+
| Building blocks | `inclusion_probabilities`, `allocate`, `grts_order`, `reverse_hierarchical_order`, `halton`, `lpm2_select` |
|
|
264
|
+
| Estimation | `ht_total`, `ht_mean`, `srs_variance`, `local_variance`, `local_weights`, `estimate`, `Estimate` |
|
|
265
|
+
| Balance | `voronoi_balance`, `spatial_balance`, `compare_designs` |
|
|
266
|
+
| Accuracy | `olofsson`, `AccuracyAssessment`, `sample_size`, `allocate_accuracy_sample` |
|
|
267
|
+
| I/O | `Sample.to_geojson`, `Sample.to_csv`, `Sample.to_geodataframe`, `read_region`, `read_strata`, `read_points`, `read_lines`, `read_matrix_csv` |
|
|
268
|
+
|
|
269
|
+
## Development and testing
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
pip install -e ".[dev,all]"
|
|
273
|
+
pytest --cov=spatialsample
|
|
274
|
+
ruff check . && ruff format --check src tests && mypy src
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
The test suite includes Monte Carlo checks: empirical inclusion frequencies of GRTS and LPM
|
|
278
|
+
match unequal target probabilities, Horvitz-Thompson estimates are unbiased for finite and
|
|
279
|
+
continuous designs, GRTS/BAS/LPM are better balanced than simple random sampling, holes are
|
|
280
|
+
never sampled, and every design is reproducible with a seed.
|
|
281
|
+
|
|
282
|
+
## References
|
|
283
|
+
|
|
284
|
+
- Cochran, W. G. (1977). *Sampling Techniques* (3rd ed.). Wiley.
|
|
285
|
+
- Cordy, C. B. (1993). An extension of the Horvitz-Thompson theorem to point sampling from
|
|
286
|
+
a continuous universe. *Statistics & Probability Letters*, 18(5), 353-362.
|
|
287
|
+
- Dumelle, M., Kincaid, T., Olsen, A. R., & Weber, M. (2023). spsurvey: Spatial sampling
|
|
288
|
+
design and analysis in R. *Journal of Statistical Software*, 105(3), 1-29.
|
|
289
|
+
- Grafström, A., Lundström, N. L. P., & Schelin, L. (2012). Spatially balanced sampling
|
|
290
|
+
through the pivotal method. *Biometrics*, 68(2), 514-520.
|
|
291
|
+
- Horvitz, D. G., & Thompson, D. J. (1952). A generalization of sampling without
|
|
292
|
+
replacement from a finite universe. *Journal of the American Statistical Association*,
|
|
293
|
+
47(260), 663-685.
|
|
294
|
+
- Olofsson, P., Foody, G. M., Herold, M., Stehman, S. V., Woodcock, C. E., & Wulder, M. A.
|
|
295
|
+
(2014). Good practices for estimating area and assessing accuracy of land change.
|
|
296
|
+
*Remote Sensing of Environment*, 148, 42-57.
|
|
297
|
+
- Robertson, B. L., Brown, J. A., McDonald, T., & Jaksons, P. (2013). BAS: Balanced
|
|
298
|
+
acceptance sampling of natural resources. *Biometrics*, 69(3), 776-784.
|
|
299
|
+
- Stevens, D. L., & Olsen, A. R. (2003). Variance estimation for spatially balanced samples
|
|
300
|
+
of environmental resources. *Environmetrics*, 14(6), 593-610.
|
|
301
|
+
- Stevens, D. L., & Olsen, A. R. (2004). Spatially balanced sampling of natural resources.
|
|
302
|
+
*Journal of the American Statistical Association*, 99(465), 262-278.
|
|
303
|
+
- Tillé, Y. (2006). *Sampling Algorithms*. Springer.
|
|
304
|
+
|
|
305
|
+
## License
|
|
306
|
+
|
|
307
|
+
MIT, see [LICENSE](https://github.com/YusufEminoglu/spatialsample_sdk/blob/main/LICENSE).
|
|
308
|
+
If you use spatialsample in research, please cite it using
|
|
309
|
+
[CITATION.cff](https://github.com/YusufEminoglu/spatialsample_sdk/blob/main/CITATION.cff).
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# spatialsample
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/spatialsample/)
|
|
4
|
+
[](https://pypi.org/project/spatialsample/)
|
|
5
|
+
[](https://github.com/YusufEminoglu/spatialsample_sdk/blob/main/LICENSE)
|
|
6
|
+
[](https://docs.astral.sh/ruff/)
|
|
7
|
+
|
|
8
|
+
**Design-based spatial sampling in Python.** Decide where to put field plots, household
|
|
9
|
+
survey points or validation samples for a land-cover map, with spatially balanced designs
|
|
10
|
+
(GRTS, BAS, LPM), classical designs, exact inclusion probabilities and design weights,
|
|
11
|
+
Horvitz-Thompson estimation, a spatial-balance metric to compare designs, and the
|
|
12
|
+
Olofsson et al. (2014) good-practice workflow for map accuracy and area estimation.
|
|
13
|
+
|
|
14
|
+
The core needs only NumPy. SciPy (faster nearest-neighbour search), Shapely and GeoPandas
|
|
15
|
+
are optional.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install spatialsample # NumPy only
|
|
21
|
+
pip install "spatialsample[scipy]" # + SciPy KD-tree for large balance/frame computations
|
|
22
|
+
pip install "spatialsample[geo]" # + Shapely and GeoPandas interoperability
|
|
23
|
+
pip install "spatialsample[all]" # everything
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick start
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import spatialsample as ss
|
|
30
|
+
|
|
31
|
+
# Study area in a projected CRS (metres): an L-shaped block with a lake (hole).
|
|
32
|
+
area = ss.Region.from_coords(
|
|
33
|
+
[(0, 0), (1000, 0), (1000, 600), (600, 600), (600, 1000), (0, 1000)],
|
|
34
|
+
holes=[[(200, 200), (400, 200), (400, 400), (200, 400)]],
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
sites = ss.grts(area, n=50, n_over=10, seed=1) # 50 base + 10 replacement sites
|
|
38
|
+
sites.to_geojson("sites.geojson") # or .to_csv(), .to_geodataframe()
|
|
39
|
+
|
|
40
|
+
print(sites.base().weight.sum()) # 800000.0 (= area; weights in m^2)
|
|
41
|
+
print(ss.spatial_balance(sites, domain=area).value) # Voronoi balance, lower is better
|
|
42
|
+
|
|
43
|
+
# After the field work: estimate the mean of a variable y observed at the base sites
|
|
44
|
+
base = sites.base()
|
|
45
|
+
y = observe(base.xy) # your measurements
|
|
46
|
+
est = ss.estimate(base, y) # HT total, ratio mean, local variance
|
|
47
|
+
print(est.mean, est.mean_se)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Inputs can be a `Region`, a `RasterMask`, any GeoJSON mapping (`Polygon`, `MultiPolygon`,
|
|
51
|
+
`Feature`, `FeatureCollection`) or any object with `__geo_interface__` (Shapely, GeoPandas).
|
|
52
|
+
GRTS and LPM also accept an `(N, 2)` array of candidate sites (a finite frame, such as
|
|
53
|
+
households, lakes or pixels), optionally with a size measure `prob=` for
|
|
54
|
+
unequal-probability sampling.
|
|
55
|
+
|
|
56
|
+
> Coordinates are treated as planar. Reproject longitude/latitude data to a projected or
|
|
57
|
+
> equal-area CRS before sampling; distance-based designs and the balance metric assume
|
|
58
|
+
> linear units.
|
|
59
|
+
|
|
60
|
+
## Designs
|
|
61
|
+
|
|
62
|
+
| Function | Design | Inclusion probabilities | Reference |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| `srs` | Simple random sampling of locations | exact, `n / area` | |
|
|
65
|
+
| `systematic` | Square or hexagonal grid, uniformly random start, optional random rotation | exact, `1 / cell area` (random `n`) | |
|
|
66
|
+
| `stratified` | Stratified by polygons or raster classes; proportional, equal or Neyman allocation; SRS, BAS, GRTS or LPM within strata | exact, `n_h / area_h` | Cochran (1977) |
|
|
67
|
+
| `stratified_pixels` | Stratified random sampling of raster pixels without replacement | exact, `n_h / N_h` | Olofsson et al. (2014) |
|
|
68
|
+
| `grts` | Generalized Random Tessellation Stratified: randomised quadrant-recursive addresses, systematic selection on the resulting line, reverse hierarchical ordering, oversample sites | exact (finite frames), equal or unequal | Stevens & Olsen (2004) |
|
|
69
|
+
| `bas` | Balanced Acceptance Sampling from a random-start Halton sequence (bases 2, 3); oversample continues the sequence | exact, `n / area` | Robertson et al. (2013) |
|
|
70
|
+
| `lpm` | Local pivotal method (LPM2), equal or unequal probabilities | exact | Grafström et al. (2012) |
|
|
71
|
+
| `poisson_disk` | Minimum-distance (simple sequential inhibition) sampling | approximate, `n / area` | |
|
|
72
|
+
| `cluster` | Cluster plots (FIA-style centre + 3 at 120 degrees, 2 x 2, line, cross or custom) around SRS/BAS/GRTS centres | `n_clusters / area` per subplot | |
|
|
73
|
+
| `two_stage` | PPS-by-area (or equal) selection of primary units, then SRS inside each | exact; self-weighting with PPS | |
|
|
74
|
+
| `points_along_lines` | Systematic points along roads/rivers/hedgerows with random start | exact, `1 / spacing` per unit length | |
|
|
75
|
+
| `random_transects` | Straight transects of fixed length and random (or fixed) bearing | `n / area` | |
|
|
76
|
+
|
|
77
|
+
Every design returns a `Sample` with `xy`, `pi` (inclusion probability for finite frames,
|
|
78
|
+
inclusion density per unit area for continuous designs, following Cordy 1993), `weight =
|
|
79
|
+
1/pi`, `site_type` (`base` / `over`), visiting `order`, optional `stratum`, `index` and
|
|
80
|
+
extra columns (`cluster`, `psu`, `chainage`, pixel `row`/`col`, ...).
|
|
81
|
+
|
|
82
|
+
GRTS and LPM on a continuous area use a dense, randomly offset grid frame (default
|
|
83
|
+
`max(4000, 100 (n + n_over))` cells for GRTS) and move each selected cell centre to a
|
|
84
|
+
uniform location within its cell, as recent versions of the R package spsurvey do.
|
|
85
|
+
|
|
86
|
+
### Which design is most spread out?
|
|
87
|
+
|
|
88
|
+
Voronoi spatial balance (Stevens & Olsen 2004): the variance of the summed inclusion
|
|
89
|
+
probability inside each site's Voronoi polygon, `mean((v_i - 1)^2)`. Zero is perfectly
|
|
90
|
+
balanced. Measured with `ss.compare_designs` on the L-shaped area above (800,000 m^2, with
|
|
91
|
+
a hole), `n = 50`, 100 independent draws per design, 20,000-point evaluation grid:
|
|
92
|
+
|
|
93
|
+
| Design | Voronoi balance, mean | sd |
|
|
94
|
+
|---|---:|---:|
|
|
95
|
+
| systematic, square (random rotation) | 0.041 | 0.011 |
|
|
96
|
+
| systematic, hexagonal (random rotation) | 0.044 | 0.008 |
|
|
97
|
+
| LPM | 0.074 | 0.017 |
|
|
98
|
+
| BAS | 0.087 | 0.021 |
|
|
99
|
+
| GRTS | 0.105 | 0.024 |
|
|
100
|
+
| Poisson-disk (default distance) | 0.118 | 0.033 |
|
|
101
|
+
| simple random | 0.352 | 0.106 |
|
|
102
|
+
|
|
103
|
+
Systematic grids are the most even but have a random sample size, no valid design-based
|
|
104
|
+
variance estimator and are vulnerable to periodic patterns; GRTS, BAS and LPM give fixed
|
|
105
|
+
sample sizes, exact inclusion probabilities and three to five times better balance than
|
|
106
|
+
simple random sampling. Reproduce with `spatialsample compare area.geojson --n 50 --reps 100`.
|
|
107
|
+
|
|
108
|
+
### Does balance pay off?
|
|
109
|
+
|
|
110
|
+
Estimating the mean of the smooth surface `10 + 0.01 x + 5 sin(y / 150)` over the same area
|
|
111
|
+
with `n = 30` (400 draws per design, ratio estimator, `ss.estimate` defaults):
|
|
112
|
+
|
|
113
|
+
| Design | bias | true SD of the estimate | mean estimated SE |
|
|
114
|
+
|---|---:|---:|---:|
|
|
115
|
+
| simple random | +0.026 | 0.905 | 0.880 (SRS variance) |
|
|
116
|
+
| GRTS | -0.015 | 0.331 | 0.399 (local variance) |
|
|
117
|
+
| LPM | -0.026 | 0.282 | 0.423 (local variance) |
|
|
118
|
+
| BAS | +0.011 | 0.180 | 0.426 (local variance) |
|
|
119
|
+
| systematic, hexagonal | -0.003 | 0.185 | 0.456 (local variance) |
|
|
120
|
+
|
|
121
|
+
All biases are within Monte Carlo error. Spatially balanced designs cut the standard error
|
|
122
|
+
by a factor of 2.7 (GRTS) to 5 (BAS) here; the local neighbourhood variance estimator captures much of that
|
|
123
|
+
gain while staying on the conservative side for smooth surfaces.
|
|
124
|
+
|
|
125
|
+
## Estimation
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
ss.ht_total(y, pi) # Horvitz-Thompson total (integral for continuous designs)
|
|
129
|
+
ss.ht_mean(y, pi, size=area) # HT mean; without size: Hajek ratio mean
|
|
130
|
+
ss.srs_variance(y, pi) # with-replacement variance of the HT total
|
|
131
|
+
ss.local_variance(y, xy, pi, k=4) # Stevens & Olsen (2003) neighbourhood variance
|
|
132
|
+
ss.estimate(sample, y) # all of the above with a CI, design-aware defaults
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`local_variance` uses neighbourhoods of each site and its three nearest sample neighbours,
|
|
136
|
+
initial weights proportional to `1/pi` balanced to a doubly stochastic matrix by
|
|
137
|
+
iterative proportional fitting. By default each neighbourhood term is divided by
|
|
138
|
+
`1 - sum_j w_ij^2`, the usual small-sample correction for a weighted variance, which makes
|
|
139
|
+
the estimator unbiased (relative to the with-replacement variance) when the variable has no
|
|
140
|
+
spatial structure; pass `correction=False` for the uncorrected form, which is about 25 %
|
|
141
|
+
smaller with `k = 4`. Tests check both properties by simulation.
|
|
142
|
+
|
|
143
|
+
## Map accuracy and area estimation (Olofsson et al. 2014)
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
import spatialsample as ss
|
|
147
|
+
|
|
148
|
+
# Section 5 worked example of Olofsson et al. (2014): sample counts, rows = map class,
|
|
149
|
+
# columns = reference class; mapped areas in 30 m pixels (0.09 ha each).
|
|
150
|
+
matrix = [[66, 0, 5, 4],
|
|
151
|
+
[0, 55, 8, 12],
|
|
152
|
+
[1, 0, 153, 11],
|
|
153
|
+
[2, 1, 9, 313]]
|
|
154
|
+
pixels = [200_000, 150_000, 3_200_000, 6_450_000]
|
|
155
|
+
labels = ["Deforestation", "Forest gain", "Stable forest", "Stable non-forest"]
|
|
156
|
+
|
|
157
|
+
res = ss.olofsson(matrix, [p * 0.09 for p in pixels], labels=labels)
|
|
158
|
+
print(res.summary())
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
class mapped area est. area +/- 95% CI user +/- producer +/-
|
|
163
|
+
-------------------------------------------------------------------------------------------------
|
|
164
|
+
Deforestation 18,000 21,158 6,158 0.880 0.074 0.749 0.213
|
|
165
|
+
Forest gain 13,500 11,686 3,756 0.733 0.101 0.847 0.254
|
|
166
|
+
Stable forest 288,000 285,770 15,510 0.927 0.040 0.935 0.034
|
|
167
|
+
Stable non-forest 580,500 581,386 16,281 0.963 0.021 0.962 0.018
|
|
168
|
+
overall accuracy 0.947 +/- 0.018 (95% CI), n = 640
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
These reproduce the published estimates (for example a deforestation area of
|
|
172
|
+
21,158 ha +/- 6,158 ha against 18,000 ha mapped; user's accuracy 0.88, producer's accuracy
|
|
173
|
+
0.75 +/- 0.21; overall accuracy 0.95). The paper's sample-size example (conjectured user's
|
|
174
|
+
accuracies 0.7, 0.6, 0.9, 0.95 and a target standard error of 0.01 for overall accuracy) is
|
|
175
|
+
`ss.sample_size(pixels, [0.7, 0.6, 0.9, 0.95], 0.01) == 641`.
|
|
176
|
+
|
|
177
|
+
To draw that sample from a classified raster:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
mask = ss.RasterMask(landcover_array, origin=(x_min, y_max), cell_size=30, nodata=0)
|
|
181
|
+
n_h = ss.allocate_accuracy_sample(641, pixels, rare={0: 75, 1: 75}) # fix rare classes
|
|
182
|
+
validation = ss.stratified_pixels(mask, dict(zip(mask.class_counts(), n_h)), seed=7)
|
|
183
|
+
validation.to_csv("validation_points.csv") # x, y, row, col, stratum, pi, weight
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Command line
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# 50 GRTS sites plus 10 replacements
|
|
190
|
+
spatialsample grts area.geojson --n 50 --over 10 --seed 1 -o sites.geojson
|
|
191
|
+
|
|
192
|
+
# other designs: srs, systematic (--shape hex --random-rotation), bas, lpm, poisson,
|
|
193
|
+
# stratified (--field zone --allocation proportional --method grts)
|
|
194
|
+
spatialsample bas area.geojson --n 40 --over 5 --seed 3 -o sites.csv
|
|
195
|
+
|
|
196
|
+
# spatial balance of any set of sites (GeoJSON points or CSV with x,y)
|
|
197
|
+
spatialsample balance sites.geojson --frame area.geojson
|
|
198
|
+
|
|
199
|
+
# compare designs on your own study area
|
|
200
|
+
spatialsample compare area.geojson --n 50 --reps 50
|
|
201
|
+
|
|
202
|
+
# Olofsson accuracy assessment from an error matrix CSV (header row and label column optional)
|
|
203
|
+
spatialsample olofsson matrix.csv --areas 200000,150000,3200000,6450000 --pixel-area 0.09
|
|
204
|
+
spatialsample samplesize --areas 200000,150000,3200000,6450000 --users 0.7,0.6,0.9,0.95 --target-se 0.01
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Output is GeoJSON (default, also to stdout when `-o` is omitted) or CSV by file extension.
|
|
208
|
+
Each site carries `site_id`, `design`, `site_type`, `order`, `pi` and `weight`.
|
|
209
|
+
|
|
210
|
+
## API overview
|
|
211
|
+
|
|
212
|
+
| Area | Names |
|
|
213
|
+
|---|---|
|
|
214
|
+
| Domains | `Region`, `Polygon`, `RasterMask` |
|
|
215
|
+
| Designs | `srs`, `systematic`, `stratified`, `stratified_pixels`, `grts`, `bas`, `lpm`, `poisson_disk`, `cluster`, `two_stage`, `points_along_lines`, `random_transects` |
|
|
216
|
+
| Building blocks | `inclusion_probabilities`, `allocate`, `grts_order`, `reverse_hierarchical_order`, `halton`, `lpm2_select` |
|
|
217
|
+
| Estimation | `ht_total`, `ht_mean`, `srs_variance`, `local_variance`, `local_weights`, `estimate`, `Estimate` |
|
|
218
|
+
| Balance | `voronoi_balance`, `spatial_balance`, `compare_designs` |
|
|
219
|
+
| Accuracy | `olofsson`, `AccuracyAssessment`, `sample_size`, `allocate_accuracy_sample` |
|
|
220
|
+
| I/O | `Sample.to_geojson`, `Sample.to_csv`, `Sample.to_geodataframe`, `read_region`, `read_strata`, `read_points`, `read_lines`, `read_matrix_csv` |
|
|
221
|
+
|
|
222
|
+
## Development and testing
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
pip install -e ".[dev,all]"
|
|
226
|
+
pytest --cov=spatialsample
|
|
227
|
+
ruff check . && ruff format --check src tests && mypy src
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The test suite includes Monte Carlo checks: empirical inclusion frequencies of GRTS and LPM
|
|
231
|
+
match unequal target probabilities, Horvitz-Thompson estimates are unbiased for finite and
|
|
232
|
+
continuous designs, GRTS/BAS/LPM are better balanced than simple random sampling, holes are
|
|
233
|
+
never sampled, and every design is reproducible with a seed.
|
|
234
|
+
|
|
235
|
+
## References
|
|
236
|
+
|
|
237
|
+
- Cochran, W. G. (1977). *Sampling Techniques* (3rd ed.). Wiley.
|
|
238
|
+
- Cordy, C. B. (1993). An extension of the Horvitz-Thompson theorem to point sampling from
|
|
239
|
+
a continuous universe. *Statistics & Probability Letters*, 18(5), 353-362.
|
|
240
|
+
- Dumelle, M., Kincaid, T., Olsen, A. R., & Weber, M. (2023). spsurvey: Spatial sampling
|
|
241
|
+
design and analysis in R. *Journal of Statistical Software*, 105(3), 1-29.
|
|
242
|
+
- Grafström, A., Lundström, N. L. P., & Schelin, L. (2012). Spatially balanced sampling
|
|
243
|
+
through the pivotal method. *Biometrics*, 68(2), 514-520.
|
|
244
|
+
- Horvitz, D. G., & Thompson, D. J. (1952). A generalization of sampling without
|
|
245
|
+
replacement from a finite universe. *Journal of the American Statistical Association*,
|
|
246
|
+
47(260), 663-685.
|
|
247
|
+
- Olofsson, P., Foody, G. M., Herold, M., Stehman, S. V., Woodcock, C. E., & Wulder, M. A.
|
|
248
|
+
(2014). Good practices for estimating area and assessing accuracy of land change.
|
|
249
|
+
*Remote Sensing of Environment*, 148, 42-57.
|
|
250
|
+
- Robertson, B. L., Brown, J. A., McDonald, T., & Jaksons, P. (2013). BAS: Balanced
|
|
251
|
+
acceptance sampling of natural resources. *Biometrics*, 69(3), 776-784.
|
|
252
|
+
- Stevens, D. L., & Olsen, A. R. (2003). Variance estimation for spatially balanced samples
|
|
253
|
+
of environmental resources. *Environmetrics*, 14(6), 593-610.
|
|
254
|
+
- Stevens, D. L., & Olsen, A. R. (2004). Spatially balanced sampling of natural resources.
|
|
255
|
+
*Journal of the American Statistical Association*, 99(465), 262-278.
|
|
256
|
+
- Tillé, Y. (2006). *Sampling Algorithms*. Springer.
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT, see [LICENSE](https://github.com/YusufEminoglu/spatialsample_sdk/blob/main/LICENSE).
|
|
261
|
+
If you use spatialsample in research, please cite it using
|
|
262
|
+
[CITATION.cff](https://github.com/YusufEminoglu/spatialsample_sdk/blob/main/CITATION.cff).
|