Skip to main content

Python implementation of R package gridmappr for automated gridmap layout generation

Project description

pygridmappr

PyPI version License: AGPL v3 Python 3.7+

A faithful Python implementation of the R package gridmappr by Roger Beecham.

Table of Contents

Overview

pygridmappr automates the generation of small multiple gridmap layouts. Given a set of geographic point locations, it creates a grid with specified row and column dimensions, placing each point in a grid cell such that the distance between points in geographic space and grid space is minimized.

This implementation maintains full feature parity with the original R package and replicates the mathematical logic as faithfully as possible.

Features

  • Algorithm replication: Uses Hungarian algorithm (linear sum assignment) for optimal point-to-grid allocation
  • Compactness parameter: Control trade-off between geographic fidelity and grid compactness (0-1 scale)
  • Spacer cells: Constrain allocation by excluding specific grid cells
  • Quality metrics: Compute RMSE, mean distance, and other quality measures
  • Visualization tools: Compare geographic and grid layouts side-by-side
  • Deterministic results: Reproducible allocations for identical inputs

Installation

# From source (recommended for development)
git clone https://github.com/tmfnk/pygridmappr
cd pygridmappr
pip install -e .

# Or via pip (when available)
pip install pygridmappr

Requirements

  • Python ≥ 3.7
  • Core Dependencies:
    • numpy ≥ 1.19.0
    • pandas ≥ 1.1.0
    • scipy ≥ 1.5.0
    • matplotlib ≥ 3.3.0

Development Installation

For contributors and developers:

git clone https://github.com/tmfnk/pygridmappr
cd pygridmappr
pip install -e ".[dev]"

Quick Start

import pandas as pd
from pygridmappr import points_to_grid, visualize_allocation

# Create point data
pts = pd.DataFrame({
    'area_name': ['A', 'B', 'C', 'D'],
    'x': [0, 100, 100, 0],
    'y': [0, 0, 100, 100]
})

# Allocate to 2×2 grid
result = points_to_grid(pts, n_row=2, n_col=2, compactness=0.5)

# Visualize
fig, axes = visualize_allocation(result, n_row=2, n_col=2)

Core Function: points_to_grid()

points_to_grid(
    pts: pd.DataFrame,
    n_row: int,
    n_col: int,
    compactness: float = 1.0,
    spacers: Optional[List[Tuple[int, int]]] = None
) -> pd.DataFrame

Parameters

  • pts: DataFrame with columns 'x' and 'y' (required), optionally 'area_name' or other identifiers
  • n_row: Number of grid rows (must be ≥ 1)
  • n_col: Number of grid columns (must be ≥ 1)
  • compactness: Value between 0 and 1:
    • 0.5: Preserves scaled geographic layout
    • 1.0: Allocates toward grid center (compact cluster)
    • 0.0: Allocates toward grid edges
  • spacers: List of (row, col) tuples using 1-based indexing with origin (1,1) at bottom-left (matches R convention)

Returns

DataFrame with added columns:

  • row: Grid row assignment (1-based)
  • col: Grid column assignment (1-based)
  • grid_x: X coordinate of grid cell center
  • grid_y: Y coordinate of grid cell center

Mathematical Approach

The algorithm minimizes the total squared distance between:

  1. Geographic positions (scaled to grid bounds)
  2. Grid cell positions

Cost Matrix

For each point i and grid cell j:

C[i,j] = (x_scaled[i] - x_grid[j])² + (y_scaled[i] - y_grid[j])²

Compactness Adjustment

When compactness ≠ 0.5, costs are modified based on distance from grid center:

penalty = -2(compactness - 0.5) × normalized_distance_from_center[j]
C[i,j] += penalty × mean(C[i,:])
  • compactness > 0.5: Reduces cost for cells near center (attraction)
  • compactness < 0.5: Increases cost for cells near center (repulsion)

Assignment

The optimal assignment is found using scipy.optimize.linear_sum_assignment (Hungarian algorithm), which solves:

minimize Σᵢ C[i, assignment[i]]

subject to:

  • Each point assigned to exactly one cell
  • Each cell contains at most one point

Examples

Example 1: Basic Allocation

from pygridmappr import points_to_grid, generate_sample_points

# Generate random points
pts = generate_sample_points(n_points=20, pattern='random', seed=42)

# Allocate with geographic preservation
result = points_to_grid(pts, n_row=5, n_col=5, compactness=0.5)

Example 2: Compactness Comparison

from pygridmappr import compare_compactness

fig, axes = compare_compactness(
    pts,
    n_row=6, n_col=6,
    compactness_values=[0.0, 0.5, 1.0]
)

Example 3: Using Spacers

# Define spacers to create separation (e.g., island from mainland)
spacers = [
    (1, 11), (2, 11), (3, 11),  # Bottom-right corner
    (1, 10), (2, 10)
]

result = points_to_grid(
    pts,
    n_row=13, n_col=12,
    compactness=0.6,
    spacers=spacers
)

Example 4: Quality Metrics

from pygridmappr import compute_allocation_quality

