From 2bde8170dfb41bb38ded042b0f39ba47345d2a74 Mon Sep 17 00:00:00 2001 From: Adelson Araujo Date: Mon, 14 Sep 2026 12:49:51 +0000 Subject: [PATCH 1/4] Package with pyproject.toml (src layout), ruff and GitHub Actions CI - pyproject.toml (PEP 621, setuptools backend) replaces setup.py, requirements.txt and install.md; version read from predspot.__version__; optional extras: `contour` (geojsoncontour) and `dev` (pytest, ruff, build) - src/ layout so tests always run against the installed package - ruff lint + format configuration; code base formatted and lint-clean - CI workflow: ruff, pytest with coverage on Python 3.10-3.13, build + twine check of the distribution - Publish workflow: on a v* tag, verify tag == package version, build and upload to PyPI via trusted publishing (no stored token) - CHANGELOG.md, CONTRIBUTING.md, MANIFEST.in; README badges and install docs Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01FtZdc2gGrDh4PGSLyT9yRE --- .github/workflows/ci.yml | 58 +++++++ .github/workflows/publish.yml | 47 ++++++ .gitignore | 3 + CHANGELOG.md | 49 ++++++ CONTRIBUTING.md | 29 ++++ MANIFEST.in | 3 + README.md | 24 ++- install.md | 6 - predspot/__init__.py | 28 ---- pyproject.toml | 95 +++++++++++ requirements.txt | 8 - setup.py | 27 ---- src/predspot/__init__.py | 46 ++++++ {predspot => src/predspot}/crime_mapping.py | 147 ++++++++++-------- .../predspot}/dataset_preparation.py | 59 +++---- .../predspot}/feature_engineering.py | 57 +++---- {predspot => src/predspot}/ml_modelling.py | 67 ++++---- {predspot => src/predspot}/pipeline.py | 110 ++++++++----- {predspot => src/predspot}/utilities.py | 29 ++-- tests/conftest.py | 24 +-- tests/test_crime_mapping.py | 107 ++++++------- tests/test_dataset_preparation.py | 19 +-- tests/test_feature_engineering.py | 71 +++++---- tests/test_ml_modelling.py | 60 +++---- tests/test_pipeline.py | 17 +- tests/test_utilities.py | 8 +- 26 files changed, 782 insertions(+), 416 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/publish.yml create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 MANIFEST.in delete mode 100644 install.md delete mode 100644 predspot/__init__.py create mode 100644 pyproject.toml delete mode 100644 requirements.txt delete mode 100644 setup.py create mode 100644 src/predspot/__init__.py rename {predspot => src/predspot}/crime_mapping.py (79%) rename {predspot => src/predspot}/dataset_preparation.py (69%) rename {predspot => src/predspot}/feature_engineering.py (80%) rename {predspot => src/predspot}/ml_modelling.py (79%) rename {predspot => src/predspot}/pipeline.py (63%) rename {predspot => src/predspot}/utilities.py (82%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..343b82a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,58 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + lint: + name: Lint (ruff) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - run: python -m pip install ruff + - run: ruff check src tests + - run: ruff format --check src tests + + test: + name: Test (Python ${{ matrix.python-version }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.10", "3.11", "3.12", "3.13"] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: pip + - name: Install + run: python -m pip install -e ".[dev,contour]" + - name: Test + run: pytest --cov --cov-report=term-missing + + build: + name: Build distribution + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - run: python -m pip install build twine + - run: python -m build + - run: twine check dist/* + - uses: actions/upload-artifact@v4 + with: + name: dist + path: dist/ diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..ff46db3 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,47 @@ +name: Publish to PyPI + +# Publishes when a version tag (v1.2.3) is pushed. Uses PyPI "trusted +# publishing" (OpenID Connect), so no API token is stored in the repository: +# register this workflow once at https://pypi.org/manage/account/publishing/ +# (owner: adaj, repository: predspot, workflow: publish.yml, environment: pypi). + +on: + push: + tags: ["v[0-9]+.[0-9]+.[0-9]+*"] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Check that the tag matches the package version + run: | + python -m pip install -e . + PKG_VERSION="v$(python -c 'import predspot; print(predspot.__version__)')" + echo "tag=${GITHUB_REF_NAME} package=${PKG_VERSION}" + test "${GITHUB_REF_NAME}" = "${PKG_VERSION}" + - run: python -m pip install build twine + - run: python -m build + - run: twine check dist/* + - uses: actions/upload-artifact@v4 + with: + name: dist + path: dist/ + + publish: + needs: build + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/predspot + permissions: + id-token: write + steps: + - uses: actions/download-artifact@v4 + with: + name: dist + path: dist/ + - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.gitignore b/.gitignore index 1c0482f..e971876 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ build/ dist/ *.egg-info/ +.coverage +htmlcov/ +.ruff_cache/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5b45ac8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,49 @@ +# Changelog + +All notable changes to Predspot are documented here. The format follows +[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses +[Semantic Versioning](https://semver.org/). + +## [Unreleased] + +## [0.2.0] - 2026-09 + +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 +- `QuadratCount` mapping (event counts per cell) as a first-class alternative + to `KDE`, usable with hexagonal (`create_gridhexagonal`) and square + (`create_gridsquares`) grids inside `PredictionPipeline`. +- `pipeline.build_default_pipeline` and reproducible + `pipeline.generate_testdata(..., seed=...)`. +- `PredictionPipeline.features`, `.next_time` and `random_state`. +- Test suite (pytest) and continuous integration for Python 3.10-3.13. +- `pyproject.toml` packaging (src layout) and automated PyPI publishing. + +### Changed +- `tfreq` is optional in the feature classes (inferred from the series). +- Debug `print`s replaced with the `logging` module (`predspot` logger); the + `debug=` arguments were removed. +- Wrapper estimators expose their inner estimator as `.estimator` + (previously `._estimator`). +- `Dataset` no longer modifies the input DataFrame and requires the study + area to have a CRS. +- Grid centroids are computed in a projected CRS; grids accept study areas in + any CRS. +- `geojsoncontour` is an optional dependency (`pip install predspot[contour]`). + +### Removed +- `QuadratCount2`, `KGrid` and the hard dependencies on `descartes`, + `contextily` and `rtree`. + +### Fixed +- Compatibility with pandas 2/3 (`'ME'` offsets, `DataFrame.append`, + positional `Series` indexing), GeoPandas 1.x (`sjoin(predicate=)`, CRS + strings, `gpd.datasets`) and scikit-learn 1.x (`FeatureUnion` internals). +- `Seasonality`/`Trend` never called `STL(...).fit()`. +- `FeatureScaling` had no `fit`, so scalers inside a `Pipeline` were never fitted. + +## [0.1.3] - 2020 + +Original master's thesis release. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5a46c84 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,29 @@ +# Contributing + +Thanks for your interest in Predspot! Issues and pull requests are welcome. + +## Development setup + +```bash +git clone https://github.com/adaj/predspot.git +cd predspot +python -m venv .venv && source .venv/bin/activate +pip install -e ".[dev,contour]" +``` + +## Checks + +```bash +ruff check src tests # lint +ruff format src tests # format +pytest # tests (~10 s) +``` + +CI runs the same checks on Python 3.10 to 3.13 for every pull request. + +## Releasing + +1. Bump `__version__` in `src/predspot/__init__.py` and update `CHANGELOG.md`. +2. Merge to `master`, then tag and push: `git tag v0.2.0 && git push origin v0.2.0`. +3. The `Publish to PyPI` workflow builds the distribution and uploads it via + PyPI trusted publishing. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..9da02ae --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include LICENSE README.md CHANGELOG.md CONTRIBUTING.md +recursive-include tests *.py +prune docs diff --git a/README.md b/README.md index 5ca6949..81c7214 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # Predspot +[![CI](https://github.com/adaj/predspot/actions/workflows/ci.yml/badge.svg)](https://github.com/adaj/predspot/actions/workflows/ci.yml) +[![PyPI](https://img.shields.io/pypi/v/predspot.svg)](https://pypi.org/project/predspot/) +[![Python](https://img.shields.io/pypi/pyversions/predspot.svg)](https://pypi.org/project/predspot/) +[![License: BSD-3](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](LICENSE) + ## Overview 📖 Predspot is a Python library for spatio-temporal crime prediction and hotspot detection. It combines machine learning techniques with spatial analysis to help predict and visualize crime patterns across time and space. @@ -89,23 +94,32 @@ Predspot has four main modules: Predspot requires Python 3.10 or newer. +```bash +pip install predspot # from PyPI +pip install "predspot[contour]" # + GeoJSON contour export (geojsoncontour) +``` + +From source, for development: + ```bash git clone https://github.com/adaj/predspot.git cd predspot -pip install . +pip install -e ".[dev,contour]" ``` Core dependencies (installed automatically): pandas, geopandas, shapely, -numpy, scipy, scikit-learn, statsmodels and matplotlib. The optional -`geojsoncontour` package enables `predspot.utilities.contour_geojson`. +numpy, scipy, scikit-learn, statsmodels and matplotlib. ### Tests 🧪 ```bash -pip install pytest -pytest +ruff check src tests # lint +pytest # ~10 s ``` +See [CONTRIBUTING.md](CONTRIBUTING.md) for the release process and +[CHANGELOG.md](CHANGELOG.md) for what changed between versions. + ### Input Data Format 📊 The crime data should be a pandas DataFrame with the following required columns: diff --git a/install.md b/install.md deleted file mode 100644 index 16269a2..0000000 --- a/install.md +++ /dev/null @@ -1,6 +0,0 @@ -``` -$ git clone https://github.com/adaj/predspot.git -$ cd predspot -$ pip install . # Python >= 3.10 -$ pip install pytest && pytest -``` diff --git a/predspot/__init__.py b/predspot/__init__.py deleted file mode 100644 index 9ba0d1f..0000000 --- a/predspot/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -Predspot — predicting crime hotspots with machine learning. - -Typical use:: - - from predspot import Dataset, PredictionPipeline - from predspot.crime_mapping import KDE, create_gridpoints - from predspot.feature_engineering import Seasonality, Trend, Diff - from predspot.utilities import PandasFeatureUnion -""" - -from predspot import (crime_mapping, dataset_preparation, feature_engineering, - ml_modelling, utilities) -from predspot.crime_mapping import (KDE, QuadratCount, create_gridhexagonal, - create_gridpoints, create_gridsquares) -from predspot.dataset_preparation import Dataset -from predspot.ml_modelling import PredictionPipeline -from predspot.utilities import PandasFeatureUnion - -__version__ = '0.2.0' - -__all__ = [ - 'Dataset', 'PredictionPipeline', 'PandasFeatureUnion', - 'KDE', 'QuadratCount', - 'create_gridpoints', 'create_gridhexagonal', 'create_gridsquares', - 'crime_mapping', 'dataset_preparation', 'feature_engineering', - 'ml_modelling', 'utilities', -] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f014c0a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,95 @@ +[build-system] +requires = ["setuptools>=68", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "predspot" +dynamic = ["version"] +description = "Predicting crime hotspots with machine learning" +readme = "README.md" +license = "BSD-3-Clause" +license-files = ["LICENSE"] +requires-python = ">=3.10" +authors = [ + { name = "Adelson Araujo", email = "adelson.dias@gmail.com" }, +] +keywords = [ + "crime", "hotspots", "spatio-temporal", "kernel density estimation", + "geopandas", "scikit-learn", "forecasting", "criminology", +] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Science/Research", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Scientific/Engineering", + "Topic :: Scientific/Engineering :: GIS", + "Topic :: Scientific/Engineering :: Artificial Intelligence", +] +dependencies = [ + "numpy>=1.24", + "pandas>=2.2", + "geopandas>=1.0", + "shapely>=2.0", + "scipy>=1.10", + "scikit-learn>=1.3", + "statsmodels>=0.14", + "matplotlib>=3.7", +] + +[project.optional-dependencies] +contour = ["geojsoncontour>=0.4"] +dev = [ + "pytest>=8", + "pytest-cov>=5", + "ruff>=0.6", + "build>=1.2", + "twine>=5", +] + +[project.urls] +Homepage = "https://github.com/adaj/predspot" +Documentation = "https://adaj.github.io/predspot/" +Repository = "https://github.com/adaj/predspot" +Issues = "https://github.com/adaj/predspot/issues" +Changelog = "https://github.com/adaj/predspot/blob/master/CHANGELOG.md" + +[tool.setuptools] +package-dir = { "" = "src" } + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.setuptools.dynamic] +version = { attr = "predspot.__version__" } + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "-ra" +filterwarnings = [ + "error::DeprecationWarning:predspot", + "error::FutureWarning:predspot", +] + +[tool.coverage.run] +source = ["predspot"] +branch = true + +[tool.ruff] +line-length = 100 +target-version = "py310" +src = ["src", "tests"] + +[tool.ruff.lint] +select = ["E", "F", "W", "I", "B", "UP", "NPY", "PD"] +ignore = [ + "PD011", # .values is used deliberately for numpy interop +] + +[tool.ruff.lint.per-file-ignores] +"tests/*" = ["B011"] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 331ba9b..0000000 --- a/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -numpy>=1.24 -pandas>=2.2 -geopandas>=1.0 -shapely>=2.0 -scipy>=1.10 -scikit-learn>=1.3 -statsmodels>=0.14 -matplotlib>=3.7 diff --git a/setup.py b/setup.py deleted file mode 100644 index b41b761..0000000 --- a/setup.py +++ /dev/null @@ -1,27 +0,0 @@ -from setuptools import setup - -setup( - name='predspot', - version='0.2.0', - description="Predicting crime hotspots with machine learning", - url='https://github.com/adaj/predspot', - author="Adelson Araujo", - author_email='adelson.dias@gmail.com', - packages=['predspot'], - install_requires=[ - 'numpy>=1.24', - 'pandas>=2.2', - 'geopandas>=1.0', - 'shapely>=2.0', - 'scipy>=1.10', - 'scikit-learn>=1.3', - 'statsmodels>=0.14', - 'matplotlib>=3.7', - ], - extras_require={'contour': ['geojsoncontour']}, - classifiers=[ - 'Intended Audience :: Science/Research', - 'License :: BSD 3-Clause License' - ], - python_requires='>=3.10', -) diff --git a/src/predspot/__init__.py b/src/predspot/__init__.py new file mode 100644 index 0000000..2dae115 --- /dev/null +++ b/src/predspot/__init__.py @@ -0,0 +1,46 @@ +""" +Predspot — predicting crime hotspots with machine learning. + +Typical use:: + + from predspot import Dataset, PredictionPipeline + from predspot.crime_mapping import KDE, create_gridpoints + from predspot.feature_engineering import Seasonality, Trend, Diff + from predspot.utilities import PandasFeatureUnion +""" + +from predspot import ( + crime_mapping, + dataset_preparation, + feature_engineering, + ml_modelling, + utilities, +) +from predspot.crime_mapping import ( + KDE, + QuadratCount, + create_gridhexagonal, + create_gridpoints, + create_gridsquares, +) +from predspot.dataset_preparation import Dataset +from predspot.ml_modelling import PredictionPipeline +from predspot.utilities import PandasFeatureUnion + +__version__ = "0.2.0" + +__all__ = [ + "Dataset", + "PredictionPipeline", + "PandasFeatureUnion", + "KDE", + "QuadratCount", + "create_gridpoints", + "create_gridhexagonal", + "create_gridsquares", + "crime_mapping", + "dataset_preparation", + "feature_engineering", + "ml_modelling", + "utilities", +] diff --git a/predspot/crime_mapping.py b/src/predspot/crime_mapping.py similarity index 79% rename from predspot/crime_mapping.py rename to src/predspot/crime_mapping.py index 623593f..941538d 100644 --- a/predspot/crime_mapping.py +++ b/src/predspot/crime_mapping.py @@ -16,7 +16,7 @@ inside :class:`predspot.ml_modelling.PredictionPipeline`. """ -__author__ = 'Adelson Araujo' +__author__ = "Adelson Araujo" import logging import math @@ -26,7 +26,7 @@ import numpy as np import pandas as pd from scipy.stats import gaussian_kde -from shapely.geometry import Point, Polygon +from shapely.geometry import Polygon from sklearn.base import BaseEstimator, TransformerMixin logger = logging.getLogger(__name__) @@ -41,9 +41,16 @@ # Public aliases accepted for the time frequency and the pandas offset alias # they map to. Old pandas used 'M' for month end; pandas >= 2.2 uses 'ME'. TFREQ_ALIASES = { - 'M': 'ME', 'ME': 'ME', 'MONTH': 'ME', 'MONTHLY': 'ME', - 'W': 'W', 'WEEK': 'W', 'WEEKLY': 'W', - 'D': 'D', 'DAY': 'D', 'DAILY': 'D', + "M": "ME", + "ME": "ME", + "MONTH": "ME", + "MONTHLY": "ME", + "W": "W", + "WEEK": "W", + "WEEKLY": "W", + "D": "D", + "DAY": "D", + "DAILY": "D", } @@ -62,28 +69,27 @@ def normalize_tfreq(tfreq): """ key = str(tfreq).upper() if key not in TFREQ_ALIASES: - raise ValueError( - f"Invalid tfreq {tfreq!r}. Choose (M)onthly, (W)eekly or (D)aily.") + raise ValueError(f"Invalid tfreq {tfreq!r}. Choose (M)onthly, (W)eekly or (D)aily.") return TFREQ_ALIASES[key] def tfreq_offset(tfreq): """Return the :class:`pandas.DateOffset` that advances one period of ``tfreq``.""" alias = normalize_tfreq(tfreq) - if alias == 'ME': + if alias == "ME": return pd.offsets.MonthEnd(1) - if alias == 'W': + if alias == "W": return pd.offsets.Week(1) return pd.offsets.Day(1) def _check_bbox(bbox): if not isinstance(bbox, gpd.GeoDataFrame): - raise TypeError('bbox must be a geopandas GeoDataFrame.') + raise TypeError("bbox must be a geopandas GeoDataFrame.") if bbox.crs is None: raise ValueError('bbox must have a CRS (e.g. bbox.set_crs("EPSG:4326")).') if len(bbox) == 0: - raise ValueError('bbox is empty.') + raise ValueError("bbox is empty.") def _wgs84_bounds(bbox): @@ -93,11 +99,9 @@ def _wgs84_bounds(bbox): def _clip_to_bbox(grid, bbox): """Keep only grid rows that intersect ``bbox`` (both in the same CRS).""" - keep = gpd.sjoin(grid, bbox[['geometry']], how='inner', - predicate='intersects').index.unique() + keep = gpd.sjoin(grid, bbox[["geometry"]], how="inner", predicate="intersects").index.unique() if len(keep) == 0: - raise ValueError( - 'resolution too big/coarse. No cells intersect the study area.') + raise ValueError("resolution too big/coarse. No cells intersect the study area.") return grid.loc[grid.index.isin(keep)] @@ -106,8 +110,8 @@ def _add_centroid_lonlat(grid): projected = grid.geometry.to_crs(grid.estimate_utm_crs()) centroids = projected.centroid.to_crs(WGS84) grid = grid.copy() - grid['lon'] = centroids.x.values - grid['lat'] = centroids.y.values + grid["lon"] = centroids.x.values + grid["lat"] = centroids.y.values return grid @@ -131,9 +135,9 @@ def create_gridpoints(bbox, resolution, return_coords=False): ``return_coords`` is True, a tuple ``(gridpoints, lonv, latv)``. """ if resolution <= 0: - raise ValueError('resolution must be a positive number of kilometers.') + raise ValueError("resolution must be a positive number of kilometers.") _check_bbox(bbox) - logger.debug('Creating point grid with resolution %s km', resolution) + logger.debug("Creating point grid with resolution %s km", resolution) b_w, b_s, b_e, b_n = _wgs84_bounds(bbox) nlon = max(int(np.ceil((b_e - b_w) / (resolution / KM_PER_DEG_LON))), 2) @@ -141,10 +145,10 @@ def create_gridpoints(bbox, resolution, return_coords=False): lonv, latv = np.meshgrid(np.linspace(b_w, b_e, nlon), np.linspace(b_s, b_n, nlat)) lon, lat = lonv.ravel(), latv.ravel() gridpoints = gpd.GeoDataFrame( - {'lon': lon, 'lat': lat}, - geometry=gpd.points_from_xy(lon, lat), crs=WGS84).to_crs(bbox.crs) + {"lon": lon, "lat": lat}, geometry=gpd.points_from_xy(lon, lat), crs=WGS84 + ).to_crs(bbox.crs) gridpoints = _clip_to_bbox(gridpoints, bbox) - gridpoints.index.name = 'places' + gridpoints.index.name = "places" if return_coords: return gridpoints, lonv, latv return gridpoints @@ -163,10 +167,12 @@ def create_hexagon(side, x, y): Returns: Polygon: The hexagon. """ - return Polygon([ - (x + math.cos(math.radians(angle)) * side, - y + math.sin(math.radians(angle)) * side) - for angle in range(0, 360, 60)]) + return Polygon( + [ + (x + math.cos(math.radians(angle)) * side, y + math.sin(math.radians(angle)) * side) + for angle in range(0, 360, 60) + ] + ) def create_gridhexagonal(bbox, resolution): @@ -186,12 +192,12 @@ def create_gridhexagonal(bbox, resolution): ``lon`` and ``lat`` (centroid) columns and an index named ``places``. """ if resolution <= 0: - raise ValueError('resolution must be a positive number of kilometers.') + raise ValueError("resolution must be a positive number of kilometers.") _check_bbox(bbox) - logger.debug('Creating hexagonal grid with resolution %s km', resolution) + logger.debug("Creating hexagonal grid with resolution %s km", resolution) # Side length such that the hexagon area equals resolution**2. - side_km = math.sqrt(resolution ** 2 * 2 / (3 * math.sqrt(3))) + side_km = math.sqrt(resolution**2 * 2 / (3 * math.sqrt(3))) side = side_km / KM_PER_DEG_LAT # degrees (isotropic approximation) x_min, y_min, x_max, y_max = _wgs84_bounds(bbox) @@ -222,7 +228,7 @@ def create_gridhexagonal(bbox, resolution): grid = gpd.GeoDataFrame(geometry=hexagons, crs=WGS84).to_crs(bbox.crs) grid = _clip_to_bbox(grid, bbox) grid = _add_centroid_lonlat(grid) - grid.index.name = 'places' + grid.index.name = "places" return grid @@ -239,21 +245,22 @@ def create_gridsquares(bbox, resolution=1): ``lon`` and ``lat`` (centroid) columns and an index named ``places``. """ if resolution <= 0: - raise ValueError('resolution must be a positive number of kilometers.') + raise ValueError("resolution must be a positive number of kilometers.") _check_bbox(bbox) - logger.debug('Creating square grid with resolution %s km', resolution) + logger.debug("Creating square grid with resolution %s km", resolution) x0, y0, xf, yf = _wgs84_bounds(bbox) dx = resolution / KM_PER_DEG_LON dy = resolution / KM_PER_DEG_LAT xs = np.arange(x0, xf, dx) ys = np.arange(y0, yf, dy) - squares = [Polygon([(x, y), (x + dx, y), (x + dx, y + dy), (x, y + dy)]) - for x in xs for y in ys] + squares = [ + Polygon([(x, y), (x + dx, y), (x + dx, y + dy), (x, y + dy)]) for x in xs for y in ys + ] grid = gpd.GeoDataFrame(geometry=squares, crs=WGS84).to_crs(bbox.crs) grid = _clip_to_bbox(grid, bbox) grid = _add_centroid_lonlat(grid) - grid.index.name = 'places' + grid.index.name = "places" return grid @@ -284,15 +291,20 @@ def __init__(self, tfreq, grid, start_time=None, end_time=None): self.end_time = end_time self._tfreq = normalize_tfreq(tfreq) - missing = [c for c in ('geometry', 'lon', 'lat') if c not in grid.columns] + missing = [c for c in ("geometry", "lon", "lat") if c not in grid.columns] if missing: raise ValueError( - f'Input grid must have `geometry`, `lon` and `lat` columns; missing {missing}.') + f"Input grid must have `geometry`, `lon` and `lat` columns; missing {missing}." + ) self._grid = grid self._start_time = pd.to_datetime(start_time) if start_time else None self._end_time = pd.to_datetime(end_time) if end_time else None - logger.debug('%s initialised with tfreq=%s and %d places', - type(self).__name__, self._tfreq, len(grid)) + logger.debug( + "%s initialised with tfreq=%s and %d places", + type(self).__name__, + self._tfreq, + len(grid), + ) @abstractmethod def fit_grid(self, data_points): @@ -329,13 +341,13 @@ def transform(self, data_points): pandas.Series: Values named ``crime_density`` indexed by ``(t, places)``, sorted. """ - if 't' not in data_points.columns: - raise ValueError('data_points must have a `t` timestamp column.') - events = data_points.set_index(pd.DatetimeIndex(data_points['t'])).sort_index() + if "t" not in data_points.columns: + raise ValueError("data_points must have a `t` timestamp column.") + events = data_points.set_index(pd.DatetimeIndex(data_points["t"])).sort_index() chunks = {label: chunk for label, chunk in events.resample(self._tfreq)} labels = pd.DatetimeIndex(list(chunks.keys())) time_index = self._time_index(labels) - logger.debug('Mapping %d events over %d periods', len(events), len(time_index)) + logger.debug("Mapping %d events over %d periods", len(events), len(time_index)) zeros = dict.fromkeys(self._grid.index, 0.0) rows = [] @@ -347,9 +359,9 @@ def transform(self, data_points): rows.append(self.fit_grid(chunk)) frame = pd.DataFrame(rows, index=time_index) frame = frame.reindex(columns=self._grid.index) - stseries = frame.stack() - stseries.index.names = ['t', 'places'] - stseries.name = 'crime_density' + stseries = frame.stack() # noqa: PD013 - long format with (t, places) index + stseries.index.names = ["t", "places"] + stseries.name = "crime_density" return stseries.sort_index() @@ -371,20 +383,20 @@ class KDE(SpatioTemporalMapping): time), or a positive number used directly as the KDE factor. """ - def __init__(self, tfreq, grid, start_time=None, end_time=None, bandwidth='silverman'): + def __init__(self, tfreq, grid, start_time=None, end_time=None, bandwidth="silverman"): super().__init__(tfreq, grid, start_time, end_time) self.bandwidth = bandwidth if isinstance(bandwidth, str): method = bandwidth.lower() - if method == 'auto': - method = 'silverman' - if method not in ('silverman', 'scott'): + if method == "auto": + method = "silverman" + if method not in ("silverman", "scott"): raise ValueError("bandwidth must be 'silverman', 'scott' or a number.") self._bw_method = method self._factor = None else: if bandwidth <= 0: - raise ValueError('bandwidth must be a positive number.') + raise ValueError("bandwidth must be a positive number.") self._bw_method = None self._factor = float(bandwidth) self._kernel = None @@ -413,13 +425,14 @@ def fit_grid(self, data_points, as_df=False): self._kernel = gaussian_kde(xy, bw_method=bw) if self._factor is None: self._factor = float(self._kernel.factor) - logger.debug('KDE bandwidth factor estimated with %s: %.5f', - self._bw_method, self._factor) - values = self._kernel(self._grid[['lon', 'lat']].values.T) - density = pd.DataFrame({'crime_density': values}, index=self._grid.index) + logger.debug( + "KDE bandwidth factor estimated with %s: %.5f", self._bw_method, self._factor + ) + values = self._kernel(self._grid[["lon", "lat"]].values.T) + density = pd.DataFrame({"crime_density": values}, index=self._grid.index) if as_df: return density - return density['crime_density'].to_dict() + return density["crime_density"].to_dict() class QuadratCount(SpatioTemporalMapping): @@ -438,9 +451,11 @@ class QuadratCount(SpatioTemporalMapping): def __init__(self, tfreq, grid, start_time=None, end_time=None): super().__init__(tfreq, grid, start_time, end_time) - if not grid.geom_type.isin(['Polygon', 'MultiPolygon']).all(): - raise ValueError('QuadratCount requires a polygonal grid ' - '(see create_gridhexagonal / create_gridsquares).') + if not grid.geom_type.isin(["Polygon", "MultiPolygon"]).all(): + raise ValueError( + "QuadratCount requires a polygonal grid " + "(see create_gridhexagonal / create_gridsquares)." + ) def fit_grid(self, data_points, as_df=False): """ @@ -453,15 +468,15 @@ def fit_grid(self, data_points, as_df=False): Returns: dict or DataFrame: Number of events per cell. """ - points = data_points[['geometry']].to_crs(self._grid.crs) - joined = gpd.sjoin(points, self._grid[['geometry']], how='inner', - predicate='within') + points = data_points[["geometry"]].to_crs(self._grid.crs) + joined = gpd.sjoin(points, self._grid[["geometry"]], how="inner", predicate="within") # The right index column is named after the grid index ('places'); # fall back to geopandas' default name otherwise. - col = 'places' if 'places' in joined.columns else 'index_right' + col = "places" if "places" in joined.columns else "index_right" counts = joined.groupby(col).size().reindex(self._grid.index, fill_value=0) - density = pd.DataFrame({'crime_density': counts.astype(float).values}, - index=self._grid.index) + density = pd.DataFrame( + {"crime_density": counts.astype(float).values}, index=self._grid.index + ) if as_df: return density - return density['crime_density'].to_dict() + return density["crime_density"].to_dict() diff --git a/predspot/dataset_preparation.py b/src/predspot/dataset_preparation.py similarity index 69% rename from predspot/dataset_preparation.py rename to src/predspot/dataset_preparation.py index 4f91cd3..8dda27c 100644 --- a/predspot/dataset_preparation.py +++ b/src/predspot/dataset_preparation.py @@ -7,7 +7,7 @@ of points in WGS84 and offers simple plotting and splitting helpers. """ -__author__ = 'Adelson Araujo' +__author__ = "Adelson Araujo" import logging @@ -17,7 +17,7 @@ logger = logging.getLogger(__name__) WGS84 = "EPSG:4326" -REQUIRED_COLUMNS = ('tag', 't', 'lon', 'lat') +REQUIRED_COLUMNS = ("tag", "t", "lon", "lat") class Dataset: @@ -40,17 +40,20 @@ class Dataset: def __init__(self, crimes, study_area): if not isinstance(study_area, gpd.GeoDataFrame): - raise TypeError('study_area must be a geopandas GeoDataFrame.') + raise TypeError("study_area must be a geopandas GeoDataFrame.") if study_area.crs is None: - raise ValueError('study_area must have a CRS set ' - '(e.g. study_area.set_crs("EPSG:4326")).') + raise ValueError( + 'study_area must have a CRS set (e.g. study_area.set_crs("EPSG:4326")).' + ) if not isinstance(crimes, pd.DataFrame): - raise TypeError('crimes must be a pandas DataFrame.') + raise TypeError("crimes must be a pandas DataFrame.") missing = [c for c in REQUIRED_COLUMNS if c not in crimes.columns] if missing: - raise ValueError('Input crime data must have at least `tag`, `t`, ' - f'`lon` and `lat` as columns; missing {missing}.') - logger.debug('Preparing dataset with %d crime events', len(crimes)) + raise ValueError( + "Input crime data must have at least `tag`, `t`, " + f"`lon` and `lat` as columns; missing {missing}." + ) + logger.debug("Preparing dataset with %d crime events", len(crimes)) self._study_area = study_area if isinstance(crimes, gpd.GeoDataFrame) and crimes.crs is not None: @@ -58,19 +61,22 @@ def __init__(self, crimes, study_area): else: events = crimes.copy() events = gpd.GeoDataFrame( - events.drop(columns=['geometry'], errors='ignore'), - geometry=gpd.points_from_xy(events['lon'], events['lat']), - crs=WGS84) - events['t'] = pd.to_datetime(events['t']) + events.drop(columns=["geometry"], errors="ignore"), + geometry=gpd.points_from_xy(events["lon"], events["lat"]), + crs=WGS84, + ) + events["t"] = pd.to_datetime(events["t"]) self._crimes = events def __repr__(self): - counts = self._crimes['tag'].value_counts().to_dict() - return ('predspot.Dataset<\n' - f' crimes = GeoDataFrame({self._crimes.shape[0]}),\n' - f' >> {counts}\n' - f' study_area = GeoDataFrame({self._study_area.shape[0]}),\n' - '>') + counts = self._crimes["tag"].value_counts().to_dict() + return ( + "predspot.Dataset<\n" + f" crimes = GeoDataFrame({self._crimes.shape[0]}),\n" + f" >> {counts}\n" + f" study_area = GeoDataFrame({self._study_area.shape[0]}),\n" + ">" + ) @property def crimes(self): @@ -85,8 +91,7 @@ def study_area(self): @property def shape(self): """dict: Shapes of ``crimes`` and ``study_area``.""" - return {'crimes': self._crimes.shape, - 'study_area': self._study_area.shape} + return {"crimes": self._crimes.shape, "study_area": self._study_area.shape} def plot(self, ax=None, crime_samples=1000, **kwargs): """ @@ -101,13 +106,13 @@ def plot(self, ax=None, crime_samples=1000, **kwargs): Returns: matplotlib.axes.Axes: The axes drawn on. """ - area_kwargs = {'color': 'white', 'edgecolor': 'black'} - area_kwargs.update(kwargs.pop('study_area', {})) + area_kwargs = {"color": "white", "edgecolor": "black"} + area_kwargs.update(kwargs.pop("study_area", {})) study_area = self.study_area.to_crs(WGS84) ax = study_area.plot(ax=ax, **area_kwargs) n = min(crime_samples, len(self.crimes)) - crimes_kwargs = {'marker': 'x'} - crimes_kwargs.update(kwargs.pop('crimes', {})) + crimes_kwargs = {"marker": "x"} + crimes_kwargs.update(kwargs.pop("crimes", {})) self.crimes.sample(n).plot(ax=ax, **crimes_kwargs) return ax @@ -123,8 +128,8 @@ def train_test_split(self, test_size=0.25, random_state=None): tuple: ``(train_dataset, test_dataset)``. """ if not 0 < test_size < 1: - raise ValueError('test_size must be between 0 and 1.') + raise ValueError("test_size must be between 0 and 1.") test = self.crimes.sample(frac=test_size, random_state=random_state) train = self.crimes.drop(index=test.index) - logger.debug('Split dataset: train=%d, test=%d', len(train), len(test)) + logger.debug("Split dataset: train=%d, test=%d", len(train), len(test)) return Dataset(train, self.study_area), Dataset(test, self.study_area) diff --git a/predspot/feature_engineering.py b/src/predspot/feature_engineering.py similarity index 80% rename from predspot/feature_engineering.py rename to src/predspot/feature_engineering.py index 5cfa02c..48ec5ed 100644 --- a/predspot/feature_engineering.py +++ b/src/predspot/feature_engineering.py @@ -16,7 +16,7 @@ observed one, so that the fitted model can forecast the next period. """ -__author__ = 'Adelson Araujo' +__author__ = "Adelson Araujo" import logging from abc import abstractmethod @@ -47,8 +47,9 @@ def infer_offset(time_index): time_index = pd.DatetimeIndex(time_index).unique().sort_values() freq = pd.infer_freq(time_index) if len(time_index) >= 3 else None if freq is None: - raise ValueError('Could not infer the time frequency of the series; ' - 'pass `tfreq` explicitly.') + raise ValueError( + "Could not infer the time frequency of the series; pass `tfreq` explicitly." + ) return pd.tseries.frequencies.to_offset(freq) @@ -64,7 +65,7 @@ class TimeSeriesFeatures(BaseEstimator, TransformerMixin): def __init__(self, lags, tfreq=None): if not isinstance(lags, int) or lags < 2: - raise ValueError('`lags` must be an integer greater than 1.') + raise ValueError("`lags` must be an integer greater than 1.") self.lags = lags self.tfreq = tfreq self._offset = tfreq_offset(tfreq) if tfreq is not None else None @@ -72,7 +73,7 @@ def __init__(self, lags, tfreq=None): @property def label(self): """str: Prefix of the feature columns (override in subclasses).""" - return 'feature' + return "feature" @abstractmethod def apply_ts_decomposition(self, ts): @@ -102,10 +103,10 @@ def make_lag_df(self, ts): original series restricted to the same index. """ if len(ts) <= self.lags: - raise ValueError('`lags` is higher than the number of time periods.') + raise ValueError("`lags` is higher than the number of time periods.") lag_df = pd.concat([ts.shift(lag) for lag in range(1, self.lags + 1)], axis=1) - lag_df = lag_df.iloc[self.lags:] - lag_df.columns = [f'{self.label}_{i}' for i in range(1, self.lags + 1)] + lag_df = lag_df.iloc[self.lags :] + lag_df.columns = [f"{self.label}_{i}" for i in range(1, self.lags + 1)] return lag_df, ts.loc[lag_df.index] def transform(self, stseries): @@ -119,21 +120,22 @@ def transform(self, stseries): pandas.DataFrame: Features indexed by ``(t, places)``, including one row for the period after the last observed one. """ - times = stseries.index.get_level_values('t') + times = stseries.index.get_level_values("t") offset = self._offset if self._offset is not None else infer_offset(times) - places = stseries.index.get_level_values('places').unique() - logger.debug('%s: computing %d lags for %d places', - type(self).__name__, self.lags, len(places)) + places = stseries.index.get_level_values("places").unique() + logger.debug( + "%s: computing %d lags for %d places", type(self).__name__, self.lags, len(places) + ) frames = [] for place in places: - ts = stseries.xs(place, level='places').sort_index() + ts = stseries.xs(place, level="places").sort_index() ts = self.apply_ts_decomposition(ts) ts.loc[ts.index[-1] + offset] = None # next period, to be forecast f, _ = self.make_lag_df(ts) - f['places'] = place - frames.append(f.set_index('places', append=True)) + f["places"] = place + frames.append(f.set_index("places", append=True)) X = pd.concat(frames) - X.index.names = ['t', 'places'] + X.index.names = ["t", "places"] return X.sort_index() @@ -142,7 +144,7 @@ class AR(TimeSeriesFeatures): @property def label(self): - return 'ar' + return "ar" def apply_ts_decomposition(self, ts): return ts @@ -153,7 +155,7 @@ class Diff(TimeSeriesFeatures): @property def label(self): - return 'diff' + return "diff" def apply_ts_decomposition(self, ts): return ts.diff().iloc[1:] @@ -166,8 +168,10 @@ class _STLFeatures(TimeSeriesFeatures): def apply_ts_decomposition(self, ts): if len(ts) < 2 * self.lags: - raise ValueError(f'{type(self).__name__} needs at least 2 * lags ' - f'({2 * self.lags}) periods; got {len(ts)}.') + raise ValueError( + f"{type(self).__name__} needs at least 2 * lags " + f"({2 * self.lags}) periods; got {len(ts)}." + ) result = STL(ts, period=self.lags).fit() return getattr(result, self.component) @@ -175,21 +179,21 @@ def apply_ts_decomposition(self, ts): class Seasonality(_STLFeatures): """Lags of the seasonal component of an STL decomposition (period = lags).""" - component = 'seasonal' + component = "seasonal" @property def label(self): - return 'seasonal' + return "seasonal" class Trend(_STLFeatures): """Lags of the trend component of an STL decomposition (period = lags).""" - component = 'trend' + component = "trend" @property def label(self): - return 'trend' + return "trend" class FeatureScaling(TransformerMixin, BaseEstimator): @@ -209,8 +213,7 @@ def fit(self, x, y=None): return self def __sklearn_is_fitted__(self): - return getattr(self, 'is_fitted_', False) + return getattr(self, "is_fitted_", False) def transform(self, x): - return pd.DataFrame(self.estimator.transform(x), - index=x.index, columns=x.columns) + return pd.DataFrame(self.estimator.transform(x), index=x.index, columns=x.columns) diff --git a/predspot/ml_modelling.py b/src/predspot/ml_modelling.py similarity index 79% rename from predspot/ml_modelling.py rename to src/predspot/ml_modelling.py index 738e567..3107ac4 100644 --- a/predspot/ml_modelling.py +++ b/src/predspot/ml_modelling.py @@ -7,7 +7,7 @@ attached to their ``(t, places)`` labels. """ -__author__ = 'Adelson Araujo' +__author__ = "Adelson Araujo" import logging @@ -22,7 +22,7 @@ idx = pd.IndexSlice -SCORERS = {'r2': r2_score, 'mse': mean_squared_error} +SCORERS = {"r2": r2_score, "mse": mean_squared_error} class FeatureSelection(TransformerMixin, BaseEstimator): @@ -42,7 +42,7 @@ def fit(self, x, y=None): return self def __sklearn_is_fitted__(self): - return getattr(self, 'is_fitted_', False) + return getattr(self, "is_fitted_", False) @property def support_(self): @@ -50,8 +50,9 @@ def support_(self): return self.estimator.support_ def transform(self, x): - return pd.DataFrame(self.estimator.transform(x), index=x.index, - columns=x.columns[self.estimator.support_]) + return pd.DataFrame( + self.estimator.transform(x), index=x.index, columns=x.columns[self.estimator.support_] + ) class Model(RegressorMixin, BaseEstimator): @@ -71,15 +72,14 @@ def fit(self, x, y=None): return self def __sklearn_is_fitted__(self): - return getattr(self, 'is_fitted_', False) + return getattr(self, "is_fitted_", False) @property def feature_importances_(self): return self.estimator.feature_importances_ def predict(self, x): - return pd.DataFrame(self.estimator.predict(x), index=x.index, - columns=['crime_density']) + return pd.DataFrame(self.estimator.predict(x), index=x.index, columns=["crime_density"]) class PredictionPipeline(RegressorMixin, BaseEstimator): @@ -140,7 +140,7 @@ def next_time(self): def _check_fitted(self): if self._X is None: - raise RuntimeError('This pipeline was not fitted yet.') + raise RuntimeError("This pipeline was not fitted yet.") @property def feature_importances(self): @@ -156,18 +156,19 @@ def feature_importances(self): pandas.DataFrame: Importance per feature, sorted descending. """ self._check_fitted() - steps = getattr(self.estimator, 'steps', [('model', self.estimator)]) + steps = getattr(self.estimator, "steps", [("model", self.estimator)]) model = steps[-1][1] try: importances = model.feature_importances_ except AttributeError as exc: - raise AttributeError('The estimator does not expose feature_importances_.') from exc + raise AttributeError("The estimator does not expose feature_importances_.") from exc columns = self._X.columns for _, step in steps[:-1]: - if hasattr(step, 'support_'): + if hasattr(step, "support_"): columns = columns[step.support_] - return (pd.DataFrame({'importance': importances}, index=columns) - .sort_values('importance', ascending=False)) + return pd.DataFrame({"importance": importances}, index=columns).sort_values( + "importance", ascending=False + ) def fit(self, dataset, y=None): """ @@ -180,17 +181,17 @@ def fit(self, dataset, y=None): Returns: PredictionPipeline: ``self``. """ - logger.debug('Fitting prediction pipeline') + logger.debug("Fitting prediction pipeline") self._dataset = dataset self._stseries = self.mapping.fit_transform(dataset.crimes) self._X = self.fextraction.fit_transform(self._stseries) - t0 = self._X.index.get_level_values('t').min() - tf = self._stseries.index.get_level_values('t').max() + t0 = self._X.index.get_level_values("t").min() + tf = self._stseries.index.get_level_values("t").max() X = self._X.loc[t0:tf].sample(frac=1, random_state=self.random_state) y = self._stseries.loc[X.index] self.estimator.fit(X, y) - self._t_plus_one = self._X.index.get_level_values('t').max() - logger.debug('Pipeline fitted on %d rows; next period is %s', len(X), self._t_plus_one) + self._t_plus_one = self._X.index.get_level_values("t").max() + logger.debug("Pipeline fitted on %d rows; next period is %s", len(X), self._t_plus_one) return self def predict(self): @@ -207,15 +208,15 @@ def predict(self): self._check_fitted() X = self._X.loc[[self._t_plus_one], :] y_pred = pd.DataFrame(self.estimator.predict(X), index=X.index) - y_pred.columns = ['crime_density'] - logger.debug('Predicted %d places for %s', len(y_pred), self._t_plus_one) - self._stseries = pd.concat([self._stseries, y_pred['crime_density']]).sort_index() - self._stseries.name = 'crime_density' + y_pred.columns = ["crime_density"] + logger.debug("Predicted %d places for %s", len(y_pred), self._t_plus_one) + self._stseries = pd.concat([self._stseries, y_pred["crime_density"]]).sort_index() + self._stseries.name = "crime_density" self._X = self.fextraction.transform(self._stseries) self._t_plus_one = self._t_plus_one + self._offset return y_pred - def evaluate(self, scoring='r2', cv=5): + def evaluate(self, scoring="r2", cv=5): """ Score the estimator with time series cross-validation. @@ -234,21 +235,25 @@ def evaluate(self, scoring='r2', cv=5): if scoring not in SCORERS: raise ValueError('invalid scoring. Try "r2" or "mse".') scorer = SCORERS[scoring] - timestamps = (self._X.index.get_level_values('t').unique() - .intersection(self._stseries.index.get_level_values('t').unique()) - .sort_values()) + timestamps = ( + self._X.index.get_level_values("t") + .unique() + .intersection(self._stseries.index.get_level_values("t").unique()) + .sort_values() + ) if not isinstance(cv, int) or cv >= len(timestamps): - raise ValueError('cv must be an integer lower than the number of periods.') + raise ValueError("cv must be an integer lower than the number of periods.") scores = [] for train_t, test_t in TimeSeriesSplit(cv).split(timestamps): - X_train = (self._X.loc[idx[timestamps[train_t], :], :] - .sample(frac=1, random_state=self.random_state)) + X_train = self._X.loc[idx[timestamps[train_t], :], :].sample( + frac=1, random_state=self.random_state + ) X_test = self._X.loc[idx[timestamps[test_t], :], :] y_train = self._stseries.loc[X_train.index] y_test = self._stseries.loc[X_test.index] self.estimator.fit(X_train, y_train) y_pred = self.estimator.predict(X_test) scores.append(scorer(y_test, y_pred)) - logger.debug('%s-fold CV %s scores: %s', cv, scoring, scores) + logger.debug("%s-fold CV %s scores: %s", cv, scoring, scores) self.fit(self._dataset) # back to normal return scores diff --git a/predspot/pipeline.py b/src/predspot/pipeline.py similarity index 63% rename from predspot/pipeline.py rename to src/predspot/pipeline.py index 589899d..6b51933 100644 --- a/predspot/pipeline.py +++ b/src/predspot/pipeline.py @@ -11,7 +11,7 @@ >>> predictions, pipeline = run_prediction_pipeline(crimes, study_area, grid_resolution=1) """ -__author__ = 'Adelson Araujo' +__author__ = "Adelson Araujo" import logging @@ -50,24 +50,31 @@ def generate_testdata(n_points, start_time, end_time, bounds=DEFAULT_BOUNDS, see """ rng = np.random.default_rng(seed) west, south, east, north = bounds - study_area = gpd.GeoDataFrame({'name': ['study_area']}, - geometry=[box(west, south, east, north)], crs='EPSG:4326') + study_area = gpd.GeoDataFrame( + {"name": ["study_area"]}, geometry=[box(west, south, east, north)], crs="EPSG:4326" + ) start, end = pd.Timestamp(start_time), pd.Timestamp(end_time) seconds = rng.integers(0, int((end - start).total_seconds()), n_points) - tags = rng.choice(['burglary', 'assault', 'drugs', 'homicide'], size=n_points, - p=np.array([1000, 100, 10, 1]) / 1111) - crimes = pd.DataFrame({ - 'tag': tags, - 't': start + pd.to_timedelta(seconds, unit='s'), - 'lon': rng.uniform(west, east, n_points), - 'lat': rng.uniform(south, north, n_points), - }) - logger.debug('Generated %d synthetic events', n_points) + tags = rng.choice( + ["burglary", "assault", "drugs", "homicide"], + size=n_points, + p=np.array([1000, 100, 10, 1]) / 1111, + ) + crimes = pd.DataFrame( + { + "tag": tags, + "t": start + pd.to_timedelta(seconds, unit="s"), + "lon": rng.uniform(west, east, n_points), + "lat": rng.uniform(south, north, n_points), + } + ) + logger.debug("Generated %d synthetic events", n_points) return crimes, study_area -def build_default_pipeline(study_area, tfreq='M', grid_resolution=1, lags=2, - bandwidth='silverman', random_state=None): +def build_default_pipeline( + study_area, tfreq="M", grid_resolution=1, lags=2, bandwidth="silverman", random_state=None +): """ Build the default Predspot pipeline: KDE mapping, seasonal/trend/diff features, quantile scaling, RFE feature selection and a random forest. @@ -86,25 +93,49 @@ def build_default_pipeline(study_area, tfreq='M', grid_resolution=1, lags=2, grid = crime_mapping.create_gridpoints(study_area, grid_resolution) return ml_modelling.PredictionPipeline( mapping=crime_mapping.KDE(tfreq=tfreq, grid=grid, bandwidth=bandwidth), - fextraction=PandasFeatureUnion([ - ('seasonal', feature_engineering.Seasonality(lags=lags, tfreq=tfreq)), - ('trend', feature_engineering.Trend(lags=lags, tfreq=tfreq)), - ('diff', feature_engineering.Diff(lags=lags, tfreq=tfreq)), - ]), - estimator=Pipeline([ - ('f_scaling', feature_engineering.FeatureScaling( - QuantileTransformer(n_quantiles=10, output_distribution='uniform'))), - ('f_selection', ml_modelling.FeatureSelection( - RFE(RandomForestRegressor(n_estimators=20, random_state=random_state)))), - ('model', ml_modelling.Model( - RandomForestRegressor(n_estimators=50, random_state=random_state))), - ]), + fextraction=PandasFeatureUnion( + [ + ("seasonal", feature_engineering.Seasonality(lags=lags, tfreq=tfreq)), + ("trend", feature_engineering.Trend(lags=lags, tfreq=tfreq)), + ("diff", feature_engineering.Diff(lags=lags, tfreq=tfreq)), + ] + ), + estimator=Pipeline( + [ + ( + "f_scaling", + feature_engineering.FeatureScaling( + QuantileTransformer(n_quantiles=10, output_distribution="uniform") + ), + ), + ( + "f_selection", + ml_modelling.FeatureSelection( + RFE(RandomForestRegressor(n_estimators=20, random_state=random_state)) + ), + ), + ( + "model", + ml_modelling.Model( + RandomForestRegressor(n_estimators=50, random_state=random_state) + ), + ), + ] + ), random_state=random_state, ) -def run_prediction_pipeline(crime_data, study_area, crime_tags=None, time_range=None, - tfreq='M', grid_resolution=1, lags=2, random_state=None): +def run_prediction_pipeline( + crime_data, + study_area, + crime_tags=None, + time_range=None, + tfreq="M", + grid_resolution=1, + lags=2, + random_state=None, +): """ Fit the default pipeline on crime data and forecast the next period. @@ -123,24 +154,29 @@ def run_prediction_pipeline(crime_data, study_area, crime_tags=None, time_range= tuple: ``(predictions, pipeline)`` — the forecast for the next period and the fitted :class:`predspot.ml_modelling.PredictionPipeline`. """ - missing = [c for c in ('tag', 't', 'lat', 'lon') if c not in crime_data.columns] + missing = [c for c in ("tag", "t", "lat", "lon") if c not in crime_data.columns] if missing: - raise ValueError(f'Crime data must contain columns tag, t, lat, lon; missing {missing}') + raise ValueError(f"Crime data must contain columns tag, t, lat, lon; missing {missing}") if crime_tags: - crime_data = crime_data.loc[crime_data['tag'].isin(crime_tags)] + crime_data = crime_data.loc[crime_data["tag"].isin(crime_tags)] if time_range: - time_ix = pd.DatetimeIndex(pd.to_datetime(crime_data['t'])) + time_ix = pd.DatetimeIndex(pd.to_datetime(crime_data["t"])) crime_data = crime_data.iloc[time_ix.indexer_between_time(time_range[0], time_range[1])] dataset = dataset_preparation.Dataset(crimes=crime_data, study_area=study_area) - pipeline = build_default_pipeline(study_area, tfreq=tfreq, grid_resolution=grid_resolution, - lags=lags, random_state=random_state) + pipeline = build_default_pipeline( + study_area, + tfreq=tfreq, + grid_resolution=grid_resolution, + lags=lags, + random_state=random_state, + ) pipeline.fit(dataset) predictions = pipeline.predict() return predictions, pipeline -def evaluate_pipeline(pipeline, scoring='r2', cv=5): +def evaluate_pipeline(pipeline, scoring="r2", cv=5): """ Cross-validate a fitted pipeline; see :meth:`PredictionPipeline.evaluate`. @@ -153,5 +189,5 @@ def evaluate_pipeline(pipeline, scoring='r2', cv=5): list: One score per fold. """ scores = pipeline.evaluate(scoring=scoring, cv=cv) - logger.debug('Evaluation complete. Mean score: %.4f', np.mean(scores)) + logger.debug("Evaluation complete. Mean score: %.4f", np.mean(scores)) return scores diff --git a/predspot/utilities.py b/src/predspot/utilities.py similarity index 82% rename from predspot/utilities.py rename to src/predspot/utilities.py index 6045f68..1e67528 100644 --- a/predspot/utilities.py +++ b/src/predspot/utilities.py @@ -7,13 +7,12 @@ GeoJSON contour export for density maps. """ -__author__ = 'Adelson Araujo' +__author__ = "Adelson Araujo" import logging import numpy as np import pandas as pd -from geopandas import GeoDataFrame from sklearn.base import BaseEstimator, TransformerMixin logger = logging.getLogger(__name__) @@ -36,7 +35,7 @@ def __init__(self, transformer_list): def _iter(self): for name, transformer in self.transformer_list: - if transformer is None or transformer == 'drop': + if transformer is None or transformer == "drop": continue yield name, transformer @@ -46,8 +45,7 @@ def fit(self, X, y=None, **fit_params): return self def fit_transform(self, X, y=None, **fit_params): - outputs = [transformer.fit_transform(X, y, **fit_params) - for _, transformer in self._iter()] + outputs = [transformer.fit_transform(X, y, **fit_params) for _, transformer in self._iter()] return self.merge_dataframes_by_column(outputs) def transform(self, X): @@ -66,9 +64,9 @@ def merge_dataframes_by_column(outputs): pandas.DataFrame: The merged features without missing rows. """ if not outputs: - raise ValueError('PandasFeatureUnion has no transformers.') - logger.debug('Merging %d feature blocks', len(outputs)) - return pd.concat(outputs, axis='columns').dropna() + raise ValueError("PandasFeatureUnion has no transformers.") + logger.debug("Merging %d feature blocks", len(outputs)) + return pd.concat(outputs, axis="columns").dropna() def contour_geojson(y, bbox, resolution, cmin, cmax): @@ -94,14 +92,16 @@ def contour_geojson(y, bbox, resolution, cmin, cmax): try: import geojsoncontour except ImportError as exc: # pragma: no cover - optional dependency - raise ImportError('contour_geojson requires the optional dependency ' - '`geojsoncontour`: pip install predspot[contour]') from exc + raise ImportError( + "contour_geojson requires the optional dependency " + "`geojsoncontour`: pip install predspot[contour]" + ) from exc import matplotlib - matplotlib.use('Agg') + + matplotlib.use("Agg") import matplotlib.pyplot as plt - from predspot.crime_mapping import (KM_PER_DEG_LAT, KM_PER_DEG_LON, - _check_bbox, _wgs84_bounds) + from predspot.crime_mapping import KM_PER_DEG_LAT, KM_PER_DEG_LON, _check_bbox, _wgs84_bounds _check_bbox(bbox) b_w, b_s, b_e, b_n = _wgs84_bounds(bbox) @@ -113,8 +113,7 @@ def contour_geojson(y, bbox, resolution, cmin, cmax): Z = Z.reshape(lonv.shape) fig, axes = plt.subplots() - contourf = axes.contourf(lonv, latv, Z, levels=np.linspace(cmin, cmax, 25), - cmap='Spectral_r') + contourf = axes.contourf(lonv, latv, Z, levels=np.linspace(cmin, cmax, 25), cmap="Spectral_r") geojson = geojsoncontour.contourf_to_geojson(contourf=contourf, fill_opacity=0.5) plt.close(fig) return geojson diff --git a/tests/conftest.py b/tests/conftest.py index 0b5cafd..7079c3e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,13 +10,12 @@ BOUNDS = (-35.30, -5.90, -35.20, -5.80) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def study_area(): - return gpd.GeoDataFrame({'name': ['test']}, - geometry=[box(*BOUNDS)], crs='EPSG:4326') + return gpd.GeoDataFrame({"name": ["test"]}, geometry=[box(*BOUNDS)], crs="EPSG:4326") -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def crimes(): """2 years of events: a uniform background plus one Gaussian hotspot.""" rng = np.random.default_rng(42) @@ -24,15 +23,18 @@ def crimes(): west, south, east, north = BOUNDS lon = np.concatenate([rng.uniform(west, east, n_bg), rng.normal(-35.23, 0.01, n_hot)]) lat = np.concatenate([rng.uniform(south, north, n_bg), rng.normal(-5.83, 0.01, n_hot)]) - start = pd.Timestamp('2019-01-01') + start = pd.Timestamp("2019-01-01") seconds = rng.integers(0, 730 * 24 * 3600, n_bg + n_hot) - return pd.DataFrame({ - 'tag': rng.choice(['burglary', 'assault'], n_bg + n_hot, p=[0.8, 0.2]), - 't': start + pd.to_timedelta(seconds, unit='s'), - 'lon': np.clip(lon, west, east), 'lat': np.clip(lat, south, north), - }) + return pd.DataFrame( + { + "tag": rng.choice(["burglary", "assault"], n_bg + n_hot, p=[0.8, 0.2]), + "t": start + pd.to_timedelta(seconds, unit="s"), + "lon": np.clip(lon, west, east), + "lat": np.clip(lat, south, north), + } + ) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def dataset(crimes, study_area): return Dataset(crimes, study_area) diff --git a/tests/test_crime_mapping.py b/tests/test_crime_mapping.py index 257eda1..641de0b 100644 --- a/tests/test_crime_mapping.py +++ b/tests/test_crime_mapping.py @@ -1,3 +1,4 @@ +import geopandas as gpd import numpy as np import pandas as pd import pytest @@ -5,24 +6,26 @@ from predspot import crime_mapping as cm -@pytest.mark.parametrize('tfreq,expected', [('M', 'ME'), ('m', 'ME'), ('ME', 'ME'), - ('W', 'W'), ('D', 'D'), ('daily', 'D')]) +@pytest.mark.parametrize( + "tfreq,expected", + [("M", "ME"), ("m", "ME"), ("ME", "ME"), ("W", "W"), ("D", "D"), ("daily", "D")], +) def test_normalize_tfreq(tfreq, expected): assert cm.normalize_tfreq(tfreq) == expected def test_normalize_tfreq_invalid(): with pytest.raises(ValueError): - cm.normalize_tfreq('Y') + cm.normalize_tfreq("Y") def test_create_gridpoints(study_area): grid = cm.create_gridpoints(study_area, resolution=1) - assert grid.index.name == 'places' - assert {'lon', 'lat', 'geometry'} <= set(grid.columns) + assert grid.index.name == "places" + assert {"lon", "lat", "geometry"} <= set(grid.columns) # ~11 x 11 points for a ~10x10 km box at 1 km spacing assert 100 <= len(grid) <= 200 - assert grid.geometry.geom_type.eq('Point').all() + assert grid.geometry.geom_type.eq("Point").all() assert grid.crs == study_area.crs @@ -45,23 +48,23 @@ def test_create_gridpoints_errors(study_area): with pytest.raises(TypeError): cm.create_gridpoints(study_area.geometry, resolution=1) # a diamond: the corners of its bounding box fall outside the polygon - from shapely.geometry import Polygon import geopandas as gpd - sliver = gpd.GeoDataFrame(geometry=[Polygon([(-35.25, -5.90), (-35.20, -5.85), (-35.25, -5.80), (-35.30, -5.85)])], - crs='EPSG:4326') - with pytest.raises(ValueError, match='coarse'): + from shapely.geometry import Polygon + + diamond = Polygon([(-35.25, -5.90), (-35.20, -5.85), (-35.25, -5.80), (-35.30, -5.85)]) + sliver = gpd.GeoDataFrame(geometry=[diamond], crs="EPSG:4326") + with pytest.raises(ValueError, match="coarse"): cm.create_gridpoints(sliver, resolution=10000) def test_create_gridhexagonal(study_area): grid = cm.create_gridhexagonal(study_area, resolution=1) - assert grid.index.name == 'places' - assert grid.geometry.geom_type.eq('Polygon').all() - assert {'lon', 'lat'} <= set(grid.columns) + assert grid.index.name == "places" + assert grid.geometry.geom_type.eq("Polygon").all() + assert {"lon", "lat"} <= set(grid.columns) # every centroid must lie inside its own hexagon - inside = [geom.contains(pt) for geom, pt in - zip(grid.geometry, __import__('geopandas').points_from_xy(grid.lon, grid.lat))] - assert all(inside) + centroids = gpd.points_from_xy(grid.lon, grid.lat) + assert all(geom.contains(pt) for geom, pt in zip(grid.geometry, centroids, strict=True)) # equal-area hexagons: the study area (~100 km2) needs ~100-150 cells of 1 km2 assert 90 <= len(grid) <= 170 assert grid.geometry.union_all().covers(study_area.geometry.iloc[0]) @@ -69,91 +72,91 @@ def test_create_gridhexagonal(study_area): def test_create_gridsquares(study_area): grid = cm.create_gridsquares(study_area, resolution=1) - assert grid.index.name == 'places' - assert grid.geometry.geom_type.eq('Polygon').all() + assert grid.index.name == "places" + assert grid.geometry.geom_type.eq("Polygon").all() assert 100 <= len(grid) <= 150 assert grid.geometry.union_all().covers(study_area.geometry.iloc[0]) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def points_grid(study_area): return cm.create_gridpoints(study_area, resolution=1) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def hex_grid(study_area): return cm.create_gridhexagonal(study_area, resolution=1) def test_kde_transform(dataset, points_grid): - kde = cm.KDE(tfreq='M', grid=points_grid) + kde = cm.KDE(tfreq="M", grid=points_grid) st = kde.fit_transform(dataset.crimes) assert isinstance(st, pd.Series) - assert st.name == 'crime_density' - assert st.index.names == ['t', 'places'] - times = st.index.get_level_values('t').unique() + assert st.name == "crime_density" + assert st.index.names == ["t", "places"] + times = st.index.get_level_values("t").unique() assert len(times) == 24 # 2019-01 .. 2020-12 - assert (times == pd.date_range('2019-01-31', '2020-12-31', freq='ME')).all() + assert (times == pd.date_range("2019-01-31", "2020-12-31", freq="ME")).all() assert len(st) == 24 * len(points_grid) assert not st.isna().any() assert (st >= 0).all() assert kde.factor is not None and kde.factor > 0 # the hotspot around (-35.23, -5.83) must be denser than the far corner - month = st.xs(times[5], level='t') + month = st.xs(times[5], level="t") hot = ((points_grid.lon - (-35.23)).abs() < 0.005) & ((points_grid.lat - (-5.83)).abs() < 0.005) cold = (points_grid.lon < -35.29) & (points_grid.lat < -5.89) assert month[hot.values].mean() > month[cold.values].mean() def test_kde_bandwidth_options(dataset, points_grid): - fixed = cm.KDE(tfreq='M', grid=points_grid, bandwidth=0.3) + fixed = cm.KDE(tfreq="M", grid=points_grid, bandwidth=0.3) fixed.fit_transform(dataset.crimes) assert fixed.factor == 0.3 - scott = cm.KDE(tfreq='M', grid=points_grid, bandwidth='scott') + scott = cm.KDE(tfreq="M", grid=points_grid, bandwidth="scott") scott.fit_transform(dataset.crimes) assert scott.factor > 0 - auto = cm.KDE(tfreq='M', grid=points_grid, bandwidth='auto') - assert auto._bw_method == 'silverman' + auto = cm.KDE(tfreq="M", grid=points_grid, bandwidth="auto") + assert auto._bw_method == "silverman" with pytest.raises(ValueError): - cm.KDE(tfreq='M', grid=points_grid, bandwidth='gaussian') + cm.KDE(tfreq="M", grid=points_grid, bandwidth="gaussian") with pytest.raises(ValueError): - cm.KDE(tfreq='M', grid=points_grid, bandwidth=-1) + cm.KDE(tfreq="M", grid=points_grid, bandwidth=-1) def test_kde_start_end_time(dataset, points_grid): - kde = cm.KDE(tfreq='M', grid=points_grid, start_time='2018-06-01', end_time='2021-03-31') + kde = cm.KDE(tfreq="M", grid=points_grid, start_time="2018-06-01", end_time="2021-03-31") st = kde.fit_transform(dataset.crimes) - times = st.index.get_level_values('t').unique() - assert times.min() == pd.Timestamp('2018-06-30') - assert times.max() == pd.Timestamp('2021-03-31') - assert (st.xs(pd.Timestamp('2018-06-30'), level='t') == 0).all() + times = st.index.get_level_values("t").unique() + assert times.min() == pd.Timestamp("2018-06-30") + assert times.max() == pd.Timestamp("2021-03-31") + assert (st.xs(pd.Timestamp("2018-06-30"), level="t") == 0).all() def test_kde_weekly_and_daily(dataset, points_grid): - weekly = cm.KDE(tfreq='W', grid=points_grid).fit_transform(dataset.crimes) - assert 100 <= len(weekly.index.get_level_values('t').unique()) <= 106 - small = dataset.crimes[dataset.crimes['t'] < '2019-02-01'] - daily = cm.KDE(tfreq='D', grid=points_grid).fit_transform(small) - assert len(daily.index.get_level_values('t').unique()) == 31 + weekly = cm.KDE(tfreq="W", grid=points_grid).fit_transform(dataset.crimes) + assert 100 <= len(weekly.index.get_level_values("t").unique()) <= 106 + small = dataset.crimes[dataset.crimes["t"] < "2019-02-01"] + daily = cm.KDE(tfreq="D", grid=points_grid).fit_transform(small) + assert len(daily.index.get_level_values("t").unique()) == 31 def test_kde_few_points_gives_zeros(dataset, points_grid): two = dataset.crimes.iloc[:2] - st = cm.KDE(tfreq='M', grid=points_grid).fit_transform(two) + st = cm.KDE(tfreq="M", grid=points_grid).fit_transform(two) assert (st == 0).all() def test_kde_grid_validation(points_grid): - with pytest.raises(ValueError, match='lon'): - cm.KDE(tfreq='M', grid=points_grid.drop(columns=['lon'])) + with pytest.raises(ValueError, match="lon"): + cm.KDE(tfreq="M", grid=points_grid.drop(columns=["lon"])) def test_quadrat_count(dataset, hex_grid): - qc = cm.QuadratCount(tfreq='M', grid=hex_grid) + qc = cm.QuadratCount(tfreq="M", grid=hex_grid) st = qc.fit_transform(dataset.crimes) - assert st.name == 'crime_density' - assert st.index.names == ['t', 'places'] - assert len(st.index.get_level_values('t').unique()) == 24 + assert st.name == "crime_density" + assert st.index.names == ["t", "places"] + assert len(st.index.get_level_values("t").unique()) == 24 assert len(st) == 24 * len(hex_grid) assert (st >= 0).all() assert np.allclose(st, np.round(st)) @@ -163,10 +166,10 @@ def test_quadrat_count(dataset, hex_grid): def test_quadrat_count_squares(dataset, study_area): grid = cm.create_gridsquares(study_area, resolution=2) - st = cm.QuadratCount(tfreq='W', grid=grid).fit_transform(dataset.crimes) + st = cm.QuadratCount(tfreq="W", grid=grid).fit_transform(dataset.crimes) assert st.sum() == len(dataset.crimes) def test_quadrat_count_requires_polygons(points_grid): - with pytest.raises(ValueError, match='polygonal'): - cm.QuadratCount(tfreq='M', grid=points_grid) + with pytest.raises(ValueError, match="polygonal"): + cm.QuadratCount(tfreq="M", grid=points_grid) diff --git a/tests/test_dataset_preparation.py b/tests/test_dataset_preparation.py index 1e7505c..2deb72d 100644 --- a/tests/test_dataset_preparation.py +++ b/tests/test_dataset_preparation.py @@ -9,25 +9,25 @@ def test_dataset_builds_points_in_wgs84(crimes, study_area): ds = Dataset(crimes, study_area) assert isinstance(ds.crimes, gpd.GeoDataFrame) assert ds.crimes.crs.to_epsg() == 4326 - assert pd.api.types.is_datetime64_any_dtype(ds.crimes['t']) - assert ds.crimes.geometry.geom_type.eq('Point').all() - assert ds.shape == {'crimes': (len(crimes), 5), 'study_area': (1, 2)} - assert 'predspot.Dataset' in repr(ds) + assert pd.api.types.is_datetime64_any_dtype(ds.crimes["t"]) + assert ds.crimes.geometry.geom_type.eq("Point").all() + assert ds.shape == {"crimes": (len(crimes), 5), "study_area": (1, 2)} + assert "predspot.Dataset" in repr(ds) def test_dataset_does_not_mutate_input(crimes, study_area): before = crimes.copy() Dataset(crimes, study_area) pd.testing.assert_frame_equal(crimes, before) - assert 'geometry' not in crimes.columns + assert "geometry" not in crimes.columns def test_dataset_validation(crimes, study_area): with pytest.raises(TypeError): Dataset(crimes, study_area.geometry.iloc[0]) - with pytest.raises(ValueError, match='missing'): - Dataset(crimes.drop(columns=['lat']), study_area) - with pytest.raises(ValueError, match='CRS'): + with pytest.raises(ValueError, match="missing"): + Dataset(crimes.drop(columns=["lat"]), study_area) + with pytest.raises(ValueError, match="CRS"): Dataset(crimes, study_area.set_crs(None, allow_override=True)) @@ -41,6 +41,7 @@ def test_train_test_split(dataset): def test_plot(dataset): import matplotlib - matplotlib.use('Agg') + + matplotlib.use("Agg") ax = dataset.plot(crime_samples=50) assert ax is not None diff --git a/tests/test_feature_engineering.py b/tests/test_feature_engineering.py index a595e7e..b58f846 100644 --- a/tests/test_feature_engineering.py +++ b/tests/test_feature_engineering.py @@ -8,68 +8,81 @@ from predspot.utilities import PandasFeatureUnion -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def stseries(dataset, study_area): grid = cm.create_gridpoints(study_area, resolution=2) - return cm.KDE(tfreq='M', grid=grid).fit_transform(dataset.crimes) + return cm.KDE(tfreq="M", grid=grid).fit_transform(dataset.crimes) def _check_features(X, stseries, label, lags, first_time): - assert list(X.columns) == [f'{label}_{i}' for i in range(1, lags + 1)] - assert X.index.names == ['t', 'places'] - times = X.index.get_level_values('t').unique() - last_obs = stseries.index.get_level_values('t').max() + assert list(X.columns) == [f"{label}_{i}" for i in range(1, lags + 1)] + assert X.index.names == ["t", "places"] + times = X.index.get_level_values("t").unique() + last_obs = stseries.index.get_level_values("t").max() assert times.max() == last_obs + pd.offsets.MonthEnd(1) # next period row assert times.min() == first_time - n_places = stseries.index.get_level_values('places').nunique() + n_places = stseries.index.get_level_values("places").nunique() assert len(X) == len(times) * n_places assert not X.isna().any().any() def test_ar_features(stseries): - X = fe.AR(lags=3, tfreq='M').fit_transform(stseries) - _check_features(X, stseries, 'ar', 3, pd.Timestamp('2019-04-30')) + X = fe.AR(lags=3, tfreq="M").fit_transform(stseries) + _check_features(X, stseries, "ar", 3, pd.Timestamp("2019-04-30")) # ar_1 at t equals the series at t-1 - place = stseries.index.get_level_values('places')[0] - assert np.isclose(X.loc[(pd.Timestamp('2019-04-30'), place), 'ar_1'], - stseries.loc[(pd.Timestamp('2019-03-31'), place)]) + place = stseries.index.get_level_values("places")[0] + assert np.isclose( + X.loc[(pd.Timestamp("2019-04-30"), place), "ar_1"], + stseries.loc[(pd.Timestamp("2019-03-31"), place)], + ) def test_diff_features(stseries): - X = fe.Diff(lags=2, tfreq='M').fit_transform(stseries) - _check_features(X, stseries, 'diff', 2, pd.Timestamp('2019-04-30')) + X = fe.Diff(lags=2, tfreq="M").fit_transform(stseries) + _check_features(X, stseries, "diff", 2, pd.Timestamp("2019-04-30")) def test_seasonality_and_trend(stseries): - S = fe.Seasonality(lags=6, tfreq='M').fit_transform(stseries) - T = fe.Trend(lags=6, tfreq='M').fit_transform(stseries) - _check_features(S, stseries, 'seasonal', 6, pd.Timestamp('2019-07-31')) - _check_features(T, stseries, 'trend', 6, pd.Timestamp('2019-07-31')) + S = fe.Seasonality(lags=6, tfreq="M").fit_transform(stseries) + T = fe.Trend(lags=6, tfreq="M").fit_transform(stseries) + _check_features(S, stseries, "seasonal", 6, pd.Timestamp("2019-07-31")) + _check_features(T, stseries, "trend", 6, pd.Timestamp("2019-07-31")) assert not np.allclose(S.values, T.values) def test_tfreq_is_inferred_when_omitted(stseries): X = fe.AR(lags=2).fit_transform(stseries) - assert X.index.get_level_values('t').max() == pd.Timestamp('2021-01-31') + assert X.index.get_level_values("t").max() == pd.Timestamp("2021-01-31") def test_validation(stseries): with pytest.raises(ValueError): fe.AR(lags=1) - with pytest.raises(ValueError, match='lags'): - fe.AR(lags=30, tfreq='M').fit_transform(stseries) - with pytest.raises(ValueError, match='2 \\* lags'): - fe.Seasonality(lags=13, tfreq='M').fit_transform(stseries) + with pytest.raises(ValueError, match="lags"): + fe.AR(lags=30, tfreq="M").fit_transform(stseries) + with pytest.raises(ValueError, match="2 \\* lags"): + fe.Seasonality(lags=13, tfreq="M").fit_transform(stseries) def test_pandas_feature_union(stseries): - union = PandasFeatureUnion([('ar', fe.AR(lags=2, tfreq='M')), - ('seasonal', fe.Seasonality(lags=4, tfreq='M')), - ('skip', None)]) + union = PandasFeatureUnion( + [ + ("ar", fe.AR(lags=2, tfreq="M")), + ("seasonal", fe.Seasonality(lags=4, tfreq="M")), + ("skip", None), + ] + ) X = union.fit_transform(stseries) - assert list(X.columns) == ['ar_1', 'ar_2', 'seasonal_1', 'seasonal_2', 'seasonal_3', 'seasonal_4'] + assert list(X.columns) == [ + "ar_1", + "ar_2", + "seasonal_1", + "seasonal_2", + "seasonal_3", + "seasonal_4", + ] # rows are aligned on the intersection of the indexes (the STL warm-up wins) - assert X.index.get_level_values('t').min() == pd.Timestamp('2019-05-31') + assert X.index.get_level_values("t").min() == pd.Timestamp("2019-05-31") assert not X.isna().any().any() pd.testing.assert_frame_equal(union.transform(stseries), X) with pytest.raises(ValueError): @@ -77,7 +90,7 @@ def test_pandas_feature_union(stseries): def test_feature_scaling(stseries): - X = fe.AR(lags=2, tfreq='M').fit_transform(stseries) + X = fe.AR(lags=2, tfreq="M").fit_transform(stseries) scaled = fe.FeatureScaling(StandardScaler()).fit_transform(X) assert isinstance(scaled, pd.DataFrame) assert scaled.index.equals(X.index) and list(scaled.columns) == list(X.columns) diff --git a/tests/test_ml_modelling.py b/tests/test_ml_modelling.py index 7ea914e..0a1479f 100644 --- a/tests/test_ml_modelling.py +++ b/tests/test_ml_modelling.py @@ -15,22 +15,30 @@ def make_pipeline(grid, mapping_cls=cm.KDE, estimator=None): return ml.PredictionPipeline( - mapping=mapping_cls(tfreq='M', grid=grid), - fextraction=PandasFeatureUnion([ - ('seasonal', fe.Seasonality(lags=4, tfreq='M')), - ('trend', fe.Trend(lags=4, tfreq='M')), - ('diff', fe.Diff(lags=4, tfreq='M')), - ]), - estimator=estimator or Pipeline([ - ('f_scaling', fe.FeatureScaling(QuantileTransformer(n_quantiles=10))), - ('f_selection', ml.FeatureSelection(RFE(RandomForestRegressor(n_estimators=5, random_state=0)))), - ('model', ml.Model(RandomForestRegressor(n_estimators=10, random_state=0))), - ]), + mapping=mapping_cls(tfreq="M", grid=grid), + fextraction=PandasFeatureUnion( + [ + ("seasonal", fe.Seasonality(lags=4, tfreq="M")), + ("trend", fe.Trend(lags=4, tfreq="M")), + ("diff", fe.Diff(lags=4, tfreq="M")), + ] + ), + estimator=estimator + or Pipeline( + [ + ("f_scaling", fe.FeatureScaling(QuantileTransformer(n_quantiles=10))), + ( + "f_selection", + ml.FeatureSelection(RFE(RandomForestRegressor(n_estimators=5, random_state=0))), + ), + ("model", ml.Model(RandomForestRegressor(n_estimators=10, random_state=0))), + ] + ), random_state=0, ) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def fitted(dataset, study_area): grid = cm.create_gridpoints(study_area, resolution=2) return make_pipeline(grid).fit(dataset) @@ -38,37 +46,37 @@ def fitted(dataset, study_area): def test_fit_predict(fitted): n_places = len(fitted.grid) - assert fitted.next_time == pd.Timestamp('2021-01-31') + assert fitted.next_time == pd.Timestamp("2021-01-31") pred = fitted.predict() - assert list(pred.columns) == ['crime_density'] + assert list(pred.columns) == ["crime_density"] assert len(pred) == n_places - assert pred.index.get_level_values('t').unique().tolist() == [pd.Timestamp('2021-01-31')] - assert (pred['crime_density'] >= 0).all() + assert pred.index.get_level_values("t").unique().tolist() == [pd.Timestamp("2021-01-31")] + assert (pred["crime_density"] >= 0).all() # the forecast is appended to the series and the horizon moves forward - assert fitted.stseries.index.get_level_values('t').max() == pd.Timestamp('2021-01-31') - assert fitted.next_time == pd.Timestamp('2021-02-28') + assert fitted.stseries.index.get_level_values("t").max() == pd.Timestamp("2021-01-31") + assert fitted.next_time == pd.Timestamp("2021-02-28") pred2 = fitted.predict() - assert pred2.index.get_level_values('t').unique().tolist() == [pd.Timestamp('2021-02-28')] + assert pred2.index.get_level_values("t").unique().tolist() == [pd.Timestamp("2021-02-28")] def test_feature_importances(fitted): fi = fitted.feature_importances - assert list(fi.columns) == ['importance'] - assert np.isclose(fi['importance'].sum(), 1) + assert list(fi.columns) == ["importance"] + assert np.isclose(fi["importance"].sum(), 1) assert set(fi.index) <= set(fitted.features.columns) def test_evaluate(dataset, study_area): grid = cm.create_gridpoints(study_area, resolution=2) pipe = make_pipeline(grid).fit(dataset) - scores = pipe.evaluate('r2', cv=3) + scores = pipe.evaluate("r2", cv=3) assert len(scores) == 3 - mse = pipe.evaluate('mse', cv=3) + mse = pipe.evaluate("mse", cv=3) assert all(s >= 0 for s in mse) with pytest.raises(ValueError): - pipe.evaluate('mae') + pipe.evaluate("mae") with pytest.raises(ValueError): - pipe.evaluate('r2', cv=100) + pipe.evaluate("r2", cv=100) # evaluate refits on the full data: predictions still work afterwards assert len(pipe.predict()) == len(grid) @@ -79,7 +87,7 @@ def test_plain_sklearn_estimator(dataset, study_area): pred = pipe.predict() assert len(pred) == len(grid) with pytest.raises(AttributeError): - pipe.feature_importances + _ = pipe.feature_importances def test_quadrat_count_pipeline(dataset, study_area): diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py index 83da5be..be7a53a 100644 --- a/tests/test_pipeline.py +++ b/tests/test_pipeline.py @@ -5,24 +5,25 @@ def test_generate_testdata(): - crimes, area = pipeline.generate_testdata(500, '2019-01-01', '2019-12-31', seed=1) - assert list(crimes.columns) == ['tag', 't', 'lon', 'lat'] + crimes, area = pipeline.generate_testdata(500, "2019-01-01", "2019-12-31", seed=1) + assert list(crimes.columns) == ["tag", "t", "lon", "lat"] assert len(crimes) == 500 assert area.crs.to_epsg() == 4326 - assert crimes['t'].between('2019-01-01', '2019-12-31').all() - again, _ = pipeline.generate_testdata(500, '2019-01-01', '2019-12-31', seed=1) + assert crimes["t"].between("2019-01-01", "2019-12-31").all() + again, _ = pipeline.generate_testdata(500, "2019-01-01", "2019-12-31", seed=1) pd.testing.assert_frame_equal(crimes, again) def test_run_prediction_pipeline(crimes, study_area): pred, pipe = pipeline.run_prediction_pipeline( - crimes, study_area, crime_tags=['burglary'], grid_resolution=2, random_state=0) + crimes, study_area, crime_tags=["burglary"], grid_resolution=2, random_state=0 + ) assert len(pred) == len(pipe.grid) - assert pred.index.get_level_values('t').unique().tolist() == [pd.Timestamp('2021-01-31')] - scores = pipeline.evaluate_pipeline(pipe, 'r2', cv=2) + assert pred.index.get_level_values("t").unique().tolist() == [pd.Timestamp("2021-01-31")] + scores = pipeline.evaluate_pipeline(pipe, "r2", cv=2) assert len(scores) == 2 def test_run_prediction_pipeline_validation(crimes, study_area): with pytest.raises(ValueError): - pipeline.run_prediction_pipeline(crimes.drop(columns=['tag']), study_area) + pipeline.run_prediction_pipeline(crimes.drop(columns=["tag"]), study_area) diff --git a/tests/test_utilities.py b/tests/test_utilities.py index 88457e1..a4ba5fc 100644 --- a/tests/test_utilities.py +++ b/tests/test_utilities.py @@ -7,9 +7,9 @@ def test_contour_geojson(dataset, study_area): - pytest.importorskip('geojsoncontour') + pytest.importorskip("geojsoncontour") grid = cm.create_gridpoints(study_area, resolution=1) - st = cm.KDE(tfreq='M', grid=grid).fit_transform(dataset.crimes) - month = st.xs(st.index.get_level_values('t')[0], level='t') + st = cm.KDE(tfreq="M", grid=grid).fit_transform(dataset.crimes) + month = st.xs(st.index.get_level_values("t")[0], level="t") geojson = contour_geojson(month, study_area, 1, cmin=0, cmax=month.max()) - assert json.loads(geojson)['type'] == 'FeatureCollection' + assert json.loads(geojson)["type"] == "FeatureCollection" From 92940f8aba0fa58e5503cb040b463034d44da34a Mon Sep 17 00:00:00 2001 From: Adelson Araujo Date: Mon, 14 Sep 2026 12:54:07 +0000 Subject: [PATCH 2/4] Add OpenStreetMap study areas and a synthetic crime event generator - crime_mapping.load_study_area("City, State, Country") geocodes a place with osmnx and returns its boundary polygon as a GeoDataFrame ready for Dataset / the create_grid* functions; osmnx is an optional extra (`predspot[osm]`), points instead of polygons raise a clear error - New predspot.synthetic module: generate_crimes(study_area, ...) draws events from a Gaussian-hotspot + uniform-background spatial mixture and an inhomogeneous temporal intensity (linear trend, annual cycle, day-of-week and hour-of-day profiles) sampled by thinning; works with any study area polygon in any CRS; reproducible with `seed`; can return hotspot centres - pipeline.generate_testdata now delegates to the synthetic generator - Tests for both features (osmnx mocked; an opt-in network test is run with PREDSPOT_NETWORK_TESTS=1); README/CHANGELOG/CONTRIBUTING updated Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01FtZdc2gGrDh4PGSLyT9yRE --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 5 + CONTRIBUTING.md | 3 +- README.md | 24 ++- src/predspot/__init__.py | 6 + src/predspot/crime_mapping.py | 51 +++++ src/predspot/pipeline.py | 24 +-- src/predspot/synthetic.py | 340 ++++++++++++++++++++++++++++++++++ tests/test_load_study_area.py | 72 +++++++ tests/test_synthetic.py | 124 +++++++++++++ 10 files changed, 630 insertions(+), 21 deletions(-) create mode 100644 src/predspot/synthetic.py create mode 100644 tests/test_load_study_area.py create mode 100644 tests/test_synthetic.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 343b82a..a88ddbe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: python-version: ${{ matrix.python-version }} cache: pip - name: Install - run: python -m pip install -e ".[dev,contour]" + run: python -m pip install -e ".[dev,osm,contour]" - name: Test run: pytest --cov --cov-report=term-missing diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b45ac8..2c94242 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ 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.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 + area: Gaussian hotspots plus uniform background, with trend, annual cycle, + day-of-week and hour-of-day patterns; reproducible with `seed`. - `QuadratCount` mapping (event counts per cell) as a first-class alternative to `KDE`, usable with hexagonal (`create_gridhexagonal`) and square (`create_gridsquares`) grids inside `PredictionPipeline`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5a46c84..b510773 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ Thanks for your interest in Predspot! Issues and pull requests are welcome. git clone https://github.com/adaj/predspot.git cd predspot python -m venv .venv && source .venv/bin/activate -pip install -e ".[dev,contour]" +pip install -e ".[dev,osm,contour]" ``` ## Checks @@ -17,6 +17,7 @@ pip install -e ".[dev,contour]" ruff check src tests # lint ruff format src tests # format pytest # tests (~10 s) +PREDSPOT_NETWORK_TESTS=1 pytest tests/test_load_study_area.py # also query OpenStreetMap ``` CI runs the same checks on Python 3.10 to 3.13 for every pull request. diff --git a/README.md b/README.md index 81c7214..d0965e0 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,23 @@ from predspot.crime_mapping import QuadratCount, create_gridhexagonal mapping = QuadratCount(tfreq='W', grid=create_gridhexagonal(study_area_gdf, resolution=1)) ``` +### Study area from OpenStreetMap and synthetic data + +You do not need real data to try Predspot. Fetch a city boundary from +OpenStreetMap (`pip install "predspot[osm]"`) and generate synthetic events +with spatial hotspots and realistic temporal patterns (trend, annual cycle, +day-of-week and hour-of-day profiles): + +```python +from predspot import Dataset, load_study_area, generate_crimes + +study_area = load_study_area("Natal, Rio Grande do Norte, Brazil") +crimes = generate_crimes(study_area, n_events=5000, n_hotspots=4, + start="2019-01-01", end="2020-12-31", seed=0) +dataset = Dataset(crimes, study_area) +dataset.plot() +``` + Or run the default pipeline in one call: ```python @@ -80,7 +97,7 @@ print(pipeline.evaluate('r2', cv=3)) ## Development ⚡ -Predspot has four main modules: +Predspot has five main modules: `dataset_preparation`: Module for preparing and managing crime datasets and study areas. @@ -90,12 +107,15 @@ Predspot has four main modules: `ml_modelling`: Module that implements the prediction pipeline and model evaluation. +`synthetic`: Module that generates synthetic crime events (hotspots + temporal patterns) inside any study area. + ### Installation steps 🛠️ Predspot requires Python 3.10 or newer. ```bash pip install predspot # from PyPI +pip install "predspot[osm]" # + study areas from OpenStreetMap (osmnx) pip install "predspot[contour]" # + GeoJSON contour export (geojsoncontour) ``` @@ -104,7 +124,7 @@ From source, for development: ```bash git clone https://github.com/adaj/predspot.git cd predspot -pip install -e ".[dev,contour]" +pip install -e ".[dev,osm,contour]" ``` Core dependencies (installed automatically): pandas, geopandas, shapely, diff --git a/src/predspot/__init__.py b/src/predspot/__init__.py index 2dae115..f85d774 100644 --- a/src/predspot/__init__.py +++ b/src/predspot/__init__.py @@ -14,6 +14,7 @@ dataset_preparation, feature_engineering, ml_modelling, + synthetic, utilities, ) from predspot.crime_mapping import ( @@ -22,9 +23,11 @@ create_gridhexagonal, create_gridpoints, create_gridsquares, + load_study_area, ) from predspot.dataset_preparation import Dataset from predspot.ml_modelling import PredictionPipeline +from predspot.synthetic import generate_crimes from predspot.utilities import PandasFeatureUnion __version__ = "0.2.0" @@ -38,6 +41,9 @@ "create_gridpoints", "create_gridhexagonal", "create_gridsquares", + "load_study_area", + "generate_crimes", + "synthetic", "crime_mapping", "dataset_preparation", "feature_engineering", diff --git a/src/predspot/crime_mapping.py b/src/predspot/crime_mapping.py index 941538d..78d16f6 100644 --- a/src/predspot/crime_mapping.py +++ b/src/predspot/crime_mapping.py @@ -14,6 +14,9 @@ Both produce the same output format, a :class:`pandas.Series` named ``crime_density`` indexed by ``(t, places)``, so they are interchangeable inside :class:`predspot.ml_modelling.PredictionPipeline`. + +The study area itself can be fetched from OpenStreetMap with +:func:`load_study_area` (requires the optional ``osmnx`` dependency). """ __author__ = "Adelson Araujo" @@ -83,6 +86,54 @@ def tfreq_offset(tfreq): return pd.offsets.Day(1) +def load_study_area(place, crs=WGS84, which_result=None): + """ + Fetch the boundary polygon of a place from OpenStreetMap. + + Uses `osmnx `_ (optional dependency: + ``pip install predspot[osm]``) to geocode ``place`` with Nominatim and + return its administrative boundary, ready to be used as the + ``study_area`` of :class:`predspot.Dataset` or as the ``bbox`` of the + ``create_grid*`` functions. + + Args: + place (str or list): Name of the place as Nominatim understands it, + e.g. ``"Natal, Rio Grande do Norte, Brazil"``. A list of names + returns one row per place. + crs: CRS of the returned GeoDataFrame (default WGS84). + which_result (int, optional): Forwarded to + ``osmnx.geocode_to_gdf`` to pick a specific Nominatim match when + the first one is not the boundary you want. + + Returns: + GeoDataFrame: One row per place with ``name``, ``display_name``, + ``osm_type``, ``osm_id`` and a (Multi)Polygon ``geometry``. + + Raises: + ImportError: If ``osmnx`` is not installed. + ValueError: If Nominatim returns a point instead of a boundary + polygon for the query. + """ + try: + import osmnx as ox + except ImportError as exc: + raise ImportError( + "load_study_area requires the optional dependency `osmnx`: pip install predspot[osm]" + ) from exc + logger.debug("Geocoding study area %r with osmnx", place) + gdf = ox.geocode_to_gdf(place, which_result=which_result) + if not gdf.geom_type.isin(["Polygon", "MultiPolygon"]).all(): + bad = gdf.loc[~gdf.geom_type.isin(["Polygon", "MultiPolygon"]), "display_name"] + raise ValueError( + "OpenStreetMap returned a non-polygon geometry for " + f"{bad.tolist()}. Try a more specific query (e.g. add the state " + "and country) or a different `which_result`." + ) + columns = [c for c in ("name", "display_name", "osm_type", "osm_id") if c in gdf.columns] + gdf = gdf[columns + ["geometry"]].reset_index(drop=True) + return gdf.to_crs(crs) + + def _check_bbox(bbox): if not isinstance(bbox, gpd.GeoDataFrame): raise TypeError("bbox must be a geopandas GeoDataFrame.") diff --git a/src/predspot/pipeline.py b/src/predspot/pipeline.py index 6b51933..d7354d6 100644 --- a/src/predspot/pipeline.py +++ b/src/predspot/pipeline.py @@ -25,6 +25,7 @@ from sklearn.preprocessing import QuantileTransformer from predspot import crime_mapping, dataset_preparation, feature_engineering, ml_modelling +from predspot.synthetic import generate_crimes from predspot.utilities import PandasFeatureUnion logger = logging.getLogger(__name__) @@ -35,7 +36,10 @@ def generate_testdata(n_points, start_time, end_time, bounds=DEFAULT_BOUNDS, seed=None): """ - Generate uniformly random crime events inside a rectangular study area. + Generate synthetic crime events inside a rectangular study area. + + A thin wrapper around :func:`predspot.synthetic.generate_crimes` with + three hotspots and the default temporal patterns. Args: n_points (int): Number of events. @@ -48,27 +52,13 @@ def generate_testdata(n_points, start_time, end_time, bounds=DEFAULT_BOUNDS, see tuple: ``(crimes, study_area)`` — a DataFrame with ``tag``, ``t``, ``lon``, ``lat`` and a one-row GeoDataFrame with the study area. """ - rng = np.random.default_rng(seed) west, south, east, north = bounds study_area = gpd.GeoDataFrame( {"name": ["study_area"]}, geometry=[box(west, south, east, north)], crs="EPSG:4326" ) - start, end = pd.Timestamp(start_time), pd.Timestamp(end_time) - seconds = rng.integers(0, int((end - start).total_seconds()), n_points) - tags = rng.choice( - ["burglary", "assault", "drugs", "homicide"], - size=n_points, - p=np.array([1000, 100, 10, 1]) / 1111, - ) - crimes = pd.DataFrame( - { - "tag": tags, - "t": start + pd.to_timedelta(seconds, unit="s"), - "lon": rng.uniform(west, east, n_points), - "lat": rng.uniform(south, north, n_points), - } + crimes = generate_crimes( + study_area, n_events=n_points, start=start_time, end=end_time, seed=seed ) - logger.debug("Generated %d synthetic events", n_points) return crimes, study_area diff --git a/src/predspot/synthetic.py b/src/predspot/synthetic.py new file mode 100644 index 0000000..bea3b9a --- /dev/null +++ b/src/predspot/synthetic.py @@ -0,0 +1,340 @@ +""" +Synthetic Data Module +===================== + +Generates realistic-looking synthetic crime events inside a study area, so +that Predspot can be tried, demonstrated and tested without real data. + +The generator is a simple inhomogeneous space-time point process: + +* **Space** — a mixture of ``n_hotspots`` Gaussian hotspots (centres drawn + uniformly inside the study area) plus a uniform background. The fraction + of events that belong to hotspots is ``hotspot_share``. +* **Time** — an intensity built from a linear trend, an annual cycle, a + day-of-week profile and an hour-of-day profile. Timestamps are drawn by + thinning uniform candidates, which is exact for a fixed number of events. + +Example:: + + from predspot.synthetic import generate_crimes + crimes = generate_crimes(study_area, n_events=5000, seed=0) + dataset = Dataset(crimes, study_area) +""" + +__author__ = "Adelson Araujo" + +import logging +import math + +import geopandas as gpd +import numpy as np +import pandas as pd +import shapely + +from predspot.crime_mapping import KM_PER_DEG_LAT, KM_PER_DEG_LON, WGS84, _check_bbox + +logger = logging.getLogger(__name__) + +DEFAULT_TAGS = {"burglary": 0.45, "robbery": 0.30, "assault": 0.20, "homicide": 0.05} + +# Relative intensity Monday..Sunday (normalised to mean 1 internally). +DEFAULT_WEEKLY_PROFILE = (0.90, 0.85, 0.90, 0.95, 1.10, 1.30, 1.00) + +# Relative intensity by hour of day, 0..23: quiet early morning, busy evening. +DEFAULT_HOURLY_PROFILE = ( + 0.7, 0.5, 0.4, 0.3, 0.25, 0.3, 0.45, 0.7, 0.9, 1.0, 1.05, 1.1, + 1.1, 1.05, 1.1, 1.15, 1.25, 1.4, 1.55, 1.7, 1.75, 1.6, 1.3, 1.0, +) # fmt: skip + + +def _study_polygon(study_area): + """Return the union of the study area geometries as a WGS84 shapely geometry.""" + _check_bbox(study_area) + return study_area.to_crs(WGS84).geometry.union_all() + + +def sample_points_in_polygon(polygon, n, rng, max_iterations=1000): + """ + Draw ``n`` points uniformly inside a polygon by rejection sampling. + + Args: + polygon (shapely.Geometry): Polygon in WGS84. + n (int): Number of points. + rng (numpy.random.Generator): Random generator. + max_iterations (int): Safety cap on rejection rounds. + + Returns: + tuple: ``(lon, lat)`` arrays of length ``n``. + """ + minx, miny, maxx, maxy = polygon.bounds + lon = np.empty(0) + lat = np.empty(0) + fill = polygon.area / ((maxx - minx) * (maxy - miny)) if polygon.area > 0 else 1.0 + batch = int(max(n / max(fill, 1e-3) * 1.2, 64)) + for _ in range(max_iterations): + cx = rng.uniform(minx, maxx, batch) + cy = rng.uniform(miny, maxy, batch) + inside = shapely.contains_xy(polygon, cx, cy) + lon = np.concatenate([lon, cx[inside]]) + lat = np.concatenate([lat, cy[inside]]) + if len(lon) >= n: + return lon[:n], lat[:n] + raise RuntimeError("Could not sample enough points inside the study area.") + + +def sample_points_around(polygon, centers, sd_km, n_per_center, rng, max_iterations=1000): + """ + Draw points from Gaussian clouds around hotspot centres, kept inside the polygon. + + Args: + polygon (shapely.Geometry): Study area in WGS84. + centers (numpy.ndarray): ``(k, 2)`` array of ``(lon, lat)`` centres. + sd_km (float or array): Standard deviation of each cloud in km. + n_per_center (array): Number of points to draw per centre. + rng (numpy.random.Generator): Random generator. + max_iterations (int): Safety cap on rejection rounds. + + Returns: + tuple: ``(lon, lat)`` arrays. + """ + sd_km = np.broadcast_to(np.asarray(sd_km, dtype=float), (len(centers),)) + lons, lats = [], [] + for (c_lon, c_lat), sd, n in zip(centers, sd_km, n_per_center, strict=True): + if n == 0: + continue + sd_lat = sd / KM_PER_DEG_LAT + sd_lon = sd / (KM_PER_DEG_LON * max(math.cos(math.radians(c_lat)), 0.05)) + lon = np.empty(0) + lat = np.empty(0) + remaining = int(n) + for _ in range(max_iterations): + cx = rng.normal(c_lon, sd_lon, remaining * 2) + cy = rng.normal(c_lat, sd_lat, remaining * 2) + inside = shapely.contains_xy(polygon, cx, cy) + lon = np.concatenate([lon, cx[inside]]) + lat = np.concatenate([lat, cy[inside]]) + if len(lon) >= n: + break + remaining = int(n - len(lon)) + else: + raise RuntimeError("Could not sample enough hotspot points inside the study area.") + lons.append(lon[:n]) + lats.append(lat[:n]) + if not lons: + return np.empty(0), np.empty(0) + return np.concatenate(lons), np.concatenate(lats) + + +def temporal_intensity( + timestamps, + start, + end, + trend=0.0, + annual_amplitude=0.0, + annual_peak_month=1, + weekly_profile=None, + hourly_profile=None, +): + """ + Relative event intensity at each timestamp (mean around 1). + + Args: + timestamps (DatetimeIndex): Times to evaluate. + start, end (Timestamp): Bounds of the simulation, used for the trend. + trend (float): Relative change of the intensity from ``start`` to + ``end`` (``0.5`` means +50% at the end, ``-0.3`` means -30%). + annual_amplitude (float): Amplitude of the annual cosine (0-1). + annual_peak_month (int): Month (1-12) where the annual cycle peaks. + weekly_profile (sequence): 7 relative weights, Monday to Sunday. + hourly_profile (sequence): 24 relative weights, hour 0 to 23. + + Returns: + numpy.ndarray: Intensity values, one per timestamp. + """ + timestamps = pd.DatetimeIndex(timestamps) + span = max((end - start).total_seconds(), 1.0) + frac = (timestamps - start).total_seconds() / span + lam = np.clip(1.0 + trend * frac, 0.0, None) + if annual_amplitude: + peak_doy = (annual_peak_month - 1) * 365.25 / 12 + 15 + lam = lam * ( + 1 + annual_amplitude * np.cos(2 * np.pi * (timestamps.dayofyear - peak_doy) / 365.25) + ) + if weekly_profile is not None: + weekly = np.asarray(weekly_profile, dtype=float) + if weekly.shape != (7,): + raise ValueError("weekly_profile must have 7 values (Monday..Sunday).") + lam = lam * (weekly / weekly.mean())[timestamps.dayofweek] + if hourly_profile is not None: + hourly = np.asarray(hourly_profile, dtype=float) + if hourly.shape != (24,): + raise ValueError("hourly_profile must have 24 values (hour 0..23).") + lam = lam * (hourly / hourly.mean())[timestamps.hour] + return np.asarray(lam) + + +def sample_timestamps(n, start, end, rng, max_iterations=1000, **intensity_kwargs): + """ + Draw ``n`` timestamps from an inhomogeneous process by thinning. + + Args: + n (int): Number of timestamps. + start, end (Timestamp): Simulation bounds. + rng (numpy.random.Generator): Random generator. + max_iterations (int): Safety cap on thinning rounds. + **intensity_kwargs: Forwarded to :func:`temporal_intensity`. + + Returns: + pandas.DatetimeIndex: ``n`` timestamps, unsorted. + """ + span = (end - start).total_seconds() + if span <= 0: + raise ValueError("end must be after start.") + # Upper bound of the intensity: product of the maxima of each factor. + lam_max = max(1.0, 1.0 + intensity_kwargs.get("trend", 0.0)) + lam_max *= 1 + abs(intensity_kwargs.get("annual_amplitude", 0.0)) + for key, size in (("weekly_profile", 7), ("hourly_profile", 24)): + profile = intensity_kwargs.get(key) + if profile is not None: + profile = np.asarray(profile, dtype=float) + if profile.shape != (size,): + raise ValueError(f"{key} must have {size} values.") + lam_max *= profile.max() / profile.mean() + accepted = [] + total = 0 + batch = int(max(n * lam_max * 1.2, 64)) + for _ in range(max_iterations): + candidates = start + pd.to_timedelta(rng.uniform(0, span, batch), unit="s") + lam = temporal_intensity(candidates, start, end, **intensity_kwargs) + keep = rng.uniform(0, lam_max, batch) < lam + accepted.append(candidates[keep]) + total += int(keep.sum()) + if total >= n: + break + else: + raise RuntimeError("Could not sample enough timestamps.") + stamps = accepted[0].append(accepted[1:]) if len(accepted) > 1 else accepted[0] + return stamps[:n].floor("s") + + +def generate_crimes( + study_area, + n_events=5000, + start="2019-01-01", + end="2020-12-31", + n_hotspots=3, + hotspot_share=0.7, + hotspot_sd_km=0.5, + tags=None, + trend=0.0, + annual_amplitude=0.2, + annual_peak_month=1, + weekly_profile=DEFAULT_WEEKLY_PROFILE, + hourly_profile=DEFAULT_HOURLY_PROFILE, + seed=None, + return_hotspots=False, +): + """ + Generate synthetic crime events inside a study area. + + Args: + study_area (GeoDataFrame): Boundary of the study area (any CRS). See + :func:`predspot.crime_mapping.load_study_area` to fetch one from + OpenStreetMap. + n_events (int): Number of events to generate. + start (str or Timestamp): First possible timestamp. + end (str or Timestamp): Last possible timestamp. + n_hotspots (int): Number of Gaussian hotspots (0 for a uniform map). + hotspot_share (float): Fraction of events that belong to hotspots; + the rest is uniform background (0-1). + hotspot_sd_km (float or sequence): Standard deviation of the hotspot + clouds in km (one value or one per hotspot). + tags (dict or sequence): Crime types. A dict maps type to relative + weight; a sequence gives equal weights. Defaults to + :data:`DEFAULT_TAGS`. + trend (float): Relative change of the event rate from ``start`` to + ``end`` (``0.5`` = +50%). + annual_amplitude (float): Amplitude of the annual cycle (0 disables). + annual_peak_month (int): Month (1-12) where the annual cycle peaks. + weekly_profile (sequence or None): 7 weights Monday..Sunday + (``None`` disables the weekly pattern). + hourly_profile (sequence or None): 24 weights, hour 0..23 + (``None`` disables the hour-of-day pattern). + seed (int, optional): Seed for reproducibility. + return_hotspots (bool): Also return the hotspot centres. + + Returns: + pandas.DataFrame: Events with ``tag``, ``t``, ``lon``, ``lat`` columns + sorted by time, ready for :class:`predspot.Dataset`. If + ``return_hotspots`` is True, a tuple ``(crimes, hotspots)`` where + ``hotspots`` is a GeoDataFrame with the centre, ``sd_km`` and + ``share`` of each hotspot. + """ + if n_events <= 0: + raise ValueError("n_events must be positive.") + if not 0 <= hotspot_share <= 1: + raise ValueError("hotspot_share must be between 0 and 1.") + if n_hotspots < 0: + raise ValueError("n_hotspots must be >= 0.") + rng = np.random.default_rng(seed) + polygon = _study_polygon(study_area) + start, end = pd.Timestamp(start), pd.Timestamp(end) + + # --- tags --------------------------------------------------------------- + if tags is None: + tags = DEFAULT_TAGS + if isinstance(tags, dict): + names, weights = list(tags.keys()), np.asarray(list(tags.values()), dtype=float) + else: + names, weights = list(tags), np.ones(len(tags)) + if len(names) == 0 or (weights < 0).any() or weights.sum() == 0: + raise ValueError("tags must contain at least one type with a positive weight.") + tag_values = rng.choice(names, size=n_events, p=weights / weights.sum()) + + # --- space -------------------------------------------------------------- + if n_hotspots == 0: + n_hot = 0 + else: + n_hot = int(round(n_events * hotspot_share)) + n_bg = n_events - n_hot + hot_lon, hot_lat = sample_points_in_polygon(polygon, n_hotspots, rng) + centers = np.column_stack([hot_lon, hot_lat]) if n_hotspots else np.empty((0, 2)) + shares = rng.dirichlet(np.full(n_hotspots, 2.0)) if n_hotspots else np.empty(0) + per_center = rng.multinomial(n_hot, shares) if n_hot else np.zeros(n_hotspots, dtype=int) + sd_km = np.broadcast_to(np.asarray(hotspot_sd_km, dtype=float), (n_hotspots,)) + lon_h, lat_h = sample_points_around(polygon, centers, sd_km, per_center, rng) + lon_b, lat_b = ( + sample_points_in_polygon(polygon, n_bg, rng) if n_bg else (np.empty(0), np.empty(0)) + ) + lon = np.concatenate([lon_h, lon_b]) + lat = np.concatenate([lat_h, lat_b]) + order = rng.permutation(n_events) + lon, lat = lon[order], lat[order] + + # --- time --------------------------------------------------------------- + timestamps = sample_timestamps( + n_events, + start, + end, + rng, + trend=trend, + annual_amplitude=annual_amplitude, + annual_peak_month=annual_peak_month, + weekly_profile=weekly_profile, + hourly_profile=hourly_profile, + ) + + crimes = ( + pd.DataFrame({"tag": tag_values, "t": timestamps, "lon": lon, "lat": lat}) + .sort_values("t") + .reset_index(drop=True) + ) + logger.debug("Generated %d synthetic events with %d hotspots", n_events, n_hotspots) + if not return_hotspots: + return crimes + hotspots = gpd.GeoDataFrame( + {"sd_km": sd_km, "share": shares * hotspot_share}, + geometry=gpd.points_from_xy(centers[:, 0], centers[:, 1]), + crs=WGS84, + ) + return crimes, hotspots diff --git a/tests/test_load_study_area.py b/tests/test_load_study_area.py new file mode 100644 index 0000000..70efc14 --- /dev/null +++ b/tests/test_load_study_area.py @@ -0,0 +1,72 @@ +import os +import sys +import types + +import geopandas as gpd +import pytest +from shapely.geometry import Point, box + +from predspot.crime_mapping import load_study_area + + +def _fake_osmnx(monkeypatch, geometry): + """Install a fake `osmnx` module whose geocode_to_gdf returns `geometry`.""" + calls = {} + + def geocode_to_gdf(query, which_result=None): + calls["query"], calls["which_result"] = query, which_result + return gpd.GeoDataFrame( + { + "osm_type": ["relation"], + "osm_id": [1], + "name": ["Natal"], + "display_name": ["Natal, Rio Grande do Norte, Brasil"], + "place_rank": [16], + }, + geometry=[geometry], + crs="EPSG:4326", + ) + + fake = types.ModuleType("osmnx") + fake.geocode_to_gdf = geocode_to_gdf + monkeypatch.setitem(sys.modules, "osmnx", fake) + return calls + + +def test_load_study_area_returns_polygon(monkeypatch): + calls = _fake_osmnx(monkeypatch, box(-35.3, -5.9, -35.2, -5.8)) + area = load_study_area("Natal, Brazil") + assert calls == {"query": "Natal, Brazil", "which_result": None} + assert list(area.columns) == ["name", "display_name", "osm_type", "osm_id", "geometry"] + assert area.crs.to_epsg() == 4326 + assert area.geometry.iloc[0].geom_type == "Polygon" + + +def test_load_study_area_reprojects(monkeypatch): + _fake_osmnx(monkeypatch, box(-35.3, -5.9, -35.2, -5.8)) + area = load_study_area("Natal, Brazil", crs="EPSG:31985") + assert area.crs.to_epsg() == 31985 + + +def test_load_study_area_rejects_points(monkeypatch): + _fake_osmnx(monkeypatch, Point(-35.2, -5.8)) + with pytest.raises(ValueError, match="non-polygon"): + load_study_area("Somewhere") + + +def test_load_study_area_without_osmnx(monkeypatch): + monkeypatch.setitem(sys.modules, "osmnx", None) + with pytest.raises(ImportError, match="predspot\\[osm\\]"): + load_study_area("Natal, Brazil") + + +@pytest.mark.skipif( + not os.environ.get("PREDSPOT_NETWORK_TESTS"), + reason="set PREDSPOT_NETWORK_TESTS=1 to query OpenStreetMap", +) +def test_load_study_area_network(): + pytest.importorskip("osmnx") + area = load_study_area("Natal, Rio Grande do Norte, Brazil") + assert len(area) == 1 + assert area.geometry.iloc[0].geom_type in ("Polygon", "MultiPolygon") + assert area.geometry.iloc[0].contains(Point(-35.2094, -5.7945)) # city centre diff --git a/tests/test_synthetic.py b/tests/test_synthetic.py new file mode 100644 index 0000000..592f0a8 --- /dev/null +++ b/tests/test_synthetic.py @@ -0,0 +1,124 @@ +import geopandas as gpd +import numpy as np +import pandas as pd +import pytest +import shapely +from shapely.geometry import Polygon + +from predspot import Dataset, synthetic +from predspot.synthetic import generate_crimes + + +@pytest.fixture(scope="module") +def irregular_area(): + poly = Polygon( + [(-35.30, -5.90), (-35.18, -5.92), (-35.16, -5.85), (-35.20, -5.82), + (-35.19, -5.78), (-35.28, -5.77), (-35.32, -5.84)] + ) # fmt: skip + return gpd.GeoDataFrame(geometry=[poly], crs="EPSG:4326") + + +def test_generate_crimes_basic(irregular_area): + crimes, hotspots = generate_crimes( + irregular_area, n_events=3000, n_hotspots=3, seed=0, return_hotspots=True + ) + assert list(crimes.columns) == ["tag", "t", "lon", "lat"] + assert len(crimes) == 3000 + assert crimes["t"].is_monotonic_increasing + assert crimes["t"].between("2019-01-01", "2020-12-31").all() + polygon = irregular_area.geometry.iloc[0] + assert shapely.contains_xy(polygon, crimes["lon"].values, crimes["lat"].values).all() + assert len(hotspots) == 3 + assert hotspots.geometry.within(polygon).all() + assert np.isclose(hotspots["share"].sum(), 0.7) + # it plugs directly into Dataset + assert Dataset(crimes, irregular_area).shape["crimes"][0] == 3000 + + +def test_generate_crimes_is_reproducible(study_area): + a = generate_crimes(study_area, n_events=500, seed=123) + b = generate_crimes(study_area, n_events=500, seed=123) + pd.testing.assert_frame_equal(a, b) + c = generate_crimes(study_area, n_events=500, seed=124) + assert not a.equals(c) + + +def test_hotspots_concentrate_events(study_area): + crimes, hotspots = generate_crimes( + study_area, n_events=4000, n_hotspots=1, hotspot_share=0.8, hotspot_sd_km=0.3, + seed=1, return_hotspots=True, + ) # fmt: skip + center = hotspots.geometry.iloc[0] + d_lon = (crimes["lon"] - center.x) * 111.32 * np.cos(np.radians(center.y)) + d_lat = (crimes["lat"] - center.y) * 110.57 + within_1km = np.hypot(d_lon, d_lat) < 1.0 + # ~80% hotspot events within ~3 sd, plus a little background in a 100 km2 box + assert 0.7 < within_1km.mean() < 0.9 + + +def test_uniform_when_no_hotspots(study_area): + crimes = generate_crimes(study_area, n_events=4000, n_hotspots=0, seed=2) + west, south, east, north = study_area.total_bounds + # split the box into 4 quadrants; each should hold ~25% of the events + q = ((crimes["lon"] > (west + east) / 2).astype(int) * 2 + + (crimes["lat"] > (south + north) / 2).astype(int)) # fmt: skip + shares = q.value_counts(normalize=True) + assert len(shares) == 4 and (shares.between(0.2, 0.3)).all() + + +def test_temporal_patterns(study_area): + crimes = generate_crimes( + study_area, n_events=20000, trend=1.0, annual_amplitude=0.5, annual_peak_month=7, + weekly_profile=(1, 1, 1, 1, 1, 1, 6), hourly_profile=None, seed=3, + ) # fmt: skip + t = crimes["t"] + # weekly: Sunday gets 6/12 = 50% of the events + assert 0.45 < (t.dt.dayofweek == 6).mean() < 0.55 + # no hourly profile: hours roughly uniform + hours = t.dt.hour.value_counts(normalize=True) + assert hours.max() < 0.07 + # trend: the last year has more events than the first year + per_year = t.dt.year.value_counts() + assert per_year[2020] > 1.3 * per_year[2019] + # annual cycle peaks in July + assert t.dt.month.value_counts().idxmax() in (6, 7, 8) + + +def test_tags_weights(study_area): + crimes = generate_crimes(study_area, n_events=5000, tags={"theft": 3, "fraud": 1}, seed=4) + shares = crimes["tag"].value_counts(normalize=True) + assert set(shares.index) == {"theft", "fraud"} + assert 0.7 < shares["theft"] < 0.8 + listed = generate_crimes(study_area, n_events=200, tags=["a", "b", "c"], seed=4) + assert set(listed["tag"]) == {"a", "b", "c"} + + +def test_projected_study_area(study_area): + projected = study_area.to_crs(study_area.estimate_utm_crs()) + crimes = generate_crimes(projected, n_events=300, seed=5) + west, south, east, north = study_area.total_bounds + assert crimes["lon"].between(west, east).all() + assert crimes["lat"].between(south, north).all() + + +def test_validation(study_area): + with pytest.raises(ValueError): + generate_crimes(study_area, n_events=0) + with pytest.raises(ValueError): + generate_crimes(study_area, n_events=10, hotspot_share=1.5) + with pytest.raises(ValueError): + generate_crimes(study_area, n_events=10, tags={}) + with pytest.raises(ValueError, match="7 values"): + generate_crimes(study_area, n_events=10, weekly_profile=(1, 2)) + with pytest.raises(ValueError, match="24 values"): + generate_crimes(study_area, n_events=10, hourly_profile=(1, 2)) + with pytest.raises(ValueError, match="end must be after"): + generate_crimes(study_area, n_events=10, start="2020-01-01", end="2019-01-01") + + +def test_temporal_intensity_shape(): + start, end = pd.Timestamp("2019-01-01"), pd.Timestamp("2019-12-31") + stamps = pd.date_range(start, end, freq="D") + lam = synthetic.temporal_intensity(stamps, start, end, trend=1.0) + assert lam.shape == (len(stamps),) + assert np.isclose(lam[0], 1.0) and np.isclose(lam[-1], 2.0) From 5300fb9cf20496b3c7e5ed8f9fc4ceacfee1f37b Mon Sep 17 00:00:00 2001 From: Adelson Araujo Date: Mon, 14 Sep 2026 13:03:05 +0000 Subject: [PATCH 3/4] Replace Sphinx docs with a MkDocs site (Material + mkdocstrings) - mkdocs.yml with Material theme, mkdocstrings API pages generated from the docstrings, autorefs cross-references, Mermaid, tabs and admonitions - New content: home, installation, quickstart, a user guide (how it works, data & study areas, grids & mapping, feature engineering, prediction & evaluation), API reference per module, changelog/contributing (included from the repo files) and a citing page; figures generated with the synthetic generator; every code example was executed - Docstrings converted from Sphinx roles to mkdocstrings cross-references - Docs workflow: strict build on PRs, deploy to the gh-pages branch on pushes to master (replaces the committed Sphinx HTML) - `docs` extra in pyproject; README points to the site Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01FtZdc2gGrDh4PGSLyT9yRE --- .github/workflows/docs.yml | 45 + .gitignore | 1 + CHANGELOG.md | 3 + CONTRIBUTING.md | 14 + README.md | 3 +- docs/Makefile | 28 - docs/api/crime_mapping.md | 1 + docs/api/dataset_preparation.md | 1 + docs/api/feature_engineering.md | 1 + docs/api/ml_modelling.md | 1 + docs/api/pipeline.md | 1 + docs/api/predspot.md | 16 + docs/api/synthetic.md | 1 + docs/api/utilities.md | 1 + docs/assets/forecast.png | Bin 0 -> 99455 bytes docs/assets/kde_vs_quadrat.png | Bin 0 -> 145243 bytes docs/assets/synthetic_dataset.png | Bin 0 -> 70327 bytes docs/build/doctrees/environment.pickle | Bin 85224 -> 0 bytes docs/build/doctrees/index.doctree | Bin 29407 -> 0 bytes .../doctrees/modules/crime_mapping.doctree | Bin 171030 -> 0 bytes .../modules/dataset_preparation.doctree | Bin 31432 -> 0 bytes .../modules/feature_engineering.doctree | Bin 157175 -> 0 bytes .../doctrees/modules/ml_modelling.doctree | Bin 159018 -> 0 bytes docs/build/doctrees/modules/pipeline.doctree | Bin 33124 -> 0 bytes docs/build/doctrees/modules/utilities.doctree | Bin 36112 -> 0 bytes docs/build/html/.buildinfo | 4 - docs/build/html/.nojekyll | 0 docs/build/html/_modules/index.html | 141 --- .../html/_modules/predspot/crime_mapping.html | 566 ----------- .../predspot/dataset_preparation.html | 305 ------ .../predspot/feature_engineering.html | 432 --------- .../html/_modules/predspot/ml_modelling.html | 453 --------- .../html/_modules/predspot/pipeline.html | 330 ------- .../html/_modules/predspot/utilities.html | 336 ------- docs/build/html/_sources/index.rst.txt | 177 ---- .../_sources/modules/crime_mapping.rst.txt | 9 - .../modules/dataset_preparation.rst.txt | 8 - .../modules/feature_engineering.rst.txt | 9 - .../_sources/modules/ml_modelling.rst.txt | 9 - .../html/_sources/modules/pipeline.rst.txt | 9 - .../html/_sources/modules/utilities.rst.txt | 9 - docs/build/html/_static/alabaster.css | 668 ------------- docs/build/html/_static/basic.css | 914 ------------------ docs/build/html/_static/custom.css | 1 - docs/build/html/_static/doctools.js | 149 --- .../html/_static/documentation_options.js | 13 - docs/build/html/_static/file.png | Bin 286 -> 0 bytes docs/build/html/_static/github-banner.svg | 5 - docs/build/html/_static/language_data.js | 192 ---- docs/build/html/_static/minus.png | Bin 90 -> 0 bytes docs/build/html/_static/plus.png | Bin 90 -> 0 bytes docs/build/html/_static/pygments.css | 84 -- docs/build/html/_static/searchtools.js | 632 ------------ docs/build/html/_static/sphinx_highlight.js | 154 --- docs/build/html/genindex.html | 140 --- docs/build/html/index.html | 319 ------ docs/build/html/modules/crime_mapping.html | 630 ------------ .../html/modules/dataset_preparation.html | 289 ------ .../html/modules/feature_engineering.html | 671 ------------- docs/build/html/modules/ml_modelling.html | 675 ------------- docs/build/html/modules/pipeline.html | 240 ----- docs/build/html/modules/utilities.html | 302 ------ docs/build/html/objects.inv | Bin 634 -> 0 bytes docs/build/html/search.html | 158 --- docs/build/html/searchindex.js | 1 - docs/changelog.md | 1 + docs/citing.md | 43 + docs/contributing.md | 1 + docs/error_message | 57 -- docs/getting-started/installation.md | 45 + docs/getting-started/quickstart.md | 124 +++ docs/guide/concepts.md | 76 ++ docs/guide/data.md | 111 +++ docs/guide/features.md | 100 ++ docs/guide/mapping.md | 105 ++ docs/guide/modelling.md | 106 ++ docs/index.md | 60 ++ docs/make.bat | 35 - docs/source/conf.py | 58 -- docs/source/index.rst | 177 ---- docs/source/modules/crime_mapping.rst | 9 - docs/source/modules/dataset_preparation.rst | 8 - docs/source/modules/feature_engineering.rst | 9 - docs/source/modules/ml_modelling.rst | 9 - docs/source/modules/pipeline.rst | 9 - docs/source/modules/utilities.rst | 9 - docs/update_docs.md | 28 - mkdocs.yml | 120 +++ pyproject.toml | 6 + src/predspot/crime_mapping.py | 50 +- src/predspot/dataset_preparation.py | 7 +- src/predspot/feature_engineering.py | 15 +- src/predspot/ml_modelling.py | 26 +- src/predspot/pipeline.py | 8 +- src/predspot/synthetic.py | 15 +- src/predspot/utilities.py | 7 +- 96 files changed, 1060 insertions(+), 9525 deletions(-) create mode 100644 .github/workflows/docs.yml delete mode 100644 docs/Makefile create mode 100644 docs/api/crime_mapping.md create mode 100644 docs/api/dataset_preparation.md create mode 100644 docs/api/feature_engineering.md create mode 100644 docs/api/ml_modelling.md create mode 100644 docs/api/pipeline.md create mode 100644 docs/api/predspot.md create mode 100644 docs/api/synthetic.md create mode 100644 docs/api/utilities.md create mode 100644 docs/assets/forecast.png create mode 100644 docs/assets/kde_vs_quadrat.png create mode 100644 docs/assets/synthetic_dataset.png delete mode 100644 docs/build/doctrees/environment.pickle delete mode 100644 docs/build/doctrees/index.doctree delete mode 100644 docs/build/doctrees/modules/crime_mapping.doctree delete mode 100644 docs/build/doctrees/modules/dataset_preparation.doctree delete mode 100644 docs/build/doctrees/modules/feature_engineering.doctree delete mode 100644 docs/build/doctrees/modules/ml_modelling.doctree delete mode 100644 docs/build/doctrees/modules/pipeline.doctree delete mode 100644 docs/build/doctrees/modules/utilities.doctree delete mode 100644 docs/build/html/.buildinfo delete mode 100644 docs/build/html/.nojekyll delete mode 100644 docs/build/html/_modules/index.html delete mode 100644 docs/build/html/_modules/predspot/crime_mapping.html delete mode 100644 docs/build/html/_modules/predspot/dataset_preparation.html delete mode 100644 docs/build/html/_modules/predspot/feature_engineering.html delete mode 100644 docs/build/html/_modules/predspot/ml_modelling.html delete mode 100644 docs/build/html/_modules/predspot/pipeline.html delete mode 100644 docs/build/html/_modules/predspot/utilities.html delete mode 100644 docs/build/html/_sources/index.rst.txt delete mode 100644 docs/build/html/_sources/modules/crime_mapping.rst.txt delete mode 100644 docs/build/html/_sources/modules/dataset_preparation.rst.txt delete mode 100644 docs/build/html/_sources/modules/feature_engineering.rst.txt delete mode 100644 docs/build/html/_sources/modules/ml_modelling.rst.txt delete mode 100644 docs/build/html/_sources/modules/pipeline.rst.txt delete mode 100644 docs/build/html/_sources/modules/utilities.rst.txt delete mode 100644 docs/build/html/_static/alabaster.css delete mode 100644 docs/build/html/_static/basic.css delete mode 100644 docs/build/html/_static/custom.css delete mode 100644 docs/build/html/_static/doctools.js delete mode 100644 docs/build/html/_static/documentation_options.js delete mode 100644 docs/build/html/_static/file.png delete mode 100644 docs/build/html/_static/github-banner.svg delete mode 100644 docs/build/html/_static/language_data.js delete mode 100644 docs/build/html/_static/minus.png delete mode 100644 docs/build/html/_static/plus.png delete mode 100644 docs/build/html/_static/pygments.css delete mode 100644 docs/build/html/_static/searchtools.js delete mode 100644 docs/build/html/_static/sphinx_highlight.js delete mode 100644 docs/build/html/genindex.html delete mode 100644 docs/build/html/index.html delete mode 100644 docs/build/html/modules/crime_mapping.html delete mode 100644 docs/build/html/modules/dataset_preparation.html delete mode 100644 docs/build/html/modules/feature_engineering.html delete mode 100644 docs/build/html/modules/ml_modelling.html delete mode 100644 docs/build/html/modules/pipeline.html delete mode 100644 docs/build/html/modules/utilities.html delete mode 100644 docs/build/html/objects.inv delete mode 100644 docs/build/html/search.html delete mode 100644 docs/build/html/searchindex.js create mode 100644 docs/changelog.md create mode 100644 docs/citing.md create mode 100644 docs/contributing.md delete mode 100644 docs/error_message create mode 100644 docs/getting-started/installation.md create mode 100644 docs/getting-started/quickstart.md create mode 100644 docs/guide/concepts.md create mode 100644 docs/guide/data.md create mode 100644 docs/guide/features.md create mode 100644 docs/guide/mapping.md create mode 100644 docs/guide/modelling.md create mode 100644 docs/index.md delete mode 100644 docs/make.bat delete mode 100644 docs/source/conf.py delete mode 100644 docs/source/index.rst delete mode 100644 docs/source/modules/crime_mapping.rst delete mode 100644 docs/source/modules/dataset_preparation.rst delete mode 100644 docs/source/modules/feature_engineering.rst delete mode 100644 docs/source/modules/ml_modelling.rst delete mode 100644 docs/source/modules/pipeline.rst delete mode 100644 docs/source/modules/utilities.rst delete mode 100644 docs/update_docs.md create mode 100644 mkdocs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..9660dd1 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,45 @@ +name: Docs + +on: + push: + branches: [master] + paths: ["docs/**", "mkdocs.yml", "src/**", "CHANGELOG.md", "CONTRIBUTING.md", ".github/workflows/docs.yml"] + pull_request: + paths: ["docs/**", "mkdocs.yml", "src/**", "CHANGELOG.md", "CONTRIBUTING.md", ".github/workflows/docs.yml"] + workflow_dispatch: + +permissions: + contents: write + +jobs: + build: + name: Build (strict) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + - run: python -m pip install -e ".[docs]" + - run: mkdocs build --strict + + deploy: + name: Deploy to GitHub Pages + needs: build + if: github.event_name != 'pull_request' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + - run: python -m pip install -e ".[docs]" + - name: Publish to the gh-pages branch + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + mkdocs gh-deploy --force --no-history diff --git a/.gitignore b/.gitignore index e971876..73b2e17 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ dist/ .coverage htmlcov/ .ruff_cache/ +site/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c94242..cff9573 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ versions of pandas (>= 2.2), GeoPandas (>= 1.0), scikit-learn and statsmodels. `pipeline.generate_testdata(..., seed=...)`. - `PredictionPipeline.features`, `.next_time` and `random_state`. - Test suite (pytest) and continuous integration for Python 3.10-3.13. +- Documentation rebuilt with MkDocs (Material + mkdocstrings), deployed + automatically to GitHub Pages; replaces the Sphinx site. - `pyproject.toml` packaging (src layout) and automated PyPI publishing. ### Changed @@ -39,6 +41,7 @@ versions of pandas (>= 2.2), GeoPandas (>= 1.0), scikit-learn and statsmodels. - `geojsoncontour` is an optional dependency (`pip install predspot[contour]`). ### Removed +- Sphinx documentation sources and the committed HTML build. - `QuadratCount2`, `KGrid` and the hard dependencies on `descartes`, `contextily` and `rtree`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b510773..e578cda 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,6 +22,20 @@ PREDSPOT_NETWORK_TESTS=1 pytest tests/test_load_study_area.py # also query Open CI runs the same checks on Python 3.10 to 3.13 for every pull request. +## Documentation + +The site is built with [MkDocs](https://www.mkdocs.org/) and +[Material](https://squidfunk.github.io/mkdocs-material/); API pages come from the +docstrings via mkdocstrings. + +```bash +pip install -e ".[docs]" +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. + ## Releasing 1. Bump `__version__` in `src/predspot/__init__.py` and update `CHANGELOG.md`. diff --git a/README.md b/README.md index d0965e0..39cae86 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,8 @@ implementation and adapt it to your own data. ## How to use? 🚀 -> Documentation under construction. +Full documentation, with a quickstart, a user guide and the API reference, lives at +**https://adaj.github.io/predspot/**. Basic usage example: diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index aea4d07..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = source -BUILDDIR = build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -# Clean build directory -clean: - rm -rf $(BUILDDIR)/* - -# Build HTML documentation -html: - $(SPHINXBUILD) -b html $(SOURCEDIR) $(BUILDDIR)/html diff --git a/docs/api/crime_mapping.md b/docs/api/crime_mapping.md new file mode 100644 index 0000000..92d82e0 --- /dev/null +++ b/docs/api/crime_mapping.md @@ -0,0 +1 @@ +::: predspot.crime_mapping diff --git a/docs/api/dataset_preparation.md b/docs/api/dataset_preparation.md new file mode 100644 index 0000000..64f8c99 --- /dev/null +++ b/docs/api/dataset_preparation.md @@ -0,0 +1 @@ +::: predspot.dataset_preparation diff --git a/docs/api/feature_engineering.md b/docs/api/feature_engineering.md new file mode 100644 index 0000000..4d6c34b --- /dev/null +++ b/docs/api/feature_engineering.md @@ -0,0 +1 @@ +::: predspot.feature_engineering diff --git a/docs/api/ml_modelling.md b/docs/api/ml_modelling.md new file mode 100644 index 0000000..1dfde0c --- /dev/null +++ b/docs/api/ml_modelling.md @@ -0,0 +1 @@ +::: predspot.ml_modelling diff --git a/docs/api/pipeline.md b/docs/api/pipeline.md new file mode 100644 index 0000000..5635563 --- /dev/null +++ b/docs/api/pipeline.md @@ -0,0 +1 @@ +::: predspot.pipeline diff --git a/docs/api/predspot.md b/docs/api/predspot.md new file mode 100644 index 0000000..6cb916f --- /dev/null +++ b/docs/api/predspot.md @@ -0,0 +1,16 @@ +# predspot + +The top-level package re-exports the most used objects: + +```python +from predspot import ( + Dataset, PredictionPipeline, PandasFeatureUnion, + KDE, QuadratCount, create_gridpoints, create_gridhexagonal, create_gridsquares, + load_study_area, generate_crimes, +) +``` + +::: predspot + options: + members: false + show_source: false diff --git a/docs/api/synthetic.md b/docs/api/synthetic.md new file mode 100644 index 0000000..4098016 --- /dev/null +++ b/docs/api/synthetic.md @@ -0,0 +1 @@ +::: predspot.synthetic diff --git a/docs/api/utilities.md b/docs/api/utilities.md new file mode 100644 index 0000000..5188b93 --- /dev/null +++ b/docs/api/utilities.md @@ -0,0 +1 @@ +::: predspot.utilities diff --git a/docs/assets/forecast.png b/docs/assets/forecast.png new file mode 100644 index 0000000000000000000000000000000000000000..bf58a8c434a9c460dd272aeb99906cc2c9385ab0 GIT binary patch literal 99455 zcmd43WmJ`6+by~}<73ewUYB3*)rAR$UA4bt79f*?pqcXv1Y zdVJq+?|purA7_l?7&?Si&vV~b%sH=luV5twsq5EnT|=Qz*Plp>tDsPqDp4qmBg`xC z8`J?qHvG%)Afe@;YGdNyY-INwC2!>L($dDk(#)8~>A9V~nT<6!I~N!GJyx0*4h}Ev z`8hbO{`WK3ZR|`re8Sg|_riK9{nQ?X!e2)IVko^^@IYapP*21ksktPsj5_P84jf1K=6N_jr)pF@BchE?eQmZ(*M2@yms;buP?p-SYH16 z+vw=1q$I1AJQT`$yf%Dh=2@y>sW@FjI5}TrVq%!D?}gNze+y%!=;+9UJUbyFTiruO z=2~Zxz`(bp9?^lbTSLgR_iyT@tXd)IQf^cT%4&%n{{G8g=q?it`wE9SH|f<;P7y0bihmns?F`1x_5_Ri*w zC$|Z8>%1sGeE6WPCptSbGcnZ?dY7G@_eIsyr%&M{T9X8vi&x!>j!eI|-S&&@%*xBd z{qf_6^pht9VHFOGgq_JE`TJ-!b#>Z@4{z?Tjs97T`&=@I&XiG9Z2PL5y0P6QVnKZV zlO^A9Yvu>rFw=F1)%>iv#AN~JHN%D9pM%1~`^fgYrKpY8Mk=;e@kCDWu}N5eFL9M~ zNZO26GLWe%DfwWNveR&L7ya1F%FedkT~bfW$nfRLaMGoVGHmq2I#~RYT)br9p0QX` z=dwvB8Gg&Z#(kgnuY&#f>m$y+t|YqJd7F-LqqNh{;+l*FR>Qnuw|HbWG&D3!R@h6k z%ErCOU%h(Ox+WRAvAZiFD(cfy``)Y8)zww2(D*VlGc)JQ3DHZJF4ecRq_$S%dlasG#9|yo|E^p6wNhvEw zjjhhj&Q3-v3OB@Yp(kc%{gqQCC;upl^erF07#4Xfe23Q0!Rheukc`LthQ8}8aYGzL zh4IgKOl~5ryUPQ}Hl7?U=krr?7x#Oxs_RucJQ5W}Rl)}^y~%wuw^#OX{-|U?|8;&o z$48H?C*ij6hfKt)+C$*Zoh#F=;TGRK*MvmiU8+yE+RfYYbt`t9yJ z7l3oq_u#;#M5~o-%za&`yt1-!e4M(*ZC9-8qtIp>wfEf?R~Hu=R#qIFnq3lR7M6q+ zw7W@1a#4iex@lx5Tets*LIF{2#r3h8$%%H)FKfB2HupYiG_u)eRo-fD|fn--aLW& zQ~aJv$c-$w*jG5LZ@jqpzPP016og}7aPWqU-_nwKqJT4QC@IGkngl}UvlCZ8e}A4s zGLGtv3)R)tE?>Hyo0_6~(w=

Iw`GzwWlP@CrUtx7vjOf|L`IyF1@N#P#GVwTI9C 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�W|Q94(={PD3Jj58WAna zd!xcHJ|Tge41EA1$Ob$ExGN4cuJ`AY8hthH&T{ehvD)5Hw-uZHDeyS71Bz<|#CNdN zDq(D1lypd#Pgq{a07wU561*y>Ro@REZu_o&w(3Lp&X6f z-2{kyAbfse9~U2Qu3q;z8j1P3lPzpS@s~>$*0VYi&)2V+Sx^4`TZuy0gA7?gUdurU zl`9i7KcZcrm>F4H=c!4_#X14Ko*_K~Ef@9dae2itg6Fbr(j69)KO=Hg(Ah_~B_@zE zS+xpKh~O{zhAO2` z73AVLF%UH!I5LKXg+CE>lyiWP4TxSTX52iv3N=jOw8*M@*X&M{T*Edj)9muKJc{pHVVuF;d z$hf$>WYK_@f#HUc^Wpkf9*x44%nW+~ne5vmCM9ixj3BxrrzE?B+)fTw=iOgsI>_BPbKZ~=dH^>cwujy zU~esb75$FzAWhvio1F?o4m3rXTgZ|XC=WT17F2Dym0v6GbE3bg>jFZ#tjUHT13_Ls zzF1IHBl*@qfq&a3v^R)0Z}arGA%2xb4fAh5&qemJ0nYUA>xJB4FH%xd*w)oJ6{&O(!U*n|r;MxR)$C z7w6Z&9w6(CKUtwjl!6wbZZ373Ff;e;>#9YDS)WAhd>Lfz6`#KT{-67c_IXGbxZ=~& z+PXzG4&3x&J@kGubeM#se!SAH-it67-BbO{I5{{8!lZ|m{;fgl`21ND1$gqQ!DTPQd5SVlaj*<^G;A+uEHqk64VR=e0-V*#=%8M_9gIMS5{Uo zhb9Y8$gofGGCY(yf%Wv@k(ryL9<7Q*LFs^SZtVpsWX8Cl6pI%Vjqs4J`1A``!u+C4Ej3 zlFj~POb(S;3>KoVvg?-neEW6}N;7(I<*&@DeBuKFxUd5#%rHz3KRR+7BeuSAcziq! zMEL-?pzUPi<->waqo#mKz^5|Vg9~=JAe#FFyZtKkF*B11E`MTrI`xnJ_U^7axmeDx zZAxwv9Ju|-m6e?B`xP)bLGU)TA(>;KpW!f*nufYA0YjCntgPQ(6scLCeiP6AnhpV( zmif6SOV!GGZRFon7cLB@GtlOQ3Sf312xZ@`%mOMVS8Zlr_|9T7Ej83eet|XTEOR}2 z36R<5k~)d0s3MSwFaZI<_wV0*pb}Df9T>41)avFc^qrj^Z^C9z{Ac#5qAe}?rx5a6 z4i*6xwh5XxR1!Q`*b%7hTT|1hmwPKuSH^0{K`i3v`SWLTX2uV0-0<(W=Q7@;_d(^? zt8y}ez!|Lb7BM$B=d>PqP`x)~LB?mD*i-T;H#A&hT*q<}4%^XFrtqhB+Abn)Gf*Chg-7y%2Q z7?`&iqtTnc_{M2q2u&D#3FPdiZ_nFZjz!?Ik@2g6=Poe>2ntG~+(*Hi95p*{tF?sY zVc#Lds5J6)Gy4@l)K${gXD`1{LMNZ!G|}+dRb>5TvcFpBgvBueMe}6^#6-BEEX~2E zNaptqI38B~*>jF$?~+d#*A(kANJ!3}0|Cc2h|EH=Ph3nou)a_5cquy{>8iJ?1Q0Wd z89jt?AK@irXwGL%f%H??Re5yKn>TF`!vwIyP=&~HsRMSP-59}0LRwy)SkA6k3MhwP zZf>sjB0)+rWC^vInVGb#tVREgsfeJ?>lj}pNokXY{IN+Rp~C`5=1K`n*%$>!C)_3?2d$uyXD5`gMBSnXO=Sk;pUG8!Iy z#silPnv~Sk&3+vlWx6jIN#rRZpdC6G!wBKQ+E@)X%4Mx$9_QN#H)5^f$IVm=fu^Ku zMLflr>_G_AD;|}6es?=uk{39r{up;TU!gkK{w_)PlkDdHw@X((4986wi@BFnUWz)xZVi!|A#54;s2e5D5RSRS;JywU@r)VgQ10yiDazR zzrEC1T3U*XjSYmGYJvtlIEo<}%>Hl5d6!|}OeW-3Tv+u8UXGul48sX9j67elM*Y`^ zB%Lt3v~*3>X>(z6(r9K3xkJCZ?VX*i0L5kpSgkM^Dv?);4c)b_ObHCU3SNP*5pv{N z_+D0-%@>GM8Ig-Xo`OkBdEvj${`a$V;D4{c>4CA?hb__@IbMn`WO<=kuSdDCTBzoz*Jm z!T8J2haZe;^(j8Nb~~MPG9i_(kmWvpjjmPrs65gs#DDI1C`#rDC5g4XsI?gn z>gikA&GC^QbJme~ADJgfQEt+(3S@Zyvm$0vxQV~SoSyKcg`JnbLq6nSRaax#DGV;v z>>`jwu5yr*=mB3+QBiH|?FE2G1s2=TLbhoh3t8THi}Sy7hYk|7bObLaR`mSB_RMM` z5TltDilH*-(UWu)1iOAwOx6&X1J2ID!I3@_yHp2LWXS~{e)KLLwq8l*D!!9ITm>MaPv+> z`MLW)bz%s!3%PvSG^+)3^sC~NkULTi6+_yk`d{GIBYAxZ^_mUAHc@Cj%8x_Nivx0? z5A-ucEhjV3)(!!D?Mf)eMf)CD4k#%pIXQBxUZ9ZsrZ$4ls2eDS!(vZ5LozdR^+DYo zx7PgAcId*n!Z5f4oo&*o{@wglxcLST5FHVj6k4I7x%tpDc)8HY>FMM;e3Gh)O3M9P z@G(xy{cIpT;J_@_eBa*I*32$GEuF!$9r4aY?wQWLRP?alxnn1Lh~1MAgmQLg=^&f? zdz3^EA08&xtfk6*f@L-Yi-Dkigrq`C^3E1wN6RtY%;1-QJFNNiX{M8brlw~2pW7gk zD*WICgWn}DfKdQ%vscf@&(8vXL^HORWoKn=v~$!=K&eHz1r+>^Mr@Ayt}eyZS`d3$ z&QHhBCqa^U>F6l&;>BGmkBvu3$L|5K*x1;>uGGU+bpALxf`uWU0=w)EO;wuqYFyxJ zVkiGADX}bUm&9R1o|m*(5Z~&Sz?ll?8x!^P6gpc!Kg(CA*1S#F0K^JrZ)Eph;M?G! zaX?+DPHhAu-OJS>o)eIKz_X-Mm7kE1&}s$-l8+!QzgYYNYm|2RIYa~l!)1Uy%0nU` zFr=?S$+?Kq_daz*ME+O3CF|w5Lgh?K0D0c5@6%6MZthi?pIKtbZVTi(4;8xb$*UB&@-lcoj8RY=&)LQt{(FrP9|j{h35lU_Ma-Q#` zgh7BN)BE9Cr`7mYaZT_#$;isi4ud%T>Bz(L=o|gWcxdo&!w;gGZ{ZUex zKlEib=UA?;`zTJ7itC9#w&}#`>UD@FkPGXFhsj}AFQ7}5H&u3udC7~ zbQzKFW}HS$y1Hxk@_HR^Z&O2A*Nlvq%&JYcdO z$M49D&<(WnmnMSX^6sZ}V5*w(C)HQt_Zs)ao9N9P3}DM}C@PAUrws=*3!Y` z`Yr+tZ=l0#Tm7Uh8}0oz4CP|0+raMyUCce*AJ%eUG8_E)CVegzUH&Gzuu>Aq6k_N| z`Y?^V8Z7P$bL5Uey~W}i(MBOh`_w_vjBhTWmX?=cUdd7({|qP^^0{w~=H})W#%cuC znct4oL1?|hq4#spg1zSC;!>97KLwijPn8GjZh}h@0G3KwFYIMjIO&8XNo8 z*VmJ*@+}Ik-Oxy>mgKd2`RU8WkySP$E29vZCqg}2EdM;}X@640NxT0-_Ro?YfquZ} zD3}=&7Z=1)cR~oxCM6k%)zKI!tg)%9fOCL%a09~Qe!`1wodb#~Jjl_UhjCf$P7R3JmU z9L~lfA#misjFL8|;zrNpm#cZUXBHNSt+3_59bJjW(S{D55T7xpDLv#Xv zYa3{kFu?*aPN<8O1LjeK5h-+TZ5{Z^?zp)q7#`ZF@x=C3efhCJ|o z@bKa1(o&K#oAFiv(wngQ%q>EPGtrzpJS_&^r~Z(wvbEzdrGbJ|=HU*rT2RJ>VCWS9 zJeXcch!Q9%CLW$m352+?bxVuznp463=)SQTLcQWGI;6xzoF8JaL@*|KVDsi2fxLgN5Q zsdlVVNaAmCH-IvOh;3Hhwzgd0^|=hLq=TcQrM`RsTflbd>FHB{9Oz7-;adcGyP4Hul@o;MjH(N{dsz!;OU`dW$hRo zrL_vHsCek%;Q`VFJsEwJG>G#I59GICv?2)t!q91@!(xi98scz(CVls&BxZL)W~N#6 zpG`)&xKFAH2m*O%_sqUhB8Vmrzot%VdPlo1AcpkMRy%FXy|}pF;|Ru10{9%%k2sD% zPMrqgWn<^+Aa)uNtk%Y6(pOP+1bp|uKxbgA7*bsdSN@7E@wGuxG;XP__-bC8i3>Dur-n1TbQbRX2 zm5H%N5C&Zb#rdn)3TUo_?d0SXf>(`eJXO-g;_(8I>#Y7xs0;4;p?=yl@P&wEJd~tKaX``N$@@O z%%mN57tF54J^Iv3gMzOs0ageeQudwimZy`r@!3`K; zTtS_D+ctPd{PaSBA5)?s8iUiGDJ_TO^vc1Me=Ny&DgV`R_e&BIhu=;AMt;*y=RFVo z>pw82n|J>fEAJ7rUC~cN6XVOOL@aS199{)W?=8ptJM)7Ru4AigJ4Dyk)hGFW;pdka zQV2E&1>n86xLOOqwgq#Il-jXo$fo8`;&eoDA*|hf733eij?50vMv7yj-o3nuqItgi zHC7{4U;DM2M!5Ay+_-}Y{6mKyV7Ua9J+s7$?%w0c%u9l22Mi5nZ-MNY?<0gIzENI} zVj)v_bgZl8O0b!~^gl0Qqyoq$;;EqHO#%1V*j@D>F5?axr4-Xn3jTiMP6jV0UkpR~ z(f+NVUkP<;As==(+NEU#1NqH(meSO}+8hiF{lozn0f$&5o;(TG9#&9L@XeOCJJ;6H zDQo?68@htRJB@)Ur$-E^*&|(wJQ`1Ps(<|V-%b18CdBH`dHNcJ--gi|0eFDG(8L78 zUR5V9VTgH^`e6^X``O72loHGcpjwSh9U_G{Q}v6Iwsuy3X;D$p1oK-MYDqvN*O`3? z{&+L;JmwoI^U0NWk9FQXRSS-JM1%r-G&(;!JviCUM{ahA z2jUoqj1vW3K>RQ!HnteXI<<4~E+j}&r1%x%DE7~RA2sJHKP6^%k2{w1+bOca5G{9-76a-<{u|DM6>eyRG z258Nq8yf5{q1E5``(d_aQDI|!$fngejZ^Z_37Qqi&{+|@UbubV+9IMa9dD?Paqemt zNtn72$^Ct<2CY6cw=Wfb(2U5Q`0v|eTAxqFz%L2h7SG$;yCLrFTf%21CJ4`~kD`~F zm=a?#CxFV`qm?Y{d7l(@@Ia@jSKQBy>9LId<^OC3KV2&sw-clE@HPy{X$1u-Ogj@B z0H+~~6BGjCcvEZZ70@~FIcY+lxeG>~v9U2k>y3?#MLLs@RGr`}i-*r$*3R^`1ky!p zI%duV0(N|5s?W5%bK-ilW85L9)40t-2bhb)sFdwttYSG1WCpXlj zfA_UsC<<^l0B2op-M7g?<2Au%8k zTO@-*mA*IGuq`DC+qSnN>yA;R2X!nhSLO#xTVpQPclOKx=}VkHFD4j5X)| z7TvKcv8tp^34mbC11ynWy>$%+fQY|>g>o2JTJzLs^*%(yn;4eZ_Ok!G{=Xm(9lcWM zk}tYK;pfk$JjB>rYF_nikeY^MeKI%+I8Qorp$HwFSPeBq7Cy;djz*!!CL3w{`3P!v z{)_C&+{P-j560htf3*lowQ5hB_6dT9?Bn=&U#zLI>XERHk5#PV>o3-tcDJ`UGSD*$Zk=6t3*rNA6V*;Su>pFm|UetI%+VB`1-mL9uZNLVWL4(Z_9}tmQ0rU*i)4+Jr){Dw3s|V9 zw#zWe#e=DHWy$9rb9T z)xhYtb5IY_|3Z4OnU$C_ucB~vZs^+*pv38-JZ4+ev|Xb>^iA}=7VHt9g5T=%PcT%v z`Z$=~7jeB+t=Bfe!I*HUIR8cp*UgtgCT1pZb#$2JKr2eZ;|580fK zWOL6P2(Z7+#bdK`pD#Onef?RSv;ia?#Delh_~<)wJuhft`cq14Z?G%qx2|1-K+!U4t9JxzD(EmM(Eak8P50XgYRh0q9Q$xA^2y)XNU8L@>kq5 z%iXWI7zb=@-+_DN(laCL=K1}2yI2C3FNKAz?t%)_pK=)_Df)Tr%Nc7Qx)DLFM{eD& zmJ|1>sv$PP*|t3GbVSj$#{Z;$8-W}+h2tMmfZy{B>Tm(gqMd{BQ;>z)n2j|^DKC%9 zU?=Q(0yV!8!~|%^8(S&eiAlt09E|g0Qh#DX4^A}AN2=xF8P87B8|YSNE|gC>AC*tw z<5-e42H0}Fw3!(6WdEhHK2jlyoPvMa_V5{xw@$Uj=giO~Fl-DXXTK1-4Z>C$B{KDt zjG%b(1Y&LRnVqbKk`^rn>cIVlzHQX>FsCEIr^QgXagtrTk{0iLxyD3-pWNB~3X6dt z?Dew0g)s~m3t;2y2u{U#0k%879v6Zehs}4sao> zYdrHEamP5jpdoVTSS>$z2nc$xG*8`qHAn;JC6{hiT?fedYVaW@wocEr!l8Xl)#0pF zbMu7!(O;9p1O$bOY!E)Y8h&gVX8Tg&;u|7$6BF=c_tGCuheQ1grRng|d)y)5J>@fqyXQNt;=x0zq@?R9?=?a;aRwD$D4--Y50 ze;2s`(~G+q{t?PU`vY%+gZ{cdVsko4q>bBJd6Di}XA}PTzT$B7>i}cz1+B5ROV9q0 z0Ic~Z-jMYa0`EqQMDxdghlapi)4f4S=SOy(FQt84wW?b;@gqC0v>b=HcI{f7EYz&x zh_Dpq^wkj+83!KF{`fy5XB0syzz=36AR=NU`x3W67@CStPM%N8tJXDG6BItHDE!?x zwpc7@@6;;Gs!u#Nqf~u3JZLa*a8P_qt4~~0yjj0N>*+8MnQLX)L#6D6Y`gqYk`&^) zy59_Qj8iPUd+VfS%5Rfp$1U&^#`xG{G5ssc)5`O<43njXP=8rW)-UTSRyQH_H>Ce} zX@%q}(-euhIg?kU+boo74j6twCjJYkdQHZc7rqTEnQ>Le@+s2G`DzN#e0*dJ;aXik$kvDT=AoH^RQTY=U15LMsWbqH%z3Y1)*nbDtMIAf76PF}l#istO&B-v2ZO~SN2d&z zUNWp6`heBS8oroAec$}IvVqkEj~)!^^hyBXlrm^f_R;ez0LmMO|K37HN;)EV>DQ0D z@8spzO>8bF2aFF?r1Kbt-7L~srIOcMxh;HlfS#F~qq}#H@XnoYCnnP0bsJpz(6qFV zZh_bX_&6z*)+wUZs;R|%+(6!HdzI8Y!N&XLhUFL_oQ8(RF2&tAr7Dj$hhVGB0ayJE zBHzNSHj~Rj5LIp~kWC1hj`j_>*1APWE@Fyl!#aNUsO?!0fy+xBRPO3U*{}wM*5fxBm{TTzP&!Y+=i3&DR=N1HOSG=|)RT$;)qroF)T9 zJOrs~MuD*~&jJAm4a#CrNXR`WXG~iD2X-@dLAY-gQ-n8>{>GVHN`i=KFkwZs+>DbNsd#dZ<3okDjRV{;dDAz|{yvMcFGq&pRgK zaB~>l!!{=7*%^p`mDw}@x z@7@-L2^a`mMdKuU9c4mvVRjSr%`eTs1QJH~C5iWXWuK2fSr$lX2WNjQPt1S1ZFF}* zR$f^V&&Ue-GN(#X-W+raN?njI;RJU0fWG^6@TMccTwDrq0D^N7#1})r&FJ4et`foQ zHHdLHmcu|}Sj27T1q_R_R_pBNalsjR0p?|4WLy{MqJqlwtfn!+9iLKGcJFnpDrwEo zhJ93Sb)13z>z*a;L7fleISAX@X(LmOt9)@?mRt3~u-2ynG2anFs8KKnuQS;8uzSTuyvu`8SeT z9!C^vJ%EFIWHlGM<7WcBU*f@exOW6k+=E)&QK*UvIB_t2&uyQ|$L!A>&FyE1Xv2i) zIn}XcNs*EN`pHybLQ@$%oiVkICB$ay|t)vneM;?NsM$Mke`2L@IDX+9dX8pqoivfkbY z!WWd?akpEq{7MQ*Z}*)Yp0YZhc&Wx}v(o#?7W3&=fyeQFu5C~R(q|qbeTKK6?ctd! z;ajh&W^w`ZRvI}slXpF}(E`ygk9^!b>N_5luba&drV6}`EI3cmww%ama50RsvxK++ zjhhRM;$yXu3O zNz<5SGSeZUM{kx^O>F$x=Q6%#@LhmhSST2>U~A-R`G)*f1aQ?&JYfj&Y zNh@MV?O!Yz7b$Ta;f54yqj2b=F85ucO-l-ih-gJpbz$KJlB%DLcXE-hS3oit=CEU((MppJGR{^okbYIApp}2}i$%&Ycg3{wA$=vPzDu3gGwNCok(l&w zl=5BRezU!op?-I*tnI;Mr{`AcZZ`cV0zFE6v6IL0L!^3BVr zjEtWKG`7Pq(&BHyI)wQbV#DW~UyQ`{Wf32*OQ7tKOW?yw{UVkU%GG~I@>o-5>E(na z32-&>NB)=etXMfM%1KRd_x(yPdy0cD=&?miO03c6XcJedrA7M)6cN^6FD62S7xJaA zs%mIl_(bJ%Q=BgTgN(F0F`S@-vjbo#?gG-(DxS?=daW!Fw+eNAoPHr zzY)$O!BmlffkCElq!b3`s27xs&*0=5=5?za+qk5pT;A(%9dJj2*w0NAbS|MpG<8s- z>1kBZlecbhw!VF<8R4%(6sf0jJqpuM?!BL^3`ZlMl#Q|G3mP|`oew~Inc2H%o48Z3 zQEM^%^*WPe-5~h!6B6=q|cIACFmm=xdcpWsx~JG4U5^%i7Gm zw{J@Wsft^2?JYJ~(A`)iUE&`1()&2>aUCA_eeG``(1)%}h6Lp$*y1%mnNKof^UarHM-95_|x$u)IH$y!3 zqj*?1p*&>^j3fvsD8i5_+AXVAGGz9Ne9;IPIHMG|-N05+2ebs9Zaorylc$255_wlX+6l0 zDjNPXkdNhDV13?lXPUQzDlD>FIQKU_2n(Lhq1l{g)2S$*$6bo`h8%wv^jgV|YCJE! z?Ho%7dq7$flz^#?-`tvElK_+$$$4&cONsH*xCfc>J%J~w7S<1_IwDZ(uOI_N%Z$4gtvX0 zT7%Mm#X^-Ds>&@5?;8;W!6;3g_WtD?2(Xts3<+K0DIXyx)O(ozZesvpb3R~jCqj-w zf#ZjO>g*yYX4ST|wDtrfrE0a5RJFv$w(mqO@xi`HZ0s!##BB_c7Ep@C$;$%QZQPZJ z2leCvA})8}5L_=Ac`YRY!SSV`q;O)RQ1zkNH%ZijI+=QUN7?NUqg%YY$T>pwG6>tp zc@3(>5Y&RIOv2XdEL6xZ!d>;Mnm1d2%7~cl zD0|N&WQOb=_v_=jzQ6na-uH3;)!{n2jyRp?=lyy=U(e^`d9N3zlYo|I>UI?)E-r?k zB*w#wt(a)JZ{<8POw)(c$~8rQGcPqp|5R5x>JEM76wCUoeo&HvER}}3j)QW6@MGI0 zA9JEsPMT2Q^)Rupn1lDXCQ40|l3h+sA->IT*~5iA1njh~jQw=V;J|E&gZ!NWy4rx1&y7VehvWwH z(EldztpQc@xIwwXCsD$^voR(FjYjZ0LkkRWFus;dmrhld(Im&U-V0-A`f9W9#NRv6 zV7huJsA7~Cf8)Uw?Nku+8pelPI;Y?5&o8R}dadREM>sB2K`7Cz^;4P2b2uZmTU3p< z-PckfuSzK#oXNc$VdGi~@C(Ez1)EckKVSCFHV^0EPKt?wfB8oE(lf?@65H=3vh|-B ztGui9o6^m=0tRo*4lNaz@iu{~o{+@O(^+p{IbmQ1XA!u?j^MJ^)F)f z1O#6AvVpsV16dycIPn5210KpHq@amW=qVZBM69gfP?cdAvm5y@_iOK?(xi~iIkM|A zJxfdG{_y7-pJoU`4!OV$`f|NiXOk(y}7SFYUsfQk4 zqta4Z{^Vqf;Y=Mi;p|Ah_TTneIFvV;Uen|Exp)cY)g)N0$mr^yLtX3bp}cC?H}{fV z^$Co^ml)H`Wa6v8Fz0ou4n-P(5K(D%g*?9|n+~3=m%eOui)CkL*OQxgpNmnbjDCp7 z4M#b!ox|-X%Jt?H@rXo*j)MmN3ovwC!0s9DE!ZqNpfcBvTIx43>VA{CKi`U%j9>c~ z9%-vRMcfOj@$L)VN!-Y60*^0b%DIY1MGs$cIt`m5;sZRONW$mDgzj8>=Xl!7x^EGD zn-6SXpJ-t#8-zxqf6l(T8w8hbOi?Ex<=JdXHq2t>j~f$}oQYCaOuC;teucKhQT8!Y z=f~^n_!q;y`2HK}W4=U&oL4iQ$qcCagm*^j#*E5&R-^;LH^Kkql=a@MMfROFng$%L zS9BB}&B=;~Qp9dJ>T(;`gh1-V3_P~~*zKip_@Y04WQV!Y9P$?k{s2Cg<_OLMnNN}> ze)z{%r|s@_a0T_u{-I}N!~|mv*N9}lO~sxcz41V0l10p0<8+<#hA|4~!*|_U*XAse zcJyQklcQGPy?~GS6W>GVmf@)`^3=OvZ;B z#mh(BP@a3SVD2@2ivkBvYnIUP0!gUMx~m;$^IIdsi^5_YAF4^D6sp@Oq^;l^-!PsPiAQ_sZwq=QeeuI27rAuCg%cSj`d zAMR38enezsA9erT9JV3G6Kak|vhp0g$?m2-BJ>_kGkb~&3TFy2l`yIOV=D2r+b68~ z8@*$eqx6qoWn$r9*`YRn;O2kn65C;ar=Rle%3Pg~NEUNskK@LVaYUE@^>0!yswvX+ zZ>4|mt!Z=Bw~Up+eMdbf5?*2w*PWv#JL?+qT<7S zmq*vYu!D`iJ)~gVi|y`I+|UBv$`~o?r@1Yz=4ImL3<7ZUQ!Gy|Pr{Q}M^i%^T^uwX za63`XRgkI5IY%zu7&MAYh(*@c5y*n8>{nn=YdN!VnRRyb{8#09d|vEqQTC1YSYR66=p^s+?T zlnzzSY;OzAmYkQ_bi!4q($CHQDRlCXhHyL?lwmr;@@(bp(OJ1U4I3wE4b~${hi@{v zYUB=cvR#TjaZWdLuf)FeLcy?wGPPmwt4?MvR{Cf?Ckr>KCs}6TUGT6~aIF+pG2+aN zexF%zHO^`|m!HH8c|+USGMlPk+*cIz^1s~Q_dTZyt#3^Wqr-B#N}G)Cmb@!aNu(HJ z>W7EPtgMdXaUqNa0a#wXKEq|r36cJiISE#a2271knl)TobnD2RoCBqMYlg+?o9gUG z3fEB2>w?AKQI24$5v)dXlSPbNs+Phmf`jPKGJ0)SXOxsGd+@fkc>JICOfGjm;n>2+ zQIcPw1vPlQt@`O?3DE&T)@bgsxOLk_Y?OzHyQGd{X{)v*ggqI*KFaQ~G_zqydH1!= z$c$?5OFvbFB427`!L5lG6XlxcPz*7$xyBHx(3e-Tq(Xh{k@Zk`vsP~PwWC8CaAt@D z65>obv=UEuIU(+f0AFB3*qJOw4k>%DHsV#Iubd54f5|s~F0?&n%(~)h+$p!d_t@jX zEbdBJoj;zUc&Dmt0$=Clr%kOh>*&k8zOSP+UgAfq8XWzJRUGAg%3C$oVe@!QKheBt zrC}fb+v41~GcH*wZ02(7f@xI@oaQ7YbnpA+NNGepwahm-3gnRK8CsZel8r}JZcB?W z7Bf~=0poMO`rXkNoZf9&pL?mf5!GHh?8Tq|VDS1Y$fQ-`EQM&WJEm3pvvS>#40kx# zoCDP9w0K-fN)&ALC?Gc{4MuH1b<&Dt1-9#9ZYn7g`{+ z2GFq=p`o}k3@$J+5%psrp9-VcbH3#ud*fN76SVe{z_+!;%EZb#3u~4c@mk9cG6g0Q zyYCgmU@vuYacKfRStLCA!6m;;Bfs6$E8^rBp2FWq9$x=#WUURKaQ8)SvO_Mjvt}DO zB}$EBV@e2GDGDBxuY{p#_(w942tfK46SH*o}0f@SD@5Z5rcnUD-BK5-(V@g zR3$)~ij9K9cP}VrWSjZ<8a#4>Xm2{7_dPjat@+|ZPtvC>t03>1b;!M}M~hVf4)JwU z(YpqNE<-I4_^H|MR~{|4#D`$Jheafc^`#8sTSn{HeIOvB;v$HIlK5zoHtG zAugiD!e89f;p3E`UrM7uixy*Ey*!oW6`c=x`f=xNmWYk+SiI!VCzW??5guG{E-Si2 zCp^bi{4TOM<|4BGrg}eo4KAnJZ)A%DYJ~*LVJ!LEN3kP>tiK^}859*osFUwS`E>83 zm71L7hOVWU!Y5VGf|{}ZaYhZFJscYctoI`W13E9mF2B%#(^q1D7aO~0IszP39iB;r z^g25U+#rysl7g2?Nbu2HWSE0f1;6##-a zQO{Y)I*Ghh`a_p0m(AVX%laLFr8PRS{myr1WP95ciM&{+ zfSVt7dC~9Q6}Zq?zr#~B`aA)4$LC*SDV1`6MIE<&}gyQ&29=z%b z8XRcB`c{sP9uC93TX1{ik;zU@>38Iu2?F~?cN$n!)5Wvlwk0$OYbP7gz82El9ya?) zuQ1vdA$;O>C(K;Sh#v#8JA_4g;jnl)Qxc?*+ztjS#CV2#U|$ z9Z0ybrSx;_7rD(5z>JK}SOR+oAi8bb{UXMg!1YYv=Wg^z>lVs$62Ln&NH8_14YMXCAW~ z=eiuC78gF(70gapl=t>bUh>ViIS5n~lAUVIk}pUD*LP(8o9{DGtg$!3_opPT7v!~6 z5Bx3i@K(#19b?v}sGbI!^3SbVzUGdimLo_x<#Xj$;LYBS`&%HkeSDr^x8FNruWqW) z^mmQ`jGuD9!sbuA9NE5?D#^)VA-Tm#T9iO)t||zDjqrM*;q7dGa(sZSt3I`u zy}vXm?d|ODK?ZDCPFD7Uojr9Hy*0HwY^!)(Jf8TM%q`gXyHK_$e6#(cuPS3?;ZqQo zQig+i#$BA!A{H2%ogcp}{#V;z#HHh&;eC7*&dQkfYDH2qsfwx$^1F}MvQU`Ve+xe~ zRtxeecYN5@viPF4uRtr@_z9ed{=ItHNnES|Jj*+1BaaNBL?>zsTG}&7IlmwNgRB9`?jL-lwegP zY4wAPNDNHX+`8lBwTQP);oYbwj&1(vI;5la@U}fK!QSn9^9i6VPW}EHFZrnt_hw(zgsuQ1gL{7q#U~nZ7AaRkVwmqep zhzV%MB=@wC@1Y4lB9D0KXcDQ=m@jc)@l-;H0@I(`f`6}-elNwPrf7|j(0_zx?n9H* zhIdgI1ewp>$$v*oAK_}Gs9vs3j!=$4ug~!LKG*`61tT`xBgBJ_B==z3@iy7A^ewCw zBnF3v5kR_V8ytlrW&g|}IJWprhK)Nm3LYiHU$3Te*VSNEUfj{u_iB-i1hE2c@{9E) zHDPgTu7ZOLRCB)xE?+(&w3(#I15IC*gNH;)G>;%qYI~e1zk=8ZRNXKR+v&ki=G@j0 zyBJS^_y$DhXn;^2{pT`z5Ax=B*KfnPd7sml-t7L0Mj(>;vI)BgzksT@!IM-yg*K_m zoRL?;byXEg4Ob9vHqP$-|HHDKGQ{J)=NN)kB|*l{)rdp<&<*mly%}P%^Og!jd>|Yf z^|IfUki8j9riWV$&OLZ-AYD11-VA<;Uk(@XSGtKaNlLDO6CngV?&Wa$~90Qw{CLFHCjbOof)yIA$=ok!WNL<|*pu9qw%@IWXH{iwzTmb8FhQ4rR*dE%l zKpIIzM5Mk`<>ni6xSqmL0I@#vn6bv&2iN7$OUp2~a zxcEsiL)1vFLDc}!(}uLjq?g9-WpN1BS(@w_?6pI@_guY63c4x6@GBCNOZT54F}mHY zT3CUDa@dXF=!HnlWN%1;P%1=6kV7^rixom0wumOCuLnzVz(v5WOyHH08>d|1#GveL zK;391b$?yzOT%Vh9Lm!BO)VeZ)8BdJNB3}r(Y???va4g#`yi}gd6jgR$VR51FW&wB z)pq01ZsTHFDXDeO~RdH}TSNLsniMz#c$Vl{$;AUd% zRHEveB!yyG%hR#?$=(L3;?TF=Gcw>QY+GDSH%i5S#Hr9T8UHAl#K`^*%#?`72iZA8 z>jE$056DNfz{QDj9(Xo}Pm=b>QJO6?EO}Rm8nKmZn&)y9=DcD}#Bz6>l+o=8I=}Fx znCi9mNZD5l-qv4%izos&p}r~ z4+Hf&a;k#wU?1L*X68~nxb(EiG7Jd#T-AU{)uNZ{o2(~yDz?J|z~tNb=zgZg?<*gM z!A~MEmTX{)!{8SmWVoVrS*QbvMl~yl#Ct{D04axF!lZ>)b{gJ>&*NXYw=AA(AvW56 zs!pzz*6~$CQxh?MBDkg5wy1>&Tu4TF_`(d;f`u#dkcx(xA9!(A zEZI4)7zFER{J#*SJerkrxo?nQe(y=wYW3LtMj57#v4`suv zj}9w-E-=5yKiQ9Z=7|Byzi}$SpoK$h<>cd-7f%Ywf>9TeC8ESOaEg7uM4+RSCoWwLw<;)#p@Se z`(o}>z^6J0w=uV>;p-d5Q8MzwHC9tlUzC<@Aae1FZKux`w};E-V=^^u?;{ z1+E0;2&uva&_mQ&)AeIw2srC@nbYp=KJ79HK7>c?pe(9Fo`c5*M1RU}{5_nYK|O>J zx9_bJ4-4_M=;#BzO6N)qyZuZqA~d7t1iVRuc?0*?*AbgLambm?9j^#XF!H#~SP(ta z>d`YgT=%(L)Inw8c9T#vnSb@@J=;5Bo%eJfeR^-|#BY(+{%?;a!_*I&v*nbBg}?tp z3)xZggWWhTu_Cb<;UA-*>H?>1U~`q$V9&y2u&$=)v;Wn`)ZY7>tNsSFPMpg$jw^p9 zo7QC(^$y&()f5#(onnOD2Rj=Qjyta(xVOJM|NW-(3*q5~KKez$^2bWD2XuYew_@t4 zZN>SB%A(YC#&u8H__UH$w9u>0Q;$Y(KdbAnh0t_=Ro{bhKbC z@P}E;=yFtf77~>16#pvOATcUF|Io(xCWQr2s0@~^*zJ52%GA;Ugw{y?$*iO=E10H2 zL#f~!xE9Xm8Nr<_@5;tG7-OKHzx7v)1Af1fFqQ&-5f$R}TZc+RqdzwC%owlG+}w;? zmX&ax`0PA47SW;q#`N!ID*Ihg)wWsG{6c2*r@qx&i(cI&zoj)dmWvwy-cvPRBFpXY zoIsE2ouo%_mYp5ykfB<}rH%ya=96YP)s-Q#0;f9qkX&)&7?%yaHw6~z3r;r??`ezNbMpOCRZ|lj~$sfx~a;-9vfdVX@A4T8bd>b;r03&|^B9ek*L`JMCvOg94 zRPI1SytVn2F*_%xA(lhe)IQ%^(sO^^4A=^cTwE6c8{x51)P@}Rxh)6<0Jch3Vb^~i zOd|+H5iF1bm@Xn=SHX>)_++eeTmpJ>s8Hu-Em<~2U#a_ z{jp!*8~0ZV6^MJrjZaDeTLTgG5=Bu}tW|IGx?nSip_u&oneg)SOm3obGxqCFxkJoB6`>!p+6DD=Y0X5|;dPMJu)J-4BKQ zX(j1YTS#uIsD!(SFD~>am?{gdZ?^;l5gWY_FU{?_fVz57ll-&tN6Fuo#8RSo4&w}{ z{Y7x_hRQgV$LF^z!v_>--1#fOIYQV-y`{PMG7PKnq|5;&zL3pLEK?|v6Rgr70?$GW zpk`oS60^4toAD61M9n}a1ER2kTtHBBFrS2cw?Jd~^_b3Qn;2NEMb-l>`$tC>4Sp98 z79p^7l7o1v#@`u(Gf32Wb{nt17~;Mt-XD;ifCb$1SEV;mP2BK}Rp5a-KFgk2l6a1sK|vulV!oJ{zk%0LKL5{1 z*M}kn{-Gt95wCm?Lr>CH*K0))*BCl;28Z?8M!?GqSI0}>dI0V0rHkX4NJjH$Kn1 z^ziOo1Q?d#YT|GNC;_S?#0>=jx&tmTvFe*@s%sxa_gZuB8+oJC{x)(Qg*#22Nu9C9cfc1yybKFL zUV)EgaX6ovhKA-E8G4S-($W(3UgzzP)>gTtifEqNeGG`2ZB77uXvb5@YdVvsza7mL zl~ne~fnCbviCEfhpt5TQ%7J8m?C83uvp#fc@+!x{o*3;ExPB z*Wd?!eYc`mU^+4}Ff zyjRZSUie@Mn>~*cy_09_t0P=+rU*fBWD_bs&TLDN&4as%xRBT)J+jc-!@hVK`O;?o zY`=Fq>Jl-0X(g`iA%i_t#J#uSOQikX1W>1dxH126YkH4`-saIz3mMYEgbXRZe|O^b zot1`=q#}Ugu>r1uxFHeV2;9HS;p9J~r&?KIapVS3Nwb$$*w{Q6xL7eOrBi4e(c9Y^ z8xGM(1Fo^T1#|S`Z%+lmpI3w$qYNGwMI|K zcZ9s?d)1Dlw%FM{uX$vKAW{!?gh;1Updo%RbF4+DcAI^lZN+|gs zC_V%A4Z&OPPo18yMpXj>3+eBIoXjkIWJjuezd!vTXgpxFQNA(!LfNUTsQyhkokL&S z^2)mtK4MmfU-Z}O@l(2JZ3b%%d)S&@^QfUvk+nAzjXx`!)FoG=;~BaezH0^G|G6tk zjz39ef;Zttn>YTd-c`~Qg5hp_}bao439|#$j*qefttS!w;lcO@SI1$xfwnP zs}7N(kOU&YHBb-IZZMPJ0{1-DJo)Wg6iQn=VjvH?wL)Nk=V+k?h6;cpD8$_D@NeNa zt=m$JX@XPmaYlNVk;0QJ;e3YHn8*bw5QpNGs)?OLDhIu)PKs?vd|J06eAL743L(J< zR}`YZ4l18R{pda%T3XBeq+tOo`oBKI7?wu*qd;6k2!5tuUia8HJ9K{?-Mx%lk;Xo= zLMNCRtW9@;jKb9opIBoF^y~^}F2}FgSxf_ivEXO$Oue<;D~*WAz(Vi`KVoWXDh7g6 z1x*=Yrhof`RO9{p5PY=}fD!OmAiD;X1Tdnrf%$hXw5Tk*T*{A;xe()=_@Y!5GaSj+ z>I{WShekQzjva_oCFU+^r5-D}?E0948*5oF1wGp^lnPdKEbae4=W84M7IBTL2Isd^ zs=f|!pMOwCee{I?B%aBh_OFhu8Qc;4q0 zL|&HvjgI!j(F`FZJiThZarMP$B?N#niK_q0CiN(FGQ(>}a`Dzz^Vpt1NVm9k3$zG} z>Adgx_&2sR_wVnbGw0N38{(W2`XA>MMo~k<=OPBXBJZaiaVf-5(vv)FU*19@l6b`lF4+#r>#Q^7|+jLZRPsM zN79+fo-W`*?^jbyMt}2{AA`%ir zejMc>=}?6RJ(Q9nQISW~yk6k1Fg=>U(NT|!MIZ2rVB4C;=ANUJSLQ=>oACK&WiMBm zW%qr}jV?6l+GkL=;zul=oH@vG5jzzuV3BPt4AFh0l#huXKmK->So*7e%E0zQg8srC zw!R(5K}y3gHF9J$W~QqM82m+mtYSx28OdXwtbx76JT-S^)LP5vE-WcjT}e-#Vv)x8 zdss|bMYaL&7Xju%J`TT35r>D(m8NgVTzp9j!TSU7RMkEm@zf&(Lxl_t08$`mFIYDD z*>f6J5JCS3@>`4NK2+6xqzW$JX9YswaggHRDZ2(iC;&Wt`rQ9VI_d+G+xwPupkO;M zbv_hAK&q-TUaU%$#72T{8gPAY91D^o6OxA*HF#dXO3=7W1v{P2$LSyu!sESmCaN)6 z1^kKu-qencv8wx_PZ67m_Ao<1;d&8?cmBEwW1iUi>;H}nx;Rd;CHiuN`i-QMZ zF+8a%yCXs%DLI)IV$m0P6jW7IAa%i=@M{L4BS3V2mEl{@Pgfne_<%j9zQ zs`~n}sw=rFA(|zrOeDJ+(~fILInhb53iWZo5(o0$^gu;h@9}^AO|Iv!J9n;|n4AJ@ z44^+Y`##?$4?dbqfD^AoDzt!%gp@QeA%UU>0HmAxbbw@G6BVTr_u2+9d?7N?P%F8{ z=**8quPX@N$!BSdQg%Hke<8IE=KEL!egDlw(JvESCbVp92#nLPzTZ+)SQcQRA6S zMIEqKz!g>;Aj73!#VDWovFe96I5c3WC(V&x5UXZkjgLI$Nrlf{GwNY_Xcl-LwgxEF zi)V+dRh_D;KW#Xux!$PKEhgr_m-5SO7rue@D|W#3x|BY~p4ikxrDrJjlc)OG(&WfL zj|io}UEM$ovEOU#2aT@{9|nxKJr_ScPdq6>J_HuyR04=ddB?l?;09vmC7YHGIWk1G8Y*t-TBHD%N*mA z_-1h?lN%;=5EnWSobWmT=w~2=(*R&%tFv*E6JC780(fXfV5&ju@Opuv3$|l&Cm#}) zT`8|ZsJN?J!@APLf}fB0!^4pv>s+teHMpPnsy!VQ{`|Gpdrmg7YSa$=n%07;w)5+2 ziT;qisy6tZkhkaH5K#d4A;3Tm4h{+D=664j8|M84Qx`QrzF@TA5h(abL~_IA_g{MY zErAk#HOr?3L8F!I6G-w*tIW+>H(u{#tNw{f`DonDhoC^ybxb`3{^D z|I(jmr53od2u}6Ax*+GZ?>CVgmYJE^{#x~)Gec$!wC$!r>m2aEv5?(1sIn!D|G2^A z#uKl3+#D6~!CrgDOuBe*|H^--)=K7W54WR2MIJK}Hl)>TY`mq<-%LnuNi%s@4-vCl zPkedulKZDdC)GEXVr)mRB!jGRNo4={eDa!s#(lKe?^7&``aQJ^KVx1@CJx1OwP}8u z?y&_rn7@TBi*PO%1`=T&e!_P>U&rK8eNzJ-_%r4>3P_{~78WX8YyM7HR*c7sX3ocfyfLd6-P=TTpb|OZDrJX;>Ys zIW$B;U&c=dp4?S1pb14h`%HNUR)fg)5agAW2+`+%k1ny(5_;aC0HD#m@p9_{vXGYQ zp>e!_xE34;o(LJ7ezaY}eFNkQOS~cp0pe<{f1GFkhO0+{RSm9z$*H+pI-zk3*ckkO zJfmB>bwwSe7V`MYioj6*zQbkUN)I(hFX7Pb#qSRLLSU%6aEQ>6lz&b9U+K9#-IX{l z3C0^U*Bn=BezJnpr@6VhYOrT!GAMB-^|j9QwjP>bm$m+!%I2>rBD@e$-`Tsdxw@G0 zGTQYggX2o}8-KooH|m?KTVN#<^9~g*I|Vm6RX;@%S?u%9y6&x3FICF0cakrZjvVAz zX1ZhWZHjyiZ=nFwxxF&>s+>zIk;%Sdi9}6-f5!%9^?US^ik4h7dDa)Keg1K_`gsR( z4hgt|VG69b^$?!7x%>@FBEHqSKgW|4Xfypp`J5l8Ifq#b$mjONZ$Db3ntagCe5Nruz zZ(i;XR_H3i>rr#RY7W?qo}^3{7PPjVZv5>>r{5F9bfZYuf?+%=ci&?W5|Wfo0XP$6 z0*I)Su0ukurk_~|eh)JLX%YlbuM28iSw3MmF3C$cBHLm*S8X>5G;_Wn;ygl_KSF4S4B2Wt2qcWv%==2!vy z7=b9QC~EqyUk;AA)cBex5Y=xYaWKs(mk^oy7oVRPWJ&A)lyh4u-ZbA_WjOFF49W)S zj?9Y1sVG*p??WtkQZQeT#mFWKwYTS@S+J!kMHh!_&Bh#CdfiqJ)ff#)ypMeu7aDt=uwJDan3YrDhXw=~mQWW+U$S(}^I;|bHY-hs zFv*CxcJPQey|$1&2nbwNLQGi%FLDQDOCKdJ%zttI&J{O=TUA$=nGhMU%D-#cYHI%iDqkU&lDTTTnW72gS2L(6KK=X0_iVcn@n+Nw z=ZEjln}E&$&_m2V03X69nfovCwf=IeDyiy|CFF}XF8KcSYGPG(A=vQYTM~5f*c)Do zCacInKfz;H*PYrnTm|k+g5q&_d$i2x%ruacAyRX_A=CK$3pv z8&o7lp#FXJ-AzO*Ck%mnsUgeJZzC1TE&dl|3#2Lf6QGfS7fubxp=hB;-ce>EVC(~O zago!s6ciW`XlrkG3G|&CT4a!Gj95|R$?j#JmK^K?+ae)02aZoIAMrOit}fsdSJ4i< zf7tZfnAP&@b(_OKLrW8~*|yhvEf`v!nAejgjeIj;ew-fCHEBW8 z!+<~oK5weyRIOi>Bu=oRB}7MY`Oen`>8qCR5WJDqUbm-Mk}i2@Zx&?q;RT%9vg($z z!hgQo`2EK_fi82oxi&E*_oJ@l*m>J-#%*EQ1%RIQUEREzE+S!?Ml8-$XiC~j>C^04zKDM{O=1LVbROm#>d-1o{xMIe@`mq0DSt)rFCpi#F0Bb3j#)@d`&)!|={eHZiRpO#rZe)OO7 zFnr2Z<^M4*>3<4NFLnA8IQ&dCmuZJ$y_*_lmh#6**=*FWr2 z=qjwYjoOtT{bh`0`(z*6H!ZuEETxk&{2x#$i%w<+c3A9UG;jsuS3Ri?VZa zHY1E>D44l+UBN>J@@d(L;!lMTksj0{1}J}o0Yb)2&?iYL0DJ|mmBi*aMw}{rlj=H* zq06pIg$2f8i`1T|=L>X;{%M6ONXCHp#(0)0OC@R*zh8G`c-C941C#_R2+L?GE757N zNpHK|_u+1_Mx%PWB!$>zG+4E%>=khM2y(Q9sHjji$<-7k_6H%IFUyL|9c9gdy;+YY zF%tDU{kx~Gr4<0pi~Z$v0Yu4Qu!Xxm662&nKmpL`0I^GoYJ>EA^WOa17;5mPnnCarq1c6Zs{-a}FZ1^N@rgn2%#)86y zfvTuWfrjFZ?-~~cqzHJ*^Yl-S`X-)hlAjAa477i+{Mgc*#I%36U&($eH(lt#K&I32 zq3wCp`{H5}sP_&^N)jWv{E8Q9Nn%I4h=iNj@%}T3{p*6-83p;upwjTB&@ncQOddyI zsU(M?X(xOVuSi7cnh=he8K@sq!hLHu4t61YNl*5NEF`(mm0QSuykBO;IdC-2T6eKD zx@Ggr)qltXHU{w{_jlZ0wIC1!-S<5}AS;uh-$kG|ZQ#+A)EoW0x%w{@=bAf$#EEnC z{eQ!LKEH3TpbvwE-Q9be^k~NQPHlUJAJK#=%|!SJJi0O;bT`iNv|60_@e%iCYhX3?Z0X- z>XTrmhyCMrJq{;DDTeyPC#LsK8$5gtsy+55Mm*?VxtbHG7K~?7sfBz1VB47-0*`es zG3T4usNRuDvS$}L?w7e48WohVz}d7@>HRi;j)RHnW#AiJLp!b*6JwAQw(IlDM!SCC zVcuGK9mzJV*};_`RF}2!`@O3}08XLm z{>}&v%wR%xLzt!&Pk89SV)!DKqv^GIi&9ZLGNL_ZGB=;`036vh7^C5D;_s^S;iek= z&wREO%gZcd!7?tUpMz?ksG$X(_>%j^-r8ZeXn0VbNC zQwk{w%xF)EsX13+}KXozWeXhKBc|D z@?^sodf1_<^p|aztCUvwI|@x62Rv#xw}GpCJQTg?X2wu`HE3VxpTZMA)8Hv#|JPt0rh~W}y2(w-(AZAP> zHfm0&pfW-sj^gRtcjZ=8kD4Opfk}hm8@0vg%Wiqwa~+TZ z(mn-bSNkrYZf0nuDU-KRlilGJCN-0CB>$1%u&YzyT7IR{-c$=VL8@}8vo$C^($~+QT1go7cAmN%!B9jEz-9F6%j8PMfRDOxIN!0GV%%cl%v8P+m+1pYe>m#iSEc;$@8DT& zLGD3aaT1>DaIv)Rkyo-qq+nhd#pvhUpMwKsI=zX}kur~E;y(5J?096R4_A;pF-eNTr=Oc$;Uq~Wi~OEQU#0w}cH?7ti@v`>t!Ymz4;W(F4J*tnTzuTeRs|q>!k#I_c12|4*1zzIJs%K#}8chx)xMGCP;EJ`^~Y>{KP2`IgqZM z1gJC4R7;73h3QNap+rDsrJ1fHy*)Y1&8>3qWZoK?Gep%ho~-fmS^MHHzPkuikWKb_ z3e7|>Px{a2wM%zU4>V`>Uj|Fh1~;$tvx`u8>=Z{XJ_4L0=;WIhnD?F1q#%`{Ze&2n zu@#pKzukw<$3>eJR(U&5%ahnA{=+o3!C zd_dbd)XN{bLsRHo zJ!p}fR14_6cNEttp8PhZym~lu9(B#D>%o52qto&rA_mUGLvhaWcjr+RhYe|q=4Sg$ z&i6bxUpkVTjmb#lJ@vjb@PzmM;U?a98sSSSA0nOZ+H+l1P~{sh#Y@@cTkFI@;wVrl zX9`tRXAj8Oh=_}bBzX+GV-d*Q2NBXm!V+}=9^CoQse#<1sta83R&X8g+Kuu;1q3;u zkr8(-yrx5d%(=`5eaWuBhc5z$JmI#FsI!a9P9&o}op7qM29K;&6T5~CzeB0)Qx5*{ z4cjqdHLi>-Tj3#$Lak_HY9M7eiI)?g*6&yalP%Fq@W7aC@DSWxb}o+nhll7fI5J`ZUJB@$t_NhsSu7m~WeUL(1r?P5 zG#dY^@9|>*tQD9w5cI3_Wm=>vNAZoL$$8|!>;cO8Jw3e;n3RzYTqKF|dImrv!2ql< zg{rjcY;2M`I%Hsf=6%>r^JPZis8`eMH8y0PX(3%Ajro1fO>3N*)z(GCHAeNaie~nJ zwl0sXvzK&gZXoO?e&u4uX$cZ5MjE|RHCQos82|O#;-TfGq#(j+E+csKb~B|Z>{ho= zd|ThC6qyXyvnXWXD;lEHq=Ot1G{cKIfv*ASiG6!GV8?g}zJKpgsulxO@Z@a!#I<6IeIIqDc$SpOe! zaI}4Ha~0hF>+ksQuXyKah0RISMHF#thYe-afh}E(M!RaQIYo$AzT(*gaI(y*XSm zVg0ppKJN#>A6|G~=c^(^ptiCPitpFYX|cPjQ&j%GY2Dc^wOhx+`-~^7hzA64f6%`Q zEeyX_d90@>F2CSgl~-9L;b`b}mdK929=Z(x!HpQU zc!5>)R@4m-cz(TXktKu>dt7(@B&DM$2-2|ivfkyjSe@Cm>?`ziggtzymc>EBYM~CK$L&o5A7z=~)=Q_VEg-^l<(1@W+{B<>?H6;H&n4CJPJ)8RXpnp5}DhYWp&YxjUQViETYea_Q^*p`A`ex0Ke zA=0UuQGok~E<|Z7x|R<9rUB;W|2?(RhKPm%2&0)80b!V^e-lZG274huC9k@j)4l9) z2FsY)GivTiM^>=`gKws|>ACKUz_*o=16(fMPDshV^9V-1&$I1qxY}&Q--DhMKHw$P zKvffkkR1h5>vZe*lT)8>|6N5{)2h7mNAI=C!MO!QFH_4Qky2*ZWB~_UGM+nP>K+yr zRwJ~$1KVZud|cV%^uLR9R_MH!Ha|%BLyGGPzGg1!zezIH5XJi#`ys;PV zWR+l~f})z5uEF_+!NDs~M^xc+RMrhY=DKY{ZGv-@j>EF9~8 z=roRb`!?KjYyONq0j<6<1DE2Cna8JrN8*h69I{4D6B7o{j$jX%=$!}VI*Y~L4+wY+ zAn?eRC}S4pN$A45SDliP!3;qO*GSvkbPcv=`_tIoN2}%8`Ezw1P#e+=1sIV?_s!;9 zho3zZS6P6;WPi1M>fZgaXZV#zf=ss_$;4Z=#F8{;w9DNR7tQ{A7w!f-V@Poeh#SCZ zH|H1uCxjaI*kbQWPz-*6$u=N!?wmTfue%Nw2u1TD7fRw-t>M%XeI1FBiJZC1Mt6Rn z9J3EE+}PWc>d6-WL@8D_bkzj*yBY{p^aWoKxL*g)aL@T;6BE*$WEKONs_-E~pzluG z*L_t5`fmab2kgSq&kVkJxxLy81q=-IQ6Z-a5Yg$8I?N0iXr=%0K=mx#X72_+e(mw5 zGVH7|kUm-H<{XTq6Zg6TAV|c}MNCbN2Cvt2>4>prSPqC?#|K+{;HZT3t2xj=*7x?> zVBFRJZB21SxR@0#N*P1>dtHy8ZcPn6g}htBn-EbME`IJlZG#@bngr&KWn8!2M~&zs zYY<{1h>ag$E2M_=1*KiwsdV-H^LQZ+Iugw(DDiSHKNTxC|c%Y$qn9wK%|a5iNjlNh}yai|QW$q-_dot@o!uQcH_RN?LW z?skiDhl!n@7G~fps1x1$BxkWg`g9~aG}*{hQ^mZk1YKGEE8cLNIX-uz_KBj5-M0*` zAu4}4i_m_V3#7^`cz$>ae?Dy;StM9?bQWXU`ATdh$iMAbDE<5=xwZT0RG6!c*e(a_}SLt2sGnHq4kyiX1mpd+ra)`#mNE5Z*2kfx@z0k{bv>xZ`yI_0IwUKgGp@f^UKmaEB*rV~#sDm6)GJXDKT46_0ZTeX<>aMxpYvCu8Dhnm^Hm>Jh zo3l1|rLt=qU6cOpa*j)#o{(d?H$DB)O}|GxhU2NP{Lf4NXmxR|yeZZ=lKfZ9LP_4O zzWNUXl&kc)Oz4PsJxyBinZUSNRiDXLv(ZY87($VTO_wBTqVs)LP*52NwAwQU2{ak= zI{vtI%X`1~yA`p0dcI&)^OxW?bQpSUn`6gs6EPei4G~&e=~PGHVd(4aeGVV;(Bb=3 zP};0l>wOb>;?YY7t=pXJmeHP^@4`IV5XU2IOmu$%9D8%H^w zg-=7HGGfVbhf9_NQ)=;W@NnfxcK;8P@LljTx9$1bhvG8H&!gWpzCaTlrG&8}~A(_SUh`oE8l!X3Lk^qW?PvK;G%MHT`Q zW@#uZRiboU?vmBsyO%3oE+eW5e+cV}${~NKimD)gJR7?wnZNrzyXBa$Qp!bYVxBsM z>HQpa3KwllE-BRl;9ul`oKm|#gO3=zk>ax}co#q>%-nMenEPP-q~qQfy?0O7ZidrI zTz$o&>Idm zh~wGdeMZh7u;du4Iy>Kp<2DLI0N>#2hgta!MEtyVS91CVgoI`w^@D>yP*WFqRh$Zi zyks!ugO=BfBO;>E+tz~_kJrwYs{DLb7hioOJ3t1blk$qGgNtbR?PpgjboKqI17klP z8h&6=F*vUmY@ka>enodkQzjlI?`uJ8q=~)X^sR4Q1S>*R!m^a=uB_j+=B5m8DliYp zzb~gQ_+I#)>M4c-g;?RTp0;)%6wV`1L1+mIOiOcn@|7OP?;b1kW`LI$sfb0O#t8g; zP<GXhI{Tg;O(CUZVQ4g*OoRyE z0jT@IU`YzvMM7l0r+-eT;nNLR0ckxwij(au2`Hs2RTWjHhkq40!$U8#^183+d2h6k zBlaT58GQK2F+4bU9!yxBiGujh8QusUTktyg0o-$j?sOm9`01)3Wa)wlp;r4jJe-k- zr#f&xTI?`Z9X z#5iL|EIxl?6{E9wdccVg+|gK7A)$fV2iQqO@A({Dq&%sybfn!_D@)lw>nJwnGk41HD@xG&1G>& zNJt#d@%T2FoNUJHl!7}9vEUDwi7LO(%*5)5=V^mGTv(AsVBK}+oi*gH%Kxz26ynsV z=THDg;8=UWg7{&fFj^`Wk3Hs*+{okKN*Nl3;s}mVp3Y}*zQ=;|P-w)qY9pGVswBtO zt}&*=P9`G%CLRP4-@eNIoOJme&W)OR6M5oaleq9|sozt|L65(!(FuUphy<6{{&`*Y zh^VLnQSTV(fo16xE_otalx|u_Lg3;}jBMMF#w&*p|H9yRyp?j=3|(e?E}2d?q>@Ad zg0rwyL)r#Gdl2#7=ambmcmcr^mn2yVjDeT6kG^75NA@{TL(g9W>>{9EH zg~vbvm-T2&*(mNr%$S(o)hbDj^*b(nv2zhe}Cz zOLsSX_n-4T^UOE%&YThSAhNJ~-`Dj^=m~@l)6vnHU0n?UCXn1D9TM#nlHp+So@)|aSz{Ho;Pp9T7f0gAJe)|UQif`XiXJn}74)YQ=7uLaMO8 z-zG?+$$R799_I_G3gppv-Kk|OZW0+ubSI#I!}1QweZ8(Z~g^fst3p^Dn>WwX# zK9LgbS+sm}EH$c7t+={8@*9mqe$uGrwBkWPhP{-pLzT&6$7JLo7$lpp`O8BQDdrptIop&ue*y%#QYRxrayCgM- zeA^csX9Y-!MmH90w2J*Uf~sS z+7*Vb?s!f^bV}i$#L$tma4FeWe2f$E>+`#Q0ax=FyF%XP^7sttxSs>SJW!xpO9@g- zl_Nx4cVMs`P&CZQS<+??Wk@8yWH&XfTvwKqyaj6n-I@UCd7+>X85pU}z$;f>c@O65 z<`%N{$^7xzX&GzMVT8=TxZ!)>!gd>4wehhXM{U9+-Cf7JylRv+LzXMwr9Ef+v;5~0 zuj_1szh0Y1!AT(=ealy2g5D#@kXp?X&NW3jG`EHnN%NE?-kdj-qFYNG&D~6SbfVp; zTPqcFm}{%r-1fW3I38Idgov9D(gZZ$2Vc_XWH9U`k;KA;5obMCa{HIBUM)_=7V*-v zvYOXvS?2^wMEiXCg7hkk%>+5ZxZl4wH>~6deKRt4SsTg|LbY1ORm5GFm5%dhXZR>@FMRuC{ zR38OH6_vTUDMrOwWYM=)KmM4a^~pIU7v!d_U`W}W23(6FI^7_$UMA+tO6bF5e(a+c zrJ&ey)m)It(+UUjTRRm2?)Jb|l54%MRESu>%ykdn*kn>*9|0%Q6L5Z7` zMT)<;)eJWlc*54cJ#kRhP4ztbnkP*Kg9vZe{cgT!knaOlid(Rw%?YR-^VMgqEqQ=u z=1E-WV2prslhbv_fPl_oGJ&rBLl_9^Sr3TeUHEIx0S^Blcxhx`ji1&1gnU#zNw0M$ zaX6`~l3p12iOIE|7FX{XnU}pmbs8E5)LK~I-3*ay3wVe*XKEA$U z0P)@5`=O4^^P|lcaMSLBq}BUuZ1}%_{{}bv$2*0#3|nvooIPhr@i*)?l@yN=vgWS8ME^PJ&Zl-LpPOsxAD8IJ zR$M|gjWFBmT4%@pk1G_<^jbox+SySuHtru&-V%jZm48ll5I&hD^V z57L6r!2=}%jzyvogA>=1zK0LhsFZYFh@7W`N9Jck`@-G?A3X)oX&son?CkCPs9hW! znn9_-Zj|@MEg+Z$p05^=-;2aM^>B5ag#tE93~$kkz7GcVVGE$UI-XPfsl~wfYz7KE zwuCX#&bdY@peQPCz@}d}L8*!w|6pSyD#Vm~^`LR9z$2RrhaG6QmHLo8)#Yt>qRv~I z(ye=+`C9OxR)k>U<;jzz!+jJR6TcrQ=jaOS%+CKpIc~)b-MPFhxLq5zVpRLcUh3KA zU@rpf58+{-sZg$w&)vG!REMYAS2z3sAnFT7ryBYDIAvT;YpQa|f-x>VAR1fcJGsjD zFD@x9;Lrh9flqo76@9%kWUM1$_GKRhInb>M8ji~ zKQ^GqU0V6WQP|`I3w`dTsuoo+sRFZB)W*;ftWujWFV}JMyzgwfXsn@54}P3|LoJxH z2rT2@zqTI9c0aW8tIdlH%MK~{^zy;V(qd@cr@I!9n*65R+_x5&d9FL7Giq=?+=GFP zh?WUYVqqPRxuzB&Y90%6?xz9kD3WYR+>ktlIC1<--TUD$^~HEI*0H3JXfTJZm<2u7 z3B%4<6ua}G9T!VRXTxo}tMSmpvim645|UW*zP7#fAqh@`*?;$%{#|)lPe+O%I;U6) zS_^t37m{_U?9LQ$!Ov^%AepqTY#rUi(>ZC&$)9D9zSNsYPB}QmPbDg#lntTbC6a)Jp-KV;<({D>lxP57AIZuGGhPIm5IrB+u*Q$ zY(djy6>%zHR|=ML7HY~3YZFVs$gKN*w?kL7xPJth6IcX#q-AETK0zd`J4S3yVJ1(I z1W?SrOO1qYX5&u^^j@Y~qLg%J%pT$6T1ry%El&J?yM3oIi+-Z7!ji?ABI*<@Q0Oq} z2-@2&E=0+0V1Ysz@8L{a??!Yk&Y^2f+1KbiN*kh!3MW_?+Uu5*#&eM!NFgiJW%K|m zmHFv0$4t_0`9+7knZl$!W#*!yAGCc1L?s{T_@_8Zc)*2?qSMqp;BtZ*&%X;*aa#QV zzq6@Ceu3WfbI)zP8+S~X^#^;|LM)!;=~A};ZSe0z^nN56Gh&I#Di|K=z%0Qk^gkj0SsL0>5_qCk6g zM|NHCbG{aQotfgo)&nyA`+R&ICz>5%Ffb zzj$-z!SnoQUl=Ja@pvu%{IHhGeUj#r$tMM^UA>XFV1z%pC@$qy5q9gve7kX}@JtO2 z7uxFMukn~`8zjg70LoLTk?~)oEPbwm+@Xe{Vu3@C` z<%P&@XW{^~M%9*WrI-95yhy>-u)mo8WZsfA(>M6M1p5_W(JAe|b0 zUv!~RywcWe{1@k(rp9P+YZBN8PkE}otQ6EQP6AJaMhl!)7jhkf{cs1;V62P}ReIUq z$N9kd2<$8`oSk_ga}jjvL$zO(HdA?39p$aanmHHm$U%I-!EglIk|Hj_q#srHb ztaO{IspS`^A0II4udL2kRn5fbcLa8%>!`P6{xkKTxGC+0JM5y(qs%%25TM#AD7}n zZ%(o!!aJIbcvAfBF4a~H8f@uyX|hdNtb##VX8w<&zM@3NNe~(Uehje2aOdIWd7P3K zL9y6zq%U#Q!)KJgEF)Le#@dhpE54KnFw3yytVlXpfn~Dc`jP{HqXD3jvd;UQL{wU9){m3SRAS}k%h9^9r` zyM;M)c2sEpGF)+KNTy}kcyTRO;%dfLEEtS>ZWZ_~j~2#a*g>McjciMFtCilS+$zo! zts#~3*^_JT=o;oA4I$@UVmS}P0t=DD3Awl1izAVm&oIbRZTqq-GLqA>FAo#tlBTB`>XWWyQqxtz^9Sif!5rDZ0UTd^z^;0#eul(EOS=y!dIYy<)!o_3=u25|=?RHSn~ zQkY4jXOQ~bXR-5lB(I)M#fyr1C)p>YWJmeK zh=}-*eF0`JP%0#RU*FfZeoDfyU)rAug(I7pDmKxztgcM)MFxwI_ipz$BDi(mzz{AO zL#7!vI?dO=x^NHSL89q{MfV`yZ8xxBQ?FWwsUYTMuRqm2 z`QQUKUkL{e+=;`mRtFo1jBt1tffs@vb{nB#)a>;w=bd?1t>B@A{A>F9b1#}Nay) zL}I|8C9k$I*3a)A;DnUlSjOgCjug1&M`$R&69Jdj#(bT#i+!`~XZMBT-x@F&DQwMK z;#S>gHvUpB0uc-bu$0HT$~YkyqsdOaW|2jcpG**OxB#}c8d24EG5w;-H>$Q)$~cN# zn}=#6LH;$QnP(ydmz*FDHnwSX?V)t)v?Rc;#SFUQxvK5N`7$ER#B^T7#RuFAe>JlN zBs0}--g-Ns@YvQqx}B5245kfaw4XhkyhR6tZr`!H`${9D{+~y}V6dn3$J-^Fc{ySt z<(UUL3mRiurkAyDOy!DHuGK#X>*+<4cfgr1H~r>|8BOe8&3nfr9*S$`fehXoT!*8? zR>OTxP-wtJ`i&;$g505PBRk4zs*%!PH5 zRf*d8=*RVwi(29sd<@na>c~)9^9Pw1RB3cRW^8jRs^R zE9hEG+`s#>LgWRZs}Z!YK$wu&^=a>aSEI>SAi)5Mf?O7ZECo6>q=2Yn%FEl}_|u}v z!btKH%!zZ*!Z4Pa8Ma=@U(p~Ytizi1dl0!yzp^pd=k!g$y40Jmmr$PO$lfI|pX^-I zj6hG%nQfvrUJ{d^8@&`Kntr5ZxV3v&oC@d`?vb--A7KDaCYR8ERdM)zOal2>RRVA8-S;Q=XHihmv@^wjClH|;;36JQi+ z;MKX|24peIQ47)0ovsz{9ZB5F#~q`i4c#mA1(@SHF~C$hUmnOnv|xT3mH0R5@A3i- zRSr*&0Lh(FP0f5B=G&d~LL_stT zsE5(nIef9satHSd)0$04AL$G2xwYX*mh$4@qD&3?jPwxSOh1gW)O(0H0UQ-6t!-m;dV7$#%$%`i%RI9AsV4qo%eDpR4{Z02f|e|btVtxEMA|>@hS%5DUwND3K^X# zdkfdt7*kClRWt$s8BC(0^xR)dS_1BY5s470-~s|IV3;j6YQuxbIXjoCh90X;Mh*@l z5M?ncWOEx;b9eg9hM_JdOd4*0hpZ`Vs#BAmv^haaI zOO0ct#$=9quiqMO*Svp1P3g~(a%U)gqVm~vaA$i_fd@79=$+riihbr&Pv_qND+0}u5D^ECB?A!gS?#bGTGRbQ=0z4e_nEdO*$YJ9 zNO9KwJiE{<{Q_&V=F*wb1M0O(GtuhcqR??^)nT55vAYA!z+*E$)6kNv; zGQxXLAO{A!M@Lt%@DE)pqb~C5h&X`aXds~fZY}`P*~F661Zw`JGoHxt(kes}+PT<& z5H4q)A696L=F2hO&i|~o8rs)klUAAjn}ydt0$-v_DVW}D71W3mv00s;QTTNWpg^P! zHt=8qemr4<96X@fwzf7*fNb4?st*A*6%O>0Vi!Ap-~kC%4@z<2i&H0pgB)m3sVDuS5kb?2H@?tlkYKL((6Ze449p~j#4?ETlvnTluPBr; zlabx-MXxZ9e=B4KTxk)0ei)t!nJ63Kaw5ctZ5Nw0n#0 zTx*H=nKYTARDb7Ryw}vjrpdR;6qhI~Gae?J`a3ibqp`5M=ARRYReJWchWDpfLBJoB zblu4iU~B$J57!H4GED1p2JCRjQZ?&^DGDYBBRz+9^X;>$>)gF!SI38!|lu? z_v-YeNrGv4->8Z|`fNB;?+GQ;Ld+u2v$Lw8js8*&H)p$dJx+6oFfU{908*);UeWrBb`u%un z#L!)@q|8j0d%WrzppiK+H<#kBu~FpyFnj4sxzXPIm_I4)Ydq0;hqhJQ9n-A~b>mX~ z=B{4vWOsKjCkE_=cZG|c6Ohy?dSfb}>7aIizlsBl+VAIx!##?!u9}8UUROKLq8C?= z)TL7s)8}b5eEsM<>9_1!!RQ#2SzPc?@k;B0byBp^)^^*i<*&YV?K1h->K~~y)4qyC zO?Ic65#Y=0e6d8YdpX)B$+?Lszd3Df&i^UCFf*Bz&Q~h`ky-d<$))K<{Fl5;U1DC6 z-h%fk#v6&7Vn&MdTzZLAvN^`5e=ze7>9jwWJl!?b+vHyTtXYc6x)Tt;kC<^gz1Yv^ z0(mwN<|73xOQfWve?gpq9(&)1Plh+EE+*J!?H9c;ZOF&G){G=SlLm2=l3;wPG}9BT z;cxDkx|20@n}wx<0Ws3IWE0l%;pg1U*BOBUJ@EPmM-#o;5Wb*q!Pj*nHoGGmR@fBN z!T7IsjQgur^hKh%t*3R|1Vp=B^}ZM!?|5#z?&iP#Wg+U^;Ozj@`)YpT@=L??sAJmk zG%vSkk<8sC-WrG%lxBVjrb~AHTlqf#E-~sCxgQu?K=Lq-si`TzGgkOfaN%53Fn$!rDRpQH4BgLH`}k9qf+7?W#+_$ll|N&(c+YgYqxZ z3kM;V{UMNl0=p;kWv??}1T>D8?}^Ed>RJEK;Qw#L5@hNbY^xq8?Oz1*DN){l#Zg0u%4)7 z6xOL;XBF3_!tKl|7NhBN8X&YlX3ad2c4OBj3KcB*n);lBZ@BLrGl?!i%dDBBXVD$$ z&?R&&pdhg-PhuBDk@oyUgIx&gYRjY1jpX3FJKJ|mA%4v>5LfSK#n$J{nGb7%#!0afThals2AA&4WlFiY&uSz5VNGwJH`HZH7MO%*pQ zB8ZgUNKO#&B`sVKk~fwTZMB^|z@oHNe?9XB@ipgP>XqRAGKX{mO!1;HDD#X=x7z zQv~)255j)T=xm^P1W%2Q{9QL;!7vUD9YF{NOR;rpZ})1azxgkYLh;sEq>iT&E;eon z*L!}@f>G8zF;Fz5HZsZ;*%=!dwpBQc|MZcLxVtt|rHUfa3CXTkrx{cKk{EoCDM7p& zlyD2FzkqJla4=W-rA&XJvgtO6W@&wbVTgB@E5$qJaAu@#6S6kvI!)|=e1k5ocSKRs zn8mK(F{y9sQWN?7u*AymoOkXSh<0IW#lE~HNOOP`L`scGNUO zhwreZob11cfv2<+^1BLEr{#B9v!}do!c!z4eVzjb5eXR?yllCz=sug(%osCCA5-)@ z^;N&x>cEK>l=q?#((oH#`vsh2(4seFtVjn=0p9t&$StDvbr?0*(c-E#XEb`z5qZ78 z8xbbdbD>pl&(-nfu}6abucuWn1?2&*{lCH>a0h|eU#Hkj65tn(;JU%Ls91LIC9Bl% zu$dib2Fjxd-LB$gnp9T0ru;=3ePZ5djZVILO~GXFrb_PYLBmH=g|5^4OLeL_-q%+k zRla50>saXKrqI&53wp@@mouW(Lo}IbPi@VX;jGDcmYB9@ZPk726VCKt~ zpr%r@@b&iy25<6wa%2-sj&^rCy1nO!*0RuzFvxM|6 zI@;hK8-?6l>(-t!w+z%J$$a$}{mA7i)3oY5`s5_K(#-&__=9-;p!yEWGWwSKedvZ* zo*46uW|L;FL@|A^sHHg0{DBj6G`ut>Wb|FjBBuFi9Wp@svqnz$X7A zYXCZJ8y^T}r-U)4g;GX1jZoPZBY+WFikwMjv%~ETln(>F*8S3j#-&|Rf^+$C?M7DH zpyi-=g#68@2b{rI&xZf1W7W>dLL0js)&7X!{Wgpm3)-W)+?mq+*m;s_+lJ!LdO=~R zUqfPOhdiyXRmhB*6)(uD2I)>*9!FMSj8-{g0-y!O4peCgh*2Cu!uC%OWp_Z`SVnq! z)1=AQ+u67A#p6TkyOXJ`E2Kw!!ALMASZnk)ILa-8QPqw_*T;Hf6^QEiohyi8oD22~ zAO;eM5G%i+;vI_#;_&~V!RCx5#>;OPxn6G0TfCiyuX0XoM1!gO;-fv)kM8gBr(;|Y z{6BCRx4SWn1bYNDgNPN-n?7TvYLs?zdQn*9@sMX$BCtz0Ghf@06|M1ziF+bcUI+!_ z_2d#0V=y;2U*f&l34MHTfo$~qx7gGR8UgYG@r#o`Jx0L8z{DbDxzj=g5`N=9d4b1* z|HndEZNv=lT`7Luhcy5(c;-%G*K9Ybn>0HWBHvlM7@4}fFSDB)TKL916l5pMM%-*a zBNOdB$*zIy5;HA1WRDIu$Fr@&adwrZ2vwBNDqEzC>j6`zFvHr^-TWAYzkFb4w#mq= z#}(b`3D$i|-}1pT+#<-mut>gRTn}70-xjaPHY}$HrT9z{C(m-&#*LnaR2u9@76H>) z%kSSIsGGZz1cVVnhC|4~o+RW#2y)+qcl=KBwH7eW%^$;1nT0n(RpgBjy#X?k2Phd65yi zHtwC?_=xVjJ^MdCU&#scA5;3^{k~*6y1xT2qLp=2H2^g}pD5%I3QC5ymsjyzsP;$j z%7^ZHShrmuT7Mx|`!UN*x+OpmC9ln7aCc(WbD;bwR=<*a0OCh;xGMkK5KvnGFS$>U zM09KBZGfc9ux}E!wy#;BRVulu4&n`+Ev*z2)6~9xUi*J50{VFHuXmbNTW#u_i)8|W z?)c<*UL6@XKzssaVWX&VMZ&p!xbr|Z_4Go!#r=}{hzU%8vD70|;|lFVGY!jdTsoo6 z1uPgB*9?=WGe>dvTy$88^X7iuVR7YVYfr0W-S~}WMoo6XS9e0ij(_(_v2!JoJhmv| zo0j4wFWH!fqie-|%G*t?%N=>c+naJNEs|q(<2H`cX&paeQDi(~4C1;ng2ls5{jNxN zhir?VT#K9uJ$nd1t|q{8UX`h>$B5BM9v0uJ(>ngXFncAdl6p0bIBOA<*K?OB+56JlNr_ zdPUdXv{Wv&tdonBOia+e+dG)gEPN~1+>HCT zA2Q8d%gn9tvB;Q~!24B3nf$Rm?4-=R!Y6b?x32k5XaMY4axEdAIB;(>27H1-z2X20 zCkPq=io(o^ea)&KP0j+LqvTF7agLCPKGH-M!qqyYo>FBjK`TB3+<-KgB2aG&1*Xi05eH1i_{)2;pkfoirJG63kr=2Wn*D*;1r{z+k8uAKtfC!h`H|2T*k;s&Ds(JaA@r(Q=g zh=q*beF#JRo2m_~^KkpvD5XX)980Hblv&P_5yts25^$o2`;E|#RU>Epx#Rf!1~dBX zhS@$Yu{t7+@7&;xfh2&K@Yr@ID+vI+F2Q6T;ws>@P6D(Fw15=OA(1jC5Y|opJ2r$% z;S|A3(iijhV#-Qlrf9P$eXy{MCYxxdgF}O!X%|T^fO-K`D2fYpOOi~6LT?kAfit?e z`#phJO0R~LlZaS-T<%D|Ap9($3e1pHeSrRa;=CK@0n(W5kjQTbKog9^CdCx}9NqNg zEWvYJtO|qp$|2Q~M_P=%f4f zB|mzeVWF)sfLjW!5j-cQM%Mm#whE5R$JmVBt#sIQCU>4&SsSm&f9x4;zKI8j-Fr_! z$~SsQD=uj@`bQse5#W9HZI{lA5hq^CC{EpU(z`e%1^WK2`C!(|%PyngHl5kV4s6I&#$159<%|(OjT7q{sv0RpnLg%u)7D2 z__s9G1d@fLQ*AFeu9ti_znQI|gZVq28lJHZQF!XKD!J2pSH)twSdCu14=R$( z+b!E<^_PJk6Dl@*!|<+Of^yVaC?&~|n)-mxkGqvu+8zT&LZQ!XhhJ%`zKJ@Mer#gM z7YT=ZmgeUEoy-P14)LbH?7R0MY91K!B1!H`&;Dh+GP)Qh$bu)NBqhr zW@VYeRY(fFyU@`xFhJ&ccF+W7cZ<);i#pwD34xEuHCO#gojcDHu#V6em-Pg|6L>Pa zpW7PqFk`inc<9|LbKT01l*8S3JX-z&>kXx zB0rWa(=We#_7Gf{UT+Hjh>za|>8Onk7Rc`|@}8Q8KSyj4hE$*%1d^zOU8i+*z?2i{ zoYhE-o&NQ{Sd099AKC8TzPM+HR=8EIL;s&T=HCb;j)9XYt@TK)^jY-cj&fP>;F}P6s_324_n^{04-Hfa2KPEl)nMv+dT8J~ST4niL{<{>c6(8(X4INwdxs%jnAlRD*lnxAlKDaGbQ8zC5k!=Bo#C z*Wy@Poi6*>z+Nh7lWC^H16xSAi(;R5ghD(&A@^4T$K{?jC|Cwe;S7+00>?Ynn|;8ksAWz`BeK-EQ$IPYYpLK%arfus=(7oTw*PKUJWjoK(7lfy$tKr(Dc0Bt z30X{I&#kU~dR*Pjh`ogMedH0vk|xiw+v}Dj4#063aULyztLlgpVJVg@3E-dY3h*y& zOQphuQkFq`PZ;Q)3#Vbe_$v!7;8cr?i%`fufZ#YCm*n09Sp*J2`7~2MNQ1?}#chg{ z)I(@caro(L=;%krIbQhzO*jU&%SQ3&Q6m%?2Bh3f+}xy~;!F_u(q~t{pTnjS6yR}? zo$Xgok9x%AVX=1`kik;{OAhhYR-pIkzaC6k(QM;u2WoJ2o|yF+)Nz(p)pV1z5#4dw z?SaolR?vFu@(#XDZp3M!KAIB;)AoF7zv0I>ZrH-If<#2R?mj~^Nr8SC)n~uo^S^mB z(gz0h@XLRkd25L~!*#yF3Dd9VzRT=Gz*h-z-k+A3$0 z=k6E15JGu)I-O{&ji6A9Zf!HH}X86`zAG1+ooxJ@`EIcBE-Co!*-Ky_} z3Qy6U++AldjsGQc$w}bXagBr>%F9@}JCmo}9p|c8C1@gT$k!Z6&QAs%5?^j+4%1jUnQ-y>z(D+PC>)$|sCqoE0Ia1#5y^1J> z;xW|J)W90Nu2jqJ>(#BS%U8(R*89ogKFR#-8uE=}C|$>==qZwan^@^x@YmT+_R5Sh zfrW?0)(`Znqw>xk@xSzWml^(g+k7{e-p(HR+%uEZqj$n0;%{VnT_u{goG=j#Vt;H! zPx=3)i;z?~qes>W(AiruKL-+>4U)230NIDN2y$v^F}nM(ulIF_MVyrfoEv!nSzla3 zgBVCrP?(~^{cz6;)Q&)K6Vx0|Pfs5YQkt&7&;IK~fiq(^@z2p`h>QSGp8%LVTIgkz z0&M8ctDpx@Nk=CfSX3;-9oK;sZO~E{^pNcB?WxL&i~HvG8WEoH=0r@DH1~wXs?vlI zBR(oRE=#c~lr8{nrWsI6=7XbXX%5W^+=a4bS8pQvW{NXI#FFu0@w|=trryrv_4M#~ zkz5+sz0%W5952z+UDBe$$sx`6HlhnE@1-` znzMeL<1!(z!gb#uYK9mPhzTJ-`VC%?ouSKGq1NDqv}2zwzx4tTx!xBR7Ty`wwyn_C z(Czj*b#fed-?%myatSPBz8)~BP9E&_s zBEq$!V6Zu91=GdQ{?tykmLY5kX-^Kz#kfu);+YN3v#AX<>cOcU6@sPbLV2?K%gs7! zFXG+_psdo{^$>Oypr2T0cDb|)VgYgfJe>twXgI+ zJW1pA_Tg1MkaN zH{6k@`I!aWk!Inzs}uR}qLQ~nBZGCa?I&UP9xZ9Nc5E$H2nain3>SIoRTPbS;hdAb zcB@d7WtOP_k)?DxSFFHj&Fmc!j0@}wN3!fhIwNEXb6dXqb>k`ffpc7M!Yb!RTFeBy zQ;Qb(P0*8adejS_YAC>}dUh=OOrJftl~ZnJn{&(Ug7=;)^@E?*6TWK|pR7{nMWxOUp%n3@mUFT#!{1$g43Rx|M3(!d(0_>T4IiRDx7WTK`7mXm zPa%%5RxkSH?n3$iuFVLCl_cxU7atV$i z@SaSMwTbvF#|>qm>iafW zS&XPha@e?S<5q^^GZ#hr1by`i_D3mEa0iGzbhEw%oAnFuD{=K-?G?ht#zi?fLx^L( zthbDCi?qfmN(gXuXwsg=VC?%_SjI-LKFBzSgim|Ae^j379cTHT*D=xEOWq>+zQox= z|Ecy{YVgvEx}?Gz%`JLb4Vw^Xoi8&Y29m`XOC9pE!NujH#oG_rkyFl~wQhw2U#pIBkDC&ko zc}!cFeWi34tl`_vL&+LOh_qW+~3lTLbk}lQAGs zLZcvW>%h%cb;O)KZv{%10g1w9y^N_N@{smJ;noVuV^%n{cj`v_l%S9!?)G{V%Lzec zlZ=LEi))d-v?4L>mi8~Q1lT8>Sm}P%_OQK1hjyWboSR+91bne^N6WL*Z>}!0H&+B> zT?ym3PppTXk(cpbLV*Wk7m`bb?Ee1svX`>_&Hq|9(0g7yZ|Qrum11=AQ)bdr;zX2S zWjYM)-ouBOKuRAkreEFiji-^5lrucID2%BbqZ5SA{r&qlZBB0f56#KRiNWHZ7+}}t z(p=u$ybVGiCTlX?l4pK)PIG|j$jlLXAklPndvjDgJ)Vn50Q%MDkMz?fgD9g` z(5RRf9>E0!1P#V=4Xi}HZq@$Snv2@UY99N8cTDs7_*m}+P;=`f)9!q=svdgALrwhm zj^gdx%~$)FCwsq~en|(dB*_NEIOUlY6YF;ZYDQ@-LvYx>-HCtF54PwEW|dJUV1)n) z^^IWg*QV0u{KNaiI;*sl1QQce>9O{V46vWe$Q&S=g8ROH2JH-#w{PFhEi6=HzXoy= zG#DV^K|nHP)-9hmWhQewb1iqb9YF;ga%~Woj9KAXAM;Z>k@rI1=`n05-yo8Uw58Ns zowc!K@@Qt~=5fRn!PdeP-K7j}AnZ7lrq+~Cg8OAaW+5iv)j5kZ!`Ke48$&ZBfF+Hw zVN7p~dI0IUFrAb8gTBLy?b&Y+ty^G4?5KCNioO!-p$YCleSNf9ndxestyNg`6eadn z`;XI~pgd|%x5BBB2UV{TRL7;8=d)zsF+T>68ESVyAyiB#@%9Y6fayG$4aF6gB~F8ctBI^lx#M(uzmb6c_YL^9u-=KhfcnLLd-B4Y!BT z8iAoI0-p*#3)DeEYwPR4RpSdOm>^pQxXI+M86(00Wd5`EhyGDvx`Kw@E1s08gvjPl z5I@R4pBS^S1I8ebBOJ(7?m`7dSWa2%2NJa6>Uz(TiHbfa*4~g*@!f<)!L>Y6o%U@l z*qCmHb}j=iVvhEYTXR!HySEjkR?%h+rP!w@x?}gj(S)y;Fgj6(RpHxlYb+NIJJ|~c zCx&c|!s#p%%7W$#OR@R0GM=S2IGwH)Dg(oZlt7>sY^zp~-S^+Wf0~}-O!tb2MXG`q zfeRnj!dVR(#6hNw-OozskY;W=D!x+`NfeRkpKBsWdkQ= zrpIaXRNHSR?P;=R4EL&U1YGd#{RNzAL?_9CK0jr$$RP3 zJVObzL+y>=8}KC7Nd;18(6s9CXMq1P4+3l3K}@D9 z9R?Z*Qo#b*MJFqnQM0qN(8IgfwDoQQ!4>u!qg;K0vP?0!m- z!pcUwAk)5eE9g8hx|2M~&uk?ACPMAvdFi%}=-S;FFo<%xp}PNO`PrL0iKNVU;ql?+&#v5SqUsHA zKIo|ZI`JeU*={xL*!qc6-=vdw0sAFudJWFyhWOO1<-F>T9-AIl3!HW0SHho7qmRX2 z+ZC5%c~86G!Z@9L#DJ~{*b^@6c+13KM`iXAgfOR|@^?@I3*9Oyy6e&2xK*G0 zJ2gc0^86ye3Zy<69^@*4*KdSCj2?aZ(?`$X3T$M5*CG+pfsm)4i75^T?6yqz<5tJE zxPPvnE%}Sm%p~d17a?ehTz)Q;2TtTG&gfbDH;OLN5Jl;<<}W6CXg$_B(e$us{*exZ zcQs&A!Udm#wnL6sX|L<>4tws(w;Tf+$THkI7i~%!%!$-e@x!8tv0_!c5$p;UjvqDTG98^Ac(3mWaUn& zvQ|=MTH14C9XaL(C~bP|2PP;u3<{I&yd?>wZ=lLHJ>3bEkSHmA%*#xprZdzA?9I@j ze6_w_{$mqsT-8C|*#Np6yP7mw(i^%NB1Nj+yuc;~HfIe6)r>}I`ju*?k)^GFl(`fX z^HUeIRw2+j=Lo478vdCyM=g4>mmJ!NzOKuuZLy8oX;j_itr zw`*SIY`l5FH+lEq@4bx%B_EC`y&xo#Qzwb`qbVbyIV1YdNhM&1IVRC1ZP%OXfkD|O zkmUsm7XyJ5?`~W#!OnZY^lSvGkwAb9!@ao9Zfk|a-~So`z&z@lsP^TrVgHm0<5{3iAvU#1!-!yUP2B<&Z z5>ggy=B##Pys^t<7mC~u1V;?P^@=I1(S@zU(7{{msdlT1?o2QhF#9f9SDr76H3u@j zFE~WpC4OSdoQ$wCftwI+1k!Ot@>B_IW)zj;-kK4D1)@3E@nb#3#)|Kb9mg7`gq_TU zD#_8z+VW~^EpVK37_NtCuMCe-e7EiO|D3t%{SQd7fh|wu-SnuNfKUE(3;>><$D4XBJLTd$F2B63Uenz z62ec5m_>CT)3*MN)|_{tSii2RrSlI65NC?M$H>_9AGAjI93(z~wq$5oc1-MNWSJ;| zHG+5#kd2V91t#IYYaUz#exUtv{#W$<(K6Ghz|Z(FL6@!YyvI`#3t*I5nXD-@1J5#7 z7l*)9=ENfRvR~Y5$RXZmhTVLostQG79Q1Mua2h|hIxBn<-D4Sx#sG$RA(htdnykd6 zhdBYf&sa-6 z{q9jaWnI|LV&8i8Yn?)r0(%A49ja*a?eAgEn=5z(1g&3KUw{1g(V*&d5FGH8FTf)N zT~~b-`PSs|Ce*w5FbqsgMt*+H3hGalC%^+HnXYt?mGu?#Au$X*O?*H?fLZv^;9wef znYFN*xNcNQfM%Xspk=I^Mv9a-)bkp;yy`C+3o7G7A3gM&VNQ~imm~wy z?pY4mpz#-fGI)!-Exr#?kbDNoxV3Nm*W5ev4XOTK+-mg0J9kEo=|;e<-*VppRo7}a zETMGx->);Ew}D)&buGLzHKY}EFO`>bDY48WEB0_e7ofWF6G3-2a=fXR+%-BtCO4!t zzNy6W$#pZXvxS(VLZyPf?9@9rVxlEKB3v`PVqI&z&35{N2T&t>7XRaknRqLCBhd>% z*J1_c{O{&fJ6qW5Y=Fp1pxnIi*`NcT&G$`XWi1lkm5ItD=P_#XC)zF_4{dUi|v=Ks*g6LuD=EH2~GnGGqE9M2FR#T9SqoBVatAc0AqqXDu zm!+fY33`Fvd4WZ;$cXbk^ldUrvQ5lvkSJ*MWa4(XGc5J9qVpnq`) z-mcR;WM7TuO}?A+Spz z?K@h%bRh!*Zq9y*fu>8@xG`R3YF)^H%V_Xe!xK6{AXo4t(LS_ubcVFE4~K5&?Ghd9 zNp$Tdp4bfWA3WVZ%~43B;Qub~J0*l_Zf=Gi1(0a_j`U@nFoFt<3k7s*M4SXk&=>Nn zAYZ<+)*>>7Gu7eNC%j&dXV6Uwnox;*T{~srkX8zJjzY7t@vu(fmqCWnn zbIw}bBfa;tFm1)w*d*39Wv#%qw!Jaa)HzW3*ao1?59yLY^WY_(SEKjSy=m+#yTkqkxIHg~yUgUzf3X{8_ID*${%R5+VbILY%+LKnh)75tTZ&wo{}_QQy%u4V zRIMI-{;nd=(<}^{3%u#xHn4HfjosQ$5KyQkV(06%YYoZyBGHnUqs zcZy;F$up;^qZSnS=wVX#mwro|js4g8B;@4&z|Q~rMxzqrINOMJa(ZeAj6X}fSuJLk z5pb>~VK-8cVAcXv5P+yD^CvYm?1Vaee0-2;L{WGb0|aV?Ks-co-5y>>W+qRi>CLG< z*yfSNllz^|GvaQb;ZBZ-!04DxnJKthT|KUzC(30pYJPaHg(9QWbUT}$GU{CWN4e6r zr7c_NT?r|hVh98UgyvTbW9Qy}<>SXm)=f@(>jRuGOS~OskfPt@LST@O6&NEziKqwx zSuSntqAm)^B24d>s@xkqKbVDP$^%z@Wra+rXAJ``rJddcPV{ww+Bm5Eo8S9N7$m6L z+zdH5(NMS&zf67ZfPAi=VHA^!cH;9GDb^-)-{MiwpQ63d3K7*S*4 z=Db1lU!?o4Y%E!sglPgXuu;>_Jr;#Urd>JuVa09J&&O%<2;13a5#G%CZNrw?O`ALC zGt?V@{`kGf5Hr$aGNdcs`X`@?ss8{^*BENsaaS7FVDix zUh%zL%I7jmX+wf|iD|ToOef*>13BHC^|I7rBLtrIjQ!ls)`|8gCqMt_^sPtf_;K_m zzbn3J`GM4s2)Jy>%6x_dko0NmP&MHn|Jj-F!-M%)mA`XRiQ%1a1Uf|1gmsNEQmVZ_ zJEzf>Fc=UJ$a&W#er4x|x2YW##s(Nrrc>p{=TY<`kY8R# z{{h&1RKVr~%v_ul@)d}*)^T;{5SrBf46+WNBe^w$h)7KK}HDM>j7AXokEJyF1jCNF(>pqypz z=sb>Baa-Q?DI*Qo)tc&)ceVx;<0l?u4gjBSA?D(%2R;p$6!URVI6>REkQA#*!c?ScaGR8k%mY%jFJn>5`^vgAW+q{Rk=_jTq4`9k9_I~jTjo|R&ecA~ zMiTI{WLWp7T`JR(qb__l^#3)%4cY(@vLKE!Kal ztZ!74E3P{cvhSK#-;6r~7#okpX;~V6mkH3D`>9VM_Z0K0m^x?wi+=qww)EDrsFLBb zs3nx-y$RiD#dYFbf!Y6$toM$m`VasAk5Ohal070SA`~Jst5PHxnV}(j96KX>YmlA2 zM`UlA$vpNJ$|ie-zSoQ1-{1H5`TX&|@pjWW$9bLC^Lah5>+!fhZb>}NDz{cJxFj)W z?AJ3$AZ&0LNBbQ)lU+DbR9=m3f&V*uR<{<8dI7##$YTfeTE}&LqA15(Z}+Q^-7?>S z%^8t4kGuYLmW=IV0c?O5nrz0~H2)~Rp0(WUoa$7WDb3}}St@<1)xvTx>AQc&{sGpF(8`U!J0>0_H!&ffrjj1{+f zEAIo54?a-LdVsXrN-S#R!bDo;A|3By!L<8XS`;bN9IQ2QX1vQ|1U+Qo4pioB+K7%J z#@Rf$to;=Q_#>p97_gWo8sXtt{IhD5OAEZ79F za?DqKAlnpCz9l3iAbS#7bnqSU<(Ex9qRN7SX?&$ZQir1E7)&-1<2w?wzEsY2KyB= zLHN#PgY3MsC3%=l5NJHyfz$z$Rl5M&?^(Y$rZOEL_X{8lrHd%B4`)ig8)u7dLOT#f zTlJroUN3c&`_z3G-gPS1hnrGeq>e8nh0X2(KjlZe$8EmQvjcR(8&6MMD>NA_CRh(1 zJlF+;#j1$T+^WuV5-4n_ID3g zP^7ZnX^(USg-z8VQZeN zp5EepG0y#URxbC+vvZ{Ju5nJK!gX063ibl8vjUgrtO6FF3bPd?P&2&tqf0Acw?&t>y>~n&Z`F8pqQXS$$3JW!GOQ;{DF*UbU)pRR;S$mEAzOE&BeWO{AenN9l9;?b%nHxK5fH^X9qd) zT%~m<=q%~&;TVl|m#c~S)n^;YPk1EHp3xs^e5IDbh08I>J=rxtwPh5iUgSsEy^B~D zZ#NA;e7qupC$b+j;msW)H6^Z9G;FcNL(A7~pVBeSUhA-L5|nQu!_NJCV9Z=mL=>0x zXB7W8TXXrwYkp6Jho5x6Q~z?W;Kgt;;+4A7kGc1JBi|_KMQm+EggF^gMfkwX>Ngqn zifxC!@~*PebDzv_@9<0PjI+;ZKpV!Ib3{8Wm9kiGv@{TDLXX^?%0Ut;S~@^Ek6!fsuso=P0KXbcx_gea;kzhjH%{4JW%gbo#Aw!t6ebihd-SHrDt| z)`l{)R{$)P9%C}R1PVnOkK`d-dNaY4=9Z8 z^0Iz3AaGeprw8BM?vM4uZkkLl_>4!JSNlP`Odp!l|FnU}BDn-#se6Qf>+?Y`2i~Aw zqg&uLl1@%dB^u6Nnez`A3+nB?5B^yk6B7gt@QJ)XQwV_jAZa`ebA%$c_-s{3;rG=@|a%BzSr5H3Fp>tW2XHV`iSw+_-Hel5U*RSDvi zbp9kns9_;4LIIgEng|_$Y9;2BzZnrS3)5#%`&G&3jTOIANuJP{G~t$stw-0(piT4Y z;dtqHPGc0BUORkemih11;{(Ux`v|X?%@O3lp{9SEkFOgIym9!IwS1>{A8n)5h+qbm z@>>h%MyH@NT*HlV_LcGgipr8Na1B#_O{~P;GSTaWBPpR-Ym^={7EZmZ7ex}AZ?Ny19rY!PU=L;4hWgXFkCb=Yz#1dg{`VRR;On}!TBTz zG$;??P`YzZy+Z108>Uh8>V%-wg1+&U)_dMICK~$RzZ4Cd<=9fd&2|PFlAuvbY{Z1Y z?fumQFM5O?)P1+lvayBmfL9$C7Z((3NwpGMq32J*&RW-nm+a>%>fRl7T2kB2T#$HW zGAKc`k~i6h${W4YG}(G~ts|Gnabkje1X-WDM$Q1(M&tQxqx%<)8v+w^TwI9(0UwJX*8f8*gX?*i1Fu(S`tX+bHeS2eSi{gv%5ygB+!{?Sk{LEbGqHED6JwgV=5B5#xA z13rm5>4S;(&Ye58K1?o#Cq+9JwYTK+=U(lpT;!hodfz-4P-iZw-3eEq?jEC)a?ES} zv7)m&?f3ml|MrUKJid`FI+Argz3OMC%yr<$Jd>42PcnX_3nKESfy0CARhxj#GF1|@Mq76BTnX0-58GmBB8rp}*KyiLK$1HEe zee>ts6u31f!44?OtzG`iOd0kqe92JM)1F{%ki)AJd5a8UA$#s?ZYIC4V^u$x;hZfb`O5s)R4OO1+C* zHgOc?xQ=D{!Q7k&Sn;g!=C~qo7&?I2qTFq4*LP@m!FA+@?ny}$AnJc#9!x9? zq?;;Q`3Fw~8f;M(HdYeaN#rnSnXcHZ*yTRt29rN^Uk?>9XZ4^kCZ1nukLJ^W6$k{$ zh@u&GS@f30=K*Xm)w1mRMWC|As0_Q|WO)-wn>D8V5dZJ~9>?pkSl%*F6m7p`(8ba*%OojTAj<^|&|?O(lB-M}PHcTQ zt$tkyK2r6qBW!;xgMkuf2+1A6K^!NcO)NE_hd)Qf{v!2{q*9q=vU%c_Q<=JjM z(~;ZG_#Zbroum87D-j~@x~C=X?GtJ_^nf@xs$WF1slO`&t$U(oHC@QFrIW|3oKS{zZ z61J{FSYG6J-JVLjNt1WI;3^m4Hu30>*(fC-~)2q)^=WEm*w

)_EHeVd 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
}GuI{Uw=6U^{tr6q-5Bs}wD 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 DXO9be literal 0 HcmV?d00001 diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle deleted file mode 100644 index 35467713c95f6b08a2551fdc5b610b4c46b0b3ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 85224 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 diff --git a/docs/build/doctrees/index.doctree b/docs/build/doctrees/index.doctree deleted file mode 100644 index 1332c52d4f1c2858d2a98619a198563d814045b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29407 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 DksoRf diff --git a/docs/build/doctrees/modules/crime_mapping.doctree b/docs/build/doctrees/modules/crime_mapping.doctree deleted file mode 100644 index fbee686e13a9e0c027aaabbdedad072b9c5b62e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171030 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 diff --git a/docs/build/doctrees/modules/ml_modelling.doctree b/docs/build/doctrees/modules/ml_modelling.doctree deleted file mode 100644 index ee87006393c497779a7735a9303834fad0ddf47a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 159018 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 QQbIGwECP<@oyqL~2Qf}Gk^lez diff --git a/docs/build/doctrees/modules/pipeline.doctree b/docs/build/doctrees/modules/pipeline.doctree deleted file mode 100644 index 323aa1be8116856b52203690f59341c9d5b7fe24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33124 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 diff --git a/docs/build/doctrees/modules/utilities.doctree b/docs/build/doctrees/modules/utilities.doctree deleted file mode 100644 index ac764ffdd058f0b2c6790953e6fa277caf923217..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36112 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 diff --git a/docs/build/html/.buildinfo b/docs/build/html/.buildinfo deleted file mode 100644 index 5d1f5a3..0000000 --- a/docs/build/html/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file records the configuration used when building these files. When it is not found, a full rebuild will be done. -config: eee754e6bcf202973259ca7c261d9fbe -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/html/.nojekyll b/docs/build/html/.nojekyll deleted file mode 100644 index e69de29..0000000 diff --git a/docs/build/html/_modules/index.html b/docs/build/html/_modules/index.html deleted file mode 100644 index da71c50..0000000 --- a/docs/build/html/_modules/index.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - Overview: module code — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/build/html/_modules/predspot/crime_mapping.html b/docs/build/html/_modules/predspot/crime_mapping.html deleted file mode 100644 index ff724a2..0000000 --- a/docs/build/html/_modules/predspot/crime_mapping.html +++ /dev/null @@ -1,566 +0,0 @@ - - - - - - - predspot.crime_mapping — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- -

Source code for predspot.crime_mapping

-"""
-Crime Mapping Module
-
-This module provides functionality for spatial and temporal crime mapping analysis.
-It includes utilities for creating grid points, hexagonal grids, and implementing
-kernel density estimation for crime hotspot detection.
-"""
-
-__author__ = 'Adelson Araujo'
-from abc import ABC, abstractmethod
-import math
-import numpy as np
-import pandas as pd
-import geopandas as gpd
-from sklearn.base import BaseEstimator, TransformerMixin
-from sklearn.cluster import KMeans
-from shapely.geometry import Point, Polygon, MultiPoint
-from scipy.stats import gaussian_kde
-
-pd.options.mode.chained_assignment = None
-
-
-
-[docs] -def create_gridpoints(bbox, resolution, return_coords=False, debug=False): - """ - Create a grid of points within a given bounding box. - - Args: - bbox (GeoDataFrame): Bounding box as a GeoDataFrame - resolution (float): Grid cell size in kilometers - return_coords (bool): If True, returns additional coordinate arrays - debug (bool): Enable debug printing - - Returns: - GeoDataFrame or tuple: Grid points as GeoDataFrame, optionally with coordinate arrays - """ - if debug: - print(f"Creating grid with resolution: {resolution}km", flush=True) - - assert resolution > 0, \ - "Invalid resolution." - assert isinstance(bbox, gpd.GeoDataFrame), \ - 'bbox must be geopandas GeoDataFrame.' - bounds = bbox.bounds - b_s, b_w = bounds.min().values[1], bounds.min().values[0] - b_n, b_e = bounds.max().values[3], bounds.max().values[2] - nlon = int(np.ceil((b_e-b_w) / (resolution/111.32))) - nlat = int(np.ceil((b_n-b_s) / (resolution/110.57))) - lonv, latv = np.meshgrid(np.linspace(b_w, b_e, nlon), np.linspace(b_s, b_n, nlat)) - gridpoints = pd.DataFrame(np.vstack([lonv.ravel(), latv.ravel()]).T, - columns=['lon', 'lat']) - gridpoints['geometry'] = gridpoints.apply(lambda x: Point([x['lon'], x['lat']]), - axis=1) - gridpoints = gpd.GeoDataFrame(gridpoints) - gridpoints.crs = {'init': 'epsg:4326'} - gridpoints = gridpoints.to_crs(bbox.crs) - grid_ix = gpd.sjoin(gridpoints, bbox, op='intersects').index.unique() - if len(grid_ix) == 0: - raise Exception("resolution too big/coarse. No cells were generated.") - # elif len(grid_ix) / bbox.area.sum() > 10: - # warnings.warn('resolution too fine/small. As consequence, your program' \ - # + 'may run very slowly.') - gridpoints = gridpoints.loc[grid_ix] - gridpoints.index.name = 'places' - if return_coords: - return gridpoints, lonv, latv - return gridpoints
- - - -
-[docs] -def create_hexagon(l, x, y): - """ - Create a hexagonal polygon. - - Args: - l (float): Length of hexagon side - x (float): X-coordinate of center - y (float): Y-coordinate of center - - Returns: - Polygon: Hexagonal polygon - """ - c = [[x + math.cos(math.radians(angle)) * l, y + math.sin(math.radians(angle)) * l] for angle in range(0, 360, 60)] - return Polygon(c)
- - - -
-[docs] -def create_gridhexagonal(bbox, resolution): - assert resolution > 0, \ - "Invalid resolution." - resolution = ((resolution)**2 * (2/(3*(3**0.5)))) ** 0.5 # normalize resolution to have the same area as if it was a square - assert isinstance(bbox, gpd.GeoDataFrame), \ - 'bbox must be geopandas GeoDataFrame.' - bbox_ = list(bbox.bounds.min().values[:2]) + list(bbox.bounds.max().values[-2:]) - x_min = min(bbox_[0], bbox_[2]) - x_max = max(bbox_[0], bbox_[2]) - y_min = min(bbox_[1], bbox_[3]) - y_max = max(bbox_[1], bbox_[3]) - grid = [] - resolution = resolution/110.6 - v_step = math.sqrt(3) * resolution - h_step = 1.5 * resolution - h_skip = math.ceil(x_min / h_step) - 1 - h_start = h_skip * h_step - v_skip = math.ceil(y_min / v_step) - 1 - v_start = v_skip * v_step - h_end = x_max + h_step - v_end = y_max + v_step - if v_start - (v_step / 2.0) < y_min: - v_start_array = [v_start + (v_step / 2.0), v_start] - else: - v_start_array = [v_start - (v_step / 2.0), v_start] - v_start_idx = int(abs(h_skip) % 2) - c_x = h_start - c_y = v_start_array[v_start_idx] - v_start_idx = (v_start_idx + 1) % 2 - while c_x < h_end: - while c_y < v_end: - grid.append(create_hexagon(resolution, c_x, c_y)) - c_y += v_step - c_x += h_step - c_y = v_start_array[v_start_idx] - v_start_idx = (v_start_idx + 1) % 2 - grid = gpd.GeoDataFrame(geometry=grid).reset_index() - grid.crs = {'init': 'epsg:4326'} - grid = grid.rename(columns={'index':'places'}).set_index('places') - if isinstance(bbox, gpd.GeoDataFrame): - grid = gpd.sjoin(grid, bbox, op='intersects')[grid.columns].drop_duplicates() - grid = grid.to_crs(bbox.crs) - grid['lon'] = grid['geometry'].centroid.x - grid['lat'] = grid['geometry'].centroid.y - return grid
- - - -
-[docs] -def create_gridsquares(city_shape, resolution=1): - """It constructs a grid of square cells. - - Parameters - ---------- - city_shape : GeoDataFrame. - Corresponds to the boundary geometry in which the grid will be formed. - - resolution : float, default is 1. - Space between the square cells. - """ - x0 = city_shape.bounds.min().values[0] - xf = city_shape.bounds.max().values[2] - y0 = city_shape.bounds.min().values[1] - yf = city_shape.bounds.max().values[3] - n_y = int((yf-y0)/(resolution/110.57)) - n_x = int((xf-x0)/(resolution/111.32)) - grid = {} - c = 0 - for i in range(n_x): - for j in range(n_y): - grid[c] = {'geometry':Polygon([[x0,y0], - [x0+(resolution/111.32),y0], - [x0+(resolution/111.32),y0+(resolution/110.57)], - [x0,y0+(resolution/110.57)]])} - c += 1 - y0 += resolution/110.57 - y0 = city_shape.bounds.min().values[1] - x0 += resolution/111.32 - grid = pd.DataFrame(grid).transpose() - grid = gpd.GeoDataFrame(grid) - grid.crs = {'init': 'epsg:4326'} - grid = grid.to_crs(city_shape.crs) - grid = gpd.sjoin(grid, city_shape, op='intersects')[grid.columns] - grid['lat'] = grid.centroid.y - grid['lon'] = grid.centroid.x - grid.index.name = 'places' - return grid[~grid.index.duplicated()]
- - - -
-[docs] -class QuadratCount(BaseEstimator, TransformerMixin): - - def __init__(self, tfreq, grid, filter_place_ratio=0.9): - self._tfreq = tfreq - self._grid = grid - self._filter_place_ratio = filter_place_ratio # pct of timestamps with at least one crime - -
-[docs] - def fit(self, x=None, y=None): - return self
- - -
-[docs] - def transform(self, data_points): - stseries = gpd.sjoin(data_points, self._grid).set_index('t')\ - .groupby([pd.Grouper(freq=self._tfreq), 'index_right'])\ - .size().unstack(fill_value=0).stack() - stseries.index.names = ['t', 'places'] - c_places = stseries.groupby(['places']).agg(lambda x: x.eq(0).sum()) - n_timestamps = len(stseries.index.get_level_values('t').unique()) - c_places = c_places.loc[c_places < self._filter_place_ratio * n_timestamps].index - stseries = stseries.loc[pd.IndexSlice[:, c_places]] - self._grid = self._grid.loc[c_places] - return stseries
-
- - - -
-[docs] -class QuadratCount2(BaseEstimator, TransformerMixin): - - def __init__(self, tfreq, grid, filter_place_ratio=0.9): - self._tfreq = tfreq - self._grid = grid - self._filter_place_ratio = filter_place_ratio # pct of timestamps with at least one crime - - -
-[docs] - def transform(self, data_points): - stseries = gpd.sjoin(data_points, self._grid).set_index('t')\ - .groupby([pd.Grouper(freq=self._tfreq), 'index_right'])\ - .size().unstack(fill_value=0).stack() - stseries.index.names = ['t', 'places'] - c_places = stseries.groupby(['places']).agg(lambda x: x.eq(0).sum()) - n_timestamps = len(stseries.index.get_level_values('t').unique()) - c_places = c_places.loc[c_places < self._filter_place_ratio * n_timestamps].index - stseries = stseries.loc[pd.IndexSlice[:, c_places]] - self._grid = self._grid.loc[c_places] - return stseries
-
- - - -
-[docs] -class KGrid: - - def __init__(self, k, tfreq): - self._K = k - self._tfreq = tfreq - -
-[docs] - def fit(self, data_points): - self.km = KMeans(self._K).fit(data_points[['lat','lon']]) - crime_data = data_points.copy(deep=True) - crime_data['K'] = self.km.labels_ - self._grid = gpd.GeoDataFrame( - geometry=crime_data.groupby('K')\ - .apply(lambda x: MultiPoint(list(x['geometry'])).convex_hull)) - self._grid.crs = {'init': 'epsg:4326'} - self._grid = self._grid.to_crs(crime_data.crs) - crimes_per_cell = gpd.sjoin(crime_data, self._grid)\ - .groupby('index_right').size() - self._grid = self._grid.loc[crimes_per_cell > crimes_per_cell.mean()] - return self
- - -
-[docs] - def transform(self, data_points): - stseries = gpd.sjoin(data_points, self._grid).set_index('t')\ - .groupby([pd.Grouper(freq=self._tfreq), 'index_right'])\ - .size().unstack(fill_value=0).stack() - stseries.index.names = ['t', 'places'] - c_places = stseries.groupby(['places']).agg(lambda x: x.eq(0).sum()) - n_timestamps = len(stseries.index.get_level_values('t').unique()) - c_places = c_places.loc[c_places < 0.9 * n_timestamps].index - stseries = stseries.loc[pd.IndexSlice[:, c_places]] - self._grid = self._grid.loc[c_places] - return stseries
-
- - - -
-[docs] -class SpatioTemporalMapping(ABC, TransformerMixin, BaseEstimator): # y - """ - Abstract base class for spatio-temporal crime mapping. - - Args: - tfreq (str): Time frequency ('M' for monthly, 'W' for weekly, 'D' for daily) - grid (GeoDataFrame): Spatial grid for analysis - start_time (str or datetime, optional): Analysis start time - end_time (str or datetime, optional): Analysis end time - """ - - def __init__(self, tfreq, grid, start_time=False, end_time=False, debug=False): - self.debug = debug - if self.debug: - print(f"Initializing SpatioTemporalMapping with frequency: {tfreq}", flush=True) - assert tfreq.upper() in ['M', 'W', 'D'], \ - "Invalid tfreq. Please choose (m)onthly, (w)eekly or (d)aily." - assert all([x in grid.columns for x in ['geometry', 'lon', 'lat']]), \ - "Input grid must have `geometry`, `lon` and `lat` columns." - self._tfreq = tfreq.upper() - self._grid = grid - self._start_time = pd.to_datetime(start_time) if start_time else False - self._end_time = pd.to_datetime(end_time) if end_time else False - -
-[docs] - @abstractmethod - def fit_grid(self, data_points=None): - pass
- - -
-[docs] - def get_time_data_chunks(self, data_points): - chunks = data_points.set_index('t').resample(self._tfreq) - chunks = pd.DataFrame(chunks, - columns=['t', 'crime_chunks']) - chunks = chunks.set_index('t').sort_values('t') - return chunks.apply(lambda x: x[0], axis=1)
- - -
-[docs] - def get_times_no_data(self, chunks): - if not self._start_time: - self._start_time = chunks.index.min() - if not self._end_time: - self._end_time = chunks.index.max() - times_between = pd.date_range(self._start_time, self._end_time, freq=self._tfreq) - no_data = {cell:0 for cell in self._grid.index} - times_no_data = set(times_between) - set(chunks.index) - if len(times_no_data) == 0: - return chunks - times_no_data = pd.DataFrame(index=times_no_data) - times_no_data['crime_density'] = [no_data] * len(times_no_data) - return times_no_data
- - -
-[docs] - def fit(self, x, y=None): - return self
- - -
-[docs] - def transform(self, data_points): - chunks = self.get_time_data_chunks(data_points) - stseries = chunks.apply(self.fit_grid).to_frame('crime_density') - times_no_data = self.get_times_no_data(chunks) - if len(times_no_data) != len(chunks): - stseries = stseries.append(times_no_data) - time_ix = stseries.index - stseries = pd.json_normalize(data=stseries['crime_density']) - stseries.index = time_ix - stseries = stseries.unstack() - stseries.index.names = ['places','t'] - stseries = stseries.swaplevel().sort_index() - return stseries
-
- - - -
-[docs] -class KDE(SpatioTemporalMapping): # y - """ - Kernel Density Estimation for crime hotspot detection. - - Args: - tfreq (str): Time frequency ('M' for monthly, 'W' for weekly, 'D' for daily) - grid (GeoDataFrame): Spatial grid for analysis - start_time (str or datetime, optional): Analysis start time - end_time (str or datetime, optional): Analysis end time - bandwidth (str or float): Bandwidth method ('silverman' or numeric value) - """ - - def __init__(self, tfreq, grid, start_time=False, end_time=False, bandwidth='silverman', debug=False): - super().__init__(tfreq, grid, start_time, end_time) - self.debug = debug - self._bandwidth = bandwidth - self._kernel = None - - if self.debug: - print(f"Initializing KDE with bandwidth: {bandwidth}", flush=True) - -
-[docs] - def fit_grid(self, data_points, as_df=False): - """ - Fit the kernel density estimation to grid points. - - Args: - data_points (GeoDataFrame): Crime incident points - as_df (bool): If True, return results as DataFrame - - Returns: - dict or DataFrame: Density estimates for grid points - """ - if self.debug: - print(f"Fitting KDE grid with {len(data_points)} points", flush=True) - - if len(data_points) < 3: - crime_density = pd.DataFrame([0]*len(self._grid.index), - index=self._grid.index, - columns=['crime_density']) - else: - if isinstance(self._bandwidth, str): - self._kernel = gaussian_kde(np.vstack([data_points.centroid.x, - data_points.centroid.y]), - bw_method='silverman') - self._bandwidth = self._kernel.factor - else: - self._kernel = gaussian_kde(np.vstack([data_points.centroid.x, - data_points.centroid.y]), - bw_method=self._bandwidth) - crime_density = pd.DataFrame(self._kernel(self._grid[['lon', 'lat']].values.T), - index=self._grid.index, columns=['crime_density']) - if as_df: - return crime_density - return crime_density.to_dict()['crime_density']
-
- -
- -
- - -
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/build/html/_modules/predspot/dataset_preparation.html b/docs/build/html/_modules/predspot/dataset_preparation.html deleted file mode 100644 index 1df371f..0000000 --- a/docs/build/html/_modules/predspot/dataset_preparation.html +++ /dev/null @@ -1,305 +0,0 @@ - - - - - - - predspot.dataset_preparation — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- -

Source code for predspot.dataset_preparation

-"""
-Dataset Preparation Module
-
-This module provides functionality for preparing and managing crime datasets along with their
-corresponding study areas. It handles spatial data processing and visualization of crime incidents.
-"""
-
-__author__ = 'Adelson Araujo'
-
-import pandas as pd
-import geopandas as gpd
-import numpy as np
-import warnings
-from shapely.geometry import Point, LineString
-
-
-
-[docs] -class Dataset: - """ - A class to handle crime datasets and their associated study areas. - - Args: - crimes (pandas.DataFrame): DataFrame containing crime data with required columns: - 'tag', 't' (timestamp), 'lon' (longitude), and 'lat' (latitude) - study_area (geopandas.GeoDataFrame): GeoDataFrame defining the study area boundaries - debug (bool, optional): Enable debug printing. Defaults to False. - - Attributes: - crimes (geopandas.GeoDataFrame): Processed crime data with geometry - study_area (geopandas.GeoDataFrame): Study area boundaries - """ - - def __init__(self, crimes, study_area, debug=False):#, poi_data=None): - self.debug = debug - - if self.debug: - print("Initializing Dataset class", flush=True) - - assert isinstance(study_area, gpd.GeoDataFrame), \ - "study_area must be a geopandas GeoDataFrame." - self._study_area = study_area - - assert isinstance(crimes, pd.DataFrame) \ - and all([x in crimes.columns for x in ['tag', 't', 'lon', 'lat']]),\ - "Input crime data must be a pandas Data Frame and " \ - + "have at least `tag`, `t`, `lon` and `lat` as columns." - - if self.debug: - print(f"Processing {len(crimes)} crime incidents", flush=True) - - self._crimes = crimes - self._crimes['geometry'] = self._crimes.apply(lambda x: Point([x['lon'], x['lat']]), - axis=1) - self._crimes = gpd.GeoDataFrame(self._crimes, crs={'init': 'epsg:4326'}) - self._crimes['t'] = self._crimes['t'].apply(pd.to_datetime) - - if self.debug: - print("Dataset initialization complete", flush=True) - - def __repr__(self): - """ - String representation of the Dataset object. - - Returns: - str: A formatted string showing dataset statistics - """ - return 'predspot.Dataset<\n'\ - + f' crimes = GeoDataFrame({self._crimes.shape[0]}),\n' \ - + f' >> {self.crimes["tag"].value_counts().to_dict()}\n' \ - + f' study_area = GeoDataFrame({self._study_area.shape[0]}),\n' \ - + '>' - - @property - def crimes(self): - """ - Get the crime incidents data. - - Returns: - geopandas.GeoDataFrame: The processed crime incidents data - """ - return self._crimes - - @property - def study_area(self): - """ - Get the study area boundaries. - - Returns: - geopandas.GeoDataFrame: The study area boundaries - """ - return self._study_area - - @property - def shape(self): - """ - Get the shapes of the dataset components. - - Returns: - dict: Dictionary containing the shapes of crimes and study_area DataFrames - """ - return {'crimes': self._crimes.shape, - 'study_area': self._study_area.shape} - -
-[docs] - def plot(self, ax=None, crime_samples=1000, **kwargs): - """ - Plot the study area and crime incidents. - - Args: - ax (matplotlib.axes.Axes, optional): Matplotlib axes for plotting - crime_samples (int, optional): Number of crime samples to plot. Defaults to 1000 - **kwargs: Additional keyword arguments for plotting - study_area: kwargs for study area plot - crimes: kwargs for crime incidents plot - - Returns: - matplotlib.axes.Axes: The plot axes - """ - if self.debug: - print(f"Plotting dataset with {crime_samples} sample points", flush=True) - - if ax is None: - ax = self.study_area.plot(color='white', edgecolor='black', - **kwargs.pop('study_area',{})) - else: - self.study_area.plot(color='white', edgecolor='black', ax=ax, - **kwargs.pop('study_area',{})) - if crime_samples > len(self.crimes): - crime_samples = len(self.crimes) - self.crimes.sample(crime_samples).plot(ax=ax, marker='x', - **kwargs.pop('crimes',{})) - return ax
- - -
-[docs] - def train_test_split(self, test_size=0.25): - """ - Split the dataset into training and testing sets. - - Args: - test_size (float): Proportion of the dataset to include in the test split. - Must be between 0 and 1. Defaults to 0.25. - - Returns: - tuple: (train_dataset, test_dataset) - Two Dataset objects containing the splits - - Raises: - AssertionError: If test_size is not between 0 and 1 - """ - if self.debug: - print(f"Splitting dataset with test_size={test_size}", flush=True) - - assert 0 < test_size < 1, \ - 'test_size must be between 0 and 1.' - test_dataset = Dataset(self.crimes.sample(frac=test_size), self.study_area) - train_ix = set(self.crimes.index) - set(test_dataset.crimes.index) - train_dataset = Dataset(self.crimes.loc[train_ix], self.study_area) - - if self.debug: - print(f"Split complete - Train size: {len(train_dataset.crimes)}, " - f"Test size: {len(test_dataset.crimes)}", flush=True) - - return train_dataset, test_dataset
-
- -
- -
- - -
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/build/html/_modules/predspot/feature_engineering.html b/docs/build/html/_modules/predspot/feature_engineering.html deleted file mode 100644 index 900a03a..0000000 --- a/docs/build/html/_modules/predspot/feature_engineering.html +++ /dev/null @@ -1,432 +0,0 @@ - - - - - - - predspot.feature_engineering — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- -

Source code for predspot.feature_engineering

-"""
-Feature Engineering Module
-
-This module provides classes for time series feature engineering and transformation,
-including autoregressive features, differencing, seasonality, and trend decomposition.
-"""
-
-__author__ = 'Adelson Araujo'
-
-from abc import abstractmethod
-from numpy import vstack
-import pandas as pd
-from sklearn.base import BaseEstimator, TransformerMixin
-from statsmodels.tsa.seasonal import STL
-
-
-
-[docs] -class TimeSeriesFeatures(BaseEstimator, TransformerMixin): - """ - Base class for time series feature engineering. - - Args: - lags (int): Number of time lags to use for feature creation - tfreq (str): Time frequency ('D' for daily, 'W' for weekly, 'M' for monthly) - debug (bool, optional): Enable debug printing. Defaults to False - - Raises: - AssertionError: If lags is not a positive integer or tfreq is invalid - """ - - def __init__(self, lags, tfreq, debug=False): - self.debug = debug - if self.debug: - print(f"Initializing TimeSeriesFeatures with lags={lags}, freq={tfreq}", flush=True) - - assert isinstance(lags, int) and lags > 1, \ - '`lags` must be a positive integer.' - self._lags = lags - assert tfreq in ['D', 'W', 'M'], \ - '`tfreq` not allowed, choose between `D`, `W`, `M`.' - self._tfreq = tfreq - - @property - def lags(self): - """int: Number of time lags""" - return self._lags - - @property - def label(self): - """str: Feature label identifier""" - return 'feature' # override this label if implementing a new feature - -
-[docs] - @abstractmethod - def apply_ts_decomposition(self, ts): - """ - Apply time series decomposition. - - Args: - ts (pandas.Series): Input time series - - Returns: - pandas.Series: Transformed time series - """ - pass
- - -
-[docs] - def make_lag_df(self, ts): - """ - Create lagged features dataframe. - - Args: - ts (pandas.Series): Input time series - - Returns: - tuple: (lag_df, aligned_ts) - Lagged features and aligned original series - - Raises: - AssertionError: If series length is less than number of lags - """ - if self.debug: - print(f"Creating lag features for series of length {len(ts)}", flush=True) - - assert len(ts) > self.lags, "`lags` are higher than temporal units." - lag_df = pd.concat([ts.shift(lag) for lag in range(1, self.lags+1)], axis=1) - lag_df = lag_df.iloc[self.lags:] - lag_df.columns = ['{}_{}'.format(self.label, i) for i in range(1, self.lags+1)] - return lag_df, ts.loc[lag_df.index]
- - -
-[docs] - def transform(self, stseries): - """ - Transform the input series into lagged features. - - Args: - stseries (pandas.Series): Input time series with multi-index (time, places) - - Returns: - pandas.DataFrame: Transformed features - """ - if self.debug: - print(f"Transforming series with {len(stseries)} observations", flush=True) - - X = pd.DataFrame() - if self._tfreq=='M': - next_time = pd.tseries.offsets.MonthEnd(1) - elif self._tfreq=='W': - next_time = pd.tseries.offsets.Week(1) - elif self._tfreq=='D': - next_time = pd.tseries.offsets.Day(1) - - places = stseries.index.get_level_values('places').unique() - for place in places: - if self.debug: - print(f"Processing features for place: {place}", flush=True) - - ts = stseries.loc[pd.IndexSlice[:, place]] - ts = self.apply_ts_decomposition(ts) - ts.loc[ts.index[-1] + next_time] = None - f, _ = self.make_lag_df(ts) - f['places'] = place - f = f.set_index('places', append=True) - X = X.append(f) - X = X.sort_index() - return X
-
- - - -
-[docs] -class AR(TimeSeriesFeatures): - """ - Autoregressive features implementation. - """ - - @property - def label(self): - """str: Feature label for autoregressive features""" - return 'ar' - -
-[docs] - def apply_ts_decomposition(self, ts): - """ - Apply autoregressive transformation (identity). - - Args: - ts (pandas.Series): Input time series - - Returns: - pandas.Series: Original time series - """ - return ts
-
- - - -
-[docs] -class Diff(TimeSeriesFeatures): - """ - Difference features implementation. - """ - - @property - def label(self): - """str: Feature label for difference features""" - return 'diff' - -
-[docs] - def apply_ts_decomposition(self, ts): - """ - Apply difference transformation. - - Args: - ts (pandas.Series): Input time series - - Returns: - pandas.Series: Differenced time series - """ - return ts.diff()[1:]
-
- - - -
-[docs] -class Seasonality(TimeSeriesFeatures): - """ - Seasonal decomposition features implementation. - """ - - @property - def label(self): - """str: Feature label for seasonal features""" - return 'seasonal' - -
-[docs] - def apply_ts_decomposition(self, ts): - """ - Extract seasonal component from time series. - - Args: - ts (pandas.Series): Input time series - - Returns: - pandas.Series: Seasonal component - """ - if self.debug: - print(f"Extracting seasonality with period={self._lags}", flush=True) - return STL(ts, period=self._lags).seasonal
-
- - - -
-[docs] -class Trend(TimeSeriesFeatures): - """ - Trend decomposition features implementation. - """ - - @property - def label(self): - """str: Feature label for trend features""" - return 'trend' - -
-[docs] - def apply_ts_decomposition(self, ts): - """ - Extract trend component from time series. - - Args: - ts (pandas.Series): Input time series - - Returns: - pandas.Series: Trend component - """ - if self.debug: - print(f"Extracting trend with period={self._lags}", flush=True) - return STL(ts, period=self._lags).trend
-
- - - -
-[docs] -class FeatureScaling(TransformerMixin, BaseEstimator): - """ - Feature scaling transformer. - - Args: - estimator: Scikit-learn compatible scaling estimator - debug (bool, optional): Enable debug printing. Defaults to False - """ - - def __init__(self, estimator, debug=False): - self.debug = debug - self._estimator = estimator - - if self.debug: - print("Initializing FeatureScaling", flush=True) - -
-[docs] - def transform(self, x): - """ - Transform features using the scaling estimator. - - Args: - x (pandas.DataFrame): Input features - - Returns: - pandas.DataFrame: Scaled features - """ - if self.debug: - print(f"Scaling features of shape {x.shape}", flush=True) - - return pd.DataFrame( - self._estimator.transform(x), - index=x.index, - columns=x.columns - )
-
- -
- -
- - -
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/build/html/_modules/predspot/ml_modelling.html b/docs/build/html/_modules/predspot/ml_modelling.html deleted file mode 100644 index 7a072a5..0000000 --- a/docs/build/html/_modules/predspot/ml_modelling.html +++ /dev/null @@ -1,453 +0,0 @@ - - - - - - - predspot.ml_modelling — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- -

Source code for predspot.ml_modelling

-"""
-Machine Learning Modelling Module
-
-This module provides classes for machine learning model pipelines, feature selection,
-and prediction functionality for crime density forecasting.
-"""
-
-__author__ = 'Adelson Araujo'
-
-import pandas as pd
-import numpy as np
-from sklearn.base import BaseEstimator, TransformerMixin, RegressorMixin
-from sklearn.pipeline import Pipeline
-from sklearn.metrics import r2_score, mean_squared_error
-from sklearn.model_selection import TimeSeriesSplit
-
-idx = pd.IndexSlice
-
-
-
-[docs] -class FeatureSelection(TransformerMixin, BaseEstimator): - """ - Feature selection transformer. - - Args: - estimator: Scikit-learn compatible feature selector - debug (bool, optional): Enable debug printing. Defaults to False - """ - - def __init__(self, estimator, debug=False): - self._estimator = estimator - self.debug = debug - - if self.debug: - print("Initializing FeatureSelection", flush=True) - -
-[docs] - def fit(self, x, y=None): - """ - Fit the feature selector. - - Args: - x (pandas.DataFrame): Input features - y (pandas.Series, optional): Target variable - - Returns: - self: The fitted instance - """ - if self.debug: - print(f"Fitting feature selector with {x.shape[1]} features", flush=True) - - self._estimator.fit(x, y) - return self
- - -
-[docs] - def transform(self, x): - """ - Transform features using the feature selector. - - Args: - x (pandas.DataFrame): Input features - - Returns: - pandas.DataFrame: Selected features - """ - if self.debug: - print(f"Transforming features, selecting {sum(self._estimator.support_)} features", flush=True) - - return pd.DataFrame( - self._estimator.transform(x), - index=x.index, - columns=x.columns[self._estimator.support_] - )
-
- - - -
-[docs] -class Model(RegressorMixin, BaseEstimator): - """ - Model wrapper for crime density prediction. - - Args: - estimator: Scikit-learn compatible regression estimator - debug (bool, optional): Enable debug printing. Defaults to False - """ - - def __init__(self, estimator, debug=False): - self._estimator = estimator - self.debug = debug - - if self.debug: - print("Initializing Model wrapper", flush=True) - -
-[docs] - def fit(self, x, y=None): - """ - Fit the regression model. - - Args: - x (pandas.DataFrame): Input features - y (pandas.Series, optional): Target variable - - Returns: - self: The fitted instance - """ - if self.debug: - print(f"Fitting model with {x.shape[1]} features", flush=True) - - self._estimator.fit(x, y) - return self
- - -
-[docs] - def predict(self, x): - """ - Make predictions using the fitted model. - - Args: - x (pandas.DataFrame): Input features - - Returns: - pandas.DataFrame: Predictions with 'crime_density' column - """ - if self.debug: - print(f"Making predictions for {len(x)} instances", flush=True) - - return pd.DataFrame( - self._estimator.predict(x), - index=x.index, - columns=['crime_density'] - )
-
- - - -
-[docs] -class PredictionPipeline(RegressorMixin, BaseEstimator): - """ - Complete pipeline for crime density prediction. - - Args: - mapping: Spatial mapping transformer - fextraction: Feature extraction transformer - estimator: Scikit-learn compatible pipeline or estimator - debug (bool, optional): Enable debug printing. Defaults to False - """ - - def __init__(self, mapping, fextraction, estimator, debug=False): - self._mapping = mapping - self._fextraction = fextraction - self._estimator = estimator - self._stseries = None - self._dataset = None - self.debug = debug - - if self.debug: - print("Initializing PredictionPipeline", flush=True) - - if mapping._tfreq == 'M': - self._offset = pd.tseries.offsets.MonthEnd(1) - elif mapping._tfreq == 'W': - self._offset = pd.tseries.offsets.Week(1) - elif mapping._tfreq == 'D': - self._offset = pd.tseries.offsets.Day(1) - - @property - def grid(self): - """GeoDataFrame: Spatial grid used for mapping""" - return self._mapping._grid - - @property - def dataset(self): - """Dataset: Current dataset being used""" - return self._dataset - - @property - def stseries(self): - """pandas.Series: Spatio-temporal series""" - return self._stseries - - @property - def feature_importances(self): - """ - Get feature importance scores. - - Returns: - pandas.DataFrame: Feature importance scores - - Raises: - Exception: If model hasn't been fitted or doesn't support feature importances - """ - assert self._X is not None, 'this instance was not fitted yet.' - try: - return pd.DataFrame( - self._estimator.steps[-1][1]._estimator.feature_importances_, - index=self._X.columns[self._estimator.steps[-2][1]._estimator.support_], - columns=['importance'] - ) - except: - raise Exception('estimator used has not feature importances implemented yet.') - -
-[docs] - def evaluate(self, scoring, cv=5): - """ - Evaluate model performance using time series cross-validation. - - Args: - scoring (str): Scoring metric ('r2' or 'mse') - cv (int): Number of cross-validation folds - - Returns: - list: Scores for each fold - - Raises: - Exception: If scoring metric is invalid - """ - if self.debug: - print(f"Evaluating model with {cv}-fold time series CV", flush=True) - - assert self._X is not None, 'this instance was not fitted yet.' - if scoring == 'r2': - scoring = r2_score - elif scoring == 'mse': - scoring = mean_squared_error - else: - raise Exception('invalid scoring. Try "r2" or "mse".') - - timestamps = self._X.index.get_level_values('t').unique()\ - .intersection(self._stseries.index.get_level_values('t').unique()) - assert isinstance(cv, int) and cv < len(timestamps), \ - 'cv must be an integer and not higher than the number of timestamps available.' - - scores = [] - for train_t, test_t in TimeSeriesSplit(cv).split(timestamps): - if self.debug: - print(f"CV fold - train size: {len(train_t)}, test size: {len(test_t)}", flush=True) - - X_train = self._X.loc[idx[timestamps[train_t], :], :].sample(frac=1) - X_test = self._X.loc[idx[timestamps[test_t], :], :] - y_train = self._stseries.loc[X_train.index] - y_test = self._stseries.loc[timestamps[test_t]] - self._estimator.fit(X_train, y_train) - y_pred = self._estimator.predict(X_test) - scores.append(scoring(y_test, y_pred)) - - self.fit(self._dataset) # back to normal - return scores
- - -
-[docs] - def fit(self, dataset, y=None): - """ - Fit the complete prediction pipeline. - - Args: - dataset: Input dataset containing crimes and study area - y: Ignored, present for scikit-learn compatibility - - Returns: - self: The fitted instance - - Raises: - Exception: If fitting fails - """ - if self.debug: - print("Fitting prediction pipeline", flush=True) - - self._dataset = dataset - self._stseries = self._mapping.fit_transform(dataset.crimes) - self._X = self._fextraction.fit_transform(self._stseries) - t0 = self._X.index.get_level_values('t').unique().min() - tf = self._stseries.index.get_level_values('t').unique().max() - X = self._X.loc[t0:tf].sample(frac=1) # shuffle for training - y = self._stseries.loc[X.index] # shuffle for training - - try: - self._estimator.fit(X, y) - except Exception as e: - raise Exception(f'ERROR: {t0}, {tf} \nX: {self._X.loc[t0:tf]}') from e - - self._t_plus_one = self._X.index.get_level_values('t').unique()[-1] - return self
- - -
-[docs] - def predict(self): - """ - Make predictions for the next time step. - - Returns: - pandas.DataFrame: Predictions for next time step - """ - if self.debug: - print(f"Predicting for time step: {self._t_plus_one}", flush=True) - - X = self._X.loc[[self._t_plus_one],:] - y_pred = self._estimator.predict(X) - y_pred = pd.DataFrame(y_pred, index=X.index) - self._stseries = self._stseries.append(y_pred['crime_density']) - self._X = self._fextraction.transform(self._stseries) - self._t_plus_one += self._offset - return y_pred
-
- -
- -
- - -
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/build/html/_modules/predspot/pipeline.html b/docs/build/html/_modules/predspot/pipeline.html deleted file mode 100644 index 2f8bdb5..0000000 --- a/docs/build/html/_modules/predspot/pipeline.html +++ /dev/null @@ -1,330 +0,0 @@ - - - - - - - predspot.pipeline — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- -

Source code for predspot.pipeline

-"""
-Pipeline Module
-
-This module provides the main pipeline functionality for crime prediction,
-including data loading, preprocessing, and model execution.
-
-Example:
-    >>> from predspot.pipeline import generate_testdata, run_prediction_pipeline
-    >>> crime_data, study_area = generate_testdata(10000, '2020-01-01', '2020-12-31')
-    >>> results = run_prediction_pipeline(crime_data, study_area)
-"""
-
-__author__ = 'Adelson Araujo'
-
-import numpy as np
-import pandas as pd
-import geopandas as gpd
-from sklearn.pipeline import Pipeline
-from sklearn.preprocessing import QuantileTransformer
-from sklearn.feature_selection import RFE
-from sklearn.ensemble import RandomForestRegressor
-
-from predspot import dataset_preparation
-from predspot import crime_mapping
-from predspot import feature_engineering
-from predspot import ml_modelling
-from predspot.utilities import PandasFeatureUnion
-
-
-
-[docs] -def generate_testdata(n_points, start_time, end_time, debug=False): - """ - Generate synthetic crime data for testing. - - Args: - n_points (int): Number of crime incidents to generate - start_time (str): Start date in 'YYYY-MM-DD' format - end_time (str): End date in 'YYYY-MM-DD' format - debug (bool, optional): Enable debug printing. Defaults to False - - Returns: - tuple: (crimes_df, study_area_gdf) - Generated crime data and study area - - Example: - >>> crimes, area = generate_testdata(1000, '2020-01-01', '2020-12-31') - """ - if debug: - print(f"Generating {n_points} test data points from {start_time} to {end_time}", flush=True) - - study_area = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres')) - study_area = study_area.loc[study_area['name']=='Brazil'] - - crimes = pd.DataFrame() - crime_types = pd.Series(['burglary', 'assault', 'drugs', 'homicide']) - bounds = study_area.geometry.bounds.values[0] - - if debug: - print("Generating random dates and locations", flush=True) - - def random_dates(start, end, n=10): - """Generate random dates within a range.""" - start, end = pd.to_datetime(start), pd.to_datetime(end) - start_u = start.value//10**9 - end_u = end.value//10**9 - return pd.to_datetime(np.random.randint(start_u, end_u, n), unit='s') - - crimes['tag'] = crime_types.sample(n_points, replace=True, - weights=[1000, 100, 10, 1]) - crimes['t'] = random_dates(start_time, end_time, n_points) - crimes['lat'] = np.random.uniform(bounds[1], bounds[3], n_points) - crimes['lon'] = np.random.uniform(bounds[0], bounds[2], n_points) - crimes.reset_index(drop=True, inplace=True) - - if debug: - print("Test data generation complete", flush=True) - - return crimes, study_area
- - - -
-[docs] -def run_prediction_pipeline(crime_data, study_area, crime_tags=None, time_range=None, - tfreq='M', grid_resolution=250, debug=False): - """ - Run the complete crime prediction pipeline. - - Args: - crime_data (pandas.DataFrame): Crime incident data - study_area (geopandas.GeoDataFrame): Study area boundaries - crime_tags (list, optional): List of crime types to include - time_range (list, optional): Time range as ['HH:MM', 'HH:MM'] - tfreq (str, optional): Time frequency ('M', 'W', 'D'). Defaults to 'M' - grid_resolution (float, optional): Spatial grid resolution in km. Defaults to 250 - debug (bool, optional): Enable debug printing. Defaults to False - - Returns: - tuple: (predictions, pipeline) - Predicted crime densities and fitted pipeline - - Raises: - ValueError: If input data is invalid or missing required columns - """ - if debug: - print("Initializing prediction pipeline", flush=True) - - # Validate input data - required_columns = ['tag', 't', 'lat', 'lon'] - if not all(col in crime_data.columns for col in required_columns): - raise ValueError(f"Crime data must contain columns: {required_columns}") - - # Filter by crime tags if specified - if crime_tags: - if debug: - print(f"Filtering for crime types: {crime_tags}", flush=True) - crime_data = crime_data.loc[crime_data['tag'].isin(crime_tags)] - - # Filter by time range if specified - if time_range: - if debug: - print(f"Filtering for time range: {time_range}", flush=True) - time_ix = pd.DatetimeIndex(crime_data['t']) - crime_data = crime_data.iloc[time_ix.indexer_between_time(time_range[0], time_range[1])] - - if debug: - print("Creating dataset", flush=True) - dataset = dataset_preparation.Dataset(crimes=crime_data, study_area=study_area) - - if debug: - print("Building prediction pipeline", flush=True) - pred_pipeline = ml_modelling.PredictionPipeline( - mapping=crime_mapping.KDE( - tfreq=tfreq, - bandwidth='auto', - grid=crime_mapping.create_gridpoints(study_area, grid_resolution) - ), - fextraction=PandasFeatureUnion([ - ('seasonal', feature_engineering.Seasonality(lags=2)), - ('trend', feature_engineering.Trend(lags=2)), - ('diff', feature_engineering.Diff(lags=2)) - ]), - estimator=Pipeline([ - ('f_scaling', feature_engineering.FeatureScaling( - QuantileTransformer(10, output_distribution='uniform'))), - ('f_selection', ml_modelling.FeatureSelection( - RFE(RandomForestRegressor()))), - ('model', ml_modelling.Model(RandomForestRegressor(n_estimators=50))) - ]) - ) - - if debug: - print("Fitting pipeline", flush=True) - pred_pipeline.fit(dataset) - - if debug: - print("Making predictions", flush=True) - predictions = pred_pipeline.predict() - - if debug: - print("Pipeline execution complete", flush=True) - - return predictions, pred_pipeline
- - - -
-[docs] -def evaluate_pipeline(pipeline, scoring='r2', cv=5, debug=False): - """ - Evaluate the prediction pipeline using cross-validation. - - Args: - pipeline (PredictionPipeline): Fitted prediction pipeline - scoring (str, optional): Scoring metric ('r2' or 'mse'). Defaults to 'r2' - cv (int, optional): Number of cross-validation folds. Defaults to 5 - debug (bool, optional): Enable debug printing. Defaults to False - - Returns: - list: Cross-validation scores - - Example: - >>> scores = evaluate_pipeline(fitted_pipeline, scoring='r2', cv=5) - """ - if debug: - print(f"Evaluating pipeline with {cv}-fold CV using {scoring} metric", flush=True) - - scores = pipeline.evaluate(scoring=scoring, cv=cv) - - if debug: - print(f"Evaluation complete. Mean score: {np.mean(scores):.4f}", flush=True) - - return scores
- -
- -
- - -
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/build/html/_modules/predspot/utilities.html b/docs/build/html/_modules/predspot/utilities.html deleted file mode 100644 index f6245fb..0000000 --- a/docs/build/html/_modules/predspot/utilities.html +++ /dev/null @@ -1,336 +0,0 @@ - - - - - - - predspot.utilities — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- -

Source code for predspot.utilities

-"""
-Utilities Module
-
-This module provides utility functions and classes for data processing and visualization,
-including contour generation, feature union operations, and pandas-specific transformations.
-"""
-
-__author__ = 'Adelson Araujo'
-
-import matplotlib
-matplotlib.use('Agg')
-import matplotlib.pyplot as plt
-import pandas as pd
-from geopandas import GeoDataFrame
-from numpy import zeros, linspace, ceil, meshgrid
-from sklearn.pipeline import FeatureUnion, Pipeline, _fit_transform_one, _transform_one
-from joblib import Parallel, delayed
-from scipy import sparse
-import geojsoncontour
-
-
-
-[docs] -def contour_geojson(y, bbox, resolution, cmin, cmax, debug=False): - """ - Generate GeoJSON contours from spatial data. - - Args: - y (pandas.Series): Values to contour - bbox (GeoDataFrame): Bounding box for the contour - resolution (float): Spatial resolution in kilometers - cmin (float): Minimum contour value - cmax (float): Maximum contour value - debug (bool, optional): Enable debug printing. Defaults to False - - Returns: - dict: GeoJSON representation of the contours - - Raises: - AssertionError: If bbox is not a GeoDataFrame - """ - if debug: - print(f"Generating contours with resolution: {resolution}km", flush=True) - - assert isinstance(bbox, GeoDataFrame) - bounds = bbox.bounds - b_s, b_w = bounds.min().values[1], bounds.min().values[0] - b_n, b_e = bounds.max().values[3], bounds.max().values[2] - - # Calculate grid dimensions - nlon = int(ceil((b_e-b_w) / (resolution/111.32))) - nlat = int(ceil((b_n-b_s) / (resolution/110.57))) - - if debug: - print(f"Grid dimensions: {nlon}x{nlat}", flush=True) - - # Create meshgrid and initialize values - lonv, latv = meshgrid(linspace(b_w, b_e, nlon), linspace(b_s, b_n, nlat)) - Z = zeros(lonv.shape[0]*lonv.shape[1]) - 999 - Z[y.index] = y.values - Z = Z.reshape(lonv.shape) - - # Generate contours - fig, axes = plt.subplots() - contourf = axes.contourf(lonv, latv, Z, - levels=linspace(cmin, cmax, 25), - cmap='Spectral_r') - - if debug: - print("Converting contours to GeoJSON", flush=True) - - geojson = geojsoncontour.contourf_to_geojson(contourf=contourf, fill_opacity=0.5) - plt.close(fig) - return geojson
- - - -
-[docs] -class PandasFeatureUnion(FeatureUnion): - """ - A FeatureUnion transformer that preserves pandas DataFrames. - - This class extends sklearn's FeatureUnion to work with pandas DataFrames, - maintaining index alignment and column names. - - Attributes: - n_jobs (int): Number of parallel jobs - transformer_list (list): List of transformer tuples - transformer_weights (dict): Weights for transformers - debug (bool): Enable debug printing - """ - - def __init__(self, transformer_list, n_jobs=None, transformer_weights=None, debug=False): - super().__init__(transformer_list, n_jobs, transformer_weights) - self.debug = debug - - if self.debug: - print("Initializing PandasFeatureUnion", flush=True) - -
-[docs] - def fit_transform(self, X, y=None, **fit_params): - """ - Fit all transformers and transform the data. - - Args: - X (pandas.DataFrame): Input features - y (array-like, optional): Target values - **fit_params: Additional fitting parameters - - Returns: - pandas.DataFrame: Transformed features - - Raises: - ValueError: If no transformers are provided - """ - if self.debug: - print(f"Fitting and transforming {len(X)} samples", flush=True) - - self._validate_transformers() - result = Parallel(n_jobs=self.n_jobs)( - delayed(_fit_transform_one)( - transformer=trans, - X=X, - y=y, - weight=weight, - **fit_params) - for name, trans, weight in self._iter()) - - if not result: - # All transformers are None - return zeros((X.shape[0], 0)) - - Xs, transformers = zip(*result) - self._update_transformer_list(transformers) - - if self.debug: - print("Merging transformed features", flush=True) - - if any(sparse.issparse(f) for f in Xs): - Xs = sparse.hstack(Xs).tocsr() - else: - Xs = self.merge_dataframes_by_column(Xs) - return Xs
- - -
-[docs] - def merge_dataframes_by_column(self, Xs): - """ - Merge transformed features into a single DataFrame. - - Args: - Xs (list): List of transformed DataFrames - - Returns: - pandas.DataFrame: Merged DataFrame - """ - if self.debug: - print(f"Merging {len(Xs)} DataFrames", flush=True) - - return pd.concat(Xs, axis="columns", copy=False).dropna()
- - -
-[docs] - def transform(self, X): - """ - Transform X separately by each transformer. - - Args: - X (pandas.DataFrame): Input features - - Returns: - pandas.DataFrame: Transformed features - """ - if self.debug: - print(f"Transforming features with {len(self.transformer_list)} transformers", flush=True) - - Xs = Parallel(n_jobs=self.n_jobs)( - delayed(_transform_one)( - transformer=trans, - X=X, - y=None, - weight=weight) - for name, trans, weight in self._iter()) - - if not Xs: - # All transformers are None - return zeros((X.shape[0], 0)) - - if any(sparse.issparse(f) for f in Xs): - Xs = sparse.hstack(Xs).tocsr() - else: - Xs = self.merge_dataframes_by_column(Xs) - return Xs
-
- -
- -
- - -
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/build/html/_sources/index.rst.txt b/docs/build/html/_sources/index.rst.txt deleted file mode 100644 index 3729b47..0000000 --- a/docs/build/html/_sources/index.rst.txt +++ /dev/null @@ -1,177 +0,0 @@ -Predspot -======== - -Overview --------- - -Predspot is an early project for a Python library for spatio-temporal crime prediction and hotspot detection. It combines machine learning techniques with spatial analysis to help predict and visualize crime patterns across time and space. To properly use this library, you preferably need to have a crime dataset and a study area. - -The results of this library are not guaranteed to be good, but it is a good starting point for spatio-temporal crime prediction. This project is barely maintained, so please if you want to collaborate, open an issue or a PR. - -Important Notice ----------------- - -.. warning:: - This project was developed as part of a master's thesis and is currently in an archived state. While the core functionality exists, you may encounter compatibility issues with newer Python package versions. The code can work with some effort, but please note: - - * This is not production-ready software - * Some dependencies are outdated and may require specific versions - * You might need to modify some code to work with newer package versions - * The project was created for research purposes - - However, we believe the methodologies and approaches used here are still valuable! If you're interested in crime hotspot prediction, feel free to: - - * Use this as a reference implementation - * Adapt the code to modern dependencies - * Build upon these concepts for your own projects - * Contribute to modernizing the codebase - - We welcome anyone interested in reviving or learning from this project! - -Key Features ------------- -* Spatial and temporal crime mapping -* Feature engineering for time series data -* Machine learning-based prediction pipeline -* Crime hotspot detection using Kernel Density Estimation -* Visualization tools for crime patterns - -Quick Start ------------ - -Basic usage example: - -.. code-block:: python - - from predspot import Dataset, PredictionPipeline - from predspot.crime_mapping import KDE, create_gridpoints - from predspot.feature_engineering import Seasonality, Trend, Diff - - # Load and prepare data - dataset = Dataset(crimes_df, study_area_gdf) - - # Create prediction pipeline - pipeline = PredictionPipeline( - mapping=KDE(tfreq='M', grid=create_gridpoints(study_area, resolution=250)), - fextraction=PandasFeatureUnion([ - ('seasonal', Seasonality(lags=12)), - ('trend', Trend(lags=12)), - ('diff', Diff(lags=12)) - ]), - estimator=your_favorite_sklearn_model - ) - - # Fit and predict - pipeline.fit(dataset) - predictions = pipeline.predict() - -Modules -------- - -The library consists of four main modules: - -* :doc:`modules/dataset_preparation`: Module for preparing and managing crime datasets and study areas. -* :doc:`modules/crime_mapping`: Module for spatial and temporal crime mapping, including KDE-based hotspot detection. -* :doc:`modules/feature_engineering`: Module for time series feature engineering, including seasonality, trend, and difference features. -* :doc:`modules/ml_modelling`: Module that implements the prediction pipeline and model evaluation. - -And two utilities: - -* :doc:`modules/utilities`: Utility functions for data preparation and visualization. -* :doc:`modules/pipeline`: Functions for the prediction pipeline. - -Installation ------------- - -Create conda env and install requirements: - -.. code-block:: bash - - conda create -n predspot python=3.8 - conda activate predspot - conda install -y rtree geopandas # if doesnt work, do: `conda clean --all` - pip install pandas statsmodels==0.10.2 geojsoncontour stldecompose scikit-learn matplotlib descartes - pip install . - -Required dependencies: - -* pandas -* geopandas -* numpy -* scikit-learn -* scipy -* stldecompose -* matplotlib - -Input Data Format ------------------ - -The crime data should be a pandas DataFrame with the following required columns: - -* ``tag``: Crime type -* ``t``: Timestamp -* ``lon``: Longitude -* ``lat``: Latitude - -The study area should be a GeoDataFrame defining the geographical boundaries of interest. - -Resources ---------- - -For more information on the methods used in Predspot, please search more about these methods: - -* Kernel Density Estimation for crime hotspot detection -* Time series decomposition for feature engineering -* Spatio-temporal crime prediction techniques - -License -------- - -BSD 3-Clause. - -Contributing ------------- - -Contributions are welcome! Please feel free to submit a Pull Request. - -Guidelines for contributing: - -1. Fork the repository -2. Create your feature branch -3. Commit your changes -4. Push to the branch -5. Create a new Pull Request - -Citation --------- - -If you use Predspot in your research, please cite us: - -.. code-block:: text - - Araujo, A., & Cacho, N. (2019). Predspot: Predicting crime hotspots with machine learning. - Master's thesis, UFRN (Universidade Federal do Rio Grande do Norte), Natal, Brazil. - - Araújo, A., Cacho, N., Bezerra, L., Vieira, C., & Borges, J. (2018, June). - Towards a crime hotspot detection framework for patrol planning. - In 2018 IEEE 20th International Conference on High Performance Computing and Communications; - IEEE 16th International Conference on Smart City; - IEEE 4th International Conference on Data Science and Systems (HPCC/SmartCity/DSS) (pp. 1256-1263). IEEE. - -.. toctree:: - :maxdepth: 2 - :caption: Documentation: - - modules/dataset_preparation - modules/crime_mapping - modules/feature_engineering - modules/ml_modelling - modules/utilities - modules/pipeline - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` \ No newline at end of file diff --git a/docs/build/html/_sources/modules/crime_mapping.rst.txt b/docs/build/html/_sources/modules/crime_mapping.rst.txt deleted file mode 100644 index 4feb87a..0000000 --- a/docs/build/html/_sources/modules/crime_mapping.rst.txt +++ /dev/null @@ -1,9 +0,0 @@ -Crime Mapping -============= - -.. automodule:: predspot.crime_mapping - :members: - :undoc-members: - :show-inheritance: - :no-index: - diff --git a/docs/build/html/_sources/modules/dataset_preparation.rst.txt b/docs/build/html/_sources/modules/dataset_preparation.rst.txt deleted file mode 100644 index ef4952c..0000000 --- a/docs/build/html/_sources/modules/dataset_preparation.rst.txt +++ /dev/null @@ -1,8 +0,0 @@ -Dataset Preparation -=================== - -.. automodule:: predspot.dataset_preparation - :members: - :undoc-members: - :show-inheritance: - :no-index: diff --git a/docs/build/html/_sources/modules/feature_engineering.rst.txt b/docs/build/html/_sources/modules/feature_engineering.rst.txt deleted file mode 100644 index 1d4e0c2..0000000 --- a/docs/build/html/_sources/modules/feature_engineering.rst.txt +++ /dev/null @@ -1,9 +0,0 @@ -Feature Engineering -=================== - -.. automodule:: predspot.feature_engineering - :members: - :undoc-members: - :show-inheritance: - :no-index: - diff --git a/docs/build/html/_sources/modules/ml_modelling.rst.txt b/docs/build/html/_sources/modules/ml_modelling.rst.txt deleted file mode 100644 index d272c45..0000000 --- a/docs/build/html/_sources/modules/ml_modelling.rst.txt +++ /dev/null @@ -1,9 +0,0 @@ -ML Modelling -============ - -.. automodule:: predspot.ml_modelling - :members: - :undoc-members: - :show-inheritance: - :no-index: - diff --git a/docs/build/html/_sources/modules/pipeline.rst.txt b/docs/build/html/_sources/modules/pipeline.rst.txt deleted file mode 100644 index 4a43db0..0000000 --- a/docs/build/html/_sources/modules/pipeline.rst.txt +++ /dev/null @@ -1,9 +0,0 @@ -Pipeline -======== - -.. automodule:: predspot.pipeline - :members: - :undoc-members: - :show-inheritance: - :no-index: - diff --git a/docs/build/html/_sources/modules/utilities.rst.txt b/docs/build/html/_sources/modules/utilities.rst.txt deleted file mode 100644 index c99dc1a..0000000 --- a/docs/build/html/_sources/modules/utilities.rst.txt +++ /dev/null @@ -1,9 +0,0 @@ -Utilities -========= - -.. automodule:: predspot.utilities - :members: - :undoc-members: - :show-inheritance: - :no-index: - diff --git a/docs/build/html/_static/alabaster.css b/docs/build/html/_static/alabaster.css deleted file mode 100644 index 96fa422..0000000 --- a/docs/build/html/_static/alabaster.css +++ /dev/null @@ -1,668 +0,0 @@ -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: Georgia, serif; - font-size: 17px; - background-color: #fff; - color: #000; - margin: 0; - padding: 0; -} - - -div.document { - width: 1000px; - margin: 30px auto 0 auto; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 250px; -} - -div.sphinxsidebar { - width: 250px; - font-size: 14px; - line-height: 1.5; -} - -hr { - border: 1px solid #B1B4B6; -} - -div.body { - background-color: #fff; - color: #3E4349; - padding: 0 30px 0 30px; -} - -div.body > .section { - text-align: left; -} - -div.footer { - width: 1000px; - margin: 20px auto 30px auto; - font-size: 14px; - color: #888; - text-align: right; -} - -div.footer a { - color: #888; -} - -p.caption { - font-family: inherit; - font-size: inherit; -} - - -div.relations { - display: none; -} - - -div.sphinxsidebar { - max-height: 100%; - overflow-y: auto; -} - -div.sphinxsidebar a { - color: #444; - text-decoration: none; - border-bottom: 1px dotted #999; -} - -div.sphinxsidebar a:hover { - border-bottom: 1px solid #999; -} - -div.sphinxsidebarwrapper { - padding: 18px 10px; -} - -div.sphinxsidebarwrapper p.logo { - padding: 0; - margin: -10px 0 0 0px; - text-align: center; -} - -div.sphinxsidebarwrapper h1.logo { - margin-top: -10px; - text-align: center; - margin-bottom: 5px; - text-align: left; -} - -div.sphinxsidebarwrapper h1.logo-name { - margin-top: 0px; -} - -div.sphinxsidebarwrapper p.blurb { - margin-top: 0; - font-style: normal; -} - -div.sphinxsidebar h3, -div.sphinxsidebar h4 { - font-family: Georgia, serif; - color: #444; - font-size: 24px; - font-weight: normal; - margin: 0 0 5px 0; - padding: 0; -} - -div.sphinxsidebar h4 { - font-size: 20px; -} - -div.sphinxsidebar h3 a { - color: #444; -} - -div.sphinxsidebar p.logo a, -div.sphinxsidebar h3 a, -div.sphinxsidebar p.logo a:hover, -div.sphinxsidebar h3 a:hover { - border: none; -} - -div.sphinxsidebar p { - color: #555; - margin: 10px 0; -} - -div.sphinxsidebar ul { - margin: 10px 0; - padding: 0; - color: #000; -} - -div.sphinxsidebar ul li.toctree-l1 > a { - font-size: 120%; -} - -div.sphinxsidebar ul li.toctree-l2 > a { - font-size: 110%; -} - -div.sphinxsidebar input { - border: 1px solid #CCC; - font-family: Georgia, serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox { - margin: 1em 0; -} - -div.sphinxsidebar .search > div { - display: table-cell; -} - -div.sphinxsidebar hr { - border: none; - height: 1px; - color: #AAA; - background: #AAA; - - text-align: left; - margin-left: 0; - width: 50%; -} - -div.sphinxsidebar .badge { - border-bottom: none; -} - -div.sphinxsidebar .badge:hover { - border-bottom: none; -} - -/* To address an issue with donation coming after search */ -div.sphinxsidebar h3.donation { - margin-top: 10px; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #004B6B; - text-decoration: underline; -} - -a:hover { - color: #6D4100; - text-decoration: underline; -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: Georgia, serif; - font-weight: normal; - margin: 30px 0px 10px 0px; - padding: 0; -} - -div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } -div.body h2 { font-size: 180%; } -div.body h3 { font-size: 150%; } -div.body h4 { font-size: 130%; } -div.body h5 { font-size: 100%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: #DDD; - padding: 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - color: #444; - background: #EAEAEA; -} - -div.body p, div.body dd, div.body li { - line-height: 1.4em; -} - -div.admonition { - margin: 20px 0px; - padding: 10px 30px; - background-color: #EEE; - border: 1px solid #CCC; -} - -div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { - background-color: #FBFBFB; - border-bottom: 1px solid #fafafa; -} - -div.admonition p.admonition-title { - font-family: Georgia, serif; - font-weight: normal; - font-size: 24px; - margin: 0 0 10px 0; - padding: 0; - line-height: 1; -} - -div.admonition p.last { - margin-bottom: 0; -} - -dt:target, .highlight { - background: #FAF3E8; -} - -div.warning { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.danger { - background-color: #FCC; - border: 1px solid #FAA; - -moz-box-shadow: 2px 2px 4px #D52C2C; - -webkit-box-shadow: 2px 2px 4px #D52C2C; - box-shadow: 2px 2px 4px #D52C2C; -} - -div.error { - background-color: #FCC; - border: 1px solid #FAA; - -moz-box-shadow: 2px 2px 4px #D52C2C; - -webkit-box-shadow: 2px 2px 4px #D52C2C; - box-shadow: 2px 2px 4px #D52C2C; -} - -div.caution { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.attention { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.important { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.note { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.tip { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.hint { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.seealso { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.topic { - background-color: #EEE; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre, tt, code { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.9em; -} - -.hll { - background-color: #FFC; - margin: 0 -12px; - padding: 0 12px; - display: block; -} - -img.screenshot { -} - -tt.descname, tt.descclassname, code.descname, code.descclassname { - font-size: 0.95em; -} - -tt.descname, code.descname { - padding-right: 0.08em; -} - -img.screenshot { - -moz-box-shadow: 2px 2px 4px #EEE; - -webkit-box-shadow: 2px 2px 4px #EEE; - box-shadow: 2px 2px 4px #EEE; -} - -table.docutils { - border: 1px solid #888; - -moz-box-shadow: 2px 2px 4px #EEE; - -webkit-box-shadow: 2px 2px 4px #EEE; - box-shadow: 2px 2px 4px #EEE; -} - -table.docutils td, table.docutils th { - border: 1px solid #888; - padding: 0.25em 0.7em; -} - -table.field-list, table.footnote { - border: none; - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - -table.footnote { - margin: 15px 0; - width: 100%; - border: 1px solid #EEE; - background: #FDFDFD; - font-size: 0.9em; -} - -table.footnote + table.footnote { - margin-top: -15px; - border-top: none; -} - -table.field-list th { - padding: 0 0.8em 0 0; -} - -table.field-list td { - padding: 0; -} - -table.field-list p { - margin-bottom: 0.8em; -} - -/* Cloned from - * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 - */ -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -table.footnote td.label { - width: .1px; - padding: 0.3em 0 0.3em 0.5em; -} - -table.footnote td { - padding: 0.3em 0.5em; -} - -dl { - margin-left: 0; - margin-right: 0; - margin-top: 0; - padding: 0; -} - -dl dd { - margin-left: 30px; -} - -blockquote { - margin: 0 0 0 30px; - padding: 0; -} - -ul, ol { - /* Matches the 30px from the narrow-screen "li > ul" selector below */ - margin: 10px 0 10px 30px; - padding: 0; -} - -pre { - background: unset; - padding: 7px 30px; - margin: 15px 0px; - line-height: 1.3em; -} - -div.viewcode-block:target { - background: #ffd; -} - -dl pre, blockquote pre, li pre { - margin-left: 0; - padding-left: 30px; -} - -tt, code { - background-color: #ecf0f3; - color: #222; - /* padding: 1px 2px; */ -} - -tt.xref, code.xref, a tt { - background-color: #FBFBFB; - border-bottom: 1px solid #fff; -} - -a.reference { - text-decoration: none; - border-bottom: 1px dotted #004B6B; -} - -a.reference:hover { - border-bottom: 1px solid #6D4100; -} - -/* Don't put an underline on images */ -a.image-reference, a.image-reference:hover { - border-bottom: none; -} - -a.footnote-reference { - text-decoration: none; - font-size: 0.7em; - vertical-align: top; - border-bottom: 1px dotted #004B6B; -} - -a.footnote-reference:hover { - border-bottom: 1px solid #6D4100; -} - -a:hover tt, a:hover code { - background: #EEE; -} -div.sphinxsidebar { - position: fixed; - margin-left: 0; -} - -@media screen and (max-width: 1000px) { - - body { - margin: 0; - padding: 20px 30px; - } - - div.documentwrapper { - float: none; - background: #fff; - margin-left: 0; - margin-top: 0; - margin-right: 0; - margin-bottom: 0; - } - - div.sphinxsidebar { - display: block; - float: none; - width: unset; - margin: -20px -30px 20px -30px; - position: static; - padding: 10px 20px; - background: #333; - color: #FFF; - } - - div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, - div.sphinxsidebar h3 a { - color: #fff; - } - - div.sphinxsidebar a { - color: #AAA; - } - - div.sphinxsidebar p.logo { - display: none; - } - - div.document { - width: 100%; - margin: 0; - } - - div.footer { - display: none; - } - - div.bodywrapper { - margin: 0; - } - - div.body { - min-height: 0; - min-width: auto; /* fixes width on small screens, breaks .hll */ - padding: 0; - } - - .hll { - /* "fixes" the breakage */ - width: max-content; - } - - .rtd_doc_footer { - display: none; - } - - .document { - width: auto; - } - - .footer { - width: auto; - } - - .github { - display: none; - } - - ul { - margin-left: 0; - } - - li > ul { - /* Matches the 30px from the "ul, ol" selector above */ - margin-left: 30px; - } -} - - -/* misc. */ - -.revsys-inline { - display: none!important; -} - -/* Hide ugly table cell borders in ..bibliography:: directive output */ -table.docutils.citation, table.docutils.citation td, table.docutils.citation th { - border: none; - /* Below needed in some edge cases; if not applied, bottom shadows appear */ - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - - -/* relbar */ - -.related { - line-height: 30px; - width: 100%; - font-size: 0.9rem; -} - -.related.top { - border-bottom: 1px solid #EEE; - margin-bottom: 20px; -} - -.related.bottom { - border-top: 1px solid #EEE; -} - -.related ul { - padding: 0; - margin: 0; - list-style: none; -} - -.related li { - display: inline; -} - -nav#rellinks { - float: right; -} - -nav#rellinks li+li:before { - content: "|"; -} - -nav#breadcrumbs li+li:before { - content: "\00BB"; -} - -/* Hide certain items when printing */ -@media print { - div.related { - display: none; - } -} - -img.github { - position: absolute; - top: 0; - border: 0; - right: 0; -} \ No newline at end of file diff --git a/docs/build/html/_static/basic.css b/docs/build/html/_static/basic.css deleted file mode 100644 index d9846da..0000000 --- a/docs/build/html/_static/basic.css +++ /dev/null @@ -1,914 +0,0 @@ -/* - * Sphinx stylesheet -- basic theme. - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -div.section::after { - display: block; - content: ''; - clear: left; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; - word-wrap: break-word; - overflow-wrap : break-word; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox form.search { - overflow: hidden; -} - -div.sphinxsidebar #searchbox input[type="text"] { - float: left; - width: 80%; - padding: 0.25em; - box-sizing: border-box; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - float: left; - width: 20%; - border-left: none; - padding: 0.25em; - box-sizing: border-box; -} - - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin-top: 10px; -} - -ul.search li { - padding: 5px 0; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li p.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; - margin-left: auto; - margin-right: auto; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable ul { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; -} - -table.indextable > tbody > tr > td > ul { - padding-left: 0em; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- domain module index --------------------------------------------------- */ - -table.modindextable td { - padding: 2px; - border-collapse: collapse; -} - -/* -- general body styles --------------------------------------------------- */ - -div.body { - min-width: inherit; - max-width: 800px; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - -moz-hyphens: auto; - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -a.headerlink { - visibility: hidden; -} - -a:visited { - color: #551A8B; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink, -caption:hover > a.headerlink, -p.caption:hover > a.headerlink, -div.code-block-caption:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, figure.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, figure.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, figure.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -img.align-default, figure.align-default, .figure.align-default { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-default { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar, -aside.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px; - background-color: #ffe; - width: 40%; - float: right; - clear: right; - overflow-x: auto; -} - -p.sidebar-title { - font-weight: bold; -} - -nav.contents, -aside.topic, -div.admonition, div.topic, blockquote { - clear: left; -} - -/* -- topics ---------------------------------------------------------------- */ - -nav.contents, -aside.topic, -div.topic { - border: 1px solid #ccc; - padding: 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- content of sidebars/topics/admonitions -------------------------------- */ - -div.sidebar > :last-child, -aside.sidebar > :last-child, -nav.contents > :last-child, -aside.topic > :last-child, -div.topic > :last-child, -div.admonition > :last-child { - margin-bottom: 0; -} - -div.sidebar::after, -aside.sidebar::after, -nav.contents::after, -aside.topic::after, -div.topic::after, -div.admonition::after, -blockquote::after { - display: block; - content: ''; - clear: both; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - margin-top: 10px; - margin-bottom: 10px; - border: 0; - border-collapse: collapse; -} - -table.align-center { - margin-left: auto; - margin-right: auto; -} - -table.align-default { - margin-left: auto; - margin-right: auto; -} - -table caption span.caption-number { - font-style: italic; -} - -table caption span.caption-text { -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -th > :first-child, -td > :first-child { - margin-top: 0px; -} - -th > :last-child, -td > :last-child { - margin-bottom: 0px; -} - -/* -- figures --------------------------------------------------------------- */ - -div.figure, figure { - margin: 0.5em; - padding: 0.5em; -} - -div.figure p.caption, figcaption { - padding: 0.3em; -} - -div.figure p.caption span.caption-number, -figcaption span.caption-number { - font-style: italic; -} - -div.figure p.caption span.caption-text, -figcaption span.caption-text { -} - -/* -- field list styles ----------------------------------------------------- */ - -table.field-list td, table.field-list th { - border: 0 !important; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -/* -- hlist styles ---------------------------------------------------------- */ - -table.hlist { - margin: 1em 0; -} - -table.hlist td { - vertical-align: top; -} - -/* -- object description styles --------------------------------------------- */ - -.sig { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; -} - -.sig-name, code.descname { - background-color: transparent; - font-weight: bold; -} - -.sig-name { - font-size: 1.1em; -} - -code.descname { - font-size: 1.2em; -} - -.sig-prename, code.descclassname { - background-color: transparent; -} - -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - -.sig-param.n { - font-style: italic; -} - -/* C++ specific styling */ - -.sig-inline.c-texpr, -.sig-inline.cpp-texpr { - font-family: unset; -} - -.sig.c .k, .sig.c .kt, -.sig.cpp .k, .sig.cpp .kt { - color: #0033B3; -} - -.sig.c .m, -.sig.cpp .m { - color: #1750EB; -} - -.sig.c .s, .sig.c .sc, -.sig.cpp .s, .sig.cpp .sc { - color: #067D17; -} - - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -:not(li) > ol > li:first-child > :first-child, -:not(li) > ul > li:first-child > :first-child { - margin-top: 0px; -} - -:not(li) > ol > li:last-child > :last-child, -:not(li) > ul > li:last-child > :last-child { - margin-bottom: 0px; -} - -ol.simple ol p, -ol.simple ul p, -ul.simple ol p, -ul.simple ul p { - margin-top: 0; -} - -ol.simple > li:not(:first-child) > p, -ul.simple > li:not(:first-child) > p { - margin-top: 0; -} - -ol.simple p, -ul.simple p { - margin-bottom: 0; -} - -aside.footnote > span, -div.citation > span { - float: left; -} -aside.footnote > span:last-of-type, -div.citation > span:last-of-type { - padding-right: 0.5em; -} -aside.footnote > p { - margin-left: 2em; -} -div.citation > p { - margin-left: 4em; -} -aside.footnote > p:last-of-type, -div.citation > p:last-of-type { - margin-bottom: 0em; -} -aside.footnote > p:last-of-type:after, -div.citation > p:last-of-type:after { - content: ""; - clear: both; -} - -dl.field-list { - display: grid; - grid-template-columns: fit-content(30%) auto; -} - -dl.field-list > dt { - font-weight: bold; - word-break: break-word; - padding-left: 0.5em; - padding-right: 5px; -} - -dl.field-list > dd { - padding-left: 0.5em; - margin-top: 0em; - margin-left: 0em; - margin-bottom: 0em; -} - -dl { - margin-bottom: 15px; -} - -dd > :first-child { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -.sig dd { - margin-top: 0px; - margin-bottom: 0px; -} - -.sig dl { - margin-top: 0px; - margin-bottom: 0px; -} - -dl > dd:last-child, -dl > dd:last-child > :last-child { - margin-bottom: 0; -} - -dt:target, span.highlighted { - background-color: #fbe54e; -} - -rect.highlighted { - fill: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -.classifier:before { - font-style: normal; - margin: 0 0.5em; - content: ":"; - display: inline-block; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -.translated { - background-color: rgba(207, 255, 207, 0.2) -} - -.untranslated { - background-color: rgba(255, 207, 207, 0.2) -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -pre, div[class*="highlight-"] { - clear: both; -} - -span.pre { - -moz-hyphens: none; - -ms-hyphens: none; - -webkit-hyphens: none; - hyphens: none; - white-space: nowrap; -} - -div[class*="highlight-"] { - margin: 1em 0; -} - -td.linenos pre { - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - display: block; -} - -table.highlighttable tbody { - display: block; -} - -table.highlighttable tr { - display: flex; -} - -table.highlighttable td { - margin: 0; - padding: 0; -} - -table.highlighttable td.linenos { - padding-right: 0.5em; -} - -table.highlighttable td.code { - flex: 1; - overflow: hidden; -} - -.highlight .hll { - display: block; -} - -div.highlight pre, -table.highlighttable pre { - margin: 0; -} - -div.code-block-caption + div { - margin-top: 0; -} - -div.code-block-caption { - margin-top: 1em; - padding: 2px 5px; - font-size: small; -} - -div.code-block-caption code { - background-color: transparent; -} - -table.highlighttable td.linenos, -span.linenos, -div.highlight span.gp { /* gp: Generic.Prompt */ - user-select: none; - -webkit-user-select: text; /* Safari fallback only */ - -webkit-user-select: none; /* Chrome/Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+ */ -} - -div.code-block-caption span.caption-number { - padding: 0.1em 0.3em; - font-style: italic; -} - -div.code-block-caption span.caption-text { -} - -div.literal-block-wrapper { - margin: 1em 0; -} - -code.xref, a code { - background-color: transparent; - font-weight: bold; -} - -h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -span.eqno a.headerlink { - position: absolute; - z-index: 1; -} - -div.math:hover a.headerlink { - visibility: visible; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/docs/build/html/_static/custom.css b/docs/build/html/_static/custom.css deleted file mode 100644 index 2a924f1..0000000 --- a/docs/build/html/_static/custom.css +++ /dev/null @@ -1 +0,0 @@ -/* This file intentionally left blank. */ diff --git a/docs/build/html/_static/doctools.js b/docs/build/html/_static/doctools.js deleted file mode 100644 index 0398ebb..0000000 --- a/docs/build/html/_static/doctools.js +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Base JavaScript utilities for all Sphinx HTML documentation. - */ -"use strict"; - -const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ - "TEXTAREA", - "INPUT", - "SELECT", - "BUTTON", -]); - -const _ready = (callback) => { - if (document.readyState !== "loading") { - callback(); - } else { - document.addEventListener("DOMContentLoaded", callback); - } -}; - -/** - * Small JavaScript module for the documentation. - */ -const Documentation = { - init: () => { - Documentation.initDomainIndexTable(); - Documentation.initOnKeyListeners(); - }, - - /** - * i18n support - */ - TRANSLATIONS: {}, - PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), - LOCALE: "unknown", - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext: (string) => { - const translated = Documentation.TRANSLATIONS[string]; - switch (typeof translated) { - case "undefined": - return string; // no translation - case "string": - return translated; // translation exists - default: - return translated[0]; // (singular, plural) translation tuple exists - } - }, - - ngettext: (singular, plural, n) => { - const translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated !== "undefined") - return translated[Documentation.PLURAL_EXPR(n)]; - return n === 1 ? singular : plural; - }, - - addTranslations: (catalog) => { - Object.assign(Documentation.TRANSLATIONS, catalog.messages); - Documentation.PLURAL_EXPR = new Function( - "n", - `return (${catalog.plural_expr})` - ); - Documentation.LOCALE = catalog.locale; - }, - - /** - * helper function to focus on search bar - */ - focusSearchBar: () => { - document.querySelectorAll("input[name=q]")[0]?.focus(); - }, - - /** - * Initialise the domain index toggle buttons - */ - initDomainIndexTable: () => { - const toggler = (el) => { - const idNumber = el.id.substr(7); - const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); - if (el.src.substr(-9) === "minus.png") { - el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; - toggledRows.forEach((el) => (el.style.display = "none")); - } else { - el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; - toggledRows.forEach((el) => (el.style.display = "")); - } - }; - - const togglerElements = document.querySelectorAll("img.toggler"); - togglerElements.forEach((el) => - el.addEventListener("click", (event) => toggler(event.currentTarget)) - ); - togglerElements.forEach((el) => (el.style.display = "")); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); - }, - - initOnKeyListeners: () => { - // only install a listener if it is really needed - if ( - !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && - !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS - ) - return; - - document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; - // bail with special keys - if (event.altKey || event.ctrlKey || event.metaKey) return; - - if (!event.shiftKey) { - switch (event.key) { - case "ArrowLeft": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const prevLink = document.querySelector('link[rel="prev"]'); - if (prevLink && prevLink.href) { - window.location.href = prevLink.href; - event.preventDefault(); - } - break; - case "ArrowRight": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const nextLink = document.querySelector('link[rel="next"]'); - if (nextLink && nextLink.href) { - window.location.href = nextLink.href; - event.preventDefault(); - } - break; - } - } - - // some keyboard layouts may need Shift to get / - switch (event.key) { - case "/": - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; - Documentation.focusSearchBar(); - event.preventDefault(); - } - }); - }, -}; - -// quick alias for translations -const _ = Documentation.gettext; - -_ready(Documentation.init); diff --git a/docs/build/html/_static/documentation_options.js b/docs/build/html/_static/documentation_options.js deleted file mode 100644 index 529239f..0000000 --- a/docs/build/html/_static/documentation_options.js +++ /dev/null @@ -1,13 +0,0 @@ -const DOCUMENTATION_OPTIONS = { - VERSION: '1.0', - LANGUAGE: 'en', - COLLAPSE_INDEX: false, - BUILDER: 'html', - FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false, - SHOW_SEARCH_SUMMARY: true, - ENABLE_SEARCH_SHORTCUTS: true, -}; \ No newline at end of file diff --git a/docs/build/html/_static/file.png b/docs/build/html/_static/file.png deleted file mode 100644 index a858a410e4faa62ce324d814e4b816fff83a6fb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( diff --git a/docs/build/html/_static/github-banner.svg b/docs/build/html/_static/github-banner.svg deleted file mode 100644 index c47d9dc..0000000 --- a/docs/build/html/_static/github-banner.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/docs/build/html/_static/language_data.js b/docs/build/html/_static/language_data.js deleted file mode 100644 index c7fe6c6..0000000 --- a/docs/build/html/_static/language_data.js +++ /dev/null @@ -1,192 +0,0 @@ -/* - * This script contains the language-specific data used by searchtools.js, - * namely the list of stopwords, stemmer, scorer and splitter. - */ - -var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; - - -/* Non-minified version is copied as a separate JS file, if available */ - -/** - * Porter Stemmer - */ -var Stemmer = function() { - - var step2list = { - ational: 'ate', - tional: 'tion', - enci: 'ence', - anci: 'ance', - izer: 'ize', - bli: 'ble', - alli: 'al', - entli: 'ent', - eli: 'e', - ousli: 'ous', - ization: 'ize', - ation: 'ate', - ator: 'ate', - alism: 'al', - iveness: 'ive', - fulness: 'ful', - ousness: 'ous', - aliti: 'al', - iviti: 'ive', - biliti: 'ble', - logi: 'log' - }; - - var step3list = { - icate: 'ic', - ative: '', - alize: 'al', - iciti: 'ic', - ical: 'ic', - ful: '', - ness: '' - }; - - var c = "[^aeiou]"; // consonant - var v = "[aeiouy]"; // vowel - var C = c + "[^aeiouy]*"; // consonant sequence - var V = v + "[aeiou]*"; // vowel sequence - - var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 - var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 - var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 - var s_v = "^(" + C + ")?" + v; // vowel in stem - - this.stemWord = function (w) { - var stem; - var suffix; - var firstch; - var origword = w; - - if (w.length < 3) - return w; - - var re; - var re2; - var re3; - var re4; - - firstch = w.substr(0,1); - if (firstch == "y") - w = firstch.toUpperCase() + w.substr(1); - - // Step 1a - re = /^(.+?)(ss|i)es$/; - re2 = /^(.+?)([^s])s$/; - - if (re.test(w)) - w = w.replace(re,"$1$2"); - else if (re2.test(w)) - w = w.replace(re2,"$1$2"); - - // Step 1b - re = /^(.+?)eed$/; - re2 = /^(.+?)(ed|ing)$/; - if (re.test(w)) { - var fp = re.exec(w); - re = new RegExp(mgr0); - if (re.test(fp[1])) { - re = /.$/; - w = w.replace(re,""); - } - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1]; - re2 = new RegExp(s_v); - if (re2.test(stem)) { - w = stem; - re2 = /(at|bl|iz)$/; - re3 = new RegExp("([^aeiouylsz])\\1$"); - re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re2.test(w)) - w = w + "e"; - else if (re3.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - else if (re4.test(w)) - w = w + "e"; - } - } - - // Step 1c - re = /^(.+?)y$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(s_v); - if (re.test(stem)) - w = stem + "i"; - } - - // Step 2 - re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step2list[suffix]; - } - - // Step 3 - re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step3list[suffix]; - } - - // Step 4 - re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; - re2 = /^(.+?)(s|t)(ion)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - if (re.test(stem)) - w = stem; - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1] + fp[2]; - re2 = new RegExp(mgr1); - if (re2.test(stem)) - w = stem; - } - - // Step 5 - re = /^(.+?)e$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - re2 = new RegExp(meq1); - re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) - w = stem; - } - re = /ll$/; - re2 = new RegExp(mgr1); - if (re.test(w) && re2.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - - // and turn initial Y back to y - if (firstch == "y") - w = firstch.toLowerCase() + w.substr(1); - return w; - } -} - diff --git a/docs/build/html/_static/minus.png b/docs/build/html/_static/minus.png deleted file mode 100644 index d96755fdaf8bb2214971e0db9c1fd3077d7c419d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu=nj kDsEF_5m^0CR;1wuP-*O&G^0G}KYk!hp00i_>zopr08q^qX#fBK diff --git a/docs/build/html/_static/plus.png b/docs/build/html/_static/plus.png deleted file mode 100644 index 7107cec93a979b9a5f64843235a16651d563ce2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu>-2 m3q%Vub%g%s<8sJhVPMczOq}xhg9DJoz~JfX=d#Wzp$Pyb1r*Kz diff --git a/docs/build/html/_static/pygments.css b/docs/build/html/_static/pygments.css deleted file mode 100644 index 04a4174..0000000 --- a/docs/build/html/_static/pygments.css +++ /dev/null @@ -1,84 +0,0 @@ -pre { line-height: 125%; } -td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -.highlight .hll { background-color: #ffffcc } -.highlight { background: #f8f8f8; } -.highlight .c { color: #8f5902; font-style: italic } /* Comment */ -.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ -.highlight .g { color: #000000 } /* Generic */ -.highlight .k { color: #004461; font-weight: bold } /* Keyword */ -.highlight .l { color: #000000 } /* Literal */ -.highlight .n { color: #000000 } /* Name */ -.highlight .o { color: #582800 } /* Operator */ -.highlight .x { color: #000000 } /* Other */ -.highlight .p { color: #000000; font-weight: bold } /* Punctuation */ -.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ -.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #8f5902 } /* Comment.Preproc */ -.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ -.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #a40000 } /* Generic.Deleted */ -.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ -.highlight .ges { color: #000000 } /* Generic.EmphStrong */ -.highlight .gr { color: #ef2929 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #00A000 } /* Generic.Inserted */ -.highlight .go { color: #888888 } /* Generic.Output */ -.highlight .gp { color: #745334 } /* Generic.Prompt */ -.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ -.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */ -.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */ -.highlight .ld { color: #000000 } /* Literal.Date */ -.highlight .m { color: #990000 } /* Literal.Number */ -.highlight .s { color: #4e9a06 } /* Literal.String */ -.highlight .na { color: #c4a000 } /* Name.Attribute */ -.highlight .nb { color: #004461 } /* Name.Builtin */ -.highlight .nc { color: #000000 } /* Name.Class */ -.highlight .no { color: #000000 } /* Name.Constant */ -.highlight .nd { color: #888888 } /* Name.Decorator */ -.highlight .ni { color: #ce5c00 } /* Name.Entity */ -.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #000000 } /* Name.Function */ -.highlight .nl { color: #f57900 } /* Name.Label */ -.highlight .nn { color: #000000 } /* Name.Namespace */ -.highlight .nx { color: #000000 } /* Name.Other */ -.highlight .py { color: #000000 } /* Name.Property */ -.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #000000 } /* Name.Variable */ -.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */ -.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */ -.highlight .w { color: #f8f8f8 } /* Text.Whitespace */ -.highlight .mb { color: #990000 } /* Literal.Number.Bin */ -.highlight .mf { color: #990000 } /* Literal.Number.Float */ -.highlight .mh { color: #990000 } /* Literal.Number.Hex */ -.highlight .mi { color: #990000 } /* Literal.Number.Integer */ -.highlight .mo { color: #990000 } /* Literal.Number.Oct */ -.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ -.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ -.highlight .sc { color: #4e9a06 } /* Literal.String.Char */ -.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ -.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ -.highlight .se { color: #4e9a06 } /* Literal.String.Escape */ -.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ -.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ -.highlight .sx { color: #4e9a06 } /* Literal.String.Other */ -.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ -.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ -.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ -.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ -.highlight .fm { color: #000000 } /* Name.Function.Magic */ -.highlight .vc { color: #000000 } /* Name.Variable.Class */ -.highlight .vg { color: #000000 } /* Name.Variable.Global */ -.highlight .vi { color: #000000 } /* Name.Variable.Instance */ -.highlight .vm { color: #000000 } /* Name.Variable.Magic */ -.highlight .il { color: #990000 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/build/html/_static/searchtools.js b/docs/build/html/_static/searchtools.js deleted file mode 100644 index 2c774d1..0000000 --- a/docs/build/html/_static/searchtools.js +++ /dev/null @@ -1,632 +0,0 @@ -/* - * Sphinx JavaScript utilities for the full-text search. - */ -"use strict"; - -/** - * Simple result scoring code. - */ -if (typeof Scorer === "undefined") { - var Scorer = { - // Implement the following function to further tweak the score for each result - // The function takes a result array [docname, title, anchor, descr, score, filename] - // and returns the new score. - /* - score: result => { - const [docname, title, anchor, descr, score, filename, kind] = result - return score - }, - */ - - // query matches the full name of an object - objNameMatch: 11, - // or matches in the last dotted part of the object name - objPartialMatch: 6, - // Additive scores depending on the priority of the object - objPrio: { - 0: 15, // used to be importantResults - 1: 5, // used to be objectResults - 2: -5, // used to be unimportantResults - }, - // Used when the priority is not in the mapping. - objPrioDefault: 0, - - // query found in title - title: 15, - partialTitle: 7, - // query found in terms - term: 5, - partialTerm: 2, - }; -} - -// Global search result kind enum, used by themes to style search results. -class SearchResultKind { - static get index() { return "index"; } - static get object() { return "object"; } - static get text() { return "text"; } - static get title() { return "title"; } -} - -const _removeChildren = (element) => { - while (element && element.lastChild) element.removeChild(element.lastChild); -}; - -/** - * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping - */ -const _escapeRegExp = (string) => - string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string - -const _displayItem = (item, searchTerms, highlightTerms) => { - const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; - const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; - const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; - const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; - const contentRoot = document.documentElement.dataset.content_root; - - const [docName, title, anchor, descr, score, _filename, kind] = item; - - let listItem = document.createElement("li"); - // Add a class representing the item's type: - // can be used by a theme's CSS selector for styling - // See SearchResultKind for the class names. - listItem.classList.add(`kind-${kind}`); - let requestUrl; - let linkUrl; - if (docBuilder === "dirhtml") { - // dirhtml builder - let dirname = docName + "/"; - if (dirname.match(/\/index\/$/)) - dirname = dirname.substring(0, dirname.length - 6); - else if (dirname === "index/") dirname = ""; - requestUrl = contentRoot + dirname; - linkUrl = requestUrl; - } else { - // normal html builders - requestUrl = contentRoot + docName + docFileSuffix; - linkUrl = docName + docLinkSuffix; - } - let linkEl = listItem.appendChild(document.createElement("a")); - linkEl.href = linkUrl + anchor; - linkEl.dataset.score = score; - linkEl.innerHTML = title; - if (descr) { - listItem.appendChild(document.createElement("span")).innerHTML = - " (" + descr + ")"; - // highlight search terms in the description - if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js - highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); - } - else if (showSearchSummary) - fetch(requestUrl) - .then((responseData) => responseData.text()) - .then((data) => { - if (data) - listItem.appendChild( - Search.makeSearchSummary(data, searchTerms, anchor) - ); - // highlight search terms in the summary - if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js - highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); - }); - Search.output.appendChild(listItem); -}; -const _finishSearch = (resultCount) => { - Search.stopPulse(); - Search.title.innerText = _("Search Results"); - if (!resultCount) - Search.status.innerText = Documentation.gettext( - "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." - ); - else - Search.status.innerText = Documentation.ngettext( - "Search finished, found one page matching the search query.", - "Search finished, found ${resultCount} pages matching the search query.", - resultCount, - ).replace('${resultCount}', resultCount); -}; -const _displayNextItem = ( - results, - resultCount, - searchTerms, - highlightTerms, -) => { - // results left, load the summary and display it - // this is intended to be dynamic (don't sub resultsCount) - if (results.length) { - _displayItem(results.pop(), searchTerms, highlightTerms); - setTimeout( - () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), - 5 - ); - } - // search finished, update title and status message - else _finishSearch(resultCount); -}; -// Helper function used by query() to order search results. -// Each input is an array of [docname, title, anchor, descr, score, filename, kind]. -// Order the results by score (in opposite order of appearance, since the -// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. -const _orderResultsByScoreThenName = (a, b) => { - const leftScore = a[4]; - const rightScore = b[4]; - if (leftScore === rightScore) { - // same score: sort alphabetically - const leftTitle = a[1].toLowerCase(); - const rightTitle = b[1].toLowerCase(); - if (leftTitle === rightTitle) return 0; - return leftTitle > rightTitle ? -1 : 1; // inverted is intentional - } - return leftScore > rightScore ? 1 : -1; -}; - -/** - * Default splitQuery function. Can be overridden in ``sphinx.search`` with a - * custom function per language. - * - * The regular expression works by splitting the string on consecutive characters - * that are not Unicode letters, numbers, underscores, or emoji characters. - * This is the same as ``\W+`` in Python, preserving the surrogate pair area. - */ -if (typeof splitQuery === "undefined") { - var splitQuery = (query) => query - .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) - .filter(term => term) // remove remaining empty strings -} - -/** - * Search Module - */ -const Search = { - _index: null, - _queued_query: null, - _pulse_status: -1, - - htmlToText: (htmlString, anchor) => { - const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); - for (const removalQuery of [".headerlink", "script", "style"]) { - htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() }); - } - if (anchor) { - const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`); - if (anchorContent) return anchorContent.textContent; - - console.warn( - `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.` - ); - } - - // if anchor not specified or not found, fall back to main content - const docContent = htmlElement.querySelector('[role="main"]'); - if (docContent) return docContent.textContent; - - console.warn( - "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template." - ); - return ""; - }, - - init: () => { - const query = new URLSearchParams(window.location.search).get("q"); - document - .querySelectorAll('input[name="q"]') - .forEach((el) => (el.value = query)); - if (query) Search.performSearch(query); - }, - - loadIndex: (url) => - (document.body.appendChild(document.createElement("script")).src = url), - - setIndex: (index) => { - Search._index = index; - if (Search._queued_query !== null) { - const query = Search._queued_query; - Search._queued_query = null; - Search.query(query); - } - }, - - hasIndex: () => Search._index !== null, - - deferQuery: (query) => (Search._queued_query = query), - - stopPulse: () => (Search._pulse_status = -1), - - startPulse: () => { - if (Search._pulse_status >= 0) return; - - const pulse = () => { - Search._pulse_status = (Search._pulse_status + 1) % 4; - Search.dots.innerText = ".".repeat(Search._pulse_status); - if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); - }; - pulse(); - }, - - /** - * perform a search for something (or wait until index is loaded) - */ - performSearch: (query) => { - // create the required interface elements - const searchText = document.createElement("h2"); - searchText.textContent = _("Searching"); - const searchSummary = document.createElement("p"); - searchSummary.classList.add("search-summary"); - searchSummary.innerText = ""; - const searchList = document.createElement("ul"); - searchList.setAttribute("role", "list"); - searchList.classList.add("search"); - - const out = document.getElementById("search-results"); - Search.title = out.appendChild(searchText); - Search.dots = Search.title.appendChild(document.createElement("span")); - Search.status = out.appendChild(searchSummary); - Search.output = out.appendChild(searchList); - - const searchProgress = document.getElementById("search-progress"); - // Some themes don't use the search progress node - if (searchProgress) { - searchProgress.innerText = _("Preparing search..."); - } - Search.startPulse(); - - // index already loaded, the browser was quick! - if (Search.hasIndex()) Search.query(query); - else Search.deferQuery(query); - }, - - _parseQuery: (query) => { - // stem the search terms and add them to the correct list - const stemmer = new Stemmer(); - const searchTerms = new Set(); - const excludedTerms = new Set(); - const highlightTerms = new Set(); - const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); - splitQuery(query.trim()).forEach((queryTerm) => { - const queryTermLower = queryTerm.toLowerCase(); - - // maybe skip this "word" - // stopwords array is from language_data.js - if ( - stopwords.indexOf(queryTermLower) !== -1 || - queryTerm.match(/^\d+$/) - ) - return; - - // stem the word - let word = stemmer.stemWord(queryTermLower); - // select the correct list - if (word[0] === "-") excludedTerms.add(word.substr(1)); - else { - searchTerms.add(word); - highlightTerms.add(queryTermLower); - } - }); - - if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js - localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) - } - - // console.debug("SEARCH: searching for:"); - // console.info("required: ", [...searchTerms]); - // console.info("excluded: ", [...excludedTerms]); - - return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; - }, - - /** - * execute search (requires search index to be loaded) - */ - _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => { - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const titles = Search._index.titles; - const allTitles = Search._index.alltitles; - const indexEntries = Search._index.indexentries; - - // Collect multiple result groups to be sorted separately and then ordered. - // Each is an array of [docname, title, anchor, descr, score, filename, kind]. - const normalResults = []; - const nonMainIndexResults = []; - - _removeChildren(document.getElementById("search-progress")); - - const queryLower = query.toLowerCase().trim(); - for (const [title, foundTitles] of Object.entries(allTitles)) { - if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { - for (const [file, id] of foundTitles) { - const score = Math.round(Scorer.title * queryLower.length / title.length); - const boost = titles[file] === title ? 1 : 0; // add a boost for document titles - normalResults.push([ - docNames[file], - titles[file] !== title ? `${titles[file]} > ${title}` : title, - id !== null ? "#" + id : "", - null, - score + boost, - filenames[file], - SearchResultKind.title, - ]); - } - } - } - - // search for explicit entries in index directives - for (const [entry, foundEntries] of Object.entries(indexEntries)) { - if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { - for (const [file, id, isMain] of foundEntries) { - const score = Math.round(100 * queryLower.length / entry.length); - const result = [ - docNames[file], - titles[file], - id ? "#" + id : "", - null, - score, - filenames[file], - SearchResultKind.index, - ]; - if (isMain) { - normalResults.push(result); - } else { - nonMainIndexResults.push(result); - } - } - } - } - - // lookup as object - objectTerms.forEach((term) => - normalResults.push(...Search.performObjectSearch(term, objectTerms)) - ); - - // lookup as search terms in fulltext - normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms)); - - // let the scorer override scores with a custom scoring function - if (Scorer.score) { - normalResults.forEach((item) => (item[4] = Scorer.score(item))); - nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); - } - - // Sort each group of results by score and then alphabetically by name. - normalResults.sort(_orderResultsByScoreThenName); - nonMainIndexResults.sort(_orderResultsByScoreThenName); - - // Combine the result groups in (reverse) order. - // Non-main index entries are typically arbitrary cross-references, - // so display them after other results. - let results = [...nonMainIndexResults, ...normalResults]; - - // remove duplicate search results - // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept - let seen = new Set(); - results = results.reverse().reduce((acc, result) => { - let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); - if (!seen.has(resultStr)) { - acc.push(result); - seen.add(resultStr); - } - return acc; - }, []); - - return results.reverse(); - }, - - query: (query) => { - const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query); - const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms); - - // for debugging - //Search.lastresults = results.slice(); // a copy - // console.info("search results:", Search.lastresults); - - // print the results - _displayNextItem(results, results.length, searchTerms, highlightTerms); - }, - - /** - * search for object names - */ - performObjectSearch: (object, objectTerms) => { - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const objects = Search._index.objects; - const objNames = Search._index.objnames; - const titles = Search._index.titles; - - const results = []; - - const objectSearchCallback = (prefix, match) => { - const name = match[4] - const fullname = (prefix ? prefix + "." : "") + name; - const fullnameLower = fullname.toLowerCase(); - if (fullnameLower.indexOf(object) < 0) return; - - let score = 0; - const parts = fullnameLower.split("."); - - // check for different match types: exact matches of full name or - // "last name" (i.e. last dotted part) - if (fullnameLower === object || parts.slice(-1)[0] === object) - score += Scorer.objNameMatch; - else if (parts.slice(-1)[0].indexOf(object) > -1) - score += Scorer.objPartialMatch; // matches in last name - - const objName = objNames[match[1]][2]; - const title = titles[match[0]]; - - // If more than one term searched for, we require other words to be - // found in the name/title/description - const otherTerms = new Set(objectTerms); - otherTerms.delete(object); - if (otherTerms.size > 0) { - const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); - if ( - [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) - ) - return; - } - - let anchor = match[3]; - if (anchor === "") anchor = fullname; - else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; - - const descr = objName + _(", in ") + title; - - // add custom score for some objects according to scorer - if (Scorer.objPrio.hasOwnProperty(match[2])) - score += Scorer.objPrio[match[2]]; - else score += Scorer.objPrioDefault; - - results.push([ - docNames[match[0]], - fullname, - "#" + anchor, - descr, - score, - filenames[match[0]], - SearchResultKind.object, - ]); - }; - Object.keys(objects).forEach((prefix) => - objects[prefix].forEach((array) => - objectSearchCallback(prefix, array) - ) - ); - return results; - }, - - /** - * search for full-text terms in the index - */ - performTermsSearch: (searchTerms, excludedTerms) => { - // prepare search - const terms = Search._index.terms; - const titleTerms = Search._index.titleterms; - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const titles = Search._index.titles; - - const scoreMap = new Map(); - const fileMap = new Map(); - - // perform the search on the required terms - searchTerms.forEach((word) => { - const files = []; - const arr = [ - { files: terms[word], score: Scorer.term }, - { files: titleTerms[word], score: Scorer.title }, - ]; - // add support for partial matches - if (word.length > 2) { - const escapedWord = _escapeRegExp(word); - if (!terms.hasOwnProperty(word)) { - Object.keys(terms).forEach((term) => { - if (term.match(escapedWord)) - arr.push({ files: terms[term], score: Scorer.partialTerm }); - }); - } - if (!titleTerms.hasOwnProperty(word)) { - Object.keys(titleTerms).forEach((term) => { - if (term.match(escapedWord)) - arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); - }); - } - } - - // no match but word was a required one - if (arr.every((record) => record.files === undefined)) return; - - // found search word in contents - arr.forEach((record) => { - if (record.files === undefined) return; - - let recordFiles = record.files; - if (recordFiles.length === undefined) recordFiles = [recordFiles]; - files.push(...recordFiles); - - // set score for the word in each file - recordFiles.forEach((file) => { - if (!scoreMap.has(file)) scoreMap.set(file, {}); - scoreMap.get(file)[word] = record.score; - }); - }); - - // create the mapping - files.forEach((file) => { - if (!fileMap.has(file)) fileMap.set(file, [word]); - else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word); - }); - }); - - // now check if the files don't contain excluded terms - const results = []; - for (const [file, wordList] of fileMap) { - // check if all requirements are matched - - // as search terms with length < 3 are discarded - const filteredTermCount = [...searchTerms].filter( - (term) => term.length > 2 - ).length; - if ( - wordList.length !== searchTerms.size && - wordList.length !== filteredTermCount - ) - continue; - - // ensure that none of the excluded terms is in the search result - if ( - [...excludedTerms].some( - (term) => - terms[term] === file || - titleTerms[term] === file || - (terms[term] || []).includes(file) || - (titleTerms[term] || []).includes(file) - ) - ) - break; - - // select one (max) score for the file. - const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); - // add result to the result list - results.push([ - docNames[file], - titles[file], - "", - null, - score, - filenames[file], - SearchResultKind.text, - ]); - } - return results; - }, - - /** - * helper function to return a node containing the - * search summary for a given text. keywords is a list - * of stemmed words. - */ - makeSearchSummary: (htmlText, keywords, anchor) => { - const text = Search.htmlToText(htmlText, anchor); - if (text === "") return null; - - const textLower = text.toLowerCase(); - const actualStartPosition = [...keywords] - .map((k) => textLower.indexOf(k.toLowerCase())) - .filter((i) => i > -1) - .slice(-1)[0]; - const startWithContext = Math.max(actualStartPosition - 120, 0); - - const top = startWithContext === 0 ? "" : "..."; - const tail = startWithContext + 240 < text.length ? "..." : ""; - - let summary = document.createElement("p"); - summary.classList.add("context"); - summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; - - return summary; - }, -}; - -_ready(Search.init); diff --git a/docs/build/html/_static/sphinx_highlight.js b/docs/build/html/_static/sphinx_highlight.js deleted file mode 100644 index 8a96c69..0000000 --- a/docs/build/html/_static/sphinx_highlight.js +++ /dev/null @@ -1,154 +0,0 @@ -/* Highlighting utilities for Sphinx HTML documentation. */ -"use strict"; - -const SPHINX_HIGHLIGHT_ENABLED = true - -/** - * highlight a given string on a node by wrapping it in - * span elements with the given class name. - */ -const _highlight = (node, addItems, text, className) => { - if (node.nodeType === Node.TEXT_NODE) { - const val = node.nodeValue; - const parent = node.parentNode; - const pos = val.toLowerCase().indexOf(text); - if ( - pos >= 0 && - !parent.classList.contains(className) && - !parent.classList.contains("nohighlight") - ) { - let span; - - const closestNode = parent.closest("body, svg, foreignObject"); - const isInSVG = closestNode && closestNode.matches("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.classList.add(className); - } - - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - const rest = document.createTextNode(val.substr(pos + text.length)); - parent.insertBefore( - span, - parent.insertBefore( - rest, - node.nextSibling - ) - ); - node.nodeValue = val.substr(0, pos); - /* There may be more occurrences of search term in this node. So call this - * function recursively on the remaining fragment. - */ - _highlight(rest, addItems, text, className); - - if (isInSVG) { - const rect = document.createElementNS( - "http://www.w3.org/2000/svg", - "rect" - ); - const bbox = parent.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute("class", className); - addItems.push({ parent: parent, target: rect }); - } - } - } else if (node.matches && !node.matches("button, select, textarea")) { - node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); - } -}; -const _highlightText = (thisNode, text, className) => { - let addItems = []; - _highlight(thisNode, addItems, text, className); - addItems.forEach((obj) => - obj.parent.insertAdjacentElement("beforebegin", obj.target) - ); -}; - -/** - * Small JavaScript module for the documentation. - */ -const SphinxHighlight = { - - /** - * highlight the search words provided in localstorage in the text - */ - highlightSearchWords: () => { - if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight - - // get and clear terms from localstorage - const url = new URL(window.location); - const highlight = - localStorage.getItem("sphinx_highlight_terms") - || url.searchParams.get("highlight") - || ""; - localStorage.removeItem("sphinx_highlight_terms") - url.searchParams.delete("highlight"); - window.history.replaceState({}, "", url); - - // get individual terms from highlight string - const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); - if (terms.length === 0) return; // nothing to do - - // There should never be more than one element matching "div.body" - const divBody = document.querySelectorAll("div.body"); - const body = divBody.length ? divBody[0] : document.querySelector("body"); - window.setTimeout(() => { - terms.forEach((term) => _highlightText(body, term, "highlighted")); - }, 10); - - const searchBox = document.getElementById("searchbox"); - if (searchBox === null) return; - searchBox.appendChild( - document - .createRange() - .createContextualFragment( - '" - ) - ); - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords: () => { - document - .querySelectorAll("#searchbox .highlight-link") - .forEach((el) => el.remove()); - document - .querySelectorAll("span.highlighted") - .forEach((el) => el.classList.remove("highlighted")); - localStorage.removeItem("sphinx_highlight_terms") - }, - - initEscapeListener: () => { - // only install a listener if it is really needed - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; - - document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; - // bail with special keys - if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; - if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { - SphinxHighlight.hideSearchWords(); - event.preventDefault(); - } - }); - }, -}; - -_ready(() => { - /* Do not call highlightSearchWords() when we are on the search page. - * It will highlight words from the *previous* search query. - */ - if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); - SphinxHighlight.initEscapeListener(); -}); diff --git a/docs/build/html/genindex.html b/docs/build/html/genindex.html deleted file mode 100644 index 4ac4e46..0000000 --- a/docs/build/html/genindex.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - Index — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- - -

Index

- -
- -
- - -
- - -
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/build/html/index.html b/docs/build/html/index.html deleted file mode 100644 index 5c8c9a7..0000000 --- a/docs/build/html/index.html +++ /dev/null @@ -1,319 +0,0 @@ - - - - - - - - Predspot — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- -
-

Predspot

-
-

Overview

-

Predspot is an early project for a Python library for spatio-temporal crime prediction and hotspot detection. It combines machine learning techniques with spatial analysis to help predict and visualize crime patterns across time and space. To properly use this library, you preferably need to have a crime dataset and a study area.

-

The results of this library are not guaranteed to be good, but it is a good starting point for spatio-temporal crime prediction. This project is barely maintained, so please if you want to collaborate, open an issue or a PR.

-
-
-

Important Notice

-
-

Warning

-

This project was developed as part of a master’s thesis and is currently in an archived state. While the core functionality exists, you may encounter compatibility issues with newer Python package versions. The code can work with some effort, but please note:

-
    -
  • This is not production-ready software

  • -
  • Some dependencies are outdated and may require specific versions

  • -
  • You might need to modify some code to work with newer package versions

  • -
  • The project was created for research purposes

  • -
-

However, we believe the methodologies and approaches used here are still valuable! If you’re interested in crime hotspot prediction, feel free to:

-
    -
  • Use this as a reference implementation

  • -
  • Adapt the code to modern dependencies

  • -
  • Build upon these concepts for your own projects

  • -
  • Contribute to modernizing the codebase

  • -
-

We welcome anyone interested in reviving or learning from this project!

-
-
-
-

Key Features

-
    -
  • Spatial and temporal crime mapping

  • -
  • Feature engineering for time series data

  • -
  • Machine learning-based prediction pipeline

  • -
  • Crime hotspot detection using Kernel Density Estimation

  • -
  • Visualization tools for crime patterns

  • -
-
-
-

Quick Start

-

Basic usage example:

-
from predspot import Dataset, PredictionPipeline
-from predspot.crime_mapping import KDE, create_gridpoints
-from predspot.feature_engineering import Seasonality, Trend, Diff
-
-# Load and prepare data
-dataset = Dataset(crimes_df, study_area_gdf)
-
-# Create prediction pipeline
-pipeline = PredictionPipeline(
-    mapping=KDE(tfreq='M', grid=create_gridpoints(study_area, resolution=250)),
-    fextraction=PandasFeatureUnion([
-        ('seasonal', Seasonality(lags=12)),
-        ('trend', Trend(lags=12)),
-        ('diff', Diff(lags=12))
-    ]),
-    estimator=your_favorite_sklearn_model
-)
-
-# Fit and predict
-pipeline.fit(dataset)
-predictions = pipeline.predict()
-
-
-
-
-

Modules

-

The library consists of four main modules:

-
    -
  • Dataset Preparation: Module for preparing and managing crime datasets and study areas.

  • -
  • Crime Mapping: Module for spatial and temporal crime mapping, including KDE-based hotspot detection.

  • -
  • Feature Engineering: Module for time series feature engineering, including seasonality, trend, and difference features.

  • -
  • ML Modelling: Module that implements the prediction pipeline and model evaluation.

  • -
-

And two utilities:

-
    -
  • Utilities: Utility functions for data preparation and visualization.

  • -
  • Pipeline: Functions for the prediction pipeline.

  • -
-
-
-

Installation

-

Create conda env and install requirements:

-
conda create -n predspot python=3.8
-conda activate predspot
-conda install -y rtree geopandas  # if doesnt work, do: `conda clean --all`
-pip install pandas statsmodels==0.10.2 geojsoncontour stldecompose scikit-learn matplotlib descartes
-pip install .
-
-
-

Required dependencies:

-
    -
  • pandas

  • -
  • geopandas

  • -
  • numpy

  • -
  • scikit-learn

  • -
  • scipy

  • -
  • stldecompose

  • -
  • matplotlib

  • -
-
-
-

Input Data Format

-

The crime data should be a pandas DataFrame with the following required columns:

-
    -
  • tag: Crime type

  • -
  • t: Timestamp

  • -
  • lon: Longitude

  • -
  • lat: Latitude

  • -
-

The study area should be a GeoDataFrame defining the geographical boundaries of interest.

-
-
-

Resources

-

For more information on the methods used in Predspot, please search more about these methods:

-
    -
  • Kernel Density Estimation for crime hotspot detection

  • -
  • Time series decomposition for feature engineering

  • -
  • Spatio-temporal crime prediction techniques

  • -
-
-
-

License

-

BSD 3-Clause.

-
-
-

Contributing

-

Contributions are welcome! Please feel free to submit a Pull Request.

-

Guidelines for contributing:

-
    -
  1. Fork the repository

  2. -
  3. Create your feature branch

  4. -
  5. Commit your changes

  6. -
  7. Push to the branch

  8. -
  9. Create a new Pull Request

  10. -
-
-
-

Citation

-

If you use Predspot in your research, please cite us:

-
Araujo, A., & Cacho, N. (2019). Predspot: Predicting crime hotspots with machine learning.
-Master's thesis, UFRN (Universidade Federal do Rio Grande do Norte), Natal, Brazil.
-
-Araújo, A., Cacho, N., Bezerra, L., Vieira, C., & Borges, J. (2018, June).
-Towards a crime hotspot detection framework for patrol planning.
-In 2018 IEEE 20th International Conference on High Performance Computing and Communications;
-IEEE 16th International Conference on Smart City;
-IEEE 4th International Conference on Data Science and Systems (HPCC/SmartCity/DSS) (pp. 1256-1263). IEEE.
-
-
- -
-
-
-

Indices and tables

- -
- - -
- - -
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/build/html/modules/crime_mapping.html b/docs/build/html/modules/crime_mapping.html deleted file mode 100644 index 871c3d2..0000000 --- a/docs/build/html/modules/crime_mapping.html +++ /dev/null @@ -1,630 +0,0 @@ - - - - - - - - Crime Mapping — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- -
-

Crime Mapping

-

Crime Mapping Module

-

This module provides functionality for spatial and temporal crime mapping analysis. -It includes utilities for creating grid points, hexagonal grids, and implementing -kernel density estimation for crime hotspot detection.

-
-
-class predspot.crime_mapping.KDE(tfreq, grid, start_time=False, end_time=False, bandwidth='silverman', debug=False)[source]
-

Bases: SpatioTemporalMapping

-

Kernel Density Estimation for crime hotspot detection.

-
-
Parameters:
-
    -
  • tfreq (str) – Time frequency (‘M’ for monthly, ‘W’ for weekly, ‘D’ for daily)

  • -
  • grid (GeoDataFrame) – Spatial grid for analysis

  • -
  • start_time (str or datetime, optional) – Analysis start time

  • -
  • end_time (str or datetime, optional) – Analysis end time

  • -
  • bandwidth (str or float) – Bandwidth method (‘silverman’ or numeric value)

  • -
-
-
-
-
-fit_grid(data_points, as_df=False)[source]
-

Fit the kernel density estimation to grid points.

-
-
Parameters:
-
    -
  • data_points (GeoDataFrame) – Crime incident points

  • -
  • as_df (bool) – If True, return results as DataFrame

  • -
-
-
Returns:
-

Density estimates for grid points

-
-
Return type:
-

dict or DataFrame

-
-
-
- -
-
-set_fit_request(*, x: bool | None | str = '$UNCHANGED$') → KDE
-

Request metadata passed to the fit method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to fit if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to fit.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

x (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for x parameter in fit.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
-
-set_transform_request(*, data_points: bool | None | str = '$UNCHANGED$') → KDE
-

Request metadata passed to the transform method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to transform if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to transform.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

data_points (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for data_points parameter in transform.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
- -
-
-class predspot.crime_mapping.KGrid(k, tfreq)[source]
-

Bases: object

-
-
-fit(data_points)[source]
-
- -
-
-transform(data_points)[source]
-
- -
- -
-
-class predspot.crime_mapping.QuadratCount(tfreq, grid, filter_place_ratio=0.9)[source]
-

Bases: BaseEstimator, TransformerMixin

-
-
-fit(x=None, y=None)[source]
-
- -
-
-set_fit_request(*, x: bool | None | str = '$UNCHANGED$') → QuadratCount
-

Request metadata passed to the fit method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to fit if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to fit.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

x (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for x parameter in fit.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
-
-set_transform_request(*, data_points: bool | None | str = '$UNCHANGED$') → QuadratCount
-

Request metadata passed to the transform method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to transform if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to transform.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

data_points (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for data_points parameter in transform.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
-
-transform(data_points)[source]
-
- -
- -
-
-class predspot.crime_mapping.QuadratCount2(tfreq, grid, filter_place_ratio=0.9)[source]
-

Bases: BaseEstimator, TransformerMixin

-
-
-set_transform_request(*, data_points: bool | None | str = '$UNCHANGED$') → QuadratCount2
-

Request metadata passed to the transform method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to transform if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to transform.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

data_points (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for data_points parameter in transform.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
-
-transform(data_points)[source]
-
- -
- -
-
-class predspot.crime_mapping.SpatioTemporalMapping(tfreq, grid, start_time=False, end_time=False, debug=False)[source]
-

Bases: ABC, TransformerMixin, BaseEstimator

-

Abstract base class for spatio-temporal crime mapping.

-
-
Parameters:
-
    -
  • tfreq (str) – Time frequency (‘M’ for monthly, ‘W’ for weekly, ‘D’ for daily)

  • -
  • grid (GeoDataFrame) – Spatial grid for analysis

  • -
  • start_time (str or datetime, optional) – Analysis start time

  • -
  • end_time (str or datetime, optional) – Analysis end time

  • -
-
-
-
-
-fit(x, y=None)[source]
-
- -
-
-abstract fit_grid(data_points=None)[source]
-
- -
-
-get_time_data_chunks(data_points)[source]
-
- -
-
-get_times_no_data(chunks)[source]
-
- -
-
-set_fit_request(*, x: bool | None | str = '$UNCHANGED$') → SpatioTemporalMapping
-

Request metadata passed to the fit method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to fit if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to fit.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

x (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for x parameter in fit.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
-
-set_transform_request(*, data_points: bool | None | str = '$UNCHANGED$') → SpatioTemporalMapping
-

Request metadata passed to the transform method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to transform if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to transform.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

data_points (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for data_points parameter in transform.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
-
-transform(data_points)[source]
-
- -
- -
-
-predspot.crime_mapping.create_gridhexagonal(bbox, resolution)[source]
-
- -
-
-predspot.crime_mapping.create_gridpoints(bbox, resolution, return_coords=False, debug=False)[source]
-

Create a grid of points within a given bounding box.

-
-
Parameters:
-
    -
  • bbox (GeoDataFrame) – Bounding box as a GeoDataFrame

  • -
  • resolution (float) – Grid cell size in kilometers

  • -
  • return_coords (bool) – If True, returns additional coordinate arrays

  • -
  • debug (bool) – Enable debug printing

  • -
-
-
Returns:
-

Grid points as GeoDataFrame, optionally with coordinate arrays

-
-
Return type:
-

GeoDataFrame or tuple

-
-
-
- -
-
-predspot.crime_mapping.create_gridsquares(city_shape, resolution=1)[source]
-

It constructs a grid of square cells.

-
-
Parameters:
-
    -
  • city_shape (GeoDataFrame.) – Corresponds to the boundary geometry in which the grid will be formed.

  • -
  • resolution (float, default is 1.) – Space between the square cells.

  • -
-
-
-
- -
-
-predspot.crime_mapping.create_hexagon(l, x, y)[source]
-

Create a hexagonal polygon.

-
-
Parameters:
-
    -
  • l (float) – Length of hexagon side

  • -
  • x (float) – X-coordinate of center

  • -
  • y (float) – Y-coordinate of center

  • -
-
-
Returns:
-

Hexagonal polygon

-
-
Return type:
-

Polygon

-
-
-
- -
- - -
- - -
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/build/html/modules/dataset_preparation.html b/docs/build/html/modules/dataset_preparation.html deleted file mode 100644 index 57bd0be..0000000 --- a/docs/build/html/modules/dataset_preparation.html +++ /dev/null @@ -1,289 +0,0 @@ - - - - - - - - Dataset Preparation — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- -
-

Dataset Preparation

-

Dataset Preparation Module

-

This module provides functionality for preparing and managing crime datasets along with their -corresponding study areas. It handles spatial data processing and visualization of crime incidents.

-
-
-class predspot.dataset_preparation.Dataset(crimes, study_area, debug=False)[source]
-

Bases: object

-

A class to handle crime datasets and their associated study areas.

-
-
Parameters:
-
    -
  • crimes (pandas.DataFrame) – DataFrame containing crime data with required columns: -‘tag’, ‘t’ (timestamp), ‘lon’ (longitude), and ‘lat’ (latitude)

  • -
  • study_area (geopandas.GeoDataFrame) – GeoDataFrame defining the study area boundaries

  • -
  • debug (bool, optional) – Enable debug printing. Defaults to False.

  • -
-
-
-
-
-crimes
-

Processed crime data with geometry

-
-
Type:
-

geopandas.GeoDataFrame

-
-
-
- -
-
-study_area
-

Study area boundaries

-
-
Type:
-

geopandas.GeoDataFrame

-
-
-
- -
-
-property crimes
-

Get the crime incidents data.

-
-
Returns:
-

The processed crime incidents data

-
-
Return type:
-

geopandas.GeoDataFrame

-
-
-
- -
-
-plot(ax=None, crime_samples=1000, **kwargs)[source]
-

Plot the study area and crime incidents.

-
-
Parameters:
-
    -
  • ax (matplotlib.axes.Axes, optional) – Matplotlib axes for plotting

  • -
  • crime_samples (int, optional) – Number of crime samples to plot. Defaults to 1000

  • -
  • **kwargs – Additional keyword arguments for plotting -study_area: kwargs for study area plot -crimes: kwargs for crime incidents plot

  • -
-
-
Returns:
-

The plot axes

-
-
Return type:
-

matplotlib.axes.Axes

-
-
-
- -
-
-property shape
-

Get the shapes of the dataset components.

-
-
Returns:
-

Dictionary containing the shapes of crimes and study_area DataFrames

-
-
Return type:
-

dict

-
-
-
- -
-
-property study_area
-

Get the study area boundaries.

-
-
Returns:
-

The study area boundaries

-
-
Return type:
-

geopandas.GeoDataFrame

-
-
-
- -
-
-train_test_split(test_size=0.25)[source]
-

Split the dataset into training and testing sets.

-
-
Parameters:
-

test_size (float) – Proportion of the dataset to include in the test split. -Must be between 0 and 1. Defaults to 0.25.

-
-
Returns:
-

(train_dataset, test_dataset) - Two Dataset objects containing the splits

-
-
Return type:
-

tuple

-
-
Raises:
-

AssertionError – If test_size is not between 0 and 1

-
-
-
- -
- -
- - -
- - -
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/build/html/modules/feature_engineering.html b/docs/build/html/modules/feature_engineering.html deleted file mode 100644 index 7895ccc..0000000 --- a/docs/build/html/modules/feature_engineering.html +++ /dev/null @@ -1,671 +0,0 @@ - - - - - - - - Feature Engineering — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- -
-

Feature Engineering

-

Feature Engineering Module

-

This module provides classes for time series feature engineering and transformation, -including autoregressive features, differencing, seasonality, and trend decomposition.

-
-
-class predspot.feature_engineering.AR(lags, tfreq, debug=False)[source]
-

Bases: TimeSeriesFeatures

-

Autoregressive features implementation.

-
-
-apply_ts_decomposition(ts)[source]
-

Apply autoregressive transformation (identity).

-
-
Parameters:
-

ts (pandas.Series) – Input time series

-
-
Returns:
-

Original time series

-
-
Return type:
-

pandas.Series

-
-
-
- -
-
-property label
-

Feature label for autoregressive features

-
-
Type:
-

str

-
-
-
- -
-
-set_transform_request(*, stseries: bool | None | str = '$UNCHANGED$') → AR
-

Request metadata passed to the transform method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to transform if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to transform.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

stseries (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for stseries parameter in transform.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
- -
-
-class predspot.feature_engineering.Diff(lags, tfreq, debug=False)[source]
-

Bases: TimeSeriesFeatures

-

Difference features implementation.

-
-
-apply_ts_decomposition(ts)[source]
-

Apply difference transformation.

-
-
Parameters:
-

ts (pandas.Series) – Input time series

-
-
Returns:
-

Differenced time series

-
-
Return type:
-

pandas.Series

-
-
-
- -
-
-property label
-

Feature label for difference features

-
-
Type:
-

str

-
-
-
- -
-
-set_transform_request(*, stseries: bool | None | str = '$UNCHANGED$') → Diff
-

Request metadata passed to the transform method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to transform if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to transform.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

stseries (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for stseries parameter in transform.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
- -
-
-class predspot.feature_engineering.FeatureScaling(estimator, debug=False)[source]
-

Bases: TransformerMixin, BaseEstimator

-

Feature scaling transformer.

-
-
Parameters:
-
    -
  • estimator – Scikit-learn compatible scaling estimator

  • -
  • debug (bool, optional) – Enable debug printing. Defaults to False

  • -
-
-
-
-
-set_transform_request(*, x: bool | None | str = '$UNCHANGED$') → FeatureScaling
-

Request metadata passed to the transform method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to transform if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to transform.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

x (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for x parameter in transform.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
-
-transform(x)[source]
-

Transform features using the scaling estimator.

-
-
Parameters:
-

x (pandas.DataFrame) – Input features

-
-
Returns:
-

Scaled features

-
-
Return type:
-

pandas.DataFrame

-
-
-
- -
- -
-
-class predspot.feature_engineering.Seasonality(lags, tfreq, debug=False)[source]
-

Bases: TimeSeriesFeatures

-

Seasonal decomposition features implementation.

-
-
-apply_ts_decomposition(ts)[source]
-

Extract seasonal component from time series.

-
-
Parameters:
-

ts (pandas.Series) – Input time series

-
-
Returns:
-

Seasonal component

-
-
Return type:
-

pandas.Series

-
-
-
- -
-
-property label
-

Feature label for seasonal features

-
-
Type:
-

str

-
-
-
- -
-
-set_transform_request(*, stseries: bool | None | str = '$UNCHANGED$') → Seasonality
-

Request metadata passed to the transform method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to transform if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to transform.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

stseries (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for stseries parameter in transform.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
- -
-
-class predspot.feature_engineering.TimeSeriesFeatures(lags, tfreq, debug=False)[source]
-

Bases: BaseEstimator, TransformerMixin

-

Base class for time series feature engineering.

-
-
Parameters:
-
    -
  • lags (int) – Number of time lags to use for feature creation

  • -
  • tfreq (str) – Time frequency (‘D’ for daily, ‘W’ for weekly, ‘M’ for monthly)

  • -
  • debug (bool, optional) – Enable debug printing. Defaults to False

  • -
-
-
Raises:
-

AssertionError – If lags is not a positive integer or tfreq is invalid

-
-
-
-
-abstract apply_ts_decomposition(ts)[source]
-

Apply time series decomposition.

-
-
Parameters:
-

ts (pandas.Series) – Input time series

-
-
Returns:
-

Transformed time series

-
-
Return type:
-

pandas.Series

-
-
-
- -
-
-property label
-

Feature label identifier

-
-
Type:
-

str

-
-
-
- -
-
-property lags
-

Number of time lags

-
-
Type:
-

int

-
-
-
- -
-
-make_lag_df(ts)[source]
-

Create lagged features dataframe.

-
-
Parameters:
-

ts (pandas.Series) – Input time series

-
-
Returns:
-

(lag_df, aligned_ts) - Lagged features and aligned original series

-
-
Return type:
-

tuple

-
-
Raises:
-

AssertionError – If series length is less than number of lags

-
-
-
- -
-
-set_transform_request(*, stseries: bool | None | str = '$UNCHANGED$') → TimeSeriesFeatures
-

Request metadata passed to the transform method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to transform if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to transform.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

stseries (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for stseries parameter in transform.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
-
-transform(stseries)[source]
-

Transform the input series into lagged features.

-
-
Parameters:
-

stseries (pandas.Series) – Input time series with multi-index (time, places)

-
-
Returns:
-

Transformed features

-
-
Return type:
-

pandas.DataFrame

-
-
-
- -
- -
-
-class predspot.feature_engineering.Trend(lags, tfreq, debug=False)[source]
-

Bases: TimeSeriesFeatures

-

Trend decomposition features implementation.

-
-
-apply_ts_decomposition(ts)[source]
-

Extract trend component from time series.

-
-
Parameters:
-

ts (pandas.Series) – Input time series

-
-
Returns:
-

Trend component

-
-
Return type:
-

pandas.Series

-
-
-
- -
-
-property label
-

Feature label for trend features

-
-
Type:
-

str

-
-
-
- -
-
-set_transform_request(*, stseries: bool | None | str = '$UNCHANGED$') → Trend
-

Request metadata passed to the transform method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to transform if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to transform.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

stseries (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for stseries parameter in transform.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
- -
- - -
- - -
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/build/html/modules/ml_modelling.html b/docs/build/html/modules/ml_modelling.html deleted file mode 100644 index a31436a..0000000 --- a/docs/build/html/modules/ml_modelling.html +++ /dev/null @@ -1,675 +0,0 @@ - - - - - - - - ML Modelling — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- -
-

ML Modelling

-

Machine Learning Modelling Module

-

This module provides classes for machine learning model pipelines, feature selection, -and prediction functionality for crime density forecasting.

-
-
-class predspot.ml_modelling.FeatureSelection(estimator, debug=False)[source]
-

Bases: TransformerMixin, BaseEstimator

-

Feature selection transformer.

-
-
Parameters:
-
    -
  • estimator – Scikit-learn compatible feature selector

  • -
  • debug (bool, optional) – Enable debug printing. Defaults to False

  • -
-
-
-
-
-fit(x, y=None)[source]
-

Fit the feature selector.

-
-
Parameters:
-
    -
  • x (pandas.DataFrame) – Input features

  • -
  • y (pandas.Series, optional) – Target variable

  • -
-
-
Returns:
-

The fitted instance

-
-
Return type:
-

self

-
-
-
- -
-
-set_fit_request(*, x: bool | None | str = '$UNCHANGED$') → FeatureSelection
-

Request metadata passed to the fit method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to fit if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to fit.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

x (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for x parameter in fit.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
-
-set_transform_request(*, x: bool | None | str = '$UNCHANGED$') → FeatureSelection
-

Request metadata passed to the transform method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to transform if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to transform.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

x (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for x parameter in transform.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
-
-transform(x)[source]
-

Transform features using the feature selector.

-
-
Parameters:
-

x (pandas.DataFrame) – Input features

-
-
Returns:
-

Selected features

-
-
Return type:
-

pandas.DataFrame

-
-
-
- -
- -
-
-class predspot.ml_modelling.Model(estimator, debug=False)[source]
-

Bases: RegressorMixin, BaseEstimator

-

Model wrapper for crime density prediction.

-
-
Parameters:
-
    -
  • estimator – Scikit-learn compatible regression estimator

  • -
  • debug (bool, optional) – Enable debug printing. Defaults to False

  • -
-
-
-
-
-fit(x, y=None)[source]
-

Fit the regression model.

-
-
Parameters:
-
    -
  • x (pandas.DataFrame) – Input features

  • -
  • y (pandas.Series, optional) – Target variable

  • -
-
-
Returns:
-

The fitted instance

-
-
Return type:
-

self

-
-
-
- -
-
-predict(x)[source]
-

Make predictions using the fitted model.

-
-
Parameters:
-

x (pandas.DataFrame) – Input features

-
-
Returns:
-

Predictions with ‘crime_density’ column

-
-
Return type:
-

pandas.DataFrame

-
-
-
- -
-
-set_fit_request(*, x: bool | None | str = '$UNCHANGED$') → Model
-

Request metadata passed to the fit method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to fit if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to fit.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

x (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for x parameter in fit.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
-
-set_predict_request(*, x: bool | None | str = '$UNCHANGED$') → Model
-

Request metadata passed to the predict method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to predict if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to predict.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

x (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for x parameter in predict.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
-
-set_score_request(*, sample_weight: bool | None | str = '$UNCHANGED$') → Model
-

Request metadata passed to the score method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to score if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to score.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

sample_weight (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for sample_weight parameter in score.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
- -
-
-class predspot.ml_modelling.PredictionPipeline(mapping, fextraction, estimator, debug=False)[source]
-

Bases: RegressorMixin, BaseEstimator

-

Complete pipeline for crime density prediction.

-
-
Parameters:
-
    -
  • mapping – Spatial mapping transformer

  • -
  • fextraction – Feature extraction transformer

  • -
  • estimator – Scikit-learn compatible pipeline or estimator

  • -
  • debug (bool, optional) – Enable debug printing. Defaults to False

  • -
-
-
-
-
-property dataset
-

Current dataset being used

-
-
Type:
-

Dataset

-
-
-
- -
-
-evaluate(scoring, cv=5)[source]
-

Evaluate model performance using time series cross-validation.

-
-
Parameters:
-
    -
  • scoring (str) – Scoring metric (‘r2’ or ‘mse’)

  • -
  • cv (int) – Number of cross-validation folds

  • -
-
-
Returns:
-

Scores for each fold

-
-
Return type:
-

list

-
-
Raises:
-

Exception – If scoring metric is invalid

-
-
-
- -
-
-property feature_importances
-

Get feature importance scores.

-
-
Returns:
-

Feature importance scores

-
-
Return type:
-

pandas.DataFrame

-
-
Raises:
-

Exception – If model hasn’t been fitted or doesn’t support feature importances

-
-
-
- -
-
-fit(dataset, y=None)[source]
-

Fit the complete prediction pipeline.

-
-
Parameters:
-
    -
  • dataset – Input dataset containing crimes and study area

  • -
  • y – Ignored, present for scikit-learn compatibility

  • -
-
-
Returns:
-

The fitted instance

-
-
Return type:
-

self

-
-
Raises:
-

Exception – If fitting fails

-
-
-
- -
-
-property grid
-

Spatial grid used for mapping

-
-
Type:
-

GeoDataFrame

-
-
-
- -
-
-predict()[source]
-

Make predictions for the next time step.

-
-
Returns:
-

Predictions for next time step

-
-
Return type:
-

pandas.DataFrame

-
-
-
- -
-
-set_fit_request(*, dataset: bool | None | str = '$UNCHANGED$') → PredictionPipeline
-

Request metadata passed to the fit method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to fit if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to fit.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

dataset (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for dataset parameter in fit.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
-
-set_score_request(*, sample_weight: bool | None | str = '$UNCHANGED$') → PredictionPipeline
-

Request metadata passed to the score method.

-

Note that this method is only relevant if -enable_metadata_routing=True (see sklearn.set_config()). -Please see User Guide on how the routing -mechanism works.

-

The options for each parameter are:

-
    -
  • True: metadata is requested, and passed to score if provided. The request is ignored if metadata is not provided.

  • -
  • False: metadata is not requested and the meta-estimator will not pass it to score.

  • -
  • None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.

  • -
  • str: metadata should be passed to the meta-estimator with this given alias instead of the original name.

  • -
-

The default (sklearn.utils.metadata_routing.UNCHANGED) retains the -existing request. This allows you to change the request for some -parameters and not others.

-
-

Added in version 1.3.

-
-
-

Note

-

This method is only relevant if this estimator is used as a -sub-estimator of a meta-estimator, e.g. used inside a -Pipeline. Otherwise it has no effect.

-
-
-
Parameters:
-

sample_weight (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for sample_weight parameter in score.

-
-
Returns:
-

self – The updated object.

-
-
Return type:
-

object

-
-
-
- -
-
-property stseries
-

Spatio-temporal series

-
-
Type:
-

pandas.Series

-
-
-
- -
- -
- - -
- - -
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/build/html/modules/pipeline.html b/docs/build/html/modules/pipeline.html deleted file mode 100644 index 3899598..0000000 --- a/docs/build/html/modules/pipeline.html +++ /dev/null @@ -1,240 +0,0 @@ - - - - - - - - Pipeline — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- -
-

Pipeline

-

Pipeline Module

-

This module provides the main pipeline functionality for crime prediction, -including data loading, preprocessing, and model execution.

-

Example

-
>>> from predspot.pipeline import generate_testdata, run_prediction_pipeline
->>> crime_data, study_area = generate_testdata(10000, '2020-01-01', '2020-12-31')
->>> results = run_prediction_pipeline(crime_data, study_area)
-
-
-
-
-predspot.pipeline.evaluate_pipeline(pipeline, scoring='r2', cv=5, debug=False)[source]
-

Evaluate the prediction pipeline using cross-validation.

-
-
Parameters:
-
    -
  • pipeline (PredictionPipeline) – Fitted prediction pipeline

  • -
  • scoring (str, optional) – Scoring metric (‘r2’ or ‘mse’). Defaults to ‘r2’

  • -
  • cv (int, optional) – Number of cross-validation folds. Defaults to 5

  • -
  • debug (bool, optional) – Enable debug printing. Defaults to False

  • -
-
-
Returns:
-

Cross-validation scores

-
-
Return type:
-

list

-
-
-

Example

-
>>> scores = evaluate_pipeline(fitted_pipeline, scoring='r2', cv=5)
-
-
-
- -
-
-predspot.pipeline.generate_testdata(n_points, start_time, end_time, debug=False)[source]
-

Generate synthetic crime data for testing.

-
-
Parameters:
-
    -
  • n_points (int) – Number of crime incidents to generate

  • -
  • start_time (str) – Start date in ‘YYYY-MM-DD’ format

  • -
  • end_time (str) – End date in ‘YYYY-MM-DD’ format

  • -
  • debug (bool, optional) – Enable debug printing. Defaults to False

  • -
-
-
Returns:
-

(crimes_df, study_area_gdf) - Generated crime data and study area

-
-
Return type:
-

tuple

-
-
-

Example

-
>>> crimes, area = generate_testdata(1000, '2020-01-01', '2020-12-31')
-
-
-
- -
-
-predspot.pipeline.run_prediction_pipeline(crime_data, study_area, crime_tags=None, time_range=None, tfreq='M', grid_resolution=250, debug=False)[source]
-

Run the complete crime prediction pipeline.

-
-
Parameters:
-
    -
  • crime_data (pandas.DataFrame) – Crime incident data

  • -
  • study_area (geopandas.GeoDataFrame) – Study area boundaries

  • -
  • crime_tags (list, optional) – List of crime types to include

  • -
  • time_range (list, optional) – Time range as [‘HH:MM’, ‘HH:MM’]

  • -
  • tfreq (str, optional) – Time frequency (‘M’, ‘W’, ‘D’). Defaults to ‘M’

  • -
  • grid_resolution (float, optional) – Spatial grid resolution in km. Defaults to 250

  • -
  • debug (bool, optional) – Enable debug printing. Defaults to False

  • -
-
-
Returns:
-

(predictions, pipeline) - Predicted crime densities and fitted pipeline

-
-
Return type:
-

tuple

-
-
Raises:
-

ValueError – If input data is invalid or missing required columns

-
-
-
- -
- - -
- - -
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/build/html/modules/utilities.html b/docs/build/html/modules/utilities.html deleted file mode 100644 index a5e4d8b..0000000 --- a/docs/build/html/modules/utilities.html +++ /dev/null @@ -1,302 +0,0 @@ - - - - - - - - Utilities — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- -
-

Utilities

-

Utilities Module

-

This module provides utility functions and classes for data processing and visualization, -including contour generation, feature union operations, and pandas-specific transformations.

-
-
-class predspot.utilities.PandasFeatureUnion(transformer_list, n_jobs=None, transformer_weights=None, debug=False)[source]
-

Bases: FeatureUnion

-

A FeatureUnion transformer that preserves pandas DataFrames.

-

This class extends sklearn’s FeatureUnion to work with pandas DataFrames, -maintaining index alignment and column names.

-
-
-n_jobs
-

Number of parallel jobs

-
-
Type:
-

int

-
-
-
- -
-
-transformer_list
-

List of transformer tuples

-
-
Type:
-

list

-
-
-
- -
-
-transformer_weights
-

Weights for transformers

-
-
Type:
-

dict

-
-
-
- -
-
-debug
-

Enable debug printing

-
-
Type:
-

bool

-
-
-
- -
-
-fit_transform(X, y=None, **fit_params)[source]
-

Fit all transformers and transform the data.

-
-
Parameters:
-
    -
  • X (pandas.DataFrame) – Input features

  • -
  • y (array-like, optional) – Target values

  • -
  • **fit_params – Additional fitting parameters

  • -
-
-
Returns:
-

Transformed features

-
-
Return type:
-

pandas.DataFrame

-
-
Raises:
-

ValueError – If no transformers are provided

-
-
-
- -
-
-merge_dataframes_by_column(Xs)[source]
-

Merge transformed features into a single DataFrame.

-
-
Parameters:
-

Xs (list) – List of transformed DataFrames

-
-
Returns:
-

Merged DataFrame

-
-
Return type:
-

pandas.DataFrame

-
-
-
- -
-
-transform(X)[source]
-

Transform X separately by each transformer.

-
-
Parameters:
-

X (pandas.DataFrame) – Input features

-
-
Returns:
-

Transformed features

-
-
Return type:
-

pandas.DataFrame

-
-
-
- -
- -
-
-predspot.utilities.contour_geojson(y, bbox, resolution, cmin, cmax, debug=False)[source]
-

Generate GeoJSON contours from spatial data.

-
-
Parameters:
-
    -
  • y (pandas.Series) – Values to contour

  • -
  • bbox (GeoDataFrame) – Bounding box for the contour

  • -
  • resolution (float) – Spatial resolution in kilometers

  • -
  • cmin (float) – Minimum contour value

  • -
  • cmax (float) – Maximum contour value

  • -
  • debug (bool, optional) – Enable debug printing. Defaults to False

  • -
-
-
Returns:
-

GeoJSON representation of the contours

-
-
Return type:
-

dict

-
-
Raises:
-

AssertionError – If bbox is not a GeoDataFrame

-
-
-
- -
- - -
- - -
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/build/html/objects.inv b/docs/build/html/objects.inv deleted file mode 100644 index 05c3ddcad1ca5ab22948f386f2b3ecc8894c01f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 634 zcmV-=0)_n}AX9K?X>NERX>N99Zgg*Qc_4OWa&u{KZXhxWBOp+6Z)#;@bUGkVa%E(5 zaBp-9BOq2~a&u{KZaN?eBOp|0Wgv28ZDDC{WMy(7Z)PBLXlZjGW@&6?AZc?TV{dJ6 za%FRKWn>_Ab7^j8AbMddhVT3er1nayUVBX&$!fOUHtKoB+LK)r z3>0wM{r4Ti_$$WF%?!`H1H3b?Rg_U#!$WL7Yk3CE#V4*z1OGl;W2@i2=XZp#+M4Lq zxr@qN_V^jbYOh-0t#odc)Y_)g5I!d4L`W7%Au(j#C5=N~u|`H2AaduyNnbeppiAIs z24;2I5Oakuq=;B4$6WCNF`;ZdIPw%ZjmDi9Tf zR;z3jbY0}V1^mhSR>mDYBt=gOBCC|!TgC5F1l2;6TEEr8)eK|LaN}gm_GJ(fBlHb3 z$hN>V-pB;lsrHZwhVO_S0$Oi2PODENk+WMZrkukAKMV_o;X;qNn;15-bB82N-9mMC z&YJPSio-Z>j_qlEHJ>}(&9w!3|vWjYc7j3Sn{MKy7FvgQ^K5bfB`5;40|+d zv>H?K^Zgs{coR$aL`9;S^4bo5u)DKY*yW{vqq z#~yF-ioLOD+&`VFpD|DR?P7@$nfaOc$Nrn)klN1IyKVn#U|0a@>+2HDv3$D6ynmDz UU`u0G@3yoCt4!wn2dx6J?7L$x^Z)<= diff --git a/docs/build/html/search.html b/docs/build/html/search.html deleted file mode 100644 index 5596f21..0000000 --- a/docs/build/html/search.html +++ /dev/null @@ -1,158 +0,0 @@ - - - - - - - Search — Predspot 1.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- -

Search

- - - - -

- Searching for multiple words only shows matches that contain - all words. -

- - -
- - - -
- - -
- - -
- - -
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/build/html/searchindex.js b/docs/build/html/searchindex.js deleted file mode 100644 index 67f14bb..0000000 --- a/docs/build/html/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({"alltitles": {"Citation": [[0, "citation"]], "Contributing": [[0, "contributing"]], "Crime Mapping": [[1, null]], "Dataset Preparation": [[2, null]], "Documentation:": [[0, null]], "Feature Engineering": [[3, null]], "Important Notice": [[0, "important-notice"]], "Indices and tables": [[0, "indices-and-tables"]], "Input Data Format": [[0, "input-data-format"]], "Installation": [[0, "installation"]], "Key Features": [[0, "key-features"]], "License": [[0, "license"]], "ML Modelling": [[4, null]], "Modules": [[0, "modules"]], "Overview": [[0, "overview"]], "Pipeline": [[5, null]], "Predspot": [[0, null]], "Quick Start": [[0, "quick-start"]], "Resources": [[0, "resources"]], "Utilities": [[6, null]]}, "docnames": ["index", "modules/crime_mapping", "modules/dataset_preparation", "modules/feature_engineering", "modules/ml_modelling", "modules/pipeline", "modules/utilities"], "envversion": {"sphinx": 64, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.intersphinx": 1, "sphinx.ext.todo": 2, "sphinx.ext.viewcode": 1}, "filenames": ["index.rst", "modules\\crime_mapping.rst", "modules\\dataset_preparation.rst", "modules\\feature_engineering.rst", "modules\\ml_modelling.rst", "modules\\pipeline.rst", "modules\\utilities.rst"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"": [0, 6], "0": [0, 1, 2], "01": 5, "1": [1, 2, 3, 4], "10": 0, "1000": [2, 5], "10000": 5, "12": [0, 5], "1256": 0, "1263": 0, "16th": 0, "2": 0, "2018": 0, "2019": 0, "2020": 5, "20th": 0, "25": 2, "250": [0, 5], "3": [0, 1, 3, 4], "31": 5, "4th": 0, "5": [4, 5], "8": 0, "9": 1, "A": [0, 2, 6], "And": 0, "For": 0, "If": [0, 1, 2, 3, 4, 5, 6], "In": 0, "It": [0, 1, 2], "The": [0, 1, 2, 3, 4], "To": 0, "abc": 1, "about": 0, "abstract": [1, 3], "across": 0, "activ": 0, "ad": [1, 3, 4], "adapt": 0, "addit": [1, 2, 6], "alia": [1, 3, 4], "align": [3, 6], "aligned_t": 3, "all": [0, 6], "allow": [1, 3, 4], "along": 2, "an": [0, 1, 3, 4], "analysi": [0, 1], "anyon": 0, "appli": 3, "apply_ts_decomposit": 3, "approach": 0, "ar": [0, 1, 3, 4, 6], "araujo": 0, "ara\u00fajo": 0, "archiv": 0, "area": [0, 2, 4, 5], "argument": 2, "arrai": [1, 6], "as_df": 1, "assertionerror": [2, 3, 6], "associ": 2, "autoregress": 3, "ax": 2, "bandwidth": 1, "bare": 0, "base": [0, 1, 2, 3, 4, 6], "baseestim": [1, 3, 4], "basic": 0, "bbox": [1, 6], "been": 4, "being": 4, "believ": 0, "between": [1, 2], "bezerra": 0, "bool": [1, 2, 3, 4, 5, 6], "borg": 0, "bound": [1, 6], "boundari": [0, 1, 2, 5], "box": [1, 6], "branch": 0, "brazil": 0, "bsd": 0, "build": 0, "c": 0, "cacho": 0, "can": 0, "cell": 1, "center": 1, "chang": [0, 1, 3, 4], "chunk": 1, "cite": 0, "citi": 0, "city_shap": 1, "class": [1, 2, 3, 4, 6], "claus": 0, "clean": 0, "cmax": 6, "cmin": 6, "code": 0, "codebas": 0, "collabor": 0, "column": [0, 2, 4, 5, 6], "combin": 0, "commit": 0, "commun": 0, "compat": [0, 3, 4], "complet": [4, 5], "compon": [2, 3], "comput": 0, "concept": 0, "conda": 0, "confer": 0, "consist": 0, "construct": 1, "contain": [2, 4], "contour": 6, "contour_geojson": 6, "coordin": 1, "core": 0, "correspond": [1, 2], "creat": [0, 1, 3], "create_gridhexagon": 1, "create_gridpoint": [0, 1], "create_gridsquar": 1, "create_hexagon": 1, "creation": 3, "crime": [0, 2, 4, 5], "crime_data": 5, "crime_dens": 4, "crime_map": [0, 1], "crime_sampl": 2, "crime_tag": 5, "crimes_df": [0, 5], "cross": [4, 5], "current": [0, 4], "cv": [4, 5], "d": [1, 3, 5], "daili": [1, 3], "data": [2, 5, 6], "data_point": 1, "datafram": [0, 1, 2, 3, 4, 5, 6], "dataset": [0, 4], "dataset_prepar": 2, "date": 5, "datetim": 1, "dd": 5, "debug": [1, 2, 3, 4, 5, 6], "decomposit": [0, 3], "default": [1, 2, 3, 4, 5, 6], "defin": [0, 2], "densiti": [0, 1, 4, 5], "depend": 0, "descart": 0, "detect": [0, 1], "develop": 0, "dict": [1, 2, 6], "dictionari": 2, "diff": [0, 3], "differ": [0, 3], "differenc": 3, "do": 0, "doesn": 4, "doesnt": 0, "dss": 0, "e": [1, 3, 4], "each": [1, 3, 4, 6], "earli": 0, "effect": [1, 3, 4], "effort": 0, "enabl": [1, 2, 3, 4, 5, 6], "enable_metadata_rout": [1, 3, 4], "encount": 0, "end": [1, 5], "end_tim": [1, 5], "engin": 0, "env": 0, "error": [1, 3, 4], "estim": [0, 1, 3, 4], "evalu": [0, 4, 5], "evaluate_pipelin": 5, "exampl": [0, 5], "except": 4, "execut": 5, "exist": [0, 1, 3, 4], "extend": 6, "extract": [3, 4], "fail": 4, "fals": [1, 2, 3, 4, 5, 6], "featur": [4, 6], "feature_engin": [0, 3], "feature_import": 4, "featuresc": 3, "featureselect": 4, "featureunion": 6, "feder": 0, "feel": 0, "fextract": [0, 4], "filter_place_ratio": 1, "fit": [0, 1, 4, 5, 6], "fit_grid": 1, "fit_param": 6, "fit_transform": 6, "fitted_pipelin": 5, "float": [1, 2, 5, 6], "fold": [4, 5], "follow": 0, "forecast": 4, "fork": 0, "form": 1, "format": 5, "four": 0, "framework": 0, "free": 0, "frequenc": [1, 3, 5], "from": [0, 3, 5, 6], "function": [0, 1, 2, 4, 5, 6], "g": [1, 3, 4], "gener": [5, 6], "generate_testdata": 5, "geodatafram": [0, 1, 2, 4, 5, 6], "geograph": 0, "geojson": 6, "geojsoncontour": 0, "geometri": [1, 2], "geopanda": [0, 2, 5], "get": [2, 4], "get_time_data_chunk": 1, "get_times_no_data": 1, "given": [1, 3, 4], "good": 0, "grand": 0, "grid": [0, 1, 4, 5], "grid_resolut": 5, "guarante": 0, "guid": [1, 3, 4], "guidelin": 0, "ha": [1, 3, 4], "handl": 2, "hasn": 4, "have": 0, "help": 0, "here": 0, "hexagon": 1, "hh": 5, "high": 0, "hotspot": [0, 1], "how": [1, 3, 4], "howev": 0, "hpcc": 0, "i": [0, 1, 2, 3, 4, 5, 6], "ident": 3, "identifi": 3, "ieee": 0, "ignor": [1, 3, 4], "implement": [0, 1, 3], "import": [4, 5], "incid": [1, 2, 5], "includ": [0, 1, 2, 3, 5, 6], "index": [0, 3, 6], "inform": 0, "input": [3, 4, 5, 6], "insid": [1, 3, 4], "instanc": 4, "instead": [1, 3, 4], "int": [2, 3, 4, 5, 6], "integ": 3, "interest": 0, "intern": 0, "invalid": [3, 4, 5], "issu": 0, "j": 0, "job": 6, "june": 0, "k": 1, "kde": [0, 1], "kernel": [0, 1], "keyword": 2, "kgrid": 1, "kilomet": [1, 6], "km": 5, "kwarg": 2, "l": [0, 1], "label": 3, "lag": [0, 3], "lag_df": 3, "lat": [0, 2], "latitud": [0, 2], "learn": [0, 3, 4], "length": [1, 3], "less": 3, "librari": 0, "like": 6, "list": [4, 5, 6], "load": [0, 5], "lon": [0, 2], "longitud": [0, 2], "m": [0, 1, 3, 5], "machin": [0, 4], "mai": 0, "main": [0, 5], "maintain": [0, 6], "make": 4, "make_lag_df": 3, "manag": [0, 2], "map": [0, 4], "master": 0, "matplotlib": [0, 2], "maximum": 6, "mechan": [1, 3, 4], "merg": 6, "merge_dataframes_by_column": 6, "meta": [1, 3, 4], "metadata": [1, 3, 4], "metadata_rout": [1, 3, 4], "method": [0, 1, 3, 4], "methodologi": 0, "metric": [4, 5], "might": 0, "minimum": 6, "miss": 5, "ml": 0, "ml_model": 4, "mm": 5, "model": [0, 5], "modern": 0, "modifi": 0, "modul": [1, 2, 3, 4, 5, 6], "monthli": [1, 3], "more": 0, "mse": [4, 5], "multi": 3, "must": 2, "n": 0, "n_job": 6, "n_point": 5, "name": [1, 3, 4, 6], "natal": 0, "need": 0, "new": 0, "newer": 0, "next": 4, "none": [1, 2, 3, 4, 5, 6], "nort": 0, "note": [0, 1, 3, 4], "number": [2, 3, 4, 5, 6], "numer": 1, "numpi": 0, "object": [1, 2, 3, 4], "onli": [1, 3, 4], "open": 0, "oper": 6, "option": [1, 2, 3, 4, 5, 6], "origin": [1, 3, 4], "other": [1, 3, 4], "otherwis": [1, 3, 4], "outdat": 0, "own": 0, "packag": 0, "page": 0, "panda": [0, 2, 3, 4, 5, 6], "pandasfeatureunion": [0, 6], "parallel": 6, "paramet": [1, 2, 3, 4, 5, 6], "part": 0, "pass": [1, 3, 4], "patrol": 0, "pattern": 0, "perform": [0, 4], "pip": 0, "pipelin": [0, 1, 3, 4], "place": 3, "plan": 0, "pleas": [0, 1, 3, 4], "plot": 2, "point": [0, 1], "polygon": 1, "posit": 3, "pp": 0, "pr": 0, "predict": [0, 4, 5], "predictionpipelin": [0, 4, 5], "predspot": [1, 2, 3, 4, 5, 6], "prefer": 0, "prepar": 0, "preprocess": 5, "present": 4, "preserv": 6, "print": [1, 2, 3, 4, 5, 6], "process": [2, 6], "product": 0, "project": 0, "properli": 0, "properti": [2, 3, 4], "proport": 2, "provid": [1, 2, 3, 4, 5, 6], "pull": 0, "purpos": 0, "push": 0, "python": 0, "quadratcount": 1, "quadratcount2": 1, "r2": [4, 5], "rais": [1, 2, 3, 4, 5, 6], "rang": 5, "re": 0, "readi": 0, "refer": 0, "regress": 4, "regressormixin": 4, "relev": [1, 3, 4], "repositori": 0, "represent": 6, "request": [0, 1, 3, 4], "requir": [0, 2, 5], "research": 0, "resolut": [0, 1, 5, 6], "result": [0, 1, 5], "retain": [1, 3, 4], "return": [1, 2, 3, 4, 5, 6], "return_coord": 1, "reviv": 0, "rio": 0, "rout": [1, 3, 4], "rtree": 0, "run": 5, "run_prediction_pipelin": 5, "sampl": 2, "sample_weight": 4, "scale": 3, "scienc": 0, "scikit": [0, 3, 4], "scipi": 0, "score": [4, 5], "search": 0, "season": [0, 3], "see": [1, 3, 4], "select": 4, "selector": 4, "self": [1, 3, 4], "separ": 6, "seri": [0, 3, 4, 6], "set": 2, "set_config": [1, 3, 4], "set_fit_request": [1, 4], "set_predict_request": 4, "set_score_request": 4, "set_transform_request": [1, 3, 4], "shape": 2, "should": [0, 1, 3, 4], "side": 1, "silverman": 1, "singl": 6, "size": 1, "sklearn": [1, 3, 4, 6], "smart": 0, "smartciti": 0, "so": 0, "softwar": 0, "some": [0, 1, 3, 4], "sourc": [1, 2, 3, 4, 5, 6], "space": [0, 1], "spatial": [0, 1, 2, 4, 5, 6], "spatio": [0, 1, 4], "spatiotemporalmap": 1, "specif": [0, 6], "split": 2, "squar": 1, "start": [1, 5], "start_tim": [1, 5], "state": 0, "statsmodel": 0, "step": 4, "still": 0, "stldecompos": 0, "str": [1, 3, 4, 5], "stseri": [3, 4], "studi": [0, 2, 4, 5], "study_area": [0, 2, 5], "study_area_gdf": [0, 5], "sub": [1, 3, 4], "submit": 0, "support": 4, "synthet": 5, "system": 0, "t": [0, 2, 3, 4], "tag": [0, 2], "target": [4, 6], "techniqu": 0, "tempor": [0, 1, 4], "test": [2, 5], "test_dataset": 2, "test_siz": 2, "tfreq": [0, 1, 3, 5], "than": 3, "thesi": 0, "thi": [0, 1, 2, 3, 4, 5, 6], "time": [0, 1, 3, 4, 5], "time_rang": 5, "timeseriesfeatur": 3, "timestamp": [0, 2], "tool": 0, "toward": 0, "train": 2, "train_dataset": 2, "train_test_split": 2, "transform": [1, 3, 4, 6], "transformer_list": 6, "transformer_weight": 6, "transformermixin": [1, 3, 4], "trend": [0, 3], "true": [1, 3, 4], "tupl": [1, 2, 3, 5, 6], "two": [0, 2], "type": [0, 1, 2, 3, 4, 5, 6], "u": 0, "ufrn": 0, "unchang": [1, 3, 4], "union": 6, "universidad": 0, "updat": [1, 3, 4], "upon": 0, "us": [0, 1, 3, 4, 5], "usag": 0, "user": [1, 3, 4], "util": [0, 1, 3, 4], "valid": [4, 5], "valu": [1, 6], "valuabl": 0, "valueerror": [5, 6], "variabl": 4, "version": [0, 1, 3, 4], "vieira": 0, "visual": [0, 2, 6], "w": [1, 3, 5], "wa": 0, "want": 0, "we": 0, "weekli": [1, 3], "weight": 6, "welcom": 0, "which": 1, "while": 0, "within": 1, "work": [0, 1, 3, 4, 6], "wrapper": 4, "x": [1, 3, 4, 6], "y": [0, 1, 4, 6], "you": [0, 1, 3, 4], "your": 0, "your_favorite_sklearn_model": 0, "yyyi": 5}, "titles": ["Predspot", "Crime Mapping", "Dataset Preparation", "Feature Engineering", "ML Modelling", "Pipeline", "Utilities"], "titleterms": {"citat": 0, "contribut": 0, "crime": 1, "data": 0, "dataset": 2, "document": 0, "engin": 3, "featur": [0, 3], "format": 0, "import": 0, "indic": 0, "input": 0, "instal": 0, "kei": 0, "licens": 0, "map": 1, "ml": 4, "model": 4, "modul": 0, "notic": 0, "overview": 0, "pipelin": 5, "predspot": 0, "prepar": 2, "quick": 0, "resourc": 0, "start": 0, "tabl": 0, "util": 6}}) \ No newline at end of file diff --git a/docs/changelog.md b/docs/changelog.md new file mode 100644 index 0000000..786b75d --- /dev/null +++ b/docs/changelog.md @@ -0,0 +1 @@ +--8<-- "CHANGELOG.md" diff --git a/docs/citing.md b/docs/citing.md new file mode 100644 index 0000000..666d07e --- /dev/null +++ b/docs/citing.md @@ -0,0 +1,43 @@ +# Citing Predspot + +If you use Predspot in your research, please cite the thesis and the paper +that introduced the framework: + +> Araujo, A., & Cacho, N. (2019). *Predspot: Predicting crime hotspots with +> machine learning*. Master's thesis, Universidade Federal do Rio Grande do +> Norte (UFRN), Natal, Brazil. + +> Araújo, A., Cacho, N., Bezerra, L., Vieira, C., & Borges, J. (2018). Towards +> a crime hotspot detection framework for patrol planning. In *2018 IEEE 20th +> International Conference on High Performance Computing and Communications; +> IEEE 16th International Conference on Smart City; IEEE 4th International +> Conference on Data Science and Systems (HPCC/SmartCity/DSS)* (pp. 1256-1263). +> IEEE. + +```bibtex +@mastersthesis{araujo2019predspot, + title = {Predspot: Predicting crime hotspots with machine learning}, + author = {Araujo, Adelson}, + year = {2019}, + school = {Universidade Federal do Rio Grande do Norte} +} + +@inproceedings{araujo2018towards, + title = {Towards a crime hotspot detection framework for patrol planning}, + author = {Ara{\'u}jo, Adelson and Cacho, N{\'a}dia and Bezerra, Lucas and Vieira, Carlos and Borges, Jo{\~a}o}, + booktitle = {2018 IEEE 20th International Conference on High Performance Computing and Communications; IEEE 16th International Conference on Smart City; IEEE 4th International Conference on Data Science and Systems (HPCC/SmartCity/DSS)}, + pages = {1256--1263}, + year = {2018}, + organization = {IEEE} +} +``` + +## Further reading + +- Chainey, S., Tompson, L., & Uhlig, S. (2008). The utility of hotspot mapping + for predicting spatial patterns of crime. *Security Journal*, 21, 4-28. +- Cleveland, R. B., Cleveland, W. S., McRae, J. E., & Terpenning, I. (1990). + STL: A seasonal-trend decomposition procedure based on loess. *Journal of + Official Statistics*, 6(1), 3-73. +- Silverman, B. W. (1986). *Density Estimation for Statistics and Data + Analysis*. Chapman & Hall. diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..ea38c9b --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1 @@ +--8<-- "CONTRIBUTING.md" diff --git a/docs/error_message b/docs/error_message deleted file mode 100644 index 0acf455..0000000 --- a/docs/error_message +++ /dev/null @@ -1,57 +0,0 @@ -Running Sphinx v8.1.3 -loading translations [en]... done -making output directory... done -building [mo]: targets for 0 po files that are out of date -writing output... -building [html]: targets for 7 source files that are out of date -updating environment: [new config] 7 added, 0 changed, 0 removed -reading sources... [100%] modules/utilities -looking for now-outdated files... none found -pickling environment... done -checking consistency... C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\docs\source\modules\crime_mapping.rst: WARNING: document isn't included in any toctree -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\docs\source\modules\dataset_preparation.rst: WARNING: document isn't included in any toctree -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\docs\source\modules\feature_engineering.rst: WARNING: document isn't included in any toctree -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\docs\source\modules\ml_modelling.rst: WARNING: document isn't included in any toctree -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\docs\source\modules\pipeline.rst: WARNING: document isn't included in any toctree -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\docs\source\modules\utilities.rst: WARNING: document isn't included in any toctree -done -preparing documents... done -copying assets... -copying static files... -Writing evaluated template result to C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\docs\build\html\_static\basic.css -Writing evaluated template result to C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\docs\build\html\_static\documentation_options.js -Writing evaluated template result to C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\docs\build\html\_static\language_data.js -Writing evaluated template result to C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\docs\build\html\_static\alabaster.css -copying static files: done -copying extra files... -copying extra files: done -copying assets: done -writing output... [100%] modules/utilities -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\crime_mapping.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\crime_mapping.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\crime_mapping.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\crime_mapping.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\crime_mapping.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\crime_mapping.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\crime_mapping.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\feature_engineering.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\feature_engineering.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\feature_engineering.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\feature_engineering.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\feature_engineering.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\feature_engineering.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\ml_modelling.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\ml_modelling.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\ml_modelling.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\ml_modelling.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\ml_modelling.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\ml_modelling.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -C:\Users\DiasDeAraujoJunioA\Documents\Repositories\predspot\predspot\ml_modelling.py:docstring of sklearn.utils._metadata_requests.RequestMethod.__get__..func:3: WARNING: undefined label: 'metadata_routing' [ref.ref] -generating indices... genindex done -highlighting module code... [100%] predspot.utilities -writing additional pages... search done -dumping search index in English (code: en)... done -dumping object inventory... done -build succeeded, 26 warnings. - -The HTML pages are in build\html. \ No newline at end of file diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md new file mode 100644 index 0000000..ed42c27 --- /dev/null +++ b/docs/getting-started/installation.md @@ -0,0 +1,45 @@ +# Installation + +Predspot requires **Python 3.10 or newer** and is published on PyPI: + +```bash +pip install predspot +``` + +Optional extras add features that need heavier dependencies: + +| Extra | Installs | Enables | +|-------|----------|---------| +| `osm` | [osmnx](https://osmnx.readthedocs.io) | [`load_study_area`][predspot.crime_mapping.load_study_area] — study areas from OpenStreetMap | +| `contour` | [geojsoncontour](https://github.com/bartromgens/geojsoncontour) | [`contour_geojson`][predspot.utilities.contour_geojson] — GeoJSON contour export | +| `dev` | pytest, ruff, build, twine | Running the test suite and building the package | + +```bash +pip install "predspot[osm,contour]" +``` + +The core dependencies — pandas, GeoPandas, Shapely, NumPy, SciPy, scikit-learn, +statsmodels and Matplotlib — are installed automatically. + +!!! tip "conda users" + GeoPandas and its GEOS/PROJ stack install fine from PyPI wheels nowadays, + but if you prefer conda: `conda install -c conda-forge geopandas` first, + then `pip install predspot` in the same environment. + +## From source + +```bash +git clone https://github.com/adaj/predspot.git +cd predspot +pip install -e ".[dev,osm,contour]" +pytest +``` + +See [Contributing](../contributing.md) for the development workflow. + +## Checking the installation + +```python +import predspot +print(predspot.__version__) +``` diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md new file mode 100644 index 0000000..b9750eb --- /dev/null +++ b/docs/getting-started/quickstart.md @@ -0,0 +1,124 @@ +# Quickstart + +This walkthrough goes from nothing to a hotspot forecast in a few minutes, using +synthetic data so you can run it as is. Every step is explained in depth in the +[user guide](../guide/concepts.md). + +## 1. A study area + +Predspot needs the boundary of the region you are studying as a GeoDataFrame +with a CRS. The easiest way is to fetch it from OpenStreetMap +(`pip install "predspot[osm]"`): + +```python +from predspot import load_study_area + +study_area = load_study_area("Natal, Rio Grande do Norte, Brazil") +``` + +Any other source works too — a shapefile or GeoJSON read with +`geopandas.read_file`, or a simple box: + +```python +import geopandas as gpd +from shapely.geometry import box + +study_area = gpd.GeoDataFrame(geometry=[box(-35.30, -5.90, -35.20, -5.80)], crs="EPSG:4326") +``` + +## 2. Crime events + +Your data must be a pandas DataFrame with four columns: + +| Column | Meaning | +|--------|---------| +| `tag` | Crime type (any string) | +| `t` | Timestamp (anything `pandas.to_datetime` understands) | +| `lon`, `lat` | Coordinates in WGS84 degrees | + +No data at hand? Generate realistic synthetic events inside the study area: + +```python +from predspot import generate_crimes + +crimes = generate_crimes(study_area, n_events=5000, n_hotspots=4, + start="2019-01-01", end="2020-12-31", seed=0) +crimes.head() +``` + +Wrap both in a [`Dataset`][predspot.dataset_preparation.Dataset]: + +```python +from predspot import Dataset + +dataset = Dataset(crimes, study_area) +dataset.plot() # study area + a sample of events +``` + +
+ ![Synthetic dataset](../assets/synthetic_dataset.png){ width="480" } +
+ +## 3. Build and fit a pipeline + +A [`PredictionPipeline`][predspot.ml_modelling.PredictionPipeline] chains a +spatio-temporal **mapping**, a **feature extraction** step and a scikit-learn +**estimator**: + +```python +from sklearn.ensemble import RandomForestRegressor + +from predspot import PredictionPipeline, PandasFeatureUnion +from predspot.crime_mapping import KDE, create_gridpoints +from predspot.feature_engineering import Seasonality, Trend, Diff + +grid = create_gridpoints(study_area, resolution=1) # points every 1 km + +pipeline = PredictionPipeline( + mapping=KDE(tfreq="M", grid=grid), # monthly density per point + fextraction=PandasFeatureUnion([ + ("seasonal", Seasonality(lags=6)), + ("trend", Trend(lags=6)), + ("diff", Diff(lags=6)), + ]), + estimator=RandomForestRegressor(n_estimators=100, random_state=0), + random_state=0, +) +pipeline.fit(dataset) +``` + +[`build_default_pipeline`][predspot.pipeline.build_default_pipeline] builds +exactly this kind of pipeline (with scaling and feature selection) in one call. + +## 4. Evaluate and forecast + +```python +pipeline.evaluate("r2", cv=3) # one score per time series fold +forecast = pipeline.predict() # DataFrame indexed by (t, places) +``` + +`forecast` holds the predicted density for the month after the last observed +one, for every grid point. Join it with the grid to map it: + +```python +hot = pipeline.grid.join(forecast.droplevel("t")) +hot.plot(column="crime_density", cmap="magma", markersize=12, legend=True) +``` + +Calling `predict()` again forecasts the following month, and so on — each +forecast is appended to the series and the features are recomputed. + +## 5. Prefer counts on hexagons? + +Swap the mapping and the grid; everything else stays the same: + +```python +from predspot.crime_mapping import QuadratCount, create_gridhexagonal + +mapping = QuadratCount(tfreq="W", grid=create_gridhexagonal(study_area, resolution=1)) +``` + +
+ ![KDE vs QuadratCount](../assets/kde_vs_quadrat.png){ width="900" } +
The same month mapped with KDE on a point grid (left) and with event counts on a hexagonal grid (right).
+
diff --git a/docs/guide/concepts.md b/docs/guide/concepts.md new file mode 100644 index 0000000..5dc896f --- /dev/null +++ b/docs/guide/concepts.md @@ -0,0 +1,76 @@ +# How Predspot works + +Predspot frames hotspot prediction as a **supervised time series problem on a +spatial grid**. The library is organised around four stages that map onto four +modules: + +```mermaid +flowchart LR + A["Crime events
(tag, t, lon, lat)"] --> B["Dataset
dataset_preparation"] + S["Study area
(GeoDataFrame)"] --> B + B --> C["Spatio-temporal mapping
crime_mapping"] + C --> D["Feature engineering
feature_engineering"] + D --> E["Estimator
ml_modelling"] + E --> F["Forecast for
the next period"] +``` + +## 1. Dataset + +A [`Dataset`][predspot.dataset_preparation.Dataset] is a validated pair of +crime events (a DataFrame with `tag`, `t`, `lon`, `lat`) and a study area +(a GeoDataFrame with a CRS). The events become a GeoDataFrame of points in +WGS84. See [Data and study areas](data.md). + +## 2. Spatio-temporal mapping + +The study area is discretised into **places** — a grid of points, hexagons or +squares — and time into **periods** (`tfreq`: daily, weekly or monthly). A +mapping assigns a value to every `(period, place)` pair: + +- [`KDE`][predspot.crime_mapping.KDE] fits a Gaussian kernel density estimate + to the events of each period and evaluates it at the grid points (the + default, and the approach of the original thesis); +- [`QuadratCount`][predspot.crime_mapping.QuadratCount] counts the events + falling in each polygonal cell. + +Both return the same object: a `pandas.Series` named `crime_density` with a +`(t, places)` MultiIndex — the *spatio-temporal series*. See +[Grids and mapping](mapping.md). + +## 3. Feature engineering + +For every place, the history of the series is turned into lagged features. +Each feature class first transforms the series (identity, first difference, +STL seasonal or trend component) and then builds `lags` columns with the +previous values. A [`PandasFeatureUnion`][predspot.utilities.PandasFeatureUnion] +concatenates several of them. The result is a feature matrix indexed by +`(t, places)`, with one extra row per place for the period *after* the last +observed one — the row the model will forecast. See +[Feature engineering](features.md). + +## 4. Estimator + +Any scikit-learn regressor learns to map the features at `t` to the series +value at `t`. Because every row is a `(period, place)` pair, a single model +is shared by all places, and the spatial structure enters through the mapping +and the features rather than through the model. `PredictionPipeline` shuffles +the rows for training, evaluates with +[`TimeSeriesSplit`](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.TimeSeriesSplit.html) +and forecasts the next period. See [Prediction and evaluation](modelling.md). + +## Design choices worth knowing + +- **Resolutions are in kilometres.** Grid functions convert them to degrees + using the length of a degree at the equator, so cells are slightly + distorted away from it; for city-scale study areas this is negligible. +- **Grids are built in WGS84 and re-projected to the study area's CRS.** + Cell centroids are computed in a projected CRS, so `lon`/`lat` columns + are accurate. +- **KDE bandwidth is estimated once.** With `bandwidth="silverman"` (or + `"scott"`) the factor is estimated on the first period with at least three + events and held fixed, so densities are comparable across periods. Pass a + number to fix it yourself. +- **Periods with no events are kept** (zeros everywhere), and `start_time` / + `end_time` can extend the series beyond the observed range. +- **Monthly periods use pandas' `ME` (month end) frequency.** You can keep + writing `tfreq="M"`; it is translated internally. diff --git a/docs/guide/data.md b/docs/guide/data.md new file mode 100644 index 0000000..137fad6 --- /dev/null +++ b/docs/guide/data.md @@ -0,0 +1,111 @@ +# Data and study areas + +## Crime events + +Predspot works with *point events*: one row per crime with a type, a +timestamp and a location. The input is a plain pandas DataFrame with the +columns below; extra columns are kept and ignored. + +| Column | Type | Notes | +|--------|------|-------| +| `tag` | str | Crime type. Filter on it to model one type at a time. | +| `t` | datetime-like | Parsed with `pandas.to_datetime`. Timezone-naive local time is the simplest. | +| `lon` | float | Longitude in WGS84 degrees (EPSG:4326). | +| `lat` | float | Latitude in WGS84 degrees. | + +```python +import pandas as pd + +crimes = pd.read_csv("crimes.csv", parse_dates=["t"]) +crimes = crimes[crimes["tag"] == "robbery"] +``` + +## Study area + +The study area is a GeoDataFrame with one or more polygons and a CRS. It +bounds the grids and is used for plotting; events outside it are not +removed, but grid cells are only created where they intersect it. + +=== "From OpenStreetMap" + + ```python + from predspot import load_study_area + + study_area = load_study_area("Natal, Rio Grande do Norte, Brazil") + ``` + + [`load_study_area`][predspot.crime_mapping.load_study_area] geocodes the + place with [osmnx](https://osmnx.readthedocs.io) (Nominatim) and returns + its administrative boundary. Be specific — add the state and country — + so that the first match is the boundary you want; `which_result` lets you + pick another match. Requires `pip install "predspot[osm]"`. + +=== "From a file" + + ```python + import geopandas as gpd + + study_area = gpd.read_file("city_limits.geojson") # or .shp, .gpkg, ... + assert study_area.crs is not None + ``` + +=== "From a bounding box" + + ```python + import geopandas as gpd + from shapely.geometry import box + + study_area = gpd.GeoDataFrame( + geometry=[box(-35.30, -5.90, -35.20, -5.80)], crs="EPSG:4326") + ``` + +Any CRS is accepted; grids are re-projected to it. + +## The `Dataset` object + +```python +from predspot import Dataset + +dataset = Dataset(crimes, study_area) +dataset # summary with counts per tag +dataset.crimes # GeoDataFrame of points (WGS84), `t` parsed +dataset.study_area +dataset.plot(crime_samples=2000) +train, test = dataset.train_test_split(test_size=0.25, random_state=0) +``` + +`Dataset` validates the inputs and never modifies the DataFrame you pass in. + +## Synthetic data + +[`generate_crimes`][predspot.synthetic.generate_crimes] produces events with +the structure real crime data tends to have, inside any study area: + +- **space**: `n_hotspots` Gaussian clusters (centres drawn inside the area, + spread `hotspot_sd_km`) holding a `hotspot_share` of the events, plus a + uniform background; +- **time**: an intensity combining a linear `trend`, an annual cycle + (`annual_amplitude`, `annual_peak_month`), a `weekly_profile` (Monday to + Sunday) and an `hourly_profile` (0-23 h). Timestamps are sampled by + thinning, so the patterns are exact in expectation. + +```python +from predspot import generate_crimes + +crimes, hotspots = generate_crimes( + study_area, n_events=8000, n_hotspots=4, hotspot_sd_km=0.6, + start="2019-01-01", end="2020-12-31", + trend=0.3, annual_amplitude=0.3, annual_peak_month=12, + tags={"robbery": 0.6, "burglary": 0.4}, + seed=7, return_hotspots=True, +) +``` + +`hotspots` is a GeoDataFrame with the centre, spread and share of each +hotspot — handy to check what a model recovers. Set `n_hotspots=0` for a +uniform map, `weekly_profile=None` / `hourly_profile=None` to switch those +patterns off, and a `seed` for reproducibility. + +
+ ![Synthetic dataset](../assets/synthetic_dataset.png){ width="480" } +
diff --git a/docs/guide/features.md b/docs/guide/features.md new file mode 100644 index 0000000..cb1a56e --- /dev/null +++ b/docs/guide/features.md @@ -0,0 +1,100 @@ +# Feature engineering + +The spatio-temporal series is a panel: one time series per place. Feature +classes in [`predspot.feature_engineering`][predspot.feature_engineering] +turn each of those series into lagged predictors. + +## Feature classes + +Every class takes `lags` (how many previous periods become columns) and an +optional `tfreq` (inferred from the series when omitted): + +| Class | Transformation before lagging | Columns | +|-------|-------------------------------|---------| +| [`AR`][predspot.feature_engineering.AR] | none (raw values) | `ar_1 … ar_k` | +| [`Diff`][predspot.feature_engineering.Diff] | first difference | `diff_1 … diff_k` | +| [`Seasonality`][predspot.feature_engineering.Seasonality] | seasonal component of an STL decomposition with period `lags` | `seasonal_1 … seasonal_k` | +| [`Trend`][predspot.feature_engineering.Trend] | trend component of the same STL decomposition | `trend_1 … trend_k` | + +`ar_1` at period `t` is the value at `t-1`, `ar_2` the value at `t-2`, and so +on. For `Seasonality` and `Trend`, `lags` doubles as the STL period: use 12 +for monthly data with a yearly cycle, 7 for daily data with a weekly cycle, +52 for weekly data, and make sure the series is at least twice as long. + +```python +from predspot.feature_engineering import AR, Seasonality + +X = AR(lags=3).fit_transform(stseries) +X.head() +``` + +``` + ar_1 ar_2 ar_3 +t places +2019-04-30 0 14.83 14.02 12.31 + 1 ... +``` + +Note the index: features at `t` only use values **before** `t`, and the +matrix always includes one extra period after the last observed one — the +row a fitted model uses to forecast. + +## Combining features + +[`PandasFeatureUnion`][predspot.utilities.PandasFeatureUnion] runs several +feature transformers and concatenates their columns, aligning on the +`(t, places)` index and dropping rows that any transformer could not fill +(warm-up periods): + +```python +from predspot import PandasFeatureUnion +from predspot.feature_engineering import Seasonality, Trend, Diff + +fextraction = PandasFeatureUnion([ + ("seasonal", Seasonality(lags=12)), + ("trend", Trend(lags=12)), + ("diff", Diff(lags=12)), +]) +X = fextraction.fit_transform(stseries) +``` + +## Scaling and selection inside the estimator + +Because features are a DataFrame, plain scikit-learn transformers would drop +the index. [`FeatureScaling`][predspot.feature_engineering.FeatureScaling] +and [`FeatureSelection`][predspot.ml_modelling.FeatureSelection] wrap any +scaler / selector so that DataFrames come out the other side, and +[`Model`][predspot.ml_modelling.Model] does the same for the regressor: + +```python +from sklearn.pipeline import Pipeline +from sklearn.preprocessing import QuantileTransformer +from sklearn.feature_selection import RFE +from sklearn.ensemble import RandomForestRegressor +from predspot.feature_engineering import FeatureScaling +from predspot.ml_modelling import FeatureSelection, Model + +estimator = Pipeline([ + ("scaling", FeatureScaling(QuantileTransformer(n_quantiles=10))), + ("selection", FeatureSelection(RFE(RandomForestRegressor(n_estimators=20)))), + ("model", Model(RandomForestRegressor(n_estimators=100))), +]) +``` + +## Writing your own feature + +Subclass [`TimeSeriesFeatures`][predspot.feature_engineering.TimeSeriesFeatures], +set `label` and implement `apply_ts_decomposition(ts)`, which receives the +series of one place and returns the series to lag: + +```python +from predspot.feature_engineering import TimeSeriesFeatures + +class RollingMean(TimeSeriesFeatures): + @property + def label(self): + return "rmean" + + def apply_ts_decomposition(self, ts): + return ts.rolling(3, min_periods=1).mean() +``` diff --git a/docs/guide/mapping.md b/docs/guide/mapping.md new file mode 100644 index 0000000..66f4a10 --- /dev/null +++ b/docs/guide/mapping.md @@ -0,0 +1,105 @@ +# Grids and spatio-temporal mapping + +Mapping turns raw events into the **spatio-temporal series**: one value per +place and period. It is the step that defines what "hotspot" means in your +analysis. + +## Grids + +All grid functions take the study area and a `resolution` in **kilometres**, +and return a GeoDataFrame with `geometry`, `lon`, `lat` columns and an +index named `places`. Only cells intersecting the study area are kept. + +| Function | Cells | Pairs with | +|----------|-------|------------| +| [`create_gridpoints`][predspot.crime_mapping.create_gridpoints] | points spaced `resolution` km apart | `KDE` (default) | +| [`create_gridhexagonal`][predspot.crime_mapping.create_gridhexagonal] | hexagons with the area of a `resolution` km square | `QuadratCount` | +| [`create_gridsquares`][predspot.crime_mapping.create_gridsquares] | squares of side `resolution` km | `QuadratCount` | + +```python +from predspot.crime_mapping import create_gridpoints, create_gridhexagonal + +points = create_gridpoints(study_area, resolution=0.5) +hexes = create_gridhexagonal(study_area, resolution=1) +hexes.plot(edgecolor="white") +``` + +Choosing a resolution is a trade-off: finer grids give more spatial detail +but more places to model (and, for KDE, more points to evaluate); the +original work used 250 m to 1 km for city-scale studies. Hexagons are +preferable to squares for counts because every neighbour is at the same +distance. + +## Time frequency + +`tfreq` sets the period: `"D"` (daily), `"W"` (weekly, Sunday-ending) or +`"M"` (monthly, month-end labels). Periods without events are kept with +zeros, and `start_time` / `end_time` can pad the series. + +## KDE + +[`KDE`][predspot.crime_mapping.KDE] fits a Gaussian kernel density estimate +(`scipy.stats.gaussian_kde`) to the event coordinates of each period and +evaluates it at the grid points. The result is a smooth density surface — +the classic hotspot map. + +```python +from predspot.crime_mapping import KDE + +mapping = KDE(tfreq="M", grid=points, bandwidth="silverman") +stseries = mapping.fit_transform(dataset.crimes) +stseries.head() +``` + +``` +t places +2019-01-31 0 12.31 + 1 14.02 + ... +Name: crime_density, dtype: float64 +``` + +`bandwidth` can be `"silverman"`, `"scott"` or a number. With a rule of thumb +the factor is estimated on the **first period with at least three events and +then held fixed**, so that densities are comparable across time; `mapping.factor` +tells you what was used. Periods with fewer than three events map to zero. + +## QuadratCount + +[`QuadratCount`][predspot.crime_mapping.QuadratCount] counts the events that +fall inside each polygonal cell. Counts are easier to interpret than +densities (they are numbers of crimes) and lend themselves to count models, +at the price of a blockier map and sensitivity to how the grid is laid out. + +```python +from predspot.crime_mapping import QuadratCount + +mapping = QuadratCount(tfreq="W", grid=hexes) +stseries = mapping.fit_transform(dataset.crimes) +stseries.groupby("t").sum() # events per week +``` + +
+ ![KDE vs QuadratCount](../assets/kde_vs_quadrat.png){ width="900" } +
+ +## Writing your own mapping + +Subclass [`SpatioTemporalMapping`][predspot.crime_mapping.SpatioTemporalMapping] +and implement `fit_grid(data_points)`, which receives the events of one period +and returns a `{place: value}` dict. The base class handles the time splitting, +empty periods and the assembly of the series, so your mapping is immediately +usable in a `PredictionPipeline`. + +```python +from predspot.crime_mapping import SpatioTemporalMapping + +class NearestCount(SpatioTemporalMapping): + """Number of events closer than 300 m to each grid point.""" + + def fit_grid(self, data_points): + pts = data_points.to_crs(self._grid.estimate_utm_crs()) + grid = self._grid.to_crs(pts.crs) + return {p: int(pts.distance(geom).lt(300).sum()) + for p, geom in grid.geometry.items()} +``` diff --git a/docs/guide/modelling.md b/docs/guide/modelling.md new file mode 100644 index 0000000..639be8f --- /dev/null +++ b/docs/guide/modelling.md @@ -0,0 +1,106 @@ +# Prediction and evaluation + +[`PredictionPipeline`][predspot.ml_modelling.PredictionPipeline] ties the +mapping, the features and a scikit-learn estimator together. + +## Fitting + +```python +from sklearn.ensemble import GradientBoostingRegressor +from predspot import PredictionPipeline, PandasFeatureUnion +from predspot.crime_mapping import KDE, create_gridpoints +from predspot.feature_engineering import AR, Seasonality, Trend + +pipeline = PredictionPipeline( + mapping=KDE(tfreq="M", grid=create_gridpoints(study_area, resolution=1)), + fextraction=PandasFeatureUnion([ + ("ar", AR(lags=3)), + ("seasonal", Seasonality(lags=12)), + ("trend", Trend(lags=12)), + ]), + estimator=GradientBoostingRegressor(random_state=0), + random_state=0, +) +pipeline.fit(dataset) +``` + +`fit` computes the spatio-temporal series (`pipeline.stseries`), the feature +matrix (`pipeline.features`) and trains the estimator on every `(t, places)` +row whose target is known. Rows are shuffled (`random_state`) since the +estimator sees them as independent samples. + +## Evaluating + +```python +pipeline.evaluate("r2", cv=5) # or "mse" +``` + +Periods are ordered and split with scikit-learn's `TimeSeriesSplit`, so each +fold trains on earlier periods and tests on later ones — no leakage from the +future. One score per fold is returned; the estimator is refitted on all the +data afterwards. + +!!! note + Scores measure how well the *density surface* of the next period is + predicted at every place. For patrol planning you may care more about the + ranking of places (e.g. hit rate of the top-*k* cells); compute it from + `pipeline.predict()` against the series of the following period. + +## Forecasting + +```python +forecast = pipeline.predict() # next period +forecast.head() +``` + +``` + crime_density +t places +2021-01-31 0 27.30 + 1 46.43 +``` + +Each `predict()` call appends its forecast to `pipeline.stseries`, recomputes +the features and advances `pipeline.next_time`, so repeated calls produce a +multi-step, recursive forecast. Join the result with `pipeline.grid` to map +it: + +```python +hot = pipeline.grid.join(forecast.droplevel("t")) +top = hot.nlargest(20, "crime_density") # top-20 places for patrol planning +hot.plot(column="crime_density", cmap="magma", markersize=12, legend=True) +``` + +
+ ![Forecast](../assets/forecast.png){ width="900" } +
+ +## Feature importances + +When the estimator (or the last step of a `Pipeline`) exposes +`feature_importances_`, `pipeline.feature_importances` returns them by +feature name, accounting for a `FeatureSelection` step if present: + +```python +pipeline.feature_importances.head() +``` + +## Modelling one crime type at a time + +Different crime types have different dynamics; the usual approach is one +pipeline per `tag`: + +```python +pipelines = {} +for tag, events in crimes.groupby("tag"): + pipelines[tag] = build_default_pipeline(study_area).fit(Dataset(events, study_area)) +``` + +## One-call helpers + +[`predspot.pipeline`][predspot.pipeline] offers +[`build_default_pipeline`][predspot.pipeline.build_default_pipeline] (KDE on +points, seasonal/trend/diff features, quantile scaling, RFE selection and a +random forest) and +[`run_prediction_pipeline`][predspot.pipeline.run_prediction_pipeline], which +also filters by tag and time of day and fits in one go. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..d9ba291 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,60 @@ +# 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. + +
+ ![Observed density and forecast](assets/forecast.png){ width="900" } +
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.
+
+ +## 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. diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index dc1312a..0000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=source -set BUILDDIR=build - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -if "%1" == "" goto help - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index 730d396..0000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,58 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# For the full list of built-in configuration values, see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Project information ----------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information - -import os -import sys -sys.path.insert(0, os.path.abspath('../..')) - -project = 'Predspot' -copyright = '2024, Adelson Araujo' -author = 'Adelson Araujo' -release = '1.0' - -# -- General configuration --------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration - -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.napoleon', - 'sphinx.ext.viewcode', - 'sphinx.ext.githubpages', - 'sphinx.ext.intersphinx', - 'sphinx.ext.autosectionlabel', - 'sphinx.ext.todo', -] - -templates_path = ['_templates'] -exclude_patterns = [] - -# -- Options for HTML output ------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output - -html_theme = 'alabaster' -html_baseurl = 'https://adaj.github.io/predspot/' - -html_theme_options = { - 'description': 'Crime hotspot prediction using machine learning', - 'github_user': 'adaj', - 'github_repo': 'predspot', - 'github_button': True, - 'github_type': 'star', - 'github_count': True, - 'fixed_sidebar': True, - 'page_width': '1000px', - 'sidebar_width': '250px', - 'show_powered_by': True, - 'show_relbars': True, - 'sidebar_collapse': True, - 'sidebar_includehidden': True, - 'extra_nav_links': { - 'Project Github': 'https://github.com/adaj/predspot', - 'Documentation': 'https://adaj.github.io/predspot/', - } -} \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst deleted file mode 100644 index 3729b47..0000000 --- a/docs/source/index.rst +++ /dev/null @@ -1,177 +0,0 @@ -Predspot -======== - -Overview --------- - -Predspot is an early project for a Python library for spatio-temporal crime prediction and hotspot detection. It combines machine learning techniques with spatial analysis to help predict and visualize crime patterns across time and space. To properly use this library, you preferably need to have a crime dataset and a study area. - -The results of this library are not guaranteed to be good, but it is a good starting point for spatio-temporal crime prediction. This project is barely maintained, so please if you want to collaborate, open an issue or a PR. - -Important Notice ----------------- - -.. warning:: - This project was developed as part of a master's thesis and is currently in an archived state. While the core functionality exists, you may encounter compatibility issues with newer Python package versions. The code can work with some effort, but please note: - - * This is not production-ready software - * Some dependencies are outdated and may require specific versions - * You might need to modify some code to work with newer package versions - * The project was created for research purposes - - However, we believe the methodologies and approaches used here are still valuable! If you're interested in crime hotspot prediction, feel free to: - - * Use this as a reference implementation - * Adapt the code to modern dependencies - * Build upon these concepts for your own projects - * Contribute to modernizing the codebase - - We welcome anyone interested in reviving or learning from this project! - -Key Features ------------- -* Spatial and temporal crime mapping -* Feature engineering for time series data -* Machine learning-based prediction pipeline -* Crime hotspot detection using Kernel Density Estimation -* Visualization tools for crime patterns - -Quick Start ------------ - -Basic usage example: - -.. code-block:: python - - from predspot import Dataset, PredictionPipeline - from predspot.crime_mapping import KDE, create_gridpoints - from predspot.feature_engineering import Seasonality, Trend, Diff - - # Load and prepare data - dataset = Dataset(crimes_df, study_area_gdf) - - # Create prediction pipeline - pipeline = PredictionPipeline( - mapping=KDE(tfreq='M', grid=create_gridpoints(study_area, resolution=250)), - fextraction=PandasFeatureUnion([ - ('seasonal', Seasonality(lags=12)), - ('trend', Trend(lags=12)), - ('diff', Diff(lags=12)) - ]), - estimator=your_favorite_sklearn_model - ) - - # Fit and predict - pipeline.fit(dataset) - predictions = pipeline.predict() - -Modules -------- - -The library consists of four main modules: - -* :doc:`modules/dataset_preparation`: Module for preparing and managing crime datasets and study areas. -* :doc:`modules/crime_mapping`: Module for spatial and temporal crime mapping, including KDE-based hotspot detection. -* :doc:`modules/feature_engineering`: Module for time series feature engineering, including seasonality, trend, and difference features. -* :doc:`modules/ml_modelling`: Module that implements the prediction pipeline and model evaluation. - -And two utilities: - -* :doc:`modules/utilities`: Utility functions for data preparation and visualization. -* :doc:`modules/pipeline`: Functions for the prediction pipeline. - -Installation ------------- - -Create conda env and install requirements: - -.. code-block:: bash - - conda create -n predspot python=3.8 - conda activate predspot - conda install -y rtree geopandas # if doesnt work, do: `conda clean --all` - pip install pandas statsmodels==0.10.2 geojsoncontour stldecompose scikit-learn matplotlib descartes - pip install . - -Required dependencies: - -* pandas -* geopandas -* numpy -* scikit-learn -* scipy -* stldecompose -* matplotlib - -Input Data Format ------------------ - -The crime data should be a pandas DataFrame with the following required columns: - -* ``tag``: Crime type -* ``t``: Timestamp -* ``lon``: Longitude -* ``lat``: Latitude - -The study area should be a GeoDataFrame defining the geographical boundaries of interest. - -Resources ---------- - -For more information on the methods used in Predspot, please search more about these methods: - -* Kernel Density Estimation for crime hotspot detection -* Time series decomposition for feature engineering -* Spatio-temporal crime prediction techniques - -License -------- - -BSD 3-Clause. - -Contributing ------------- - -Contributions are welcome! Please feel free to submit a Pull Request. - -Guidelines for contributing: - -1. Fork the repository -2. Create your feature branch -3. Commit your changes -4. Push to the branch -5. Create a new Pull Request - -Citation --------- - -If you use Predspot in your research, please cite us: - -.. code-block:: text - - Araujo, A., & Cacho, N. (2019). Predspot: Predicting crime hotspots with machine learning. - Master's thesis, UFRN (Universidade Federal do Rio Grande do Norte), Natal, Brazil. - - Araújo, A., Cacho, N., Bezerra, L., Vieira, C., & Borges, J. (2018, June). - Towards a crime hotspot detection framework for patrol planning. - In 2018 IEEE 20th International Conference on High Performance Computing and Communications; - IEEE 16th International Conference on Smart City; - IEEE 4th International Conference on Data Science and Systems (HPCC/SmartCity/DSS) (pp. 1256-1263). IEEE. - -.. toctree:: - :maxdepth: 2 - :caption: Documentation: - - modules/dataset_preparation - modules/crime_mapping - modules/feature_engineering - modules/ml_modelling - modules/utilities - modules/pipeline - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` \ No newline at end of file diff --git a/docs/source/modules/crime_mapping.rst b/docs/source/modules/crime_mapping.rst deleted file mode 100644 index 4feb87a..0000000 --- a/docs/source/modules/crime_mapping.rst +++ /dev/null @@ -1,9 +0,0 @@ -Crime Mapping -============= - -.. automodule:: predspot.crime_mapping - :members: - :undoc-members: - :show-inheritance: - :no-index: - diff --git a/docs/source/modules/dataset_preparation.rst b/docs/source/modules/dataset_preparation.rst deleted file mode 100644 index ef4952c..0000000 --- a/docs/source/modules/dataset_preparation.rst +++ /dev/null @@ -1,8 +0,0 @@ -Dataset Preparation -=================== - -.. automodule:: predspot.dataset_preparation - :members: - :undoc-members: - :show-inheritance: - :no-index: diff --git a/docs/source/modules/feature_engineering.rst b/docs/source/modules/feature_engineering.rst deleted file mode 100644 index 1d4e0c2..0000000 --- a/docs/source/modules/feature_engineering.rst +++ /dev/null @@ -1,9 +0,0 @@ -Feature Engineering -=================== - -.. automodule:: predspot.feature_engineering - :members: - :undoc-members: - :show-inheritance: - :no-index: - diff --git a/docs/source/modules/ml_modelling.rst b/docs/source/modules/ml_modelling.rst deleted file mode 100644 index d272c45..0000000 --- a/docs/source/modules/ml_modelling.rst +++ /dev/null @@ -1,9 +0,0 @@ -ML Modelling -============ - -.. automodule:: predspot.ml_modelling - :members: - :undoc-members: - :show-inheritance: - :no-index: - diff --git a/docs/source/modules/pipeline.rst b/docs/source/modules/pipeline.rst deleted file mode 100644 index 4a43db0..0000000 --- a/docs/source/modules/pipeline.rst +++ /dev/null @@ -1,9 +0,0 @@ -Pipeline -======== - -.. automodule:: predspot.pipeline - :members: - :undoc-members: - :show-inheritance: - :no-index: - diff --git a/docs/source/modules/utilities.rst b/docs/source/modules/utilities.rst deleted file mode 100644 index c99dc1a..0000000 --- a/docs/source/modules/utilities.rst +++ /dev/null @@ -1,9 +0,0 @@ -Utilities -========= - -.. automodule:: predspot.utilities - :members: - :undoc-members: - :show-inheritance: - :no-index: - diff --git a/docs/update_docs.md b/docs/update_docs.md deleted file mode 100644 index 25f1354..0000000 --- a/docs/update_docs.md +++ /dev/null @@ -1,28 +0,0 @@ -``` -cd docs -make clean -make html -cd .. - -# Delete local gh-pages branch if it exists -git branch -D gh-pages - -# Delete remote gh-pages branch -git push origin --delete gh-pages - -# Create new gh-pages branch without history -git checkout --orphan gh-pages - -# Copy the built files to root -cp -r docs/build/html/* . - -# Add and commit -git add . -git commit -m "Deploy documentation" - -# Force push the new branch -git push -f origin gh-pages - -# Go back to your previous branch (probably main or master) -git checkout - -``` \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..78309a3 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,120 @@ +site_name: Predspot +site_description: Predicting crime hotspots with machine learning +site_url: https://adaj.github.io/predspot/ +repo_url: https://github.com/adaj/predspot +repo_name: adaj/predspot +edit_uri: edit/master/docs/ +copyright: Copyright © 2019-2026 Adelson Araujo — BSD-3-Clause + +theme: + name: material + language: en + icon: + repo: fontawesome/brands/github + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + primary: deep purple + accent: amber + toggle: + icon: material/weather-night + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: deep purple + accent: amber + toggle: + icon: material/weather-sunny + name: Switch to light mode + features: + - navigation.tabs + - navigation.sections + - navigation.top + - navigation.footer + - search.suggest + - search.highlight + - content.code.copy + - content.code.annotate + - toc.follow + +nav: + - Home: index.md + - Getting started: + - Installation: getting-started/installation.md + - Quickstart: getting-started/quickstart.md + - User guide: + - How Predspot works: guide/concepts.md + - Data and study areas: guide/data.md + - Grids and spatio-temporal mapping: guide/mapping.md + - Feature engineering: guide/features.md + - Prediction and evaluation: guide/modelling.md + - API reference: + - predspot: api/predspot.md + - dataset_preparation: api/dataset_preparation.md + - crime_mapping: api/crime_mapping.md + - feature_engineering: api/feature_engineering.md + - ml_modelling: api/ml_modelling.md + - synthetic: api/synthetic.md + - pipeline: api/pipeline.md + - utilities: api/utilities.md + - Project: + - Changelog: changelog.md + - Contributing: contributing.md + - Citing Predspot: citing.md + +plugins: + - search + - autorefs + - mkdocstrings: + handlers: + python: + paths: [src] + options: + docstring_style: google + docstring_options: + returns_named_value: false + returns_multiple_items: false + ignore_init_summary: true + docstring_section_style: table + show_source: true + show_root_heading: true + show_symbol_type_heading: true + show_symbol_type_toc: true + members_order: source + separate_signature: true + show_signature_annotations: false + merge_init_into_class: true + filters: ["!^_"] + +markdown_extensions: + - admonition + - attr_list + - md_in_html + - toc: + permalink: true + - pymdownx.details + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed: + alternate_style: true + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.snippets: + check_paths: true + base_path: [".", "docs"] + - pymdownx.arithmatex: + generic: true + +extra_javascript: + - https://unpkg.com/mathjax@3/es5/tex-mml-chtml.js + +extra: + social: + - icon: fontawesome/brands/github + link: https://github.com/adaj/predspot + - icon: fontawesome/brands/python + link: https://pypi.org/project/predspot/ diff --git a/pyproject.toml b/pyproject.toml index f014c0a..5c61551 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,12 @@ dependencies = [ [project.optional-dependencies] contour = ["geojsoncontour>=0.4"] +docs = [ + "mkdocs>=1.6,<2", + "mkdocs-material>=9.5,<10", + "mkdocstrings[python]>=0.26", + "mkdocs-autorefs>=1.2", +] dev = [ "pytest>=8", "pytest-cov>=5", diff --git a/src/predspot/crime_mapping.py b/src/predspot/crime_mapping.py index 78d16f6..710d7c7 100644 --- a/src/predspot/crime_mapping.py +++ b/src/predspot/crime_mapping.py @@ -6,17 +6,21 @@ timestamped crime events into a *spatio-temporal series*: a value per grid cell per time period. Two families of mapping are available: -* :class:`KDE` — kernel density estimation evaluated on a grid of **points** - (see :func:`create_gridpoints`). This is the default approach of Predspot. -* :class:`QuadratCount` — plain event counts per **cell** of a polygonal grid - (see :func:`create_gridhexagonal` and :func:`create_gridsquares`). - -Both produce the same output format, a :class:`pandas.Series` named +* [`KDE`][predspot.crime_mapping.KDE] — kernel density estimation evaluated on a grid of **points** + (see [`create_gridpoints`][predspot.crime_mapping.create_gridpoints]). This is the default + approach of Predspot. +* [`QuadratCount`][predspot.crime_mapping.QuadratCount] — plain event counts per **cell** of a +polygonal grid + (see [`create_gridhexagonal`][predspot.crime_mapping.create_gridhexagonal] and + [`create_gridsquares`][predspot.crime_mapping.create_gridsquares]). + +Both produce the same output format, a ``pandas.Series`` named ``crime_density`` indexed by ``(t, places)``, so they are interchangeable -inside :class:`predspot.ml_modelling.PredictionPipeline`. +inside [`PredictionPipeline`][predspot.ml_modelling.PredictionPipeline]. The study area itself can be fetched from OpenStreetMap with -:func:`load_study_area` (requires the optional ``osmnx`` dependency). +[`load_study_area`][predspot.crime_mapping.load_study_area] (requires the optional ``osmnx`` +dependency). """ __author__ = "Adelson Araujo" @@ -77,7 +81,7 @@ def normalize_tfreq(tfreq): def tfreq_offset(tfreq): - """Return the :class:`pandas.DateOffset` that advances one period of ``tfreq``.""" + """Return the ``pandas.DateOffset`` that advances one period of ``tfreq``.""" alias = normalize_tfreq(tfreq) if alias == "ME": return pd.offsets.MonthEnd(1) @@ -93,14 +97,14 @@ def load_study_area(place, crs=WGS84, which_result=None): Uses `osmnx `_ (optional dependency: ``pip install predspot[osm]``) to geocode ``place`` with Nominatim and return its administrative boundary, ready to be used as the - ``study_area`` of :class:`predspot.Dataset` or as the ``bbox`` of the + ``study_area`` of [`Dataset`][predspot.Dataset] or as the ``bbox`` of the ``create_grid*`` functions. Args: place (str or list): Name of the place as Nominatim understands it, e.g. ``"Natal, Rio Grande do Norte, Brazil"``. A list of names returns one row per place. - crs: CRS of the returned GeoDataFrame (default WGS84). + crs (str or pyproj.CRS): CRS of the returned GeoDataFrame (default WGS84). which_result (int, optional): Forwarded to ``osmnx.geocode_to_gdf`` to pick a specific Nominatim match when the first one is not the boundary you want. @@ -170,7 +174,7 @@ def create_gridpoints(bbox, resolution, return_coords=False): """ Create a regular grid of points covering a study area. - This is the grid used by :class:`KDE`: the density is evaluated at each + This is the grid used by [`KDE`][predspot.crime_mapping.KDE]: the density is evaluated at each point. The grid is built in WGS84 with the requested spacing and then re-projected to the CRS of ``bbox``. @@ -319,8 +323,8 @@ class SpatioTemporalMapping(ABC, TransformerMixin, BaseEstimator): """ Abstract base class for spatio-temporal crime mapping. - Subclasses implement :meth:`fit_grid`, which maps the events of a single - time period onto the grid. :meth:`transform` takes care of splitting the + Subclasses implement ``fit_grid``, which maps the events of a single + time period onto the grid. ``transform`` takes care of splitting the events into periods, filling periods with no events and assembling the result into a series indexed by ``(t, places)``. @@ -426,8 +430,12 @@ class KDE(SpatioTemporalMapping): Args: tfreq (str): Time frequency (``'M'``, ``'W'`` or ``'D'``). - grid (GeoDataFrame): Point grid, see :func:`create_gridpoints`. - start_time, end_time: See :class:`SpatioTemporalMapping`. + grid (GeoDataFrame): Point grid, see + [`create_gridpoints`][predspot.crime_mapping.create_gridpoints]. + start_time (str or datetime, optional): See + [`SpatioTemporalMapping`][predspot.crime_mapping.SpatioTemporalMapping]. + end_time (str or datetime, optional): See + [`SpatioTemporalMapping`][predspot.crime_mapping.SpatioTemporalMapping]. bandwidth (str or float): ``'silverman'`` (default) or ``'scott'`` to estimate the bandwidth from the first period with enough events and keep it fixed afterwards (so densities are comparable across @@ -490,14 +498,18 @@ class QuadratCount(SpatioTemporalMapping): """ Count of crime events per grid cell (quadrat count). - An alternative to :class:`KDE` that works on polygonal grids (hexagons or - squares, see :func:`create_gridhexagonal` and :func:`create_gridsquares`): + An alternative to [`KDE`][predspot.crime_mapping.KDE] that works on polygonal grids (hexagons or + squares, see [`create_gridhexagonal`][predspot.crime_mapping.create_gridhexagonal] and + [`create_gridsquares`][predspot.crime_mapping.create_gridsquares]): the value of a cell in a period is the number of events that fall in it. Args: tfreq (str): Time frequency (``'M'``, ``'W'`` or ``'D'``). grid (GeoDataFrame): Polygonal grid. - start_time, end_time: See :class:`SpatioTemporalMapping`. + start_time (str or datetime, optional): See + [`SpatioTemporalMapping`][predspot.crime_mapping.SpatioTemporalMapping]. + end_time (str or datetime, optional): See + [`SpatioTemporalMapping`][predspot.crime_mapping.SpatioTemporalMapping]. """ def __init__(self, tfreq, grid, start_time=None, end_time=None): diff --git a/src/predspot/dataset_preparation.py b/src/predspot/dataset_preparation.py index 8dda27c..bfd5140 100644 --- a/src/predspot/dataset_preparation.py +++ b/src/predspot/dataset_preparation.py @@ -3,7 +3,8 @@ ========================== Prepares crime event data together with the study area it belongs to. -:class:`Dataset` validates the input, converts the events to a GeoDataFrame +[`Dataset`][predspot.dataset_preparation.Dataset] validates the input, converts the events to a +GeoDataFrame of points in WGS84 and offers simple plotting and splitting helpers. """ @@ -27,7 +28,7 @@ class Dataset: Args: crimes (pandas.DataFrame): Crime events with at least the columns ``tag`` (crime type), ``t`` (timestamp, anything - :func:`pandas.to_datetime` understands), ``lon`` and ``lat`` + ``pandas.to_datetime`` understands), ``lon`` and ``lat`` (WGS84 degrees). The input is **not** modified. study_area (geopandas.GeoDataFrame): Boundary of the study area. It must have a CRS set. @@ -100,7 +101,7 @@ def plot(self, ax=None, crime_samples=1000, **kwargs): Args: ax (matplotlib.axes.Axes, optional): Axes to draw on. crime_samples (int): Number of events to draw (random sample). - **kwargs: ``study_area=dict(...)`` and ``crimes=dict(...)`` are + **kwargs (dict): ``study_area=dict(...)`` and ``crimes=dict(...)`` are forwarded to the respective ``GeoDataFrame.plot`` calls. Returns: diff --git a/src/predspot/feature_engineering.py b/src/predspot/feature_engineering.py index 48ec5ed..43f737b 100644 --- a/src/predspot/feature_engineering.py +++ b/src/predspot/feature_engineering.py @@ -3,14 +3,15 @@ ========================== Turns the spatio-temporal series produced by a mapping (see -:mod:`predspot.crime_mapping`) into lagged features, one row per +[`crime_mapping`][predspot.crime_mapping]) into lagged features, one row per ``(t, places)``. Each feature class applies a time series transformation to the history of every place and then builds ``lags`` lagged columns from it: -* :class:`AR` — the raw series (autoregressive features); -* :class:`Diff` — first differences; -* :class:`Seasonality` — the seasonal component of an STL decomposition; -* :class:`Trend` — the trend component of an STL decomposition. +* [`AR`][predspot.feature_engineering.AR] — the raw series (autoregressive features); +* [`Diff`][predspot.feature_engineering.Diff] — first differences; +* [`Seasonality`][predspot.feature_engineering.Seasonality] — the seasonal component of an STL +decomposition; +* [`Trend`][predspot.feature_engineering.Trend] — the trend component of an STL decomposition. The output always contains one extra row for the period right after the last observed one, so that the fitted model can forecast the next period. @@ -32,7 +33,7 @@ def infer_offset(time_index): """ - Infer the :class:`pandas.DateOffset` between consecutive periods. + Infer the ``pandas.DateOffset`` between consecutive periods. Args: time_index (pandas.DatetimeIndex): Unique, sorted period labels. @@ -201,7 +202,7 @@ class FeatureScaling(TransformerMixin, BaseEstimator): Wrap a scikit-learn scaler so that it returns DataFrames. Args: - estimator: Any scikit-learn transformer (e.g. ``QuantileTransformer``). + estimator (TransformerMixin): Any scikit-learn transformer (e.g. ``QuantileTransformer``). """ def __init__(self, estimator): diff --git a/src/predspot/ml_modelling.py b/src/predspot/ml_modelling.py index 3107ac4..ec7e4e1 100644 --- a/src/predspot/ml_modelling.py +++ b/src/predspot/ml_modelling.py @@ -30,7 +30,7 @@ class FeatureSelection(TransformerMixin, BaseEstimator): Wrap a scikit-learn feature selector so that it returns DataFrames. Args: - estimator: A selector exposing ``support_`` after fit (e.g. ``RFE``). + estimator (object): A selector exposing ``support_`` after fit (e.g. ``RFE``). """ def __init__(self, estimator): @@ -60,7 +60,7 @@ class Model(RegressorMixin, BaseEstimator): Wrap a scikit-learn regressor so that predictions come back as DataFrames. Args: - estimator: Any scikit-learn regressor. + estimator (sklearn.base.RegressorMixin): Any scikit-learn regressor. """ def __init__(self, estimator): @@ -87,18 +87,19 @@ class PredictionPipeline(RegressorMixin, BaseEstimator): End-to-end crime hotspot prediction. The pipeline chains three stages: a spatio-temporal ``mapping`` (e.g. - :class:`predspot.crime_mapping.KDE`) that turns events into a series per + [`KDE`][predspot.crime_mapping.KDE]) that turns events into a series per place and period; a feature extraction step (e.g. - :class:`predspot.utilities.PandasFeatureUnion` of lag features) and a + [`PandasFeatureUnion`][predspot.utilities.PandasFeatureUnion] of lag features) and a scikit-learn ``estimator`` (or ``Pipeline``) that learns to predict the next period's value from the features. Args: - mapping: A :class:`predspot.crime_mapping.SpatioTemporalMapping`. - fextraction: A transformer taking the series and returning features. - estimator: A scikit-learn regressor or ``Pipeline`` whose last step + mapping (SpatioTemporalMapping): A + [`SpatioTemporalMapping`][predspot.crime_mapping.SpatioTemporalMapping]. + fextraction (object): A transformer taking the series and returning features. + estimator (object): A scikit-learn regressor or ``Pipeline`` whose last step returns a DataFrame with a ``crime_density`` column (see - :class:`Model`). + [`Model`][predspot.ml_modelling.Model]). random_state (int, optional): Seed used to shuffle the training rows. """ @@ -148,8 +149,9 @@ def feature_importances(self): Importance of each selected feature. Works when ``estimator`` is a ``Pipeline`` whose last step exposes - ``feature_importances_`` (e.g. :class:`Model` around a random - forest); an optional :class:`FeatureSelection` step before it is + ``feature_importances_`` (e.g. [`Model`][predspot.ml_modelling.Model] around a random + forest); an optional [`FeatureSelection`][predspot.ml_modelling.FeatureSelection] step + before it is taken into account. Returns: @@ -176,7 +178,7 @@ def fit(self, dataset, y=None): Args: dataset (predspot.Dataset): Crime events and study area. - y: Ignored; present for scikit-learn compatibility. + y (None): Ignored; present for scikit-learn compatibility. Returns: PredictionPipeline: ``self``. @@ -221,7 +223,7 @@ def evaluate(self, scoring="r2", cv=5): Score the estimator with time series cross-validation. Periods are split in ``cv`` consecutive folds - (:class:`sklearn.model_selection.TimeSeriesSplit`); the estimator is + (``sklearn.model_selection.TimeSeriesSplit``); the estimator is refitted on the original data afterwards. Args: diff --git a/src/predspot/pipeline.py b/src/predspot/pipeline.py index d7354d6..77b8b04 100644 --- a/src/predspot/pipeline.py +++ b/src/predspot/pipeline.py @@ -38,7 +38,7 @@ def generate_testdata(n_points, start_time, end_time, bounds=DEFAULT_BOUNDS, see """ Generate synthetic crime events inside a rectangular study area. - A thin wrapper around :func:`predspot.synthetic.generate_crimes` with + A thin wrapper around [`generate_crimes`][predspot.synthetic.generate_crimes] with three hotspots and the default temporal patterns. Args: @@ -74,7 +74,7 @@ def build_default_pipeline( tfreq (str): Time frequency (``'M'``, ``'W'`` or ``'D'``). grid_resolution (float): Grid spacing in kilometers. lags (int): Number of lags (and STL period) of the features. - bandwidth (str or float): KDE bandwidth, see :class:`predspot.crime_mapping.KDE`. + bandwidth (str or float): KDE bandwidth, see [`KDE`][predspot.crime_mapping.KDE]. random_state (int, optional): Seed for the estimator and shuffling. Returns: @@ -142,7 +142,7 @@ def run_prediction_pipeline( Returns: tuple: ``(predictions, pipeline)`` — the forecast for the next period - and the fitted :class:`predspot.ml_modelling.PredictionPipeline`. + and the fitted [`PredictionPipeline`][predspot.ml_modelling.PredictionPipeline]. """ missing = [c for c in ("tag", "t", "lat", "lon") if c not in crime_data.columns] if missing: @@ -168,7 +168,7 @@ def run_prediction_pipeline( def evaluate_pipeline(pipeline, scoring="r2", cv=5): """ - Cross-validate a fitted pipeline; see :meth:`PredictionPipeline.evaluate`. + Cross-validate a fitted pipeline; see ``PredictionPipeline.evaluate``. Args: pipeline (PredictionPipeline): A fitted pipeline. diff --git a/src/predspot/synthetic.py b/src/predspot/synthetic.py index bea3b9a..73e2720 100644 --- a/src/predspot/synthetic.py +++ b/src/predspot/synthetic.py @@ -140,7 +140,8 @@ def temporal_intensity( Args: timestamps (DatetimeIndex): Times to evaluate. - start, end (Timestamp): Bounds of the simulation, used for the trend. + start (Timestamp): Start of the simulation, used for the trend. + end (Timestamp): End of the simulation, used for the trend. trend (float): Relative change of the intensity from ``start`` to ``end`` (``0.5`` means +50% at the end, ``-0.3`` means -30%). annual_amplitude (float): Amplitude of the annual cosine (0-1). @@ -179,10 +180,12 @@ def sample_timestamps(n, start, end, rng, max_iterations=1000, **intensity_kwarg Args: n (int): Number of timestamps. - start, end (Timestamp): Simulation bounds. + start (Timestamp): Start of the simulation. + end (Timestamp): End of the simulation. rng (numpy.random.Generator): Random generator. max_iterations (int): Safety cap on thinning rounds. - **intensity_kwargs: Forwarded to :func:`temporal_intensity`. + **intensity_kwargs (dict): Forwarded to + [`temporal_intensity`][predspot.synthetic.temporal_intensity]. Returns: pandas.DatetimeIndex: ``n`` timestamps, unsorted. @@ -239,7 +242,7 @@ def generate_crimes( Args: study_area (GeoDataFrame): Boundary of the study area (any CRS). See - :func:`predspot.crime_mapping.load_study_area` to fetch one from + [`load_study_area`][predspot.crime_mapping.load_study_area] to fetch one from OpenStreetMap. n_events (int): Number of events to generate. start (str or Timestamp): First possible timestamp. @@ -251,7 +254,7 @@ def generate_crimes( clouds in km (one value or one per hotspot). tags (dict or sequence): Crime types. A dict maps type to relative weight; a sequence gives equal weights. Defaults to - :data:`DEFAULT_TAGS`. + ``DEFAULT_TAGS``. trend (float): Relative change of the event rate from ``start`` to ``end`` (``0.5`` = +50%). annual_amplitude (float): Amplitude of the annual cycle (0 disables). @@ -265,7 +268,7 @@ def generate_crimes( Returns: pandas.DataFrame: Events with ``tag``, ``t``, ``lon``, ``lat`` columns - sorted by time, ready for :class:`predspot.Dataset`. If + sorted by time, ready for [`Dataset`][predspot.Dataset]. If ``return_hotspots`` is True, a tuple ``(crimes, hotspots)`` where ``hotspots`` is a GeoDataFrame with the centre, ``sd_km`` and ``share`` of each hotspot. diff --git a/src/predspot/utilities.py b/src/predspot/utilities.py index 1e67528..0033ddd 100644 --- a/src/predspot/utilities.py +++ b/src/predspot/utilities.py @@ -2,7 +2,8 @@ Utilities Module ================ -Helpers used across Predspot: a :class:`PandasFeatureUnion` that keeps +Helpers used across Predspot: a [`PandasFeatureUnion`][predspot.utilities.PandasFeatureUnion] that +keeps DataFrames (and their index) when combining feature transformers, and a GeoJSON contour export for density maps. """ @@ -22,7 +23,7 @@ class PandasFeatureUnion(TransformerMixin, BaseEstimator): """ Concatenate the DataFrame outputs of several transformers column-wise. - Unlike :class:`sklearn.pipeline.FeatureUnion`, the transformers' outputs + Unlike ``sklearn.pipeline.FeatureUnion``, the transformers' outputs are aligned on their index and returned as a DataFrame. Rows with missing values after alignment (e.g. warm-up rows of lag features) are dropped. @@ -79,7 +80,7 @@ def contour_geojson(y, bbox, resolution, cmin, cmax): Args: y (pandas.Series): Values indexed by the positional index of the full point grid returned by - :func:`predspot.crime_mapping.create_gridpoints` (before + [`create_gridpoints`][predspot.crime_mapping.create_gridpoints] (before clipping), i.e. the ``places`` index. bbox (GeoDataFrame): Study area used to build the grid. resolution (float): Grid resolution in kilometers (same as the grid). From 3a14cb71a247dbe4206b3c07b2534bd32b27142c Mon Sep 17 00:00:00 2001 From: Adelson Araujo Date: Mon, 14 Sep 2026 16:18:18 +0000 Subject: [PATCH 4/4] Add get_city_shape, the Natal example notebook and the framework README - crime_mapping.get_city_shape(place_query): thin wrapper around osmnx.geocode_to_gdf returning the raw city shape (tests included) - PredictionPipeline.evaluate accepts a list of scorings and returns a DataFrame from a single time series CV pass - examples/natal.ipynb: end-to-end walkthrough on Natal with synthetic data (study area from OSM, synthetic events, grids, KDE vs QuadratCount, bandwidth comparison, features, pipeline evaluation, recursive forecasts, hotspot recovery check); examples/build_natal_notebook.py regenerates it - README reorganised: framework section summarising Chapter 3 of the thesis with Figures 7-12 (docs/assets/thesis), thesis linked in Resources, Status moved to the end - Documentation: the home page is now generated from the README (docs/hooks/readme.py) and the notebook is rendered via mkdocs-jupyter under "Examples" Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01FtZdc2gGrDh4PGSLyT9yRE --- .gitignore | 2 + CHANGELOG.md | 8 + CONTRIBUTING.md | 10 +- README.md | 293 ++++--- .../thesis/fig07-dataset-preparation.png | Bin 0 -> 54050 bytes docs/assets/thesis/fig08-feature-ingest.png | Bin 0 -> 69674 bytes .../fig09-temporal-feature-extraction.png | Bin 0 -> 41286 bytes .../thesis/fig10-feature-set-example.png | Bin 0 -> 71289 bytes docs/assets/thesis/fig11-ml-modelling.png | Bin 0 -> 39625 bytes .../thesis/fig12-prediction-pipeline.png | Bin 0 -> 50485 bytes docs/examples/natal.ipynb | 1 + docs/guide/modelling.md | 2 +- docs/hooks/readme.py | 31 + docs/index.md | 64 +- examples/build_natal_notebook.py | 515 +++++++++++++ examples/natal.ipynb | 728 ++++++++++++++++++ mkdocs.yml | 9 + pyproject.toml | 2 + src/predspot/__init__.py | 2 + src/predspot/crime_mapping.py | 30 + src/predspot/ml_modelling.py | 22 +- tests/test_load_study_area.py | 16 +- tests/test_ml_modelling.py | 4 + 23 files changed, 1573 insertions(+), 166 deletions(-) create mode 100644 docs/assets/thesis/fig07-dataset-preparation.png create mode 100644 docs/assets/thesis/fig08-feature-ingest.png create mode 100644 docs/assets/thesis/fig09-temporal-feature-extraction.png create mode 100644 docs/assets/thesis/fig10-feature-set-example.png create mode 100644 docs/assets/thesis/fig11-ml-modelling.png create mode 100644 docs/assets/thesis/fig12-prediction-pipeline.png create mode 120000 docs/examples/natal.ipynb create mode 100644 docs/hooks/readme.py create mode 100644 examples/build_natal_notebook.py create mode 100644 examples/natal.ipynb diff --git a/.gitignore b/.gitignore index 73b2e17..7591d61 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ dist/ htmlcov/ .ruff_cache/ site/ +examples/cache/ +.cache/ diff --git a/CHANGELOG.md b/CHANGELOG.md index cff9573..e400e6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e578cda..06ae5fb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 diff --git a/README.md b/README.md index 39cae86..a341d08 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,44 @@ # Predspot [![CI](https://github.com/adaj/predspot/actions/workflows/ci.yml/badge.svg)](https://github.com/adaj/predspot/actions/workflows/ci.yml) +[![Docs](https://github.com/adaj/predspot/actions/workflows/docs.yml/badge.svg)](https://adaj.github.io/predspot/) [![PyPI](https://img.shields.io/pypi/v/predspot.svg)](https://pypi.org/project/predspot/) [![Python](https://img.shields.io/pypi/pyversions/predspot.svg)](https://pypi.org/project/predspot/) -[![License: BSD-3](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](LICENSE) +[![License: BSD-3](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](https://github.com/adaj/predspot/blob/master/LICENSE) ## Overview 📖 -Predspot is a Python library for spatio-temporal crime prediction and hotspot detection. It combines machine learning techniques with spatial analysis to help predict and visualize crime patterns across time and space. +Predspot is a Python library for spatio-temporal crime prediction and hotspot +detection. 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. Key features: -- Spatial and temporal crime mapping -- Feature engineering for time series data -- Machine learning-based prediction pipeline -- Crime hotspot detection using Kernel Density Estimation -- Visualization tools for crime patterns -## Status 🚧 +- Spatio-temporal crime mapping: kernel density estimation (KDE) on point grids, + or event counts on hexagonal and square grids, at daily, weekly or monthly resolution +- Time series feature engineering: lagged autoregressive, difference, seasonal and + trend features (STL decomposition) +- A prediction pipeline that accepts any scikit-learn regressor, with time series + cross-validation and recursive multi-step forecasts +- Study areas fetched from OpenStreetMap by name +- A synthetic crime generator (hotspots, trend, annual, weekly and hourly patterns) + to try everything without real data -Predspot started as part of a master's thesis (2018-2019) and is being revived -and modernised. The code base now targets Python 3.10+ with current versions of -pandas (>= 2.2), GeoPandas (>= 1.0), scikit-learn and statsmodels, and is -covered by a test suite. It remains research software: use it as a reference -implementation and adapt it to your own data. +Full documentation, with a quickstart, a user guide and the API reference, lives at +**https://adaj.github.io/predspot/**. A complete, executed walkthrough for the city +of Natal is in [`examples/natal.ipynb`](https://github.com/adaj/predspot/blob/master/examples/natal.ipynb). ## How to use? 🚀 -Full documentation, with a quickstart, a user guide and the API reference, lives at -**https://adaj.github.io/predspot/**. +Install from PyPI (Python 3.10 or newer): + +```bash +pip install predspot # core +pip install "predspot[osm]" # + study areas from OpenStreetMap (osmnx) +pip install "predspot[contour]" # + GeoJSON contour export (geojsoncontour) +``` Basic usage example: @@ -35,15 +46,14 @@ Basic usage example: from predspot import Dataset, PredictionPipeline from predspot.crime_mapping import KDE, create_gridpoints from predspot.feature_engineering import Seasonality, Trend, Diff - from predspot.utilities import PandasFeatureUnion from sklearn.ensemble import RandomForestRegressor -# Load and prepare data: crimes_df needs `tag`, `t`, `lon`, `lat` columns and +# crimes_df needs `tag`, `t`, `lon`, `lat` columns; # study_area_gdf is a GeoDataFrame with the boundary of the study area dataset = Dataset(crimes_df, study_area_gdf) -# Create prediction pipeline (monthly KDE on a 1 km point grid) +# Monthly KDE on a 1 km point grid, lag features, random forest pipeline = PredictionPipeline( mapping=KDE(tfreq='M', grid=create_gridpoints(study_area_gdf, resolution=1)), fextraction=PandasFeatureUnion([ @@ -76,12 +86,12 @@ with spatial hotspots and realistic temporal patterns (trend, annual cycle, day-of-week and hour-of-day profiles): ```python -from predspot import Dataset, load_study_area, generate_crimes +from predspot import Dataset, get_city_shape, generate_crimes -study_area = load_study_area("Natal, Rio Grande do Norte, Brazil") -crimes = generate_crimes(study_area, n_events=5000, n_hotspots=4, +city = get_city_shape("Natal, RN, Brazil") +crimes = generate_crimes(city, n_events=5000, n_hotspots=4, start="2019-01-01", end="2020-12-31", seed=0) -dataset = Dataset(crimes, study_area) +dataset = Dataset(crimes, city) dataset.plot() ``` @@ -95,72 +105,186 @@ predictions, pipeline = run_prediction_pipeline(crimes, study_area, grid_resolut print(pipeline.evaluate('r2', cv=3)) ``` +### Input data format 📊 -## Development ⚡ +The crime data should be a pandas DataFrame with the following required columns: -Predspot has five main modules: +- `tag`: crime type +- `t`: timestamp +- `lon`: longitude (WGS84 degrees) +- `lat`: latitude (WGS84 degrees) + +The study area should be a GeoDataFrame (with a CRS) defining the boundaries +of interest. + +## The Predspot framework 🧭 + +Predspot implements the framework described in Chapter 3 of the master's +thesis [*Predspot: predicting crime hotspots with machine learning*](https://repositorio.ufrn.br/server/api/core/bitstreams/3655b8e1-2f32-4ce9-af9c-0e6b64d7af84/content) +(Araújo Jr., 2019). The framework is split into two phases, mirroring the +training and prediction steps of a machine learning system: **model +selection**, where a model is trained, evaluated and saved, and **prediction +service**, where that model is used operationally, period after period. The +figures below are reproduced from the thesis. + +### Model selection + +**1. Dataset preparation** *(Figure 7)* — everything starts from three inputs: +a crime database, the city shape and, optionally, auxiliary Points of Interest +(PoI) from OpenStreetMap. + +

Figure 7 - dataset preparation

+ +- Crime records must carry at least latitude, longitude, timestamp and crime type. +- The city shape acts as a spatial filter: events and PoI outside the boundary are dropped, + along with duplicates and "default" locations assigned to badly registered events. +- Crimes are split into **crime scenarios** (one per crime type, and possibly + per day/night period) that are modelled separately, since aggregating different + crime types blurs their distinct spatial patterns. +- In the package: `Dataset` (validation, WGS84 points) and `get_city_shape` / + `load_study_area` (city boundary from OpenStreetMap). + +**2. Feature ingest** *(Figures 8, 9 and 10)* — the step that assembles the +feature matrix `X` and the target `y`. Before it starts, two units of analysis +are chosen: the **spatial unit** (a grid derived from the crime mapping method) +and the **temporal unit** (daily, weekly or monthly samples — finer units give +sparser, harder-to-predict series). + +

Figure 8 - feature ingest

+ +- **Spatio-temporal aggregation**: the mapping method (KDE on a point grid, or + counts on polygonal cells) turns the events of each period into one value per + grid cell, producing the time series `C_ij` of cell *i* at period *j*. +- **Temporal feature extraction** *(Figure 9)*: each cell's series is decomposed + with STL into **trend** (`T`) and **seasonal** (`S`) components, and + **differenced** (`D`); the features are the *k* most recent lags of each + component, and the target is the series value one period ahead. + +

Figure 9 - temporal feature extraction

+ +- **Spatial aggregation of PoI** (optional): the density of each PoI category + around each cell (`G`) describes places geographically, complementing the + temporal features. +- **Join**: temporal and geographic features are joined into one row per + `(place, time)` pair — the layout shown in Figure 10. + +

Figure 10 - an artificial feature set

+ +- In the package: `create_gridpoints` / `create_gridhexagonal` / + `create_gridsquares` (grid), `KDE` / `QuadratCount` (spatio-temporal + aggregation), `Trend`, `Seasonality`, `Diff`, `AR` and `PandasFeatureUnion` + (temporal feature extraction and join). PoI features are not part of the + current package release. + +**3. Machine learning modelling** *(Figure 11)* — the feature set feeds a +model-agnostic training loop. + +

Figure 11 - machine learning modelling

+ +- **Feature selection** first: noisy lags and PoI layers are filtered by a + learning-based selector (an embedded, tree-based method in the thesis). +- **Several supervised algorithms** are trained and **tuned**, rather than + betting on a single one; the thesis compared random forests and gradient boosting. +- **Evaluation** uses time series K-fold cross-validation (train on the first + *k* folds, test on fold *k*+1), so no information from the future leaks into + training. The best model per crime scenario is the **golden model**, saved + together with its selected features. +- In the package: `PredictionPipeline` (with `FeatureScaling`, + `FeatureSelection` and `Model` wrappers and `evaluate()` for the time series CV). + +### Prediction service + +**4. Prediction pipeline** *(Figure 12)* — the model selection steps are +tailored for operation. + +

Figure 12 - prediction pipeline

+ +- For each new period, only the most recent events are loaded (enough to + compute the *k* lags), filtered and split into scenarios as before. +- The spatio-temporal aggregation and temporal feature extraction are repeated; + PoI features are reused, since they do not change over time. +- The previously selected features are kept and the golden model returns the + crime incidence level `y_i` of every place *i* one period ahead — a + prediction layer ready to be mapped. The process repeats every period. +- In the package: `PredictionPipeline.predict()` appends each forecast to the + series and advances one period, so repeated calls walk forward in time. + +**5. Web service** — the thesis also outlines how to wrap the pipeline in a +decoupled service (with a file *volume* for data, models and predictions, and +an ETL controller triggering the pipeline each period) that serves predictions +as GeoJSON to existing GIS tools. That layer is outside the scope of this +package, which covers the model selection phase and the prediction pipeline. -`dataset_preparation`: Module for preparing and managing crime datasets and study areas. +## Resources 📚 -`crime_mapping`: Module for spatial and temporal crime mapping: point, hexagonal and square grids, KDE-based density surfaces and per-cell counts (`QuadratCount`). +- Master's thesis (full description of the framework, evaluation on Natal and + Boston, feature importance analysis): Araújo Jr., A. (2019). + [*Predspot: Predicting Crime Hotspots with Machine Learning*](https://repositorio.ufrn.br/server/api/core/bitstreams/3655b8e1-2f32-4ce9-af9c-0e6b64d7af84/content). + M.Sc. dissertation, PPgSC/UFRN, Natal, Brazil. +- The earlier version of the framework: Araújo et al. (2018), *Towards a crime + hotspot detection framework for patrol planning* (HPCC/SmartCity/DSS). +- Methods worth reading about: kernel density estimation for hotspot mapping + (Chainey, Tompson & Uhlig, 2008), STL time series decomposition (Cleveland + et al., 1990) and time series cross-validation (Bergmeir, Hyndman & Koo, 2018). -`feature_engineering`: Module for time series feature engineering, including seasonality, trend, and difference features. +## Cite us -`ml_modelling`: Module that implements the prediction pipeline and model evaluation. +If you use Predspot in your research, please cite us: -`synthetic`: Module that generates synthetic crime events (hotspots + temporal patterns) inside any study area. +APA: +``` +Araújo Jr., A. (2019). Predspot: Predicting Crime Hotspots with Machine Learning. Master's dissertation, UFRN (Universidade Federal do Rio Grande do Norte), Natal, Brazil. -### Installation steps 🛠️ +Araújo, A., Cacho, N., Bezerra, L., Vieira, C., & Borges, J. (2018, June). Towards a crime hotspot detection framework for patrol planning. In 2018 IEEE 20th International Conference on High Performance Computing and Communications; IEEE 16th International Conference on Smart City; IEEE 4th International Conference on Data Science and Systems (HPCC/SmartCity/DSS) (pp. 1256-1263). IEEE. +``` -Predspot requires Python 3.10 or newer. +or bibtex: +``` +@mastersthesis{araujo2019predspot, + title={Predspot: Predicting crime hotspots with machine learning}, + author={Araujo, Adelson}, + year={2019}, + school={Universidade Federal do Rio Grande do Norte}, + url={https://repositorio.ufrn.br/server/api/core/bitstreams/3655b8e1-2f32-4ce9-af9c-0e6b64d7af84/content} +} -```bash -pip install predspot # from PyPI -pip install "predspot[osm]" # + study areas from OpenStreetMap (osmnx) -pip install "predspot[contour]" # + GeoJSON contour export (geojsoncontour) +@inproceedings{araujo2018towards, + title={Towards a crime hotspot detection framework for patrol planning}, + author={Ara{\'u}jo, Adelson and Cacho, N{\'a}dia and Bezerra, Lucas and Vieira, Carlos and Borges, Jo{\~a}o}, + booktitle={2018 IEEE 20th International Conference on High Performance Computing and Communications; IEEE 16th International Conference on Smart City; IEEE 4th International Conference on Data Science and Systems (HPCC/SmartCity/DSS)}, + pages={1256--1263}, + year={2018}, + organization={IEEE} +} ``` +## Development ⚡ + +Predspot has five main modules: + +- `dataset_preparation`: preparing and managing crime datasets and study areas. +- `crime_mapping`: spatial and temporal crime mapping — point, hexagonal and + square grids, KDE density surfaces, per-cell counts (`QuadratCount`) and + study areas from OpenStreetMap. +- `feature_engineering`: time series feature engineering (seasonality, trend, + difference and autoregressive lags). +- `ml_modelling`: the prediction pipeline and model evaluation. +- `synthetic`: synthetic crime events (hotspots + temporal patterns) inside any study area. + From source, for development: ```bash git clone https://github.com/adaj/predspot.git cd predspot pip install -e ".[dev,osm,contour]" -``` - -Core dependencies (installed automatically): pandas, geopandas, shapely, -numpy, scipy, scikit-learn, statsmodels and matplotlib. - -### Tests 🧪 - -```bash ruff check src tests # lint pytest # ~10 s ``` -See [CONTRIBUTING.md](CONTRIBUTING.md) for the release process and -[CHANGELOG.md](CHANGELOG.md) for what changed between versions. - -### Input Data Format 📊 - -The crime data should be a pandas DataFrame with the following required columns: -- `tag`: Crime type -- `t`: Timestamp -- `lon`: Longitude -- `lat`: Latitude - -The study area should be a GeoDataFrame defining the geographical boundaries of interest. - -## Resources 📚 - -For more information on the methods used in Predspot, please search more about these methods: -- Kernel Density Estimation for crime hotspot detection -- Time series decomposition for feature engineering -- Spatio-temporal crime prediction techniques - -## License 📜 - -BSD 3-Clause +See [CONTRIBUTING.md](https://github.com/adaj/predspot/blob/master/CONTRIBUTING.md) +for the release process and +[CHANGELOG.md](https://github.com/adaj/predspot/blob/master/CHANGELOG.md) for +what changed between versions. ## Contributing 💡 @@ -173,35 +297,14 @@ Guidelines for contributing: 4. Push to the branch 5. Create a new Pull Request +## License 📜 -## Cite us - -If you use Predspot in your research, please cite us: - -APA: -``` -Araujo, A., & Cacho, N. (2019). Predspot: Predicting crime hotspots with machine learning. Master’s thesis, UFRN (Universidade Federal do Rio Grande do Norte), Natal, Brazil. - -Araújo, A., Cacho, N., Bezerra, L., Vieira, C., & Borges, J. (2018, June). Towards a crime hotspot detection framework for patrol planning. In 2018 IEEE 20th International Conference on High Performance Computing and Communications; IEEE 16th International Conference on Smart City; IEEE 4th International Conference on Data Science and Systems (HPCC/SmartCity/DSS) (pp. 1256-1263). IEEE. -``` - -or bibtex: -``` -@article{araujo2019predspot, - title={Predspot: Predicting crime hotspots with machine learning}, - author={Araujo, Adelson}, - year={2019}, - school={Universidade Federal do Rio Grande do Norte} -} - -@inproceedings{araujo2018towards, - title={Towards a crime hotspot detection framework for patrol planning}, - author={Ara{\'u}jo, Adelson and Cacho, N{\'a}dia and Bezerra, Lucas and Vieira, Carlos and Borges, Jo{\~a}o}, - booktitle={2018 IEEE 20th International Conference on High Performance Computing and Communications; IEEE 16th International Conference on Smart City; IEEE 4th International Conference on Data Science and Systems (HPCC/SmartCity/DSS)}, - pages={1256--1263}, - year={2018}, - organization={IEEE} -} -``` +BSD 3-Clause +## Status 🚧 +Predspot started as part of a master's thesis (2018-2019) and was revived in +2026. The code base now targets Python 3.10+ with current versions of pandas +(>= 2.2), GeoPandas (>= 1.0), scikit-learn and statsmodels, and is covered by a +test suite and continuous integration. It remains research software: use it as +a reference implementation and adapt it to your own data. diff --git a/docs/assets/thesis/fig07-dataset-preparation.png b/docs/assets/thesis/fig07-dataset-preparation.png new file mode 100644 index 0000000000000000000000000000000000000000..8e9b663220b37c4ee78ba7518544c5d76856a9cc GIT binary patch literal 54050 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 literal 0 HcmV?d00001 diff --git a/docs/assets/thesis/fig09-temporal-feature-extraction.png b/docs/assets/thesis/fig09-temporal-feature-extraction.png new file mode 100644 index 0000000000000000000000000000000000000000..0c125189294dc7f1d12a8896b7ccc676e4cdb9e6 GIT binary patch literal 41286 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 U3}gP11ZI_Sv~#tsvALA=A19fAssI20 literal 0 HcmV?d00001 diff --git a/docs/assets/thesis/fig10-feature-set-example.png b/docs/assets/thesis/fig10-feature-set-example.png new file mode 100644 index 0000000000000000000000000000000000000000..0cb40d4d72f2b2857db775289aa2b07514db443d GIT binary patch literal 71289 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
- ![Observed density and forecast](assets/forecast.png){ width="900" } -
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.
-
- -## 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 +--- + diff --git a/examples/build_natal_notebook.py b/examples/build_natal_notebook.py new file mode 100644 index 0000000..b6e8863 --- /dev/null +++ b/examples/build_natal_notebook.py @@ -0,0 +1,515 @@ +"""Build examples/natal.ipynb from source cells. + +The notebook itself is the deliverable; this script only exists so that the +notebook can be regenerated (and re-executed) reproducibly: + + python examples/build_natal_notebook.py + jupyter nbconvert --to notebook --execute --inplace examples/natal.ipynb +""" + +from pathlib import Path + +import nbformat as nbf + +cells = [] + + +def md(text): + cells.append(nbf.v4.new_markdown_cell(text.strip())) + + +def code(text): + cells.append(nbf.v4.new_code_cell(text.strip())) + + +md(""" +# Predspot end to end: crime hotspots for Natal, Brazil + +This notebook walks through the whole Predspot workflow on the city of Natal +(Rio Grande do Norte, Brazil), using **synthetic** crime events so that it +runs anywhere without confidential police data: + +1. fetch the city boundary from OpenStreetMap; +2. generate synthetic crime events with spatial hotspots and temporal patterns; +3. build the `Dataset`, the spatial grids and the spatio-temporal series (KDE and counts); +4. extract time series features; +5. fit, evaluate and inspect a `PredictionPipeline`; +6. forecast the next months and check how well the true hotspots are recovered. + +Every intermediate object is displayed so you can see exactly what flows +between the steps. Requirements: `pip install "predspot[osm]" matplotlib`. +""") + +code(""" +import warnings + +import geopandas as gpd +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +import predspot +from predspot import Dataset, PredictionPipeline, PandasFeatureUnion, generate_crimes, get_city_shape +from predspot.crime_mapping import KDE, QuadratCount, create_gridhexagonal, create_gridpoints +from predspot.feature_engineering import AR, Diff, Seasonality, Trend + +warnings.filterwarnings("ignore", category=UserWarning) +pd.set_option("display.width", 120) +pd.set_option("display.max_columns", 20) +plt.rcParams["figure.dpi"] = 100 + +print("predspot", predspot.__version__, "| geopandas", gpd.__version__, "| pandas", pd.__version__) +""") + +md(""" +## 1. Study area: the boundary of Natal + +`get_city_shape` geocodes a place name with [osmnx](https://osmnx.readthedocs.io) +(Nominatim) and returns its boundary as a GeoDataFrame in WGS84. The query +should be specific enough for the first match to be the municipality. +""") + +code(""" +city = get_city_shape("Natal, RN, Brazil") +city +""") + +code(""" +polygon = city.geometry.iloc[0] +print("geometry:", polygon.geom_type, "| vertices:", len(polygon.exterior.coords)) +print("bounds (W, S, E, N):", np.round(city.total_bounds, 4)) +area_km2 = city.to_crs(city.estimate_utm_crs()).area.iloc[0] / 1e6 +print(f"area: {area_km2:.1f} km²") + +ax = city.plot(figsize=(5, 6), color="#f2f2f2", edgecolor="black") +ax.set_title("Natal, RN (OpenStreetMap boundary)") +ax.set_axis_off() +""") + +md(""" +## 2. Synthetic crime events + +`generate_crimes` draws events from a space-time point process inside the +polygon: a mixture of Gaussian **hotspots** plus a uniform background in space, +and a temporal intensity with a linear **trend**, an **annual** cycle and +**day-of-week** / **hour-of-day** profiles. With `return_hotspots=True` we also +get the hotspot centres, which lets us check later whether the model finds them. +""") + +code(""" +crimes, hotspots = generate_crimes( + city, + n_events=12_000, + start="2019-01-01", + end="2021-12-31", + n_hotspots=5, + hotspot_share=0.65, + hotspot_sd_km=0.7, + trend=0.4, # +40% events from start to end + annual_amplitude=0.25, # busier around the peak month... + annual_peak_month=12, # ...December + seed=2019, + return_hotspots=True, +) +crimes.head(10) +""") + +code(""" +print(crimes.shape) +crimes.describe(include="all").T +""") + +code(""" +hotspots +""") + +code(""" +crimes["tag"].value_counts().to_frame("events").assign(share=lambda d: (d["events"] / len(crimes)).round(3)) +""") + +md(""" +### 2.1 Where and when do the events happen? +""") + +code(""" +fig, ax = plt.subplots(figsize=(7, 8)) +city.plot(ax=ax, color="#f7f7f7", edgecolor="black") +sample = crimes.sample(4000, random_state=0) +ax.scatter(sample["lon"], sample["lat"], s=3, alpha=0.35, color="#1f4e79", label="events (sample)") +hotspots.plot(ax=ax, color="#d62728", marker="*", markersize=150, zorder=5, label="hotspot centres") +ax.legend(loc="lower left") +ax.set_title("Synthetic crime events in Natal") +ax.set_axis_off() +""") + +code(""" +monthly = crimes.set_index("t").resample("ME").size().rename("events") +fig, axes = plt.subplots(1, 3, figsize=(15, 3.6)) +monthly.plot(ax=axes[0], marker="o") +axes[0].set_title("Events per month (trend + annual cycle)") +axes[0].set_xlabel("") +crimes["t"].dt.day_name().value_counts().reindex( + ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] +).plot.bar(ax=axes[1], color="#1f4e79") +axes[1].set_title("Events per weekday") +crimes["t"].dt.hour.value_counts().sort_index().plot.bar(ax=axes[2], color="#1f4e79", width=0.9) +axes[2].set_title("Events per hour of day") +fig.tight_layout() +""") + +md(""" +## 3. Dataset, grids and the spatio-temporal series + +`Dataset` validates the events (columns `tag`, `t`, `lon`, `lat`) and turns +them into a GeoDataFrame of points in WGS84, together with the study area. +""") + +code(""" +dataset = Dataset(crimes, city) +dataset +""") + +code(""" +dataset.crimes.head() +""") + +md(""" +### 3.1 Grids + +Predspot discretises the city into **places**. Two kinds of grid are built +below: a grid of **points** every 500 m (used by `KDE`) and a grid of +**hexagons** of 1 km² (used by `QuadratCount`). Only cells intersecting the +city are kept; each grid has `lon`/`lat` columns with the cell centroid and an +index named `places`. +""") + +code(""" +points = create_gridpoints(city, resolution=0.5) +hexes = create_gridhexagonal(city, resolution=1.0) +print(f"{len(points)} grid points at 500 m | {len(hexes)} hexagons of 1 km²") +points.head() +""") + +code(""" +fig, axes = plt.subplots(1, 2, figsize=(12, 7)) +city.boundary.plot(ax=axes[0], color="black") +points.plot(ax=axes[0], markersize=4, color="#1f4e79") +axes[0].set_title(f"Point grid, 500 m ({len(points)} places)") +city.boundary.plot(ax=axes[1], color="black") +hexes.plot(ax=axes[1], facecolor="none", edgecolor="#1f4e79", linewidth=0.6) +axes[1].set_title(f"Hexagonal grid, 1 km² ({len(hexes)} places)") +for ax in axes: + ax.set_axis_off() +""") + +md(""" +### 3.2 Spatio-temporal mapping + +A mapping assigns one value to every `(period, place)` pair. `KDE` fits a +Gaussian kernel density estimate to the events of each period and evaluates it +at the grid points; `QuadratCount` counts the events inside each polygon. Both +return the same object: a `pandas.Series` named `crime_density` with a +`(t, places)` MultiIndex — the **spatio-temporal series**. +""") + +code(""" +kde = KDE(tfreq="M", grid=points, bandwidth="silverman") +stseries = kde.fit_transform(dataset.crimes) +print(type(stseries).__name__, stseries.shape, "| KDE factor:", round(kde.factor, 4)) +stseries.head(8) +""") + +code(""" +# The same series in wide form: one row per month, one column per place +wide = stseries.unstack("places") +wide.iloc[:6, :8] +""") + +code(""" +counts = QuadratCount(tfreq="M", grid=hexes).fit_transform(dataset.crimes) +counts_wide = counts.unstack("places") +print("events per month recovered by the counts:", counts_wide.sum(axis=1).astype(int).head(3).tolist(), "...") +counts_wide.iloc[:6, :8] +""") + +code(""" +month = pd.Timestamp("2021-06-30") +fig, axes = plt.subplots(1, 2, figsize=(13, 6.5)) + +g = points.copy() +g["density"] = stseries.xs(month, level="t").reindex(points.index).values +g.plot(ax=axes[0], column="density", cmap="viridis", markersize=12, marker="s", legend=True, + legend_kwds={"shrink": 0.6, "label": "KDE density"}) +city.boundary.plot(ax=axes[0], color="black", linewidth=1) +hotspots.plot(ax=axes[0], color="#d62728", marker="*", markersize=150, zorder=5) +axes[0].set_title(f"KDE on the point grid - {month:%B %Y}") + +h = hexes.copy() +h["events"] = counts.xs(month, level="t").reindex(hexes.index).values +h.plot(ax=axes[1], column="events", cmap="viridis", edgecolor="white", linewidth=0.3, legend=True, + legend_kwds={"shrink": 0.6, "label": "events in the cell"}) +city.boundary.plot(ax=axes[1], color="black", linewidth=1) +hotspots.plot(ax=axes[1], color="#d62728", marker="*", markersize=150, zorder=5) +axes[1].set_title(f"QuadratCount on hexagons - {month:%B %Y}") +for ax in axes: + ax.set_axis_off() +""") + +md(""" +### 3.3 The KDE bandwidth + +`bandwidth="silverman"` estimates the kernel width from the spread of the +events in the first period and keeps it fixed, which makes densities comparable +over time but tends to over-smooth a whole city. A numeric `bandwidth` is used +directly as the KDE factor (a multiple of the city-wide spread of the events): +smaller values give sharper maps. Compare three settings for the same month — +this is the trade-off discussed in the thesis (Chapter 2, Figure 3). +""") + +code(""" +fig, axes = plt.subplots(1, 3, figsize=(16, 5.5)) +for ax, bw in zip(axes, ["silverman", 0.2, 0.08]): + m = KDE(tfreq="M", grid=points, bandwidth=bw) + st = m.fit_transform(dataset.crimes) + g = points.copy() + g["density"] = st.xs(month, level="t").reindex(points.index).values + g.plot(ax=ax, column="density", cmap="viridis", markersize=10, marker="s") + city.boundary.plot(ax=ax, color="black", linewidth=1) + hotspots.plot(ax=ax, color="#d62728", marker="*", markersize=120, zorder=5) + ax.set_title(f"bandwidth={bw!r} (factor {m.factor:.3f})") + ax.set_axis_off() +""") + +md(""" +## 4. Time series features + +From here on we use `bandwidth=0.2`, a middle ground between the +over-smoothed Silverman estimate and a noisy small kernel. + +Each place has a monthly series. The feature classes transform it (STL trend, +STL seasonal component, first difference or the raw series) and build `lags` +lagged columns. `PandasFeatureUnion` aligns them on the `(t, places)` index and +drops the warm-up rows. Note the extra row for the month **after** the last +observed one — that is the row the model will forecast. +""") + +code(""" +BANDWIDTH = 0.2 +stseries = KDE(tfreq="M", grid=points, bandwidth=BANDWIDTH).fit_transform(dataset.crimes) + +LAGS = 6 +fextraction = PandasFeatureUnion([ + ("ar", AR(lags=3)), + ("seasonal", Seasonality(lags=LAGS)), + ("trend", Trend(lags=LAGS)), + ("diff", Diff(lags=LAGS)), +]) +X = fextraction.fit_transform(stseries) +print(X.shape, "| periods:", X.index.get_level_values("t").min().date(), "->", X.index.get_level_values("t").max().date()) +X.head(8) +""") + +code(""" +# What the decomposition looks like for the busiest place +busiest = stseries.groupby("places").mean().idxmax() +ts = stseries.xs(busiest, level="places") +from statsmodels.tsa.seasonal import STL +res = STL(ts, period=LAGS).fit() +fig, axes = plt.subplots(4, 1, figsize=(10, 7), sharex=True) +ts.plot(ax=axes[0], title=f"place {busiest}: KDE density per month") +res.trend.plot(ax=axes[1], title="STL trend") +res.seasonal.plot(ax=axes[2], title=f"STL seasonal (period={LAGS})") +ts.diff().plot(ax=axes[3], title="first difference") +for ax in axes: + ax.set_xlabel("") +fig.tight_layout() +""") + +md(""" +## 5. Fit and evaluate a prediction pipeline + +The pipeline chains the mapping, the feature extraction and a scikit-learn +estimator. Here the estimator is itself a `Pipeline`: quantile scaling, +recursive feature elimination and a random forest, each wrapped so that +DataFrames (and the `(t, places)` index) survive every step. +""") + +code(""" +from sklearn.ensemble import RandomForestRegressor +from sklearn.feature_selection import RFE +from sklearn.pipeline import Pipeline +from sklearn.preprocessing import QuantileTransformer + +from predspot.feature_engineering import FeatureScaling +from predspot.ml_modelling import FeatureSelection, Model + +pipeline = PredictionPipeline( + mapping=KDE(tfreq="M", grid=points, bandwidth=BANDWIDTH), + fextraction=PandasFeatureUnion([ + ("ar", AR(lags=3)), + ("seasonal", Seasonality(lags=LAGS)), + ("trend", Trend(lags=LAGS)), + ("diff", Diff(lags=LAGS)), + ]), + estimator=Pipeline([ + ("scaling", FeatureScaling(QuantileTransformer(n_quantiles=20, output_distribution="uniform"))), + ("selection", FeatureSelection(RFE(RandomForestRegressor(n_estimators=20, random_state=0, n_jobs=-1), + n_features_to_select=12, step=3))), + ("model", Model(RandomForestRegressor(n_estimators=100, min_samples_leaf=2, random_state=0, n_jobs=-1))), + ]), + random_state=0, +) +pipeline.fit(dataset) +print("training rows:", pipeline.features.loc[: stseries.index.get_level_values("t").max()].shape[0]) +print("next period to forecast:", pipeline.next_time.date()) +""") + +code(""" +scores = pipeline.evaluate(["r2", "mse"], cv=3) # time series CV: train on earlier folds, test on the next +scores.loc["mean"] = scores.mean() +scores +""") + +code(""" +fi = pipeline.feature_importances +ax = fi.sort_values("importance").plot.barh(figsize=(7, 5), legend=False, color="#1f4e79") +ax.set_title("Selected features and their importance") +fi.head(12) +""") + +md(""" +## 6. Forecast the next months + +`predict()` returns the density of every place for the period after the last +observed one. Each call appends its forecast to the series, recomputes the +features and moves the horizon one period forward, so calling it three times +gives a three-month recursive forecast. +""") + +code(""" +forecasts = pd.concat([pipeline.predict() for _ in range(3)]) +forecasts.groupby("t").describe().round(2) +""") + +code(""" +first_month = forecasts.index.get_level_values("t").min() +fc = points.join(forecasts.xs(first_month, level="t")) +top10 = fc.nlargest(10, "crime_density")[["lon", "lat", "crime_density"]].round(4) +print(f"Top-10 places for {first_month:%B %Y}:") +top10 +""") + +code(""" +fig, ax = plt.subplots(figsize=(7, 8)) +fc.plot(ax=ax, column="crime_density", cmap="magma", markersize=14, marker="s", legend=True, + legend_kwds={"shrink": 0.6, "label": "forecast density"}) +city.boundary.plot(ax=ax, color="black", linewidth=1) +hotspots.plot(ax=ax, color="#00e5ff", marker="*", markersize=170, zorder=5, label="true hotspot centres") +top = fc.nlargest(20, "crime_density") +ax.scatter(top["lon"], top["lat"], s=110, facecolors="none", edgecolors="#00e5ff", linewidths=1.6, label="top-20 forecast") +ax.legend(loc="lower left") +ax.set_title(f"Forecast hotspots for {first_month:%B %Y}") +ax.set_axis_off() +""") + +md(""" +### 6.1 Does the forecast find the true hotspots? + +Since the data are synthetic we know where the hotspots are. For each true +hotspot centre we look up the nearest grid point and check how it ranks in the +forecast (1 = hottest place of the city). Hotspots holding a larger share of the +events should rank near the top; small hotspots on a 500 m grid compete with the +many grid points that surround the biggest one. +""") + +code(""" +def distance_km(lon1, lat1, lon2, lat2): + dx = (lon1 - lon2) * 111.32 * np.cos(np.radians((lat1 + lat2) / 2)) + dy = (lat1 - lat2) * 110.57 + return np.hypot(dx, dy) + +ranked = fc.sort_values("crime_density", ascending=False) +ranked["rank"] = np.arange(1, len(ranked) + 1) +rows = [] +for i, h in hotspots.iterrows(): + d = distance_km(ranked["lon"].values, ranked["lat"].values, h.geometry.x, h.geometry.y) + nearest = ranked.iloc[int(d.argmin())] + rows.append({ + "hotspot": i, + "share_of_events": round(h["share"], 3), + "nearest_place": int(nearest.name), + "distance_km": round(float(d.min()), 2), + "forecast_rank": int(nearest["rank"]), + "percentile": round(100 * (1 - nearest["rank"] / len(ranked)), 1), + }) +pd.DataFrame(rows).set_index("hotspot").sort_values("forecast_rank") +""") + +code(""" +# The full history + forecast of the top forecast place +place = fc["crime_density"].idxmax() +series = pipeline.stseries.xs(place, level="places") +observed = series.loc[: stseries.index.get_level_values("t").max()] +predicted = series.loc[forecasts.index.get_level_values("t").min():] +ax = observed.plot(figsize=(10, 3.5), marker="o", label="observed (KDE)") +predicted.plot(ax=ax, marker="*", markersize=12, linestyle="--", color="#d62728", label="forecast") +ax.set_title(f"Place {place}: monthly density and 3-month recursive forecast") +ax.set_xlabel("") +ax.legend() +""") + +md(""" +## 7. The same pipeline with counts on hexagons + +`QuadratCount` is a drop-in replacement for `KDE`: swap the mapping and the +grid, keep everything else. +""") + +code(""" +hex_pipeline = PredictionPipeline( + mapping=QuadratCount(tfreq="M", grid=hexes), + fextraction=PandasFeatureUnion([ + ("ar", AR(lags=3)), + ("seasonal", Seasonality(lags=LAGS)), + ("trend", Trend(lags=LAGS)), + ]), + estimator=RandomForestRegressor(n_estimators=100, min_samples_leaf=2, random_state=0, n_jobs=-1), + random_state=0, +).fit(dataset) +print("r2 per fold:", np.round(hex_pipeline.evaluate("r2", cv=3), 3)) +hex_fc = hexes.join(hex_pipeline.predict().droplevel("t")) + +fig, ax = plt.subplots(figsize=(7, 8)) +hex_fc.plot(ax=ax, column="crime_density", cmap="magma", edgecolor="white", linewidth=0.3, legend=True, + legend_kwds={"shrink": 0.6, "label": "forecast events"}) +city.boundary.plot(ax=ax, color="black", linewidth=1) +hotspots.plot(ax=ax, color="#00e5ff", marker="*", markersize=170, zorder=5) +ax.set_title(f"Forecast events per hexagon for {hex_pipeline.stseries.index.get_level_values('t').max():%B %Y}") +ax.set_axis_off() +""") + +md(""" +## Wrapping up + +- `get_city_shape` gave us the study area; `generate_crimes` produced events with known hotspots. +- `Dataset` → grid → `KDE` / `QuadratCount` turned the events into a spatio-temporal series. +- Lagged STL trend/seasonal, difference and autoregressive features fed a scikit-learn pipeline + wrapped by `PredictionPipeline`, evaluated with time series cross-validation. +- `predict()` forecast the following months; the grid points nearest to the true hotspot + centres rank at the top of the forecast, in proportion to each hotspot's share of events. + +To run this on real data, replace the synthetic `crimes` DataFrame with your own +`tag, t, lon, lat` table and model each crime type (`tag`) separately, as the +framework recommends. See the [documentation](https://adaj.github.io/predspot/) +for the details of every step. +""") + +nb = nbf.v4.new_notebook() +nb["cells"] = cells +nb["metadata"] = { + "kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, + "language_info": {"name": "python"}, +} +out = Path(__file__).with_name("natal.ipynb") +nbf.write(nb, out) +print(f"wrote {out} with {len(cells)} cells") diff --git a/examples/natal.ipynb b/examples/natal.ipynb new file mode 100644 index 0000000..e995e61 --- /dev/null +++ b/examples/natal.ipynb @@ -0,0 +1,728 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7e4e38dc", + "metadata": {}, + "source": [ + "# Predspot end to end: crime hotspots for Natal, Brazil\n", + "\n", + "This notebook walks through the whole Predspot workflow on the city of Natal\n", + "(Rio Grande do Norte, Brazil), using **synthetic** crime events so that it\n", + "runs anywhere without confidential police data:\n", + "\n", + "1. fetch the city boundary from OpenStreetMap;\n", + "2. generate synthetic crime events with spatial hotspots and temporal patterns;\n", + "3. build the `Dataset`, the spatial grids and the spatio-temporal series (KDE and counts);\n", + "4. extract time series features;\n", + "5. fit, evaluate and inspect a `PredictionPipeline`;\n", + "6. forecast the next months and check how well the true hotspots are recovered.\n", + "\n", + "Every intermediate object is displayed so you can see exactly what flows\n", + "between the steps. Requirements: `pip install \"predspot[osm]\" matplotlib`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1dac71a9", + "metadata": {}, + "outputs": [], + "source": [ + "import warnings\n", + "\n", + "import geopandas as gpd\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "import predspot\n", + "from predspot import Dataset, PredictionPipeline, PandasFeatureUnion, generate_crimes, get_city_shape\n", + "from predspot.crime_mapping import KDE, QuadratCount, create_gridhexagonal, create_gridpoints\n", + "from predspot.feature_engineering import AR, Diff, Seasonality, Trend\n", + "\n", + "warnings.filterwarnings(\"ignore\", category=UserWarning)\n", + "pd.set_option(\"display.width\", 120)\n", + "pd.set_option(\"display.max_columns\", 20)\n", + "plt.rcParams[\"figure.dpi\"] = 100\n", + "\n", + "print(\"predspot\", predspot.__version__, \"| geopandas\", gpd.__version__, \"| pandas\", pd.__version__)" + ] + }, + { + "cell_type": "markdown", + "id": "cb9de6ba", + "metadata": {}, + "source": [ + "## 1. Study area: the boundary of Natal\n", + "\n", + "`get_city_shape` geocodes a place name with [osmnx](https://osmnx.readthedocs.io)\n", + "(Nominatim) and returns its boundary as a GeoDataFrame in WGS84. The query\n", + "should be specific enough for the first match to be the municipality." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0fbc889", + "metadata": {}, + "outputs": [], + "source": [ + "city = get_city_shape(\"Natal, RN, Brazil\")\n", + "city" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "88ba713c", + "metadata": {}, + "outputs": [], + "source": [ + "polygon = city.geometry.iloc[0]\n", + "print(\"geometry:\", polygon.geom_type, \"| vertices:\", len(polygon.exterior.coords))\n", + "print(\"bounds (W, S, E, N):\", np.round(city.total_bounds, 4))\n", + "area_km2 = city.to_crs(city.estimate_utm_crs()).area.iloc[0] / 1e6\n", + "print(f\"area: {area_km2:.1f} km²\")\n", + "\n", + "ax = city.plot(figsize=(5, 6), color=\"#f2f2f2\", edgecolor=\"black\")\n", + "ax.set_title(\"Natal, RN (OpenStreetMap boundary)\")\n", + "ax.set_axis_off()" + ] + }, + { + "cell_type": "markdown", + "id": "e1f4ca42", + "metadata": {}, + "source": [ + "## 2. Synthetic crime events\n", + "\n", + "`generate_crimes` draws events from a space-time point process inside the\n", + "polygon: a mixture of Gaussian **hotspots** plus a uniform background in space,\n", + "and a temporal intensity with a linear **trend**, an **annual** cycle and\n", + "**day-of-week** / **hour-of-day** profiles. With `return_hotspots=True` we also\n", + "get the hotspot centres, which lets us check later whether the model finds them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "caf16e7f", + "metadata": {}, + "outputs": [], + "source": [ + "crimes, hotspots = generate_crimes(\n", + " city,\n", + " n_events=12_000,\n", + " start=\"2019-01-01\",\n", + " end=\"2021-12-31\",\n", + " n_hotspots=5,\n", + " hotspot_share=0.65,\n", + " hotspot_sd_km=0.7,\n", + " trend=0.4, # +40% events from start to end\n", + " annual_amplitude=0.25, # busier around the peak month...\n", + " annual_peak_month=12, # ...December\n", + " seed=2019,\n", + " return_hotspots=True,\n", + ")\n", + "crimes.head(10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "256277d3", + "metadata": {}, + "outputs": [], + "source": [ + "print(crimes.shape)\n", + "crimes.describe(include=\"all\").T" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb00b456", + "metadata": {}, + "outputs": [], + "source": [ + "hotspots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "999d61a3", + "metadata": {}, + "outputs": [], + "source": [ + "crimes[\"tag\"].value_counts().to_frame(\"events\").assign(share=lambda d: (d[\"events\"] / len(crimes)).round(3))" + ] + }, + { + "cell_type": "markdown", + "id": "bd6b0177", + "metadata": {}, + "source": [ + "### 2.1 Where and when do the events happen?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4f4153c8", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(figsize=(7, 8))\n", + "city.plot(ax=ax, color=\"#f7f7f7\", edgecolor=\"black\")\n", + "sample = crimes.sample(4000, random_state=0)\n", + "ax.scatter(sample[\"lon\"], sample[\"lat\"], s=3, alpha=0.35, color=\"#1f4e79\", label=\"events (sample)\")\n", + "hotspots.plot(ax=ax, color=\"#d62728\", marker=\"*\", markersize=150, zorder=5, label=\"hotspot centres\")\n", + "ax.legend(loc=\"lower left\")\n", + "ax.set_title(\"Synthetic crime events in Natal\")\n", + "ax.set_axis_off()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b15e0701", + "metadata": {}, + "outputs": [], + "source": [ + "monthly = crimes.set_index(\"t\").resample(\"ME\").size().rename(\"events\")\n", + "fig, axes = plt.subplots(1, 3, figsize=(15, 3.6))\n", + "monthly.plot(ax=axes[0], marker=\"o\")\n", + "axes[0].set_title(\"Events per month (trend + annual cycle)\")\n", + "axes[0].set_xlabel(\"\")\n", + "crimes[\"t\"].dt.day_name().value_counts().reindex(\n", + " [\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\", \"Sunday\"]\n", + ").plot.bar(ax=axes[1], color=\"#1f4e79\")\n", + "axes[1].set_title(\"Events per weekday\")\n", + "crimes[\"t\"].dt.hour.value_counts().sort_index().plot.bar(ax=axes[2], color=\"#1f4e79\", width=0.9)\n", + "axes[2].set_title(\"Events per hour of day\")\n", + "fig.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "id": "b0340fd2", + "metadata": {}, + "source": [ + "## 3. Dataset, grids and the spatio-temporal series\n", + "\n", + "`Dataset` validates the events (columns `tag`, `t`, `lon`, `lat`) and turns\n", + "them into a GeoDataFrame of points in WGS84, together with the study area." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a810174", + "metadata": {}, + "outputs": [], + "source": [ + "dataset = Dataset(crimes, city)\n", + "dataset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d915daaf", + "metadata": {}, + "outputs": [], + "source": [ + "dataset.crimes.head()" + ] + }, + { + "cell_type": "markdown", + "id": "5552a527", + "metadata": {}, + "source": [ + "### 3.1 Grids\n", + "\n", + "Predspot discretises the city into **places**. Two kinds of grid are built\n", + "below: a grid of **points** every 500 m (used by `KDE`) and a grid of\n", + "**hexagons** of 1 km² (used by `QuadratCount`). Only cells intersecting the\n", + "city are kept; each grid has `lon`/`lat` columns with the cell centroid and an\n", + "index named `places`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "534bcc4d", + "metadata": {}, + "outputs": [], + "source": [ + "points = create_gridpoints(city, resolution=0.5)\n", + "hexes = create_gridhexagonal(city, resolution=1.0)\n", + "print(f\"{len(points)} grid points at 500 m | {len(hexes)} hexagons of 1 km²\")\n", + "points.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77bc64fc", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axes = plt.subplots(1, 2, figsize=(12, 7))\n", + "city.boundary.plot(ax=axes[0], color=\"black\")\n", + "points.plot(ax=axes[0], markersize=4, color=\"#1f4e79\")\n", + "axes[0].set_title(f\"Point grid, 500 m ({len(points)} places)\")\n", + "city.boundary.plot(ax=axes[1], color=\"black\")\n", + "hexes.plot(ax=axes[1], facecolor=\"none\", edgecolor=\"#1f4e79\", linewidth=0.6)\n", + "axes[1].set_title(f\"Hexagonal grid, 1 km² ({len(hexes)} places)\")\n", + "for ax in axes:\n", + " ax.set_axis_off()" + ] + }, + { + "cell_type": "markdown", + "id": "e96fee0a", + "metadata": {}, + "source": [ + "### 3.2 Spatio-temporal mapping\n", + "\n", + "A mapping assigns one value to every `(period, place)` pair. `KDE` fits a\n", + "Gaussian kernel density estimate to the events of each period and evaluates it\n", + "at the grid points; `QuadratCount` counts the events inside each polygon. Both\n", + "return the same object: a `pandas.Series` named `crime_density` with a\n", + "`(t, places)` MultiIndex — the **spatio-temporal series**." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "110f6eb3", + "metadata": {}, + "outputs": [], + "source": [ + "kde = KDE(tfreq=\"M\", grid=points, bandwidth=\"silverman\")\n", + "stseries = kde.fit_transform(dataset.crimes)\n", + "print(type(stseries).__name__, stseries.shape, \"| KDE factor:\", round(kde.factor, 4))\n", + "stseries.head(8)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9cf26da", + "metadata": {}, + "outputs": [], + "source": [ + "# The same series in wide form: one row per month, one column per place\n", + "wide = stseries.unstack(\"places\")\n", + "wide.iloc[:6, :8]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b92ac677", + "metadata": {}, + "outputs": [], + "source": [ + "counts = QuadratCount(tfreq=\"M\", grid=hexes).fit_transform(dataset.crimes)\n", + "counts_wide = counts.unstack(\"places\")\n", + "print(\"events per month recovered by the counts:\", counts_wide.sum(axis=1).astype(int).head(3).tolist(), \"...\")\n", + "counts_wide.iloc[:6, :8]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef9896a8", + "metadata": {}, + "outputs": [], + "source": [ + "month = pd.Timestamp(\"2021-06-30\")\n", + "fig, axes = plt.subplots(1, 2, figsize=(13, 6.5))\n", + "\n", + "g = points.copy()\n", + "g[\"density\"] = stseries.xs(month, level=\"t\").reindex(points.index).values\n", + "g.plot(ax=axes[0], column=\"density\", cmap=\"viridis\", markersize=12, marker=\"s\", legend=True,\n", + " legend_kwds={\"shrink\": 0.6, \"label\": \"KDE density\"})\n", + "city.boundary.plot(ax=axes[0], color=\"black\", linewidth=1)\n", + "hotspots.plot(ax=axes[0], color=\"#d62728\", marker=\"*\", markersize=150, zorder=5)\n", + "axes[0].set_title(f\"KDE on the point grid - {month:%B %Y}\")\n", + "\n", + "h = hexes.copy()\n", + "h[\"events\"] = counts.xs(month, level=\"t\").reindex(hexes.index).values\n", + "h.plot(ax=axes[1], column=\"events\", cmap=\"viridis\", edgecolor=\"white\", linewidth=0.3, legend=True,\n", + " legend_kwds={\"shrink\": 0.6, \"label\": \"events in the cell\"})\n", + "city.boundary.plot(ax=axes[1], color=\"black\", linewidth=1)\n", + "hotspots.plot(ax=axes[1], color=\"#d62728\", marker=\"*\", markersize=150, zorder=5)\n", + "axes[1].set_title(f\"QuadratCount on hexagons - {month:%B %Y}\")\n", + "for ax in axes:\n", + " ax.set_axis_off()" + ] + }, + { + "cell_type": "markdown", + "id": "c0a2c16f", + "metadata": {}, + "source": [ + "### 3.3 The KDE bandwidth\n", + "\n", + "`bandwidth=\"silverman\"` estimates the kernel width from the spread of the\n", + "events in the first period and keeps it fixed, which makes densities comparable\n", + "over time but tends to over-smooth a whole city. A numeric `bandwidth` is used\n", + "directly as the KDE factor (a multiple of the city-wide spread of the events):\n", + "smaller values give sharper maps. Compare three settings for the same month —\n", + "this is the trade-off discussed in the thesis (Chapter 2, Figure 3)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "859b64f4", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axes = plt.subplots(1, 3, figsize=(16, 5.5))\n", + "for ax, bw in zip(axes, [\"silverman\", 0.2, 0.08]):\n", + " m = KDE(tfreq=\"M\", grid=points, bandwidth=bw)\n", + " st = m.fit_transform(dataset.crimes)\n", + " g = points.copy()\n", + " g[\"density\"] = st.xs(month, level=\"t\").reindex(points.index).values\n", + " g.plot(ax=ax, column=\"density\", cmap=\"viridis\", markersize=10, marker=\"s\")\n", + " city.boundary.plot(ax=ax, color=\"black\", linewidth=1)\n", + " hotspots.plot(ax=ax, color=\"#d62728\", marker=\"*\", markersize=120, zorder=5)\n", + " ax.set_title(f\"bandwidth={bw!r} (factor {m.factor:.3f})\")\n", + " ax.set_axis_off()" + ] + }, + { + "cell_type": "markdown", + "id": "47e84097", + "metadata": {}, + "source": [ + "## 4. Time series features\n", + "\n", + "From here on we use `bandwidth=0.2`, a middle ground between the\n", + "over-smoothed Silverman estimate and a noisy small kernel.\n", + "\n", + "Each place has a monthly series. The feature classes transform it (STL trend,\n", + "STL seasonal component, first difference or the raw series) and build `lags`\n", + "lagged columns. `PandasFeatureUnion` aligns them on the `(t, places)` index and\n", + "drops the warm-up rows. Note the extra row for the month **after** the last\n", + "observed one — that is the row the model will forecast." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1d901dc3", + "metadata": {}, + "outputs": [], + "source": [ + "BANDWIDTH = 0.2\n", + "stseries = KDE(tfreq=\"M\", grid=points, bandwidth=BANDWIDTH).fit_transform(dataset.crimes)\n", + "\n", + "LAGS = 6\n", + "fextraction = PandasFeatureUnion([\n", + " (\"ar\", AR(lags=3)),\n", + " (\"seasonal\", Seasonality(lags=LAGS)),\n", + " (\"trend\", Trend(lags=LAGS)),\n", + " (\"diff\", Diff(lags=LAGS)),\n", + "])\n", + "X = fextraction.fit_transform(stseries)\n", + "print(X.shape, \"| periods:\", X.index.get_level_values(\"t\").min().date(), \"->\", X.index.get_level_values(\"t\").max().date())\n", + "X.head(8)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66dd5428", + "metadata": {}, + "outputs": [], + "source": [ + "# What the decomposition looks like for the busiest place\n", + "busiest = stseries.groupby(\"places\").mean().idxmax()\n", + "ts = stseries.xs(busiest, level=\"places\")\n", + "from statsmodels.tsa.seasonal import STL\n", + "res = STL(ts, period=LAGS).fit()\n", + "fig, axes = plt.subplots(4, 1, figsize=(10, 7), sharex=True)\n", + "ts.plot(ax=axes[0], title=f\"place {busiest}: KDE density per month\")\n", + "res.trend.plot(ax=axes[1], title=\"STL trend\")\n", + "res.seasonal.plot(ax=axes[2], title=f\"STL seasonal (period={LAGS})\")\n", + "ts.diff().plot(ax=axes[3], title=\"first difference\")\n", + "for ax in axes:\n", + " ax.set_xlabel(\"\")\n", + "fig.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "id": "0d3cca51", + "metadata": {}, + "source": [ + "## 5. Fit and evaluate a prediction pipeline\n", + "\n", + "The pipeline chains the mapping, the feature extraction and a scikit-learn\n", + "estimator. Here the estimator is itself a `Pipeline`: quantile scaling,\n", + "recursive feature elimination and a random forest, each wrapped so that\n", + "DataFrames (and the `(t, places)` index) survive every step." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a613809", + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.ensemble import RandomForestRegressor\n", + "from sklearn.feature_selection import RFE\n", + "from sklearn.pipeline import Pipeline\n", + "from sklearn.preprocessing import QuantileTransformer\n", + "\n", + "from predspot.feature_engineering import FeatureScaling\n", + "from predspot.ml_modelling import FeatureSelection, Model\n", + "\n", + "pipeline = PredictionPipeline(\n", + " mapping=KDE(tfreq=\"M\", grid=points, bandwidth=BANDWIDTH),\n", + " fextraction=PandasFeatureUnion([\n", + " (\"ar\", AR(lags=3)),\n", + " (\"seasonal\", Seasonality(lags=LAGS)),\n", + " (\"trend\", Trend(lags=LAGS)),\n", + " (\"diff\", Diff(lags=LAGS)),\n", + " ]),\n", + " estimator=Pipeline([\n", + " (\"scaling\", FeatureScaling(QuantileTransformer(n_quantiles=20, output_distribution=\"uniform\"))),\n", + " (\"selection\", FeatureSelection(RFE(RandomForestRegressor(n_estimators=20, random_state=0, n_jobs=-1),\n", + " n_features_to_select=12, step=3))),\n", + " (\"model\", Model(RandomForestRegressor(n_estimators=100, min_samples_leaf=2, random_state=0, n_jobs=-1))),\n", + " ]),\n", + " random_state=0,\n", + ")\n", + "pipeline.fit(dataset)\n", + "print(\"training rows:\", pipeline.features.loc[: stseries.index.get_level_values(\"t\").max()].shape[0])\n", + "print(\"next period to forecast:\", pipeline.next_time.date())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eee77d05", + "metadata": {}, + "outputs": [], + "source": [ + "scores = pipeline.evaluate([\"r2\", \"mse\"], cv=3) # time series CV: train on earlier folds, test on the next\n", + "scores.loc[\"mean\"] = scores.mean()\n", + "scores" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f797ad0", + "metadata": {}, + "outputs": [], + "source": [ + "fi = pipeline.feature_importances\n", + "ax = fi.sort_values(\"importance\").plot.barh(figsize=(7, 5), legend=False, color=\"#1f4e79\")\n", + "ax.set_title(\"Selected features and their importance\")\n", + "fi.head(12)" + ] + }, + { + "cell_type": "markdown", + "id": "47c7fc5c", + "metadata": {}, + "source": [ + "## 6. Forecast the next months\n", + "\n", + "`predict()` returns the density of every place for the period after the last\n", + "observed one. Each call appends its forecast to the series, recomputes the\n", + "features and moves the horizon one period forward, so calling it three times\n", + "gives a three-month recursive forecast." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18edefe0", + "metadata": {}, + "outputs": [], + "source": [ + "forecasts = pd.concat([pipeline.predict() for _ in range(3)])\n", + "forecasts.groupby(\"t\").describe().round(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "af03b229", + "metadata": {}, + "outputs": [], + "source": [ + "first_month = forecasts.index.get_level_values(\"t\").min()\n", + "fc = points.join(forecasts.xs(first_month, level=\"t\"))\n", + "top10 = fc.nlargest(10, \"crime_density\")[[\"lon\", \"lat\", \"crime_density\"]].round(4)\n", + "print(f\"Top-10 places for {first_month:%B %Y}:\")\n", + "top10" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2bc5e223", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(figsize=(7, 8))\n", + "fc.plot(ax=ax, column=\"crime_density\", cmap=\"magma\", markersize=14, marker=\"s\", legend=True,\n", + " legend_kwds={\"shrink\": 0.6, \"label\": \"forecast density\"})\n", + "city.boundary.plot(ax=ax, color=\"black\", linewidth=1)\n", + "hotspots.plot(ax=ax, color=\"#00e5ff\", marker=\"*\", markersize=170, zorder=5, label=\"true hotspot centres\")\n", + "top = fc.nlargest(20, \"crime_density\")\n", + "ax.scatter(top[\"lon\"], top[\"lat\"], s=110, facecolors=\"none\", edgecolors=\"#00e5ff\", linewidths=1.6, label=\"top-20 forecast\")\n", + "ax.legend(loc=\"lower left\")\n", + "ax.set_title(f\"Forecast hotspots for {first_month:%B %Y}\")\n", + "ax.set_axis_off()" + ] + }, + { + "cell_type": "markdown", + "id": "822a11ea", + "metadata": {}, + "source": [ + "### 6.1 Does the forecast find the true hotspots?\n", + "\n", + "Since the data are synthetic we know where the hotspots are. For each true\n", + "hotspot centre we look up the nearest grid point and check how it ranks in the\n", + "forecast (1 = hottest place of the city). Hotspots holding a larger share of the\n", + "events should rank near the top; small hotspots on a 500 m grid compete with the\n", + "many grid points that surround the biggest one." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb82e3a9", + "metadata": {}, + "outputs": [], + "source": [ + "def distance_km(lon1, lat1, lon2, lat2):\n", + " dx = (lon1 - lon2) * 111.32 * np.cos(np.radians((lat1 + lat2) / 2))\n", + " dy = (lat1 - lat2) * 110.57\n", + " return np.hypot(dx, dy)\n", + "\n", + "ranked = fc.sort_values(\"crime_density\", ascending=False)\n", + "ranked[\"rank\"] = np.arange(1, len(ranked) + 1)\n", + "rows = []\n", + "for i, h in hotspots.iterrows():\n", + " d = distance_km(ranked[\"lon\"].values, ranked[\"lat\"].values, h.geometry.x, h.geometry.y)\n", + " nearest = ranked.iloc[int(d.argmin())]\n", + " rows.append({\n", + " \"hotspot\": i,\n", + " \"share_of_events\": round(h[\"share\"], 3),\n", + " \"nearest_place\": int(nearest.name),\n", + " \"distance_km\": round(float(d.min()), 2),\n", + " \"forecast_rank\": int(nearest[\"rank\"]),\n", + " \"percentile\": round(100 * (1 - nearest[\"rank\"] / len(ranked)), 1),\n", + " })\n", + "pd.DataFrame(rows).set_index(\"hotspot\").sort_values(\"forecast_rank\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3350c32", + "metadata": {}, + "outputs": [], + "source": [ + "# The full history + forecast of the top forecast place\n", + "place = fc[\"crime_density\"].idxmax()\n", + "series = pipeline.stseries.xs(place, level=\"places\")\n", + "observed = series.loc[: stseries.index.get_level_values(\"t\").max()]\n", + "predicted = series.loc[forecasts.index.get_level_values(\"t\").min():]\n", + "ax = observed.plot(figsize=(10, 3.5), marker=\"o\", label=\"observed (KDE)\")\n", + "predicted.plot(ax=ax, marker=\"*\", markersize=12, linestyle=\"--\", color=\"#d62728\", label=\"forecast\")\n", + "ax.set_title(f\"Place {place}: monthly density and 3-month recursive forecast\")\n", + "ax.set_xlabel(\"\")\n", + "ax.legend()" + ] + }, + { + "cell_type": "markdown", + "id": "f01c22b2", + "metadata": {}, + "source": [ + "## 7. The same pipeline with counts on hexagons\n", + "\n", + "`QuadratCount` is a drop-in replacement for `KDE`: swap the mapping and the\n", + "grid, keep everything else." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c8e650f", + "metadata": {}, + "outputs": [], + "source": [ + "hex_pipeline = PredictionPipeline(\n", + " mapping=QuadratCount(tfreq=\"M\", grid=hexes),\n", + " fextraction=PandasFeatureUnion([\n", + " (\"ar\", AR(lags=3)),\n", + " (\"seasonal\", Seasonality(lags=LAGS)),\n", + " (\"trend\", Trend(lags=LAGS)),\n", + " ]),\n", + " estimator=RandomForestRegressor(n_estimators=100, min_samples_leaf=2, random_state=0, n_jobs=-1),\n", + " random_state=0,\n", + ").fit(dataset)\n", + "print(\"r2 per fold:\", np.round(hex_pipeline.evaluate(\"r2\", cv=3), 3))\n", + "hex_fc = hexes.join(hex_pipeline.predict().droplevel(\"t\"))\n", + "\n", + "fig, ax = plt.subplots(figsize=(7, 8))\n", + "hex_fc.plot(ax=ax, column=\"crime_density\", cmap=\"magma\", edgecolor=\"white\", linewidth=0.3, legend=True,\n", + " legend_kwds={\"shrink\": 0.6, \"label\": \"forecast events\"})\n", + "city.boundary.plot(ax=ax, color=\"black\", linewidth=1)\n", + "hotspots.plot(ax=ax, color=\"#00e5ff\", marker=\"*\", markersize=170, zorder=5)\n", + "ax.set_title(f\"Forecast events per hexagon for {hex_pipeline.stseries.index.get_level_values('t').max():%B %Y}\")\n", + "ax.set_axis_off()" + ] + }, + { + "cell_type": "markdown", + "id": "08c02abe", + "metadata": {}, + "source": [ + "## Wrapping up\n", + "\n", + "- `get_city_shape` gave us the study area; `generate_crimes` produced events with known hotspots.\n", + "- `Dataset` → grid → `KDE` / `QuadratCount` turned the events into a spatio-temporal series.\n", + "- Lagged STL trend/seasonal, difference and autoregressive features fed a scikit-learn pipeline\n", + " wrapped by `PredictionPipeline`, evaluated with time series cross-validation.\n", + "- `predict()` forecast the following months; the grid points nearest to the true hotspot\n", + " centres rank at the top of the forecast, in proportion to each hotspot's share of events.\n", + "\n", + "To run this on real data, replace the synthetic `crimes` DataFrame with your own\n", + "`tag, t, lon, lat` table and model each crime type (`tag`) separately, as the\n", + "framework recommends. See the [documentation](https://adaj.github.io/predspot/)\n", + "for the details of every step." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/mkdocs.yml b/mkdocs.yml index 78309a3..d91c589 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -48,6 +48,8 @@ nav: - Grids and spatio-temporal mapping: guide/mapping.md - Feature engineering: guide/features.md - Prediction and evaluation: guide/modelling.md + - Examples: + - Natal, Brazil (end to end): examples/natal.ipynb - API reference: - predspot: api/predspot.md - dataset_preparation: api/dataset_preparation.md @@ -62,8 +64,15 @@ nav: - Contributing: contributing.md - Citing Predspot: citing.md +hooks: + - docs/hooks/readme.py + plugins: - search + - mkdocs-jupyter: + execute: false + include_source: true + include_requirejs: true - autorefs - mkdocstrings: handlers: diff --git a/pyproject.toml b/pyproject.toml index 5c61551..89b35b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,7 @@ docs = [ "mkdocs-material>=9.5,<10", "mkdocstrings[python]>=0.26", "mkdocs-autorefs>=1.2", + "mkdocs-jupyter>=0.25", ] dev = [ "pytest>=8", @@ -88,6 +89,7 @@ branch = true [tool.ruff] line-length = 100 +exclude = ["examples"] # tutorial code: readability over line length target-version = "py310" src = ["src", "tests"] diff --git a/src/predspot/__init__.py b/src/predspot/__init__.py index f85d774..5d2ca3d 100644 --- a/src/predspot/__init__.py +++ b/src/predspot/__init__.py @@ -23,6 +23,7 @@ create_gridhexagonal, create_gridpoints, create_gridsquares, + get_city_shape, load_study_area, ) from predspot.dataset_preparation import Dataset @@ -42,6 +43,7 @@ "create_gridhexagonal", "create_gridsquares", "load_study_area", + "get_city_shape", "generate_crimes", "synthetic", "crime_mapping", diff --git a/src/predspot/crime_mapping.py b/src/predspot/crime_mapping.py index 710d7c7..f2ccfa9 100644 --- a/src/predspot/crime_mapping.py +++ b/src/predspot/crime_mapping.py @@ -138,6 +138,36 @@ def load_study_area(place, crs=WGS84, which_result=None): return gdf.to_crs(crs) +def get_city_shape(place_query): + """ + Fetch the shape (polygon) of a city or region from OpenStreetMap. + + A thin wrapper around ``osmnx.geocode_to_gdf`` that returns the raw + Nominatim result. Prefer [`load_study_area`][predspot.crime_mapping.load_study_area] + when you want the result validated (polygon geometry, tidy columns). + Requires the optional ``osmnx`` dependency (``pip install predspot[osm]``). + + Args: + place_query (str): Name of the place, in a format Nominatim accepts, + e.g. ``"Natal, RN, Brazil"`` or ``"Rio Grande do Norte, Brazil"``. + + Returns: + GeoDataFrame: One row (or more, if the query is ambiguous) with the + place geometry (Polygon/MultiPolygon) in WGS84. + + Example: + >>> city = get_city_shape("Natal, RN, Brazil") + >>> city.geometry.iloc[0] # shapely Polygon/MultiPolygon + """ + try: + import osmnx as ox + except ImportError as exc: + raise ImportError( + "get_city_shape requires the optional dependency `osmnx`: pip install predspot[osm]" + ) from exc + return ox.geocode_to_gdf(place_query) + + def _check_bbox(bbox): if not isinstance(bbox, gpd.GeoDataFrame): raise TypeError("bbox must be a geopandas GeoDataFrame.") diff --git a/src/predspot/ml_modelling.py b/src/predspot/ml_modelling.py index ec7e4e1..32c6e6c 100644 --- a/src/predspot/ml_modelling.py +++ b/src/predspot/ml_modelling.py @@ -227,16 +227,19 @@ def evaluate(self, scoring="r2", cv=5): refitted on the original data afterwards. Args: - scoring (str): ``'r2'`` or ``'mse'``. + scoring (str or list): ``'r2'``, ``'mse'`` or a list of them. cv (int): Number of folds (must be lower than the number of periods). Returns: - list: One score per fold. + list or pandas.DataFrame: One score per fold; with a list of + scorings, a DataFrame with one column per scoring and one row + per fold. """ self._check_fitted() - if scoring not in SCORERS: + scorings = [scoring] if isinstance(scoring, str) else list(scoring) + unknown = [s for s in scorings if s not in SCORERS] + if unknown or not scorings: raise ValueError('invalid scoring. Try "r2" or "mse".') - scorer = SCORERS[scoring] timestamps = ( self._X.index.get_level_values("t") .unique() @@ -245,7 +248,7 @@ def evaluate(self, scoring="r2", cv=5): ) if not isinstance(cv, int) or cv >= len(timestamps): raise ValueError("cv must be an integer lower than the number of periods.") - scores = [] + scores = {name: [] for name in scorings} for train_t, test_t in TimeSeriesSplit(cv).split(timestamps): X_train = self._X.loc[idx[timestamps[train_t], :], :].sample( frac=1, random_state=self.random_state @@ -255,7 +258,10 @@ def evaluate(self, scoring="r2", cv=5): y_test = self._stseries.loc[X_test.index] self.estimator.fit(X_train, y_train) y_pred = self.estimator.predict(X_test) - scores.append(scorer(y_test, y_pred)) - logger.debug("%s-fold CV %s scores: %s", cv, scoring, scores) + for name in scorings: + scores[name].append(SCORERS[name](y_test, y_pred)) + logger.debug("%s-fold CV scores: %s", cv, scores) self.fit(self._dataset) # back to normal - return scores + if isinstance(scoring, str): + return scores[scoring] + return pd.DataFrame(scores, index=[f"fold {i + 1}" for i in range(cv)]) diff --git a/tests/test_load_study_area.py b/tests/test_load_study_area.py index 70efc14..92b7640 100644 --- a/tests/test_load_study_area.py +++ b/tests/test_load_study_area.py @@ -6,7 +6,7 @@ import pytest from shapely.geometry import Point, box -from predspot.crime_mapping import load_study_area +from predspot.crime_mapping import get_city_shape, load_study_area def _fake_osmnx(monkeypatch, geometry): @@ -70,3 +70,17 @@ def test_load_study_area_network(): assert len(area) == 1 assert area.geometry.iloc[0].geom_type in ("Polygon", "MultiPolygon") assert area.geometry.iloc[0].contains(Point(-35.2094, -5.7945)) # city centre + + +def test_get_city_shape_returns_raw_geocode(monkeypatch): + calls = _fake_osmnx(monkeypatch, box(-35.3, -5.9, -35.2, -5.8)) + city = get_city_shape("Natal, RN, Brazil") + assert calls["query"] == "Natal, RN, Brazil" + assert city.geometry.iloc[0].geom_type == "Polygon" + assert "place_rank" in city.columns # raw Nominatim columns are kept + + +def test_get_city_shape_without_osmnx(monkeypatch): + monkeypatch.setitem(sys.modules, "osmnx", None) + with pytest.raises(ImportError, match="predspot\\[osm\\]"): + get_city_shape("Natal, RN, Brazil") diff --git a/tests/test_ml_modelling.py b/tests/test_ml_modelling.py index 0a1479f..d6cac36 100644 --- a/tests/test_ml_modelling.py +++ b/tests/test_ml_modelling.py @@ -73,8 +73,12 @@ def test_evaluate(dataset, study_area): assert len(scores) == 3 mse = pipe.evaluate("mse", cv=3) assert all(s >= 0 for s in mse) + both = pipe.evaluate(["r2", "mse"], cv=3) + assert list(both.columns) == ["r2", "mse"] and len(both) == 3 with pytest.raises(ValueError): pipe.evaluate("mae") + with pytest.raises(ValueError): + pipe.evaluate([]) with pytest.raises(ValueError): pipe.evaluate("r2", cv=100) # evaluate refits on the full data: predictions still work afterwards

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 f|7lv!uK&WmHA((b)m{AimgZi2v` literal 0 HcmV?d00001 diff --git a/docs/assets/thesis/fig11-ml-modelling.png b/docs/assets/thesis/fig11-ml-modelling.png new file mode 100644 index 0000000000000000000000000000000000000000..f2c1f9c5f1e84fbdef3af79d482ffad20bde97bb GIT binary patch literal 39625 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 V-g=U_Kz#G_XRi>F&)jW}1o`{RU!m`4tuw|m|M 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 literal 0 HcmV?d00001 diff --git a/docs/examples/natal.ipynb b/docs/examples/natal.ipynb new file mode 120000 index 0000000..a40a4d7 --- /dev/null +++ b/docs/examples/natal.ipynb @@ -0,0 +1 @@ +../../examples/natal.ipynb \ No newline at end of file diff --git a/docs/guide/modelling.md b/docs/guide/modelling.md index 639be8f..c3fa9c3 100644 --- a/docs/guide/modelling.md +++ b/docs/guide/modelling.md @@ -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 diff --git a/docs/hooks/readme.py b/docs/hooks/readme.py new file mode 100644 index 0000000..516e311 --- /dev/null +++ b/docs/hooks/readme.py @@ -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 diff --git a/docs/index.md b/docs/index.md index d9ba291..c4f5189 100644 --- a/docs/index.md +++ b/docs/index.md @@ -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. - -

- - -
-
- - - - - - -
-
-
-