Source code for geosnap.analyze.incs

"""
Indicators of Neighborhood Change
"""

from collections import defaultdict
import numpy as np

def _labels_to_neighborhoods(labels):
    """Convert a list of labels to neighborhoods dictionary

    Parameters
    -----------
    labels: list of neighborhood labels

    Returns
    -------
    neighborhoods: dictionary
                   key is the label for each neighborhood, value is the list of
                   area indexes defining that neighborhood

    Examples
    --------
    >>> labels = [1,1,1,2,2,3]
    >>> neighborhoods = _labels_to_neighborhoods(labels)
    >>> neighborhoods[1]
    [0, 1, 2]
    >>> neighborhoods[2]
    [3, 4]
    >>> neighborhoods[3]
    [5]
    """
    neighborhoods = defaultdict(list)
    for i, label in enumerate(labels):
        neighborhoods[label].append(i)
    return neighborhoods


[docs]def linc(labels_sequence): """Local Indicator of Neighborhood Change Parameters ----------- labels_sequence: sequence of neighborhood labels (n,t) n areas in t periods first element is a list of neighborhood labels per area in period 0, second element is a list of neighborhood labels per area in period 1, and so on for all T periods. Returns ------- lincs: array local indicator of neighborhood change over all periods Notes ----- The local indicator of neighborhood change defined here allows for singleton neighborhoods (i.e., neighborhoods composed of a single primitive area such as a tract or block.). This is in contrast to the initial implementation in :cite:`Rey_2011` which prohibited singletons. Examples -------- Time period 0 has the city defined as four neighborhoods on 10 tracts: >>> labels_0 = [1, 1, 1, 1, 2, 2, 3, 3, 3, 4] Time period 1 in the same city, with slight change in composition of the four neighborhoods >>> labels_1 = [1, 1, 1, 1, 1, 2, 3, 3, 3, 4] >>> res = linc([labels_0, labels_1]) >>> res[4] 1.0 >>> res[1] 0.25 >>> res[7] 0.0 >>> res[-1] 0.0 And, in period 2, no change >>> labels_2 = [1, 1, 1, 1, 1, 2, 3, 3, 3, 4] >>> res = linc([labels_1, labels_2]) >>> res[0] 0.0 We can pass more than two time periods, and get a "time-wise global linc" for each unit >>> res = linc([labels_0, labels_1, labels_2]) >>> res[0] 0.25 """ ltn = _labels_to_neighborhoods neighborhood_sequences = [ltn(labels) for labels in labels_sequence] ns = neighborhood_sequences n_areas = len(labels_sequence[0]) lincs = np.zeros((n_areas,)) T = len(labels_sequence) for i in range(n_areas): neighbors = [] for t in range(T): neighbors.append(set(ns[t][labels_sequence[t][i]])) intersection = set.intersection(*neighbors) union = set.union(*neighbors) n_union = len(union) if n_union == 1: # singleton at all points in time lincs[i] = 0. else: lincs[i] = 1.0 - ((len(intersection)-1)/(n_union-1)) return lincs