<jupyter_start><jupyter_text>---------------**If any part of this notebook is used in your research, please cite with the reference found in** **[README.md](https://github.com/pysal/spaghettibibtex-citation).**---------------- Spatial network segmentation Demonstrating network segmentation**Author: <NAME>** ******This notebook is an advanced walk-through for:**1. Demonstrating network segmentation2. Understanding observation counts per network arc3. Visualizing network representations<jupyter_code>%load_ext watermark
%watermark
import libpysal
import matplotlib
import matplotlib_scalebar
from matplotlib_scalebar.scalebar import ScaleBar
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy
import spaghetti

%matplotlib inline
%watermark -w
%watermark -iv
try:
    from IPython.display import set_matplotlib_formats
    set_matplotlib_formats("retina")
except ImportError:
    pass<jupyter_output><empty_output><jupyter_text>---------------- Instantiating a `spaghetti.Network` object and a point pattern Instantiate the network from a `.shp` file<jupyter_code>ntw = spaghetti.Network(in_data=libpysal.examples.get_path("streets.shp"))<jupyter_output><empty_output><jupyter_text>Associate the network with a point pattern<jupyter_code>pp_name = "crimes"
pp_shp = libpysal.examples.get_path("%s.shp" % pp_name)
ntw.snapobservations(pp_shp, pp_name, attribute=True)
ntw.pointpatterns<jupyter_output><empty_output><jupyter_text>----------------------------- 1. Network segmentation Split network arcs into segments of 200 ([US feet is this case](https://github.com/pysal/libpysal/blob/master/libpysal/examples/geodanet/streets.prj))<jupyter_code>n200 = ntw.split_arcs(200.0)
n200<jupyter_output><empty_output><jupyter_text>The returned object inherits many of the attributes from the original network<jupyter_code>print(dir(n200))
n200.pointpatterns<jupyter_output><empty_output><jupyter_text>Extract the elements from both the original network and the split network<jupyter_code># 'full' unsegmented network
vtx_df, arc_df = spaghetti.element_as_gdf(ntw, vertices=True, arcs=True)

# network segmented at 200-meter increments
vtx200_df, arc200_df = spaghetti.element_as_gdf(n200, vertices=True, arcs=True)

# crimes point pattern
pp_df = spaghetti.element_as_gdf(ntw, pp_name=pp_name)<jupyter_output><empty_output><jupyter_text>Plotter function<jupyter_code>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": 1., "handletextpad":1.})
        lkws.update({"title": "Characteristics of a\nsegmented network", "title_fontsize":20})
        matplotlib.pyplot.legend(handles=handles, **lkws)
    
    base = arc_df.plot(color="k", figsize=(9, 9), alpha=.25, zorder=0)
    patches = []
    gdfs, alphas = [vtx_df, vtx200_df, pp_df], [.25, .5, .5]
    colors, zo = ["k", "g", "r"], [1 ,2 ,3]
    markers, markersizes = ["o", "o", "x"], [100, 15, 30]
    labels = [["Original vertices"], ["200-meter vertices"], ["Crimes"]]
    iterinfo = list(zip(gdfs, colors, zo, markers, markersizes, labels, alphas))
    for gdf, c, z, m, ms, lab, a in iterinfo:
        gdf.plot(ax=base, c=c, marker=m, markersize=ms, zorder=z, alpha=a)
        ms = ms/5. if z != 1 else ms/9.
        patch_args = {"marker":m, "markersize":ms,"c":c}, lab+[gdf.shape[0]]
        patches.append(_patch(*patch_args))
    _legend(patches)
    carto_elements(base)

def carto_elements(b):
    """Add/adjust cartographic elements."""
    scalebar = ScaleBar(1, units="m", location="lower left")
    b.add_artist(scalebar)
    b.set(xticklabels=[], xticks=[], yticklabels=[], yticks=[]);
plotter()<jupyter_output><empty_output><jupyter_text>**As is shown above, performing (relatively) uniform segmentation results in a more granulated network.**--------------------- 2. Counts per link Counts per link may vary considerably depending on the granularity of the network.<jupyter_code>def fetch_cpl(net, pp, mean=True):
    """Create a counts per link object and find mean."""
    cpl = net.count_per_link(net.pointpatterns[pp].obs_to_arc, graph=False)
    if mean:
        mean_cpl = sum(list(cpl.values())) / float(len(cpl.keys()))
        return cpl, mean_cpl
    return cpl
ntw_counts, ntw_ctmean = fetch_cpl(ntw, pp_name)
ntw_ctmean
n200_counts, n200_ctmean = fetch_cpl(n200, pp_name)
n200_ctmean
def counts_col(cts, df, join, col, transform="log10"):
    """Create a counts per link dataframe column and transform."""
    df[col] = df[join].map(cts).fillna(0.)
    if transform:
        t = getattr(numpy, "log10")
        df["%s_%s"%(col, transform)] = df[col].apply(lambda x:
            0.0 if x == 0. else (.2 if x == 1. else t(x))
        )
    return df<jupyter_output><empty_output><jupyter_text>Original network<jupyter_code>arc_df = counts_col(ntw_counts, arc_df, "id", "n_crimes")
arc_df.head()<jupyter_output><empty_output><jupyter_text>200-meter segmented network<jupyter_code>arc200_df = counts_col(n200_counts, arc200_df, "id", "n_crimes")
arc200_df.head()<jupyter_output><empty_output><jupyter_text>------------------- 3. Visualizing differences for comparison**Frequently it is acceptable to transform raw counts in order to better visualize the data.**<jupyter_code>def truncated_cmap(cm, vmin, vmax, steps):
    """Truncate a matplotlib colormap object"""
    lspace = numpy.linspace(vmin, vmax, steps)
    args = "trunc(%s,%.2f,%.2f)" % (cm.name, vmin, vmax), cm(lspace)
    tcmap = matplotlib.colors.LinearSegmentedColormap.from_list(*args)
    return tcmap
in_cmap = matplotlib.pyplot.get_cmap("inferno")
out_cmap = truncated_cmap(in_cmap, 0.2, 0.8, 100)
def sidexside_plot(df1, df2, col, cmap, supt, subt1, subt2, figsize=(12, 12)):
    """Create a side-by-side plot."""
    # set figure & subplot args
    sub_args = {"gridspec_kw":{"width_ratios": [1, .86]}, "figsize":figsize}
    fig, arr = matplotlib.pyplot.subplots(1, 2, **sub_args)
    # set plotting args and plot
    arc_args = {"column":col, "cmap":cmap, "lw":6, "alpha":.9, "legend":True}
    for ar, df, t in zip([0,1], (df1, df2), (subt1, subt2)):
        if ar == 1:
            arc_args["legend"], cax = False, None
        else:
            divider = make_axes_locatable(arr[ar])
            cax = divider.append_axes("right", size="10%", pad=0.3)
        df.plot(ax=arr[ar], cax=cax, **arc_args)
        arr[ar].set_title(t, fontsize=20)
        carto_elements(arr[ar])
    fig.suptitle(supt, y=0.8, fontsize=25)
    fig.tight_layout()<jupyter_output><empty_output><jupyter_text>Raw counts<jupyter_code>suptitle = "Crimes associated with network arcs — raw count"
subtitle1  = "Original ($n=%s$)" % arc_df.shape[0]
subtitle2 = "200-meter split ($n=%s$)" % arc200_df.shape[0]
title_args = (suptitle, subtitle1, subtitle2)
sidexside_plot(arc_df, arc200_df, "n_crimes", out_cmap, *title_args)<jupyter_output><empty_output><jupyter_text>Transformed counts (`log10`)<jupyter_code>suptitle = "Crimes associated with network arcs — log10 transformed"
subtitle1  = "Original ($n=%s$)" % arc_df.shape[0]
subtitle2 = "200-meter split ($n=%s$)" % arc200_df.shape[0]
title_args = (suptitle, subtitle1, subtitle2)
sidexside_plot(arc_df, arc200_df, "n_crimes_log10", out_cmap, *title_args)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.15588312027132795, 'fraction_numerical': 0.025567440647012784, 'mean_word_length': 4.6375, 'pattern_counts': {'":': 23, '<': 54, '<?xml version=': 0, '>': 54, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '311233', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 2918, 'n_tokens_neox': 2747, 'n_words': 608, 'path': 'notebooks/network-segmentation.ipynb', 'repo_name': 'slumnitz/spaghetti', 'size': '7677'}