# pySpat

**pySpat** is a small, native Python library for spatial point pattern analysis. It is also very much a work in progress.

## What’s implemented

### Windows (study regions)
- **`MaskWindow`** – binary raster window on a regular grid  
  - `area()`, `contains(xy)`, `sample_uniform(n)`, `erode(r)`, `distance_to_boundary()`, `overlap_fraction(shift)`, `rescale(factor)`
- **`RectangleWindow`** – axis-aligned rectangle \[xmin, xmax) × \[ymin, ymax)  
  - Analytic `contains`, `area`, `sample_uniform`, `erode`, `overlap_fraction`, `distance_to_boundary` (raster image), `rescale`
- **`PolyWindow`** – polygon/multipolygon (with holes) via Shapely  
  - Exact `area`, `contains`, `erode` (buffer), `overlap_fraction` (intersection), `dtb_at_points`, raster `distance_to_boundary`, `sample_uniform` (rejection in bbox), `rescale`

**Notes**
- `erode(r)` is Minkowski erosion (negative buffer for polygons).
- `distance_to_boundary()` returns an `Image` with pixel-centre distances inside the window; useful for border corrections.
- `overlap_fraction(shift)` gives |W ∩ (W+shift)| / |W|.  
  - Fast FFT path for `MaskWindow` (sub-pixel via bilinear interpolation).  
  - Exact path for `RectangleWindow` (closed form) and `PolyWindow` (Shapely intersection).

### Point patterns
- **`PPP`** – planar point pattern with:
  - Validation that all points lie inside the window
  - Optional `marks`
  - `bbox()`, `intensity()`, `thin(p)`, `rescale(factor)`, `nndist(k)`
  - Simple unit labelling (`unit` string carried through)

### Summary statistics
- **Ripley’s K**: `Kest(ppp, r, correction="translation"|"border")`
- **Besag’s L**: `Lest(ppp, r, correction=...)`
- **Pair correlation**: `pcf(ppp, r_edges, correction=...)` (annular estimator)
- **Nearest-neighbour distribution**: `Gest(ppp, r, method="border"|"raw")`
- **Empty-space function**: `Fest(ppp, r, method="border"|"raw", nsamp=..., rng=...)`
- **J-function**: `Jest(ppp, r, method_G=..., method_F=..., nsamp_F=..., rng=...)`

**Edge correction details**
- **Translation**: weights are `1 / overlap_fraction(Δ)`.  
  - `MaskWindow`: fast via FFT precomputation + bilinear interpolation.  
  - `RectangleWindow` / `PolyWindow`: exact overlap per query (memoised).
- **Border**: anchors (or sample locations) require distance-to-boundary ≥ r; distances are sampled from the window’s `distance_to_boundary()` image.

**Optional Plots**
- Boolean `showplot` argument for each summary statistic, `False` by default. Setting `True` gives a basic matplotlib figure of the function against radius, with the function-specific CSR baseline also plotted.

## Dependencies

- **Required**: `numpy`, `scipy`
- **Optional**: `shapely` (>=2.0 recommended)

## Install

Not always up to date on PyPI. Install from source:

```bash
git clone https://github.com/j-peyton/pySpat
pip install ./pySpat
# or editable:
pip install -e ./pySpat
```

## Quick start

```python
import numpy as np
from pyspat.core import RectangleWindow, PPP
from pyspat.stats import Kest, Lest, pcf

# Window: 200 x 200 square
W = RectangleWindow(0.0, 0.0, 200.0, 200.0)

# CSR sample
rng = np.random.default_rng(7)
xy = W.sample_uniform(n=3000, rng=rng)
X = PPP(xy, W, unit="µm")

# K and L
r = np.linspace(2.0, 40.0, 25)
K = Kest(X, r, correction="translation")
L = Lest(X, r, correction="translation")

# Pair correlation
edges = np.linspace(2.0, 40.0, 60)
r_mid, g = pcf(X, edges, correction="translation")
```

## Licence

MIT

## Author

Jack Peyton, November 2025
