This page was generated from docsrc/notebooks/pointpattern-attributes.ipynb. Interactive online version: Binder badge

If any part of this notebook is used in your research, please cite with the reference found in

Network point pattern attributes

Demonstrating network point pattern representation

Author: James D. Gaboardi

This notebook is an basic walk-through for:

  1. Exploring the attributes of network objects and point patterns

  2. Generating observation counts per network link

  3. Simulating a point pattern

%load_ext watermark

CPython 3.7.3
IPython 7.10.2

compiler   : Clang 9.0.0 (tags/RELEASE_900/final)
system     : Darwin
release    : 19.4.0
machine    : x86_64
processor  : i386
CPU cores  : 4
interpreter: 64bit
import geopandas
import libpysal
import matplotlib
import matplotlib_scalebar
from matplotlib_scalebar.scalebar import ScaleBar
import numpy
import pandas
import shapely
from shapely.geometry import Point
import spaghetti

%matplotlib inline
%watermark -w
%watermark -iv
watermark 2.0.2
libpysal            4.2.2
numpy               1.18.1
matplotlib_scalebar 0.6.1
spaghetti           1.5.0.rc0
shapely             1.7.0
pandas              1.0.3
geopandas           0.7.0
matplotlib          3.1.2

    from IPython.display import set_matplotlib_formats
except ImportError:

Instantiating a spaghetti.Network object

Instantiate the network from a .shp file

ntw = spaghetti.Network(in_data=libpysal.examples.get_path("streets.shp"))

1. Allocating observations (snapping points) to a network:

A network is composed of a single topological representation of network elements (arcs and vertices) to which point patterns may be snapped.

pp_name = "crimes"
pp_shp = libpysal.examples.get_path("%s.shp" % pp_name)
ntw.snapobservations(pp_shp, pp_name, attribute=True)
{'crimes': < at 0x1217665c0>}

Attributes for every point pattern

  1. dist_snapped dict keyed by point id with the value as snapped distance from observation to network arc

  1. dist_to_vertex dict keyed by pointid with the value being a dict in the form {node: distance to vertex, node: distance to vertex}

{161: 83.70599311338093, 162: 316.8274480625799}
  1. npoints point observations in set

  1. obs_to_arc dict keyed by arc with the value being a dict in the form {pointID:(x-coord, y-coord), pointID:(x-coord, y-coord), … }

ntw.pointpatterns[pp_name].obs_to_arc[(161, 162)]
{0: (727919.2473619275, 875942.4986759046)}
  1. obs_to_vertex list of incident network vertices to snapped observation points

  1. points geojson like representation of the point pattern. Includes properties if read with attributes=True

{'coordinates': (727913.0000000029, 875720.9999999977), 'properties': [[1, 1]]}
  1. snapped_coordinates dict keyed by pointid with the value being (x-coord, y-coord)

(727919.2473619275, 875942.4986759046)

3. Simulate a point pattern on the network

  • The number of points must supplied.

  • The only distribution currently supported is uniform.

  • Generally, this will not be called by the user since the simulation will be used for Monte Carlo permutation.

npts = ntw.pointpatterns[pp_name].npoints
sim_uniform = ntw.simulate_observations(npts)
< at 0x12179fe10>
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'dist_to_vertex', 'npoints', 'obs_to_arc', 'obs_to_vertex', 'points', 'snapped_coordinates']

Extract the simulated points along the network a geopandas.GeoDataFrame

def as_gdf(pp):
    pp = {idx: Point(coords) for idx, coords in pp.items()}
    df = pandas.DataFrame.from_dict(pp, orient="index"), df.columns = "id", ["geometry"]
    gdf = geopandas.GeoDataFrame(df, geometry=df.geometry)
    return gdf

sim_uniform_gdf = as_gdf(sim_uniform.points)
0 POINT (726808.487 877801.878)
1 POINT (726956.795 880638.515)
2 POINT (726017.344 879947.648)
3 POINT (728151.386 879025.697)
4 POINT (727590.963 879451.415)

Create geopandas.GeoDataFrame objects of the vertices and arcs

vertices_df, arcs_df = spaghetti.element_as_gdf(
    ntw, vertices=ntw.vertex_coords, arcs=ntw.arcs

Create geopandas.GeoDataFrame objects of the actual and snapped crime locations

crimes = spaghetti.element_as_gdf(ntw, pp_name=pp_name)
crimes_snapped = spaghetti.element_as_gdf(ntw, pp_name=pp_name, snapped=True)

Helper plotting function

def plotter():
    """Generate a spatial plot."""

    def _patch(_kws, labinfo):
        """Generate a legend patch."""
        label = "%s%s" % tuple(labinfo)
        _kws.update({"lw":0, "label":label, "alpha":.5})
        return matplotlib.lines.Line2D([], [], **_kws)

    def _legend(handles, anchor=(1., .75)):
        """Generate a legend."""
        lkws = {"fancybox":True,"framealpha":0.85, "fontsize":"xx-large"}
        lkws.update({"bbox_to_anchor": anchor, "labelspacing": 2.})
        lkws.update({"borderpad": 2., "handletextpad":1.5})
        lkws.update({"title": "Crime locations & counts", "title_fontsize":25})
        matplotlib.pyplot.legend(handles=handles, **lkws)

    def carto_elements(b):
        """Add/adjust cartographic elements."""
        scalebar = ScaleBar(1, units="m", location="lower left")
        b.set(xticklabels=[], xticks=[], yticklabels=[], yticks=[]);

    pkws = {"alpha":0.25}
    base = arcs_df.plot(color="k", figsize=(9, 9), zorder=0, **pkws)
    patches = []
    gdfs = [crimes, crimes_snapped, sim_uniform_gdf]
    colors, zo = ["k", "g", "b"], [1 ,2 ,3]
    markers, markersizes = ["o", "X", "X"], [150, 150, 150]
    labels = [["Empirical"], ["Network-snapped"], ["Simulated"]]
    iterinfo = list(zip(gdfs, colors, zo, markers, markersizes, labels))
    for gdf, c, z, m, ms, lab in iterinfo:
        gdf.plot(ax=base, c=c, marker=m, markersize=ms, zorder=z, **pkws)
        patch_args = {"marker":m, "markersize":ms/10,"c":c}, lab+[gdf.shape[0]]

Crimes: empirical, network-snapped, and simulated locations