If any part of this notebook is used in your research, please cite with the reference found in README.md.
Author: James D. Gaboardi jgaboardi@gmail.com
This notebook is an advanced walk-through for:
In [1]:
%load_ext watermark
%watermark
In [2]:
import esda
import libpysal
import matplotlib
import matplotlib_scalebar
from matplotlib_scalebar.scalebar import ScaleBar
import numpy
import spaghetti
import splot
%matplotlib inline
%watermark -w
%watermark -iv
In [3]:
try:
from IPython.display import set_matplotlib_formats
set_matplotlib_formats("retina")
except ImportError:
pass
In [4]:
ntw = spaghetti.Network(in_data=libpysal.examples.get_path("streets.shp"))
ntw
Out[4]:
In [5]:
_, arc_df = spaghetti.element_as_gdf(ntw, vertices=True, arcs=True)
arc_df.head()
Out[5]:
In [6]:
pp_name = "crimes"
pp_shp = libpysal.examples.get_path("%s.shp" % pp_name)
ntw.snapobservations(pp_shp, pp_name, attribute=True)
ntw.pointpatterns
Out[6]:
In [7]:
pp_df = spaghetti.element_as_gdf(ntw, pp_name=pp_name)
pp_df.head()
Out[7]:
The Moran's I test statistic allows for the inference of how clustered (or dispersed) a dataset is while considering both attribute values and spatial relationships. A value of closer to +1 indicates absolute clustering while a value of closer to -1 indicates absolute dispersion. Complete spatial randomness takes the value of 0. See the esda documentation for in-depth descriptions and tutorials.
In [8]:
def calc_moran(net, pp_name, w):
"""Calculate a Moran's I statistic based on network arcs."""
# Compute the counts
pointpat = net.pointpatterns[pp_name]
counts = net.count_per_link(pointpat.obs_to_arc, graph=False)
# Build the y vector
arcs = w.neighbors.keys()
y = [counts[a] if a in counts.keys() else 0. for i, a in enumerate(arcs)]
# Moran's I
moran = esda.moran.Moran(y, w, permutations=99)
return moran, y
In [9]:
moran_ntwwn, yaxis_ntwwn = calc_moran(ntw, pp_name, ntw.w_network)
moran_ntwwn.I
Out[9]:
In [10]:
moran_ntwwg, yaxis_ntwwg = calc_moran(ntw, pp_name, ntw.w_graph)
moran_ntwwg.I
Out[10]:
Interpretation:
moran_ntwwn
and moran_ntwwg
, respectively) display minimal postive spatial autocorrelation, a slighly higher value is observed in the graph represention. This is likely due to more direct connectivity in the graph representation; a direct result of eliminating degree-2 vertices). The Moran's I for both the network and graph representations suggest that network arcs/graph edges attributed with associated crime counts are nearly randomly distributed.
In [11]:
n200 = ntw.split_arcs(200.0)
n200
Out[11]:
In [12]:
moran_n200, yaxis_n200 = calc_moran(n200, pp_name, n200.w_network)
moran_n200.I
Out[12]:
In [13]:
n50 = ntw.split_arcs(50.0)
n50
Out[13]:
In [14]:
moran_n50, yaxis_n50 = calc_moran(n50, pp_name, n50.w_network)
moran_n50.I
Out[14]:
Interpretation:
moran_n200
and moran_n50
, respectively) display minimal negative spatial autocorrelation, with slighly lower values being observed in the 200-meter representation. However, similar to above the Moran's I for both the these representations suggest that network arcs attributed with associated crime counts are nearly randomly distributed.splot
Here we are demonstrating spatial lag, which refers to attribute similarity. See the splot documentation for in-depth descriptions and tutorials.
In [15]:
from splot.esda import moran_scatterplot, lisa_cluster, plot_moran
In [16]:
moran_scatterplot(moran_ntwwn, aspect_equal=True);
Plotted without equal aspect
In [17]:
moran_scatterplot(moran_ntwwn, aspect_equal=False);
In [18]:
plot_moran(moran_ntwwn, zstandard=True, figsize=(10,4));
This figure incorporates the reference distribution of Moran's I values into the above scatterplot of the network representation's W (moran_ntwwn
).
The demonstrations above considered the dataset as a whole, providing a global measure. The following demostrates the consideration of local spatial autocorrelation, providing a measure for each observation. This is best interpreted visually, here with another scatterplot colored to indicate relationship type.
Plotted with equal aspect
In [19]:
p = 0.05
moran_loc_ntwwn = esda.moran.Moran_Local(yaxis_ntwwn, ntw.w_network)
fig, ax = moran_scatterplot(moran_loc_ntwwn, p=p, aspect_equal=True)
ax.set(xlabel="Crimes", ylabel="Spatial Lag of Crimes");
Plotted without equal aspect
In [20]:
fig, ax = moran_scatterplot(moran_loc_ntwwn, aspect_equal=False, p=p)
ax.set(xlabel="Crimes", ylabel="Spatial Lag of Crimes");
Interpretation:
In [21]:
f, ax = lisa_cluster(moran_loc_ntwwn, arc_df, p=p, figsize=(12,12), lw=5, zorder=0)
pp_df.plot(ax=ax, zorder=1, alpha=.25, color="g", markersize=30)
suptitle = "LISA for Crime-weighted Networks Arcs"
matplotlib.pyplot.suptitle(suptitle, fontsize=20, x=.51, y=.93)
subtitle = "Crimes ($n=%s$) are represented as semi-opaque green circles"
matplotlib.pyplot.title(subtitle % pp_df.shape[0], fontsize=15);