Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ dist/
htmlcov/
.ruff_cache/
site/
examples/cache/
.cache/
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ Revival release: the code base now targets Python 3.10+ with current
versions of pandas (>= 2.2), GeoPandas (>= 1.0), scikit-learn and statsmodels.

### Added
- `crime_mapping.get_city_shape("Natal, RN, Brazil")` — thin wrapper around
`osmnx.geocode_to_gdf` returning the raw city shape.
- `examples/natal.ipynb`: an executed end-to-end walkthrough on Natal with
synthetic data, also rendered in the documentation.
- `PredictionPipeline.evaluate` accepts a list of scorings and returns a
DataFrame (one CV pass for all metrics).
- README section explaining the framework (thesis, Chapter 3, Figures 7-12);
the README is now the documentation home page.
- `crime_mapping.load_study_area("City, Country")` fetches a study area
boundary from OpenStreetMap via `osmnx` (`pip install predspot[osm]`).
- `synthetic.generate_crimes` generates synthetic events inside any study
Expand Down
10 changes: 9 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,15 @@ mkdocs serve # live preview at http://127.0.0.1:8000
mkdocs build --strict # what CI runs
```

Pushing to `master` deploys the site to GitHub Pages automatically.
Pushing to `master` deploys the site to GitHub Pages automatically. The home
page is generated from `README.md` (see `docs/hooks/readme.py`), and the
example notebook is rendered from `examples/natal.ipynb`; regenerate and
re-execute it with:

```bash
python examples/build_natal_notebook.py
jupyter nbconvert --to notebook --execute --inplace examples/natal.ipynb
```

## Releasing

Expand Down
293 changes: 198 additions & 95 deletions README.md

Large diffs are not rendered by default.

Binary file added docs/assets/thesis/fig07-dataset-preparation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/thesis/fig08-feature-ingest.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/thesis/fig10-feature-set-example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/thesis/fig11-ml-modelling.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/thesis/fig12-prediction-pipeline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/examples/natal.ipynb
2 changes: 1 addition & 1 deletion docs/guide/modelling.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ estimator sees them as independent samples.
## Evaluating

```python
pipeline.evaluate("r2", cv=5) # or "mse"
pipeline.evaluate("r2", cv=5) # or "mse", or ["r2", "mse"] for a DataFrame
```

Periods are ordered and split with scikit-learn's `TimeSeriesSplit`, so each
Expand Down
31 changes: 31 additions & 0 deletions docs/hooks/readme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""MkDocs hook: the documentation home page is the repository README.

The README is written for GitHub (paths relative to the repository root); this
hook rewrites those paths so the same content renders on the site.
"""

import re
from pathlib import Path

README = Path(__file__).resolve().parents[2] / "README.md"

REPO_BLOB = "https://github.com/adaj/predspot/blob/master/"
LINKS = {
REPO_BLOB + "CONTRIBUTING.md": "contributing.md",
REPO_BLOB + "CHANGELOG.md": "changelog.md",
REPO_BLOB + "examples/natal.ipynb": "examples/natal.ipynb",
"https://adaj.github.io/predspot/": "index.md",
}


def on_page_markdown(markdown, page, config, files):
if page.file.src_path != "index.md":
return markdown
text = README.read_text(encoding="utf-8")
text = text.replace('src="docs/assets/', 'src="assets/')
text = text.replace("](docs/assets/", "](assets/")
for url, target in LINKS.items():
text = text.replace(f"]({url})", f"]({target})")
# Keep the page's own front matter (title etc.), if any.
front = re.match(r"^---\n.*?\n---\n", markdown, flags=re.S)
return (front.group(0) if front else "") + text
64 changes: 4 additions & 60 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,60 +1,4 @@
# Predspot

**Predicting crime hotspots with machine learning.**

Predspot is a Python library for spatio-temporal crime prediction. It turns a
table of georeferenced, timestamped crime events into a grid of *places* and a
sequence of *periods*, builds time series features for every place and trains a
scikit-learn model to forecast where the next period's hotspots will be.

<figure markdown="span">
![Observed density and forecast](assets/forecast.png){ width="900" }
<figcaption>Left: forecast density for the next month on a 500 m point grid.
Right: observed monthly density and the forecast at the hottest grid point.</figcaption>
</figure>

## What it does

- **Spatio-temporal mapping** — kernel density estimation (KDE) on a grid of
points, or event counts on hexagonal / square grids, at daily, weekly or
monthly resolution.
- **Feature engineering** — lagged autoregressive, difference, seasonal and
trend features (STL decomposition) for every place.
- **Prediction pipeline** — any scikit-learn regressor (or `Pipeline` with
scaling and feature selection) trained to forecast the next period, with
time series cross-validation.
- **Study areas from OpenStreetMap** — fetch a city boundary from a name.
- **Synthetic data** — generate realistic events (hotspots, trend, annual,
weekly and hourly patterns) inside any study area to try things out.

## In a nutshell

```python
from predspot import Dataset, load_study_area, generate_crimes
from predspot.pipeline import build_default_pipeline

study_area = load_study_area("Natal, Rio Grande do Norte, Brazil") # (1)!
crimes = generate_crimes(study_area, n_events=5000, seed=0) # (2)!

dataset = Dataset(crimes, study_area)
pipeline = build_default_pipeline(study_area, tfreq="M", grid_resolution=1)
pipeline.fit(dataset)

print(pipeline.evaluate("r2", cv=3)) # time series cross-validation
forecast = pipeline.predict() # density per grid point, next month
```

1. Needs `pip install "predspot[osm]"`. Any GeoDataFrame with a boundary works too.
2. Replace with your own DataFrame with `tag`, `t`, `lon`, `lat` columns.

Head to the [installation](getting-started/installation.md) and
[quickstart](getting-started/quickstart.md) pages, or read
[how Predspot works](guide/concepts.md).

## About

Predspot was created by [Adelson Araujo](https://github.com/adaj) as part of
his master's thesis at the Universidade Federal do Rio Grande do Norte (UFRN),
Brazil, and revived in 2026 for current versions of Python and its scientific
stack. It is research software released under the BSD-3-Clause license; see
[Citing Predspot](citing.md) if you use it in your work.
---
title: Home
---
<!-- This page is generated from the repository README by docs/hooks/readme.py -->
Loading
Loading