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 README.md.


Network point pattern attributes

Demonstrating network point pattern representation

Author: James D. Gaboardi jgaboardi@gmail.com

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

[1]:
%load_ext watermark
%watermark
2020-10-22T17:29:05-04:00

CPython 3.8.5
IPython 7.18.1

compiler   : Clang 10.0.1
system     : Darwin
release    : 19.6.0
machine    : x86_64
processor  : i386
CPU cores  : 8
interpreter: 64bit
[2]:
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
matplotlib_scalebar 0.6.2
geopandas           0.8.1
numpy               1.19.1
shapely             1.7.1
matplotlib          3.3.2
libpysal            4.3.0
pandas              1.1.2
spaghetti           1.5.1

[3]:
try:
    from IPython.display import set_matplotlib_formats
    set_matplotlib_formats("retina")
except ImportError:
    pass

Instantiating a spaghetti.Network object

Instantiate the network from a .shp file

[4]:
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.

[5]:
pp_name = "crimes"
pp_shp = libpysal.examples.get_path("%s.shp" % pp_name)
ntw.snapobservations(pp_shp, pp_name, attribute=True)
ntw.pointpatterns
[5]:
{'crimes': <spaghetti.network.PointPattern at 0x7fe939469f10>}

Attributes for every point pattern

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

[6]:
ntw.pointpatterns[pp_name].dist_snapped[0]
[6]:
221.58676169738433
  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}

[7]:
ntw.pointpatterns[pp_name].dist_to_vertex[0]
[7]:
{161: 83.70599311338091, 162: 316.8274480625799}
  1. npoints point observations in set

[8]:
ntw.pointpatterns[pp_name].npoints
[8]:
287
  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), … }

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

[10]:
ntw.pointpatterns[pp_name].obs_to_vertex[0]
[10]:
161
  1. points geojson like representation of the point pattern. Includes properties if read with attributes=True

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

[12]:
ntw.pointpatterns[pp_name].snapped_coordinates[0]
[12]:
(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.

[16]:
npts = ntw.pointpatterns[pp_name].npoints
npts
[16]:
287
[17]:
sim_uniform = ntw.simulate_observations(npts)
sim_uniform
[17]:
<spaghetti.network.SimulatedPointPattern at 0x7fe949129820>
[18]:
print(dir(sim_uniform))
['__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

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

sim_uniform_gdf = as_gdf(sim_uniform.points)
sim_uniform_gdf.head()
[19]:
geometry
id
0 POINT (726803.405 878093.538)
1 POINT (725241.798 876770.922)
2 POINT (728421.522 880799.119)
3 POINT (723637.547 876511.650)
4 POINT (724926.803 881208.809)

Create geopandas.GeoDataFrame objects of the vertices and arcs

[20]:
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

[21]:
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

[22]:
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."""
        kw = {"units":"ft", "dimension":"imperial-length", "fixed_value":1000}
        b.add_artist(ScaleBar(1, **kw))
        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]]
        patches.append(_patch(*patch_args))
    _legend(patches)
    carto_elements(base)

Crimes: empirical, network-snapped, and simulated locations

[23]:
plotter()
../_images/notebooks_pointpattern-attributes_39_0.png