quality = compute_allocation_quality(result)
print(f"RMSE: {quality['rmse']:.3f}")
print(f"Mean distance: {quality['mean_distance']:.3f}")
print(f"Max distance: {quality['max_distance']:.3f}")

Demonstration Gallery

The package includes comprehensive demonstrations that showcase the key features and capabilities of the gridmappr algorithm. These examples illustrate the mathematical principles and practical applications of the grid allocation method.

Quick Start

Run the complete demonstration suite:

cd examples
python demo.py

This generates five professional visualizations that demonstrate different aspects of the algorithm:


Demo 1: Basic Point Allocation

Basic Allocation Example

Figure 1: Basic 2×2 grid allocation demonstrating optimal assignment of four corner points to grid cells while preserving geographic relationships. The algorithm minimizes total squared distance between original geographic positions and grid cell centers.


Demo 2: Compactness Parameter Effect

Compactness Parameter Effect

Figure 2: Systematic comparison of compactness parameter effects (0.0, 0.5, 1.0). The compactness parameter controls the trade-off between geographic fidelity and grid clustering: lower values preserve spatial relationships while higher values create more compact, centralized clusters.


Demo 3: Spacer Constraints

Spacer Constraints

Figure 3: Demonstration of spacer constraints for geographic separation. Left panel shows unconstrained allocation allowing mainland-island mixing; right panel shows allocation with spacer barriers creating forced separation, effectively mimicking the geographic isolation of Corsica from mainland France.


Demo 4: Geographic Pattern Analysis

Geographic Patterns

Figure 4: Comparative analysis of different geographic input patterns (random, cluster, ring, grid) and their transformation into grid layouts. Each column shows the original geographic distribution (top row) and resulting grid allocation (bottom row) with quantitative RMSE quality metrics.


Demo 5: Grid Size Optimization

Grid Size Exploration

Figure 5: Systematic exploration of grid size effects on allocation quality. Analysis shows how different grid dimensions (8×8, 10×10, 12×12, 15×15) balance between available graphic space and geographic fidelity, with quantitative RMSE measurements for each configuration.

Design Philosophy

This implementation prioritizes accuracy over optimization. The code structure and logic closely mirror the original R implementation to ensure:

  1. Mathematical fidelity: Exact replication of cost calculations and compactness effects
  2. Reproducibility: Deterministic results for research and documentation
  3. Transparency: Clear documentation of algorithm steps with references

Differences from R Implementation

  • Language: Python instead of R
  • Solver: scipy.optimize.linear_sum_assignment instead of R's linear programming solver
  • Visualization: matplotlib instead of ggplot2
  • Data structures: pandas DataFrame instead of tibble

Core algorithm behavior is identical.

References

Original R Package

Publications

  • Beecham, R., Dykes, J., Hama, L. and Lomax, N. (2021). On the Use of 'Glyphmaps' for Analysing the Scale and Temporal Spread of COVID-19 Reported Cases. ISPRS International Journal of Geo-Information, 10(4), 213. https://doi.org/10.3390/ijgi10040213

  • Beecham, R. and Slingsby, A. (2019). Characterising labour market self-containment in London with geographically arranged small multiples. Environment and Planning A: Economy and Space, 51(6), 1217–1224. https://doi.org/10.1177/0308518X19850580

Inspiration

License

AGPL-3.0 License (matching original R package)

Citation

If you use this package, please cite the original R package:

@software{beecham2021gridmappr,
  author = {Beecham, Roger},
  title = {gridmappr: Gridmap Allocations with Approximate Spatial Arrangements},
  year = {2021},
  url = {https://github.com/rogerbeecham/gridmappr}
}

Contributing

Contributions are welcome! Please ensure any changes maintain mathematical fidelity with the original R implementation.

Acknowledgments

This Python implementation is based on Roger Beecham's excellent R package gridmappr. All credit for the algorithm design and innovation goes to Roger Beecham and Jo Wood.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pygridmappr-0.1.0.tar.gz (322.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pygridmappr-0.1.0-py3-none-any.whl (26.1 kB view details)

Uploaded Python 3

File details

Details for the file pygridmappr-0.1.0.tar.gz.

File metadata

  • Download URL: pygridmappr-0.1.0.tar.gz
  • Upload date:
  • Size: 322.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for pygridmappr-0.1.0.tar.gz
Algorithm Hash digest
SHA256 56e8c07d7e1167faa9181731916265ba0c2b217511cc4d9212db7db5220e2265
MD5 4b8ba31b0c4cd2916a773e5f4174fe6f
BLAKE2b-256 43b76c9b416780f0d4a6ebb91082b09efcd7762bea18f9d1ca0ee60646c7bf43

See more details on using hashes here.

File details

Details for the file pygridmappr-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pygridmappr-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 26.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for pygridmappr-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 50e25bd2c971ffcffab5a339b78fcb650bec7bcc844d84fbe29e7faa26fca216
MD5 94addb12407ab4d07eca86e27c95cd82
BLAKE2b-256 6cd2a4a90e14308681dd81d80a642e9208fa23d656a96d2cfcbbf5621f6227ce

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page