From a06e6f63f0984dd6d5bdf79fc863cc3013b2d3ed Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Mon, 13 Jul 2020 20:10:43 -0400 Subject: [PATCH 01/33] remove empty libpysal/weights/test.py --- libpysal/weights/test.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 libpysal/weights/test.py diff --git a/libpysal/weights/test.py b/libpysal/weights/test.py deleted file mode 100644 index e69de29bb..000000000 From 8f03d7aff9d20c1f4205460a4c32c5f8e925df62 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Tue, 14 Jul 2020 10:09:12 -0400 Subject: [PATCH 02/33] blackening test_NameSpace.py --- libpysal/test_NameSpace.py | 119 +++++++++++++++++++++++++++---------- 1 file changed, 89 insertions(+), 30 deletions(-) diff --git a/libpysal/test_NameSpace.py b/libpysal/test_NameSpace.py index 6fa272e70..7b8af4d71 100644 --- a/libpysal/test_NameSpace.py +++ b/libpysal/test_NameSpace.py @@ -1,50 +1,109 @@ import os import unittest -@unittest.skip('Skipping unittest for namespace') + +@unittest.skip("Skipping unittest for namespace") class TestNameSpace(unittest.TestCase): """ - This test makes sure we don't remove anything from the pysal NameSpace that - 1.0 users might expect to be there. 1.0 Namespace was taken from the 1.1 - Code sprint wave, with special names removes (__all__, etc) + This test makes sure we don't remove anything from the pysal NameSpace that + 1.0 users might expect to be there. 1.0 Namespace was taken from the 1.1 + Code sprint wave, with special names removes (__all__, etc). """ + def test_contents(self): - namespace_v1_0 = ['Box_Plot', 'DistanceBand', 'Equal_Interval', - 'Fisher_Jenks', 'Geary', 'Jenks_Caspall', - 'Jenks_Caspall_Forced', 'Jenks_Caspall_Sampled', - 'Join_Counts', 'K_classifiers', 'Kernel', - 'LISA_Markov', 'Markov', 'Max_P_Classifier', - 'Maximum_Breaks', 'Maxp', 'Maxp_LISA', 'Moran', - 'Moran_BV', 'Moran_BV_matrix', 'Moran_Local', - 'Natural_Breaks', 'Percentiles', 'Quantiles', - 'SpatialTau', 'Spatial_Markov', 'Std_Mean', 'Theil', - 'TheilD', 'TheilDSim', 'Theta', 'User_Defined', 'W', 'adaptive_kernelW', - 'adaptive_kernelW_from_shapefile', 'bin', 'bin1d', - 'binC', 'buildContiguity', 'cg', 'comb', 'common', - 'core', 'directional', 'ergodic', 'esda', 'full', - 'gadf', 'higher_order', 'inequality', 'kernelW', - 'kernelW_from_shapefile', 'knnW', 'knnW_from_array', - 'knnW_from_shapefile', 'lag_spatial', 'lat2W', - 'min_threshold_dist_from_shapefile', 'open', - 'order', 'quantile', 'queen_from_shapefile', - 'block_weights', 'region', 'remap_ids', - 'rook_from_shapefile', 'shimbel', 'spatial_dynamics', - 'threshold_binaryW_from_array', 'threshold_binaryW_from_shapefile', - 'threshold_continuousW_from_array', 'threshold_continuousW_from_shapefile', - 'version', 'w_difference', 'w_intersection', 'w_subset', - 'w_symmetric_difference', 'w_union', 'weights'] + namespace_v1_0 = [ + "Box_Plot", + "DistanceBand", + "Equal_Interval", + "Fisher_Jenks", + "Geary", + "Jenks_Caspall", + "Jenks_Caspall_Forced", + "Jenks_Caspall_Sampled", + "Join_Counts", + "K_classifiers", + "Kernel", + "LISA_Markov", + "Markov", + "Max_P_Classifier", + "Maximum_Breaks", + "Maxp", + "Maxp_LISA", + "Moran", + "Moran_BV", + "Moran_BV_matrix", + "Moran_Local", + "Natural_Breaks", + "Percentiles", + "Quantiles", + "SpatialTau", + "Spatial_Markov", + "Std_Mean", + "Theil", + "TheilD", + "TheilDSim", + "Theta", + "User_Defined", + "W", + "adaptive_kernelW", + "adaptive_kernelW_from_shapefile", + "bin", + "bin1d", + "binC", + "buildContiguity", + "cg", + "comb", + "common", + "core", + "directional", + "ergodic", + "esda", + "full", + "gadf", + "higher_order", + "inequality", + "kernelW", + "kernelW_from_shapefile", + "knnW", + "knnW_from_array", + "knnW_from_shapefile", + "lag_spatial", + "lat2W", + "min_threshold_dist_from_shapefile", + "open", + "order", + "quantile", + "queen_from_shapefile", + "block_weights", + "region", + "remap_ids", + "rook_from_shapefile", + "shimbel", + "spatial_dynamics", + "threshold_binaryW_from_array", + "threshold_binaryW_from_shapefile", + "threshold_continuousW_from_array", + "threshold_continuousW_from_shapefile", + "version", + "w_difference", + "w_intersection", + "w_subset", + "w_symmetric_difference", + "w_union", + "weights", + ] current_namespace = dir(pysal) for item in namespace_v1_0: self.assertTrue(item in current_namespace) for item in current_namespace: - if item not in namespace_v1_0 and not item.startswith('__'): + if item not in namespace_v1_0 and not item.startswith("__"): print(item, "added to name space") suite = unittest.TestLoader().loadTestsFromTestCase(TestNameSpace) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() runner = unittest.TextTestRunner() runner.run(suite) From 159f254ac670e5c3ec63363daa2c32d827cce825 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Tue, 14 Jul 2020 10:26:27 -0400 Subject: [PATCH 03/33] updating weights/_contW_lists.py and test file --- libpysal/weights/_contW_lists.py | 54 ++++++++------- libpysal/weights/tests/test__contW_lists.py | 75 ++++++++++----------- 2 files changed, 64 insertions(+), 65 deletions(-) diff --git a/libpysal/weights/_contW_lists.py b/libpysal/weights/_contW_lists.py index 8fe7cbfe8..10b46fdf5 100644 --- a/libpysal/weights/_contW_lists.py +++ b/libpysal/weights/_contW_lists.py @@ -1,54 +1,58 @@ from ..cg.shapes import Polygon, Chain import itertools as it import collections + QUEEN = 1 ROOK = 2 __author__ = "Jay Laura jlaura@asu.edu" + def _get_verts(shape): if isinstance(shape, (Polygon, Chain)): return shape.vertices else: return _get_boundary_points(shape) -def _get_boundary_points(shape): + +def _get_boundary_points(shape) -> list: """ Recursively handle polygons vs. multipolygons to - extract the boundary point set from each. + extract the boundary point set from each. """ - if shape.type.lower() == 'polygon': + if shape.type.lower() == "polygon": shape = shape.boundary return _get_boundary_points(shape) - elif shape.type.lower() == 'linestring': + elif shape.type.lower() == "linestring": return list(map(tuple, list(zip(*shape.coords.xy)))) - elif shape.type.lower() == 'multilinestring': - return list(it.chain(*(list(zip(*shape.coords.xy)) - for shape in shape))) - elif shape.type.lower() == 'multipolygon': - return list(it.chain(*(_get_boundary_points(part.boundary) - for part in shape))) + elif shape.type.lower() == "multilinestring": + return list(it.chain(*(list(zip(*shape.coords.xy)) for shape in shape))) + elif shape.type.lower() == "multipolygon": + return list(it.chain(*(_get_boundary_points(part.boundary) for part in shape))) else: - raise TypeError('Input shape must be a Polygon, Multipolygon, LineString, ' - ' or MultiLinestring and was ' - ' instead: {}'.format(shape.type)) + raise TypeError( + "Input shape must be a Polygon, Multipolygon, LineString, " + " or MultiLinestring and was " + " instead: {}".format(shape.type) + ) class ContiguityWeightsLists: """ Contiguity for a collection of polygons using high performance - list, set, and dict containers + list, set, and dict containers. """ + def __init__(self, collection, wttype=1): """ - Arguments - ========= - + + Parameters + ---------- collection: PySAL PolygonCollection - + wttype: int - 1: Queen - 2: Rook + 1: Queen; 2: Rook + """ self.collection = list(collection) self.wttype = wttype @@ -67,10 +71,10 @@ def jcontiguity(self): if self.wttype == QUEEN: for n in range(numPoly): - verts = _get_verts(self.collection[n]) - offsets += [c] * len(verts) - geoms += (verts) - c += 1 + verts = _get_verts(self.collection[n]) + offsets += [c] * len(verts) + geoms += verts + c += 1 items = collections.defaultdict(set) for i, vertex in enumerate(geoms): @@ -114,5 +118,5 @@ def jcontiguity(self): except: pass else: - raise Exception('Weight type {} Not Understood!'.format(self.wttype)) + raise Exception("Weight type {} not understood!".format(self.wttype)) self.w = w diff --git a/libpysal/weights/tests/test__contW_lists.py b/libpysal/weights/tests/test__contW_lists.py index f444e8602..f9214838d 100644 --- a/libpysal/weights/tests/test__contW_lists.py +++ b/libpysal/weights/tests/test__contW_lists.py @@ -14,11 +14,10 @@ from ... import examples as pysal_examples - class TestContiguityWeights(unittest.TestCase): def setUp(self): - """ Setup the binning contiguity weights""" - shpObj = ps_open(pysal_examples.get_path('virginia.shp'), 'r') + """Setup the binning contiguity weights.""" + shpObj = ps_open(pysal_examples.get_path("virginia.shp"), "r") self.binningW = ContiguityWeightsLists(shpObj, QUEEN) shpObj.close() @@ -32,17 +31,17 @@ def test_ROOK(self): self.assertEqual(ROOK, 2) def test_ContiguityWeightsLists(self): - self.assertTrue(hasattr(self.binningW, 'w')) + self.assertTrue(hasattr(self.binningW, "w")) self.assertTrue(issubclass(dict, type(self.binningW.w))) self.assertEqual(len(self.binningW.w), 136) def test_nested_polygons(self): - # load queen gal file created using Open Geoda. - geodaW = ps_open( - pysal_examples.get_path('virginia.gal'), 'r').read() + # load queen gal file created using Open Geoda + geodaW = ps_open(pysal_examples.get_path("virginia.gal"), "r").read() # build matching W with pysal pysalWb = self.build_W( - pysal_examples.get_path('virginia.shp'), QUEEN, 'POLY_ID') + pysal_examples.get_path("virginia.shp"), QUEEN, "POLY_ID" + ) # compare output. for key in geodaW.neighbors: geoda_neighbors = list(map(int, geodaW.neighbors[key])) @@ -52,13 +51,11 @@ def test_nested_polygons(self): self.assertEqual(geoda_neighbors, pysalb_neighbors) def test_true_rook(self): - # load queen gal file created using Open Geoda. - geodaW = ps_open(pysal_examples.get_path('rook31.gal'), 'r').read() + # load queen gal file created using Open Geoda + geodaW = ps_open(pysal_examples.get_path("rook31.gal"), "r").read() # build matching W with pysal - #pysalW = pysal.rook_from_shapefile(pysal_examples.get_path('rook31.shp'),','POLY_ID') - pysalWb = self.build_W( - pysal_examples.get_path('rook31.shp'), ROOK, 'POLY_ID') - # compare output. + pysalWb = self.build_W(pysal_examples.get_path("rook31.shp"), ROOK, "POLY_ID") + # compare output for key in geodaW.neighbors: geoda_neighbors = list(map(int, geodaW.neighbors[key])) pysalb_neighbors = pysalWb.neighbors[int(key)] @@ -67,15 +64,13 @@ def test_true_rook(self): self.assertEqual(geoda_neighbors, pysalb_neighbors) def test_true_rook2(self): - # load queen gal file created using Open Geoda. - - stl = pysal_examples.load_example('stl') - gal_file = test_file = stl.get_path('stl_hom_rook.gal') - geodaW = ps_open(gal_file, 'r').read() + # load queen gal file created using Open Geoda + stl = pysal_examples.load_example("stl") + gal_file = test_file = stl.get_path("stl_hom_rook.gal") + geodaW = ps_open(gal_file, "r").read() # build matching W with pysal - pysalWb = self.build_W(stl.get_path( - 'stl_hom.shp'), ROOK, 'POLY_ID_OG') - # compare output. + pysalWb = self.build_W(stl.get_path("stl_hom.shp"), ROOK, "POLY_ID_OG") + # compare output for key in geodaW.neighbors: geoda_neighbors = list(map(int, geodaW.neighbors[key])) pysalb_neighbors = pysalWb.neighbors[int(key)] @@ -84,13 +79,11 @@ def test_true_rook2(self): self.assertEqual(geoda_neighbors, pysalb_neighbors) def test_true_rook3(self): - # load queen gal file created using Open Geoda. - geodaW = ps_open( - pysal_examples.get_path('virginia_rook.gal'), 'r').read() + # load queen gal file created using Open Geoda + geodaW = ps_open(pysal_examples.get_path("virginia_rook.gal"), "r").read() # build matching W with pysal - pysalWb = self.build_W( - pysal_examples.get_path('virginia.shp'), ROOK, 'POLY_ID') - # compare output. + pysalWb = self.build_W(pysal_examples.get_path("virginia.shp"), ROOK, "POLY_ID") + # compare output for key in geodaW.neighbors: geoda_neighbors = list(map(int, geodaW.neighbors[key])) pysalb_neighbors = pysalWb.neighbors[int(key)] @@ -98,21 +91,26 @@ def test_true_rook3(self): pysalb_neighbors.sort() self.assertEqual(geoda_neighbors, pysalb_neighbors) - @unittest.skipIf(gpd is None, 'geopandas is missing in the testing environment') + @unittest.skipIf(gpd is None, "geopandas is missing in the testing environment") def test_shapely(self): - pysalneighbs = ContiguityWeightsLists(ps_open( - pysal_examples.get_path('virginia.shp')), ROOK) - gdf = gpd.read_file(pysal_examples.get_path('virginia.shp')) + pysalneighbs = ContiguityWeightsLists( + ps_open(pysal_examples.get_path("virginia.shp")), ROOK + ) + gdf = gpd.read_file(pysal_examples.get_path("virginia.shp")) shplyneighbs = ContiguityWeightsLists(gdf.geometry.tolist(), ROOK) self.assertEqual(pysalneighbs.w, shplyneighbs.w) - pysalneighbs = ContiguityWeightsLists(ps_open( - pysal_examples.get_path('virginia.shp')), QUEEN) + pysalneighbs = ContiguityWeightsLists( + ps_open(pysal_examples.get_path("virginia.shp")), QUEEN + ) shplyneighbs = ContiguityWeightsLists(gdf.geometry.tolist(), QUEEN) self.assertEqual(pysalneighbs.w, shplyneighbs.w) def build_W(self, shapefile, type, idVariable=None): - """ Building 2 W's the hard way. We need to do this so we can test both rtree and binning """ - dbname = os.path.splitext(shapefile)[0] + '.dbf' + """ + Building 2 W's the hard way. + We need to do this so we can test both rtree and binning. + """ + dbname = os.path.splitext(shapefile)[0] + ".dbf" db = ps_open(dbname) shpObj = ps_open(shapefile) neighbor_data = ContiguityWeightsLists(shpObj, type).w @@ -134,9 +132,6 @@ def build_W(self, shapefile, type, idVariable=None): binningW = W(neighbors) return binningW -#suite = unittest.TestLoader().loadTestsFromTestCase(_TestContiguityWeights) -if __name__ == '__main__': - #runner = unittest.TextTestRunner() - #runner.run(suite) +if __name__ == "__main__": unittest.main() From 2295d58d572fe20b3a22d728a6cb6ce0a45866e4 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Tue, 11 Aug 2020 10:13:09 -0400 Subject: [PATCH 04/33] doc review form adjtools.py & tests/test_adjlist.py --- libpysal/weights/adjtools.py | 259 +++++++++++++++---------- libpysal/weights/tests/test_adjlist.py | 105 +++++----- 2 files changed, 213 insertions(+), 151 deletions(-) diff --git a/libpysal/weights/adjtools.py b/libpysal/weights/adjtools.py index 11b12941b..f4f26374c 100644 --- a/libpysal/weights/adjtools.py +++ b/libpysal/weights/adjtools.py @@ -1,69 +1,84 @@ import numpy as np + def adjlist_apply(X, W=None, alist=None, func=np.subtract, skip_verify=False): - """ - apply a function to an adajcency list, getting an adjacency list and result. + """Apply a function to an adajcency list, getting an adjacency list and result. Parameters ---------- - X : iterable - an (N,P)-length iterable to apply ``func'' to. If (N,1), then `func` - must take 2 arguments and return a single reduction. If P>1, then - func must take two P-length arrays and return a single - reduction of them. - W : pysal.weights.W object - a weights object that provides adjacency information - alist : pandas DataFrame - a table containing an adajacency list representation of a W matrix - func : callable - a function taking two arguments and returning a single argument. - This will be evaluated for every (focal, neighbor) pair, or each - row of the adjacency list. If `X` has more than one column, this - function should take two arrays and provide a single scalar in - return. - Example scalars include: lambda x,y: x < y, np.subtract - Example multivariates: lambda (x,y): np.all(x < y)'' - lambda (x,y): np.sum((x-y)**2) - sklearn.metrics.euclidean_distance + X : iterable + An `(N,P)`-length iterable to apply ``func`` to. If (N,1), then ``func`` + must take 2 arguments and return a single reduction. If `P`>1, then ``func`` + must take two `P`-length arrays and return a single reduction of them. + W : pysal.weights.W object + A weights object that provides adjacency information. + alist : pandas DataFrame + A table containing an adajacency list representation of a `W` matrix. + func : callable + A function taking two arguments and returning a single argument. This will + be evaluated for every (focal, neighbor) pair, or each row of the adjacency + list. If ``X`` has more than one column, this function should take two arrays + and provide a single scalar in return. + Example scalars include: + ``lambda x,y: x < y, np.subtract`` + Example multivariates: + ``lambda (x,y): np.all(x < y)`` + ``lambda (x,y): np.sum((x-y)**2)`` + ``sklearn.metrics.euclidean_distance`` skip_verify: bool - Whether or not to skip verifying that the W is the same as an adjacency list. - Do this if you are certain the adjacency list and W agree and would like to - avoid re-instantiating a W from the adjacency list. + Whether or not to skip verifying that the `W` is the same as an adjacency + list. Do this if you are certain the adjacency list and ``W`` agree and + would like to avoid re-instantiating a `W` from the adjacency list. Returns ------- - an adjacency list (or modifies alist inplace) with the function applied to each row. + alist_atts : list + An adjacency list (or modifies alist inplace) with the + function applied to each row. + """ + try: import pandas as pd except ImportError: - raise ImportError('pandas must be installed to use this function') - W,alist = _get_W_and_alist(W, alist, skip_verify=skip_verify) + raise ImportError("pandas must be installed to use this function") + W, alist = _get_W_and_alist(W, alist, skip_verify=skip_verify) if len(X.shape) > 1: if X.shape[-1] > 1: - return _adjlist_mvapply(X, W=W, alist=alist, func=func, - skip_verify=skip_verify) + return _adjlist_mvapply( + X, W=W, alist=alist, func=func, skip_verify=skip_verify + ) else: vec = np.asarray(X).flatten() - ids = np.asarray(W.id_order)[:,None] - table = pd.DataFrame(ids, columns=['id']) - table = pd.concat((table, pd.DataFrame(vec[:,None], columns=('att',))), - axis=1) - alist_atts = pd.merge(alist, table, how='left', - left_on='focal', right_on='id') - alist_atts = pd.merge(alist_atts, table, how='left', - left_on='neighbor', right_on='id', - suffixes=('_focal','_neighbor')) - alist_atts.drop(['id_focal', 'id_neighbor'], axis=1, inplace=True) - alist_atts[func.__name__] = alist_atts[['att_focal', 'att_neighbor']].apply(lambda x: func(x.att_focal, - x.att_neighbor), axis=1) + ids = np.asarray(W.id_order)[:, None] + table = pd.DataFrame(ids, columns=["id"]) + table = pd.concat((table, pd.DataFrame(vec[:, None], columns=("att",))), axis=1) + alist_atts = pd.merge(alist, table, how="left", left_on="focal", right_on="id") + alist_atts = pd.merge( + alist_atts, + table, + how="left", + left_on="neighbor", + right_on="id", + suffixes=("_focal", "_neighbor"), + ) + alist_atts.drop(["id_focal", "id_neighbor"], axis=1, inplace=True) + alist_atts[func.__name__] = alist_atts[["att_focal", "att_neighbor"]].apply( + lambda x: func(x.att_focal, x.att_neighbor), axis=1 + ) return alist_atts + def _adjlist_mvapply(X, W=None, alist=None, func=None, skip_verify=False): + """This function is used when ``X`` is multi-dimensional. + See ``weights.adjtools.adjlist_apply()`` for Parameters and Returns information. + + """ + try: import pandas as pd except ImportError: - raise ImportError('pandas must be installed to use this function') + raise ImportError("pandas must be installed to use this function") assert len(X.shape) == 2, "data is not two-dimensional" W, alist = _get_W_and_alist(W=W, alist=alist, skip_verify=skip_verify) assert X.shape[0] == W.n, "number of samples in X does not match W" @@ -71,76 +86,107 @@ def _adjlist_mvapply(X, W=None, alist=None, func=None, skip_verify=False): names = X.columns.tolist() except AttributeError: names = list(map(str, list(range(X.shape[1])))) - ids = np.asarray(W.id_order)[:,None] - table = pd.DataFrame(ids, columns=['id']) + ids = np.asarray(W.id_order)[:, None] + table = pd.DataFrame(ids, columns=["id"]) table = pd.concat((table, pd.DataFrame(X, columns=names)), axis=1) - alist_atts = pd.merge(alist, table, how='left', - left_on='focal', right_on='id') - alist_atts = pd.merge(alist_atts, table, how='left', - left_on='neighbor', right_on='id', - suffixes=('_focal','_neighbor')) - alist_atts.drop(['id_focal', 'id_neighbor'], axis=1, inplace=True) - alist_atts[func.__name__] = list(map(func, - list(zip(alist_atts.filter(like='_focal').values, - alist_atts.filter(like='_neighbor').values)))) + alist_atts = pd.merge(alist, table, how="left", left_on="focal", right_on="id") + alist_atts = pd.merge( + alist_atts, + table, + how="left", + left_on="neighbor", + right_on="id", + suffixes=("_focal", "_neighbor"), + ) + alist_atts.drop(["id_focal", "id_neighbor"], axis=1, inplace=True) + alist_atts[func.__name__] = list( + map( + func, + list( + zip( + alist_atts.filter(like="_focal").values, + alist_atts.filter(like="_neighbor").values, + ) + ), + ) + ) return alist_atts + def _get_W_and_alist(W, alist, skip_verify=False): + """ Either: + 1. compute a `W` from an alist + 2. adjacencylist from a W + 3. raise ValueError if neither are provided, + 4. raise AssertionError if both W and adjlist are provided and don't match. + + If this completes successfully, the `W` and ``adjlist`` will both + be returned and are checked for equality. See ``weights.adjtools.adjlist_apply()`` + for Parameters and Returns information. + """ - Either: - 1. compute a W from an alist - 2. adjacencylist from a W - 3. raise ValueError if neither are provided, - 4. raise AssertionError if both W and adjlist are provided and don't match. - If this completes successfully, the W/adjlist will both be returned and are checked for equality. - """ + if (alist is None) and (W is not None): alist = W.to_adjlist() elif (W is None) and (alist is not None): from .weights import W + W = W.from_adjlist(alist) elif (W is None) and (alist is None): - raise ValueError('Either W or Adjacency List must be provided') + raise ValueError("Either W or Adjacency List must be provided") elif (W is not None) and (alist is not None) and (not skip_verify): from .weights import W as W_ - np.testing.assert_allclose(W.sparse.toarray(), W_.from_adjlist(alist).sparse.toarray()) + + np.testing.assert_allclose( + W.sparse.toarray(), W_.from_adjlist(alist).sparse.toarray() + ) return W, alist -def adjlist_map(data, funcs=(np.subtract,), W=None, alist=None, - focal_col = 'focal', neighbor_col='neighbor'): - """ - Map a set of functions over a W or adjacency list + +def adjlist_map( + data, + funcs=(np.subtract,), + W=None, + alist=None, + focal_col="focal", + neighbor_col="neighbor", +): + """ Map a set of functions over a `W` or adjacency list. Parameters ---------- - data : np.ndarray or pandas dataframe - N x P array of N observations and P covariates. - funcs : iterable or callable - a function to apply to each of the P columns in ``data'', or a list of functions - to apply to each column of P. This function must take two arguments, compare them, - and return a value. Examples may be ``lambda x,y: x < y'' or ``np.subtract''. - W : pysal.weights.W object - a pysal weights object. If not provided, one is constructed from - the given adjacency list. - alist : pandas dataframe - an adjacency list representation of a weights matrix. If not provided, - one is constructed from the weights object. If both are provided, - they are validated against one another to ensure they provide identical weights - matrices. - focal_col : string - name of column in alist containing the focal observation ids - neighbor_col: string - name of column in alist containing the neighboring observation ids + data : {numpy.ndarray, pandas.Dataframe} + `N x P` array of `N` observations and `P` covariates. + funcs : iterable or callable + A function to apply to each of the `P` columns in ``data``, or a + list of functions to apply to each column of `P`. This function + must take two arguments, compare them, and return a value. Examples + may be ``lambda x,y: x < y`` or ``np.subtract``. + W : pysal.weights.W + A pysal weights object. If not provided, one is + constructed from the given adjacency list. + alist : pandas.Dataframe + An adjacency list representation of a weights matrix. If not + provided, one is constructed from the weights object. If both are + provided, they are validated against one another to ensure they + provide identical weights matrices. + focal_col : str + The name of column in ``alist`` containing the focal observation ids. + neighbor_col : str + The name of column in ``alist`` containing the neighboring observation ids. Returns ------- - returns an adjacency list (or modifies one if provided) with each function applied to the column - of the data. + alist : list + An adjacency list (or modifies one if provided) with each function + applied to the column of the data. + """ + try: import pandas as pd except ImportError: - raise ImportError('pandas must be installed to use this function') + raise ImportError("pandas must be installed to use this function") if isinstance(data, pd.DataFrame): names = data.columns data = data.values @@ -151,35 +197,44 @@ def adjlist_map(data, funcs=(np.subtract,), W=None, alist=None, funcs = (funcs,) if len(funcs) == 1: funcs = [funcs[0] for _ in range(data.shape[1])] - assert data.shape[1] == len(funcs), "shape of data does not match the number of functions provided" - W, alist = _get_W_and_alist(W,alist) + assert data.shape[1] == len( + funcs + ), "shape of data does not match the number of functions provided" + W, alist = _get_W_and_alist(W, alist) fnames = set([f.__name__ for f in funcs]) for i, (column, function) in enumerate(zip(data.T, funcs)): alist = adjlist_apply(X=column, W=W, alist=alist, skip_verify=True) - alist.drop(['att_focal', 'att_neighbor'], axis=1, inplace=True) - alist = alist.rename(columns={function.__name__ :'_'.join((function.__name__, names[i]))}) + alist.drop(["att_focal", "att_neighbor"], axis=1, inplace=True) + alist = alist.rename( + columns={function.__name__: "_".join((function.__name__, names[i]))} + ) fnames.update((function.__name__,)) return alist -def filter_adjlist(adjlist, focal_col = 'focal', neighbor_col = 'neighbor'): + +def filter_adjlist(adjlist, focal_col="focal", neighbor_col="neighbor"): """ - This dedupes an adjacency list by examining both (a,b) and (b,a) when (a,b) is enountered. - The removal is done in order of the iteration order of the input adjacency list. So, if a - special order of removal is desired, you need to sort the list before this function. + This deduplicates an adjacency list by examining both ``(a,b)` and `(b,a)` + when `(a,b)` is encountered. The removal is done in order of the iteration + order of the input adjacency list. So, if a special order of removal is + desired, you need to sort the list before this function. Parameters ---------- - adjlist : pandas DataFrame - a dataframe that contains focal and neighbor columns - focal_col : string - the name of the column with the focal observation id - neighbor_col: string - the name of the column with the neighbor observation id + adjlist : pandas.DataFrame + A dataframe that contains focal and neighbor columns. + focal_col : str + The name of the column with the focal observation id. + neighbor_col : str + The name of the column with the neighbor observation id. Returns ------- - an adjacency table with reversible entries removed. + adjlist : pandas.DataFrame + An adjacency table with reversible entries removed. + """ + edges = adjlist.loc[:, [focal_col, neighbor_col]] undirected = set() to_remove = [] diff --git a/libpysal/weights/tests/test_adjlist.py b/libpysal/weights/tests/test_adjlist.py index 5ae9c886d..f28482bd1 100644 --- a/libpysal/weights/tests/test_adjlist.py +++ b/libpysal/weights/tests/test_adjlist.py @@ -11,100 +11,107 @@ try: import pandas import geopandas + PANDAS_MISSING = False except ImportError: PANDAS_MISSING = True -@ut.skipIf(PANDAS_MISSING, 'Pandas is gone') + +@ut.skipIf(PANDAS_MISSING, "Pandas is missing") class Test_Adjlist(ut.TestCase): def setUp(self): - self.knownW = io.open(examples.get_path('columbus.gal')).read() + self.knownW = io.open(examples.get_path("columbus.gal")).read() def test_round_trip(self): adjlist = self.knownW.to_adjlist(remove_symmetric=False).astype(int) w_from_adj = weights.W.from_adjlist(adjlist) - np.testing.assert_allclose(w_from_adj.sparse.toarray(), - self.knownW.sparse.toarray()) + np.testing.assert_allclose( + w_from_adj.sparse.toarray(), self.knownW.sparse.toarray() + ) def test_filter(self): - grid = lat2W(2,2) + grid = lat2W(2, 2) alist = grid.to_adjlist(remove_symmetric=True) assert len(alist) == 4 with self.assertRaises(AssertionError): badgrid = weights.W.from_adjlist(alist) - np.testing.assert_allclose(badgrid.sparse.toarray(), - grid.sparse.toarray()) + np.testing.assert_allclose(badgrid.sparse.toarray(), grid.sparse.toarray()) assert set(alist.focal.unique()) == {0, 1, 2} assert set(alist.neighbor.unique()) == {1, 2, 3} assert alist.weight.unique().item() == 1 - grid = lat2W(2,2, id_type='string') + grid = lat2W(2, 2, id_type="string") alist = grid.to_adjlist(remove_symmetric=True) assert len(alist) == 4 with self.assertRaises(AssertionError): badgrid = weights.W.from_adjlist(alist) - np.testing.assert_allclose(badgrid.sparse.toarray(), - grid.sparse.toarray()) + np.testing.assert_allclose(badgrid.sparse.toarray(), grid.sparse.toarray()) print(alist) - tuples = set([tuple(t) for t in alist[['focal','neighbor']].values]) + tuples = set([tuple(t) for t in alist[["focal", "neighbor"]].values]) full_alist = grid.to_adjlist() - all_possible = set([tuple(t) for t in full_alist[['focal','neighbor']].values]) - assert tuples.issubset(all_possible), ('the de-duped adjlist has links ' - 'not in the duplicated adjlist.') + all_possible = set([tuple(t) for t in full_alist[["focal", "neighbor"]].values]) + assert tuples.issubset(all_possible), ( + "the de-duplicated adjlist has links " "not in the duplicated adjlist." + ) complements = all_possible.difference(tuples) reversed_complements = set([t[::-1] for t in complements]) - assert reversed_complements == tuples, ('the remaining links in the duplicated' - ' adjlist are not the reverse of the links' - ' in the deduplicated adjlist.') + assert reversed_complements == tuples, ( + "the remaining links in the duplicated" + " adjlist are not the reverse of the links" + " in the deduplicated adjlist." + ) assert alist.weight.unique().item() == 1 - def apply_and_compare_columbus(self, col): import geopandas - df = geopandas.read_file(examples.get_path('columbus.dbf')).head() + + df = geopandas.read_file(examples.get_path("columbus.dbf")).head() W = weights.Queen.from_dataframe(df) alist = adj.adjlist_apply(df[col], W=W) - right_hovals = alist.groupby('focal').att_focal.unique() + right_hovals = alist.groupby("focal").att_focal.unique() assert (right_hovals == df[col]).all() allpairs = np.subtract.outer(df[col].values, df[col].values) flat_diffs = allpairs[W.sparse.toarray().astype(bool)] - np.testing.assert_allclose(flat_diffs, alist['subtract'].values) + np.testing.assert_allclose(flat_diffs, alist["subtract"].values) return flat_diffs def test_apply(self): - self.apply_and_compare_columbus('HOVAL') + self.apply_and_compare_columbus("HOVAL") def test_mvapply(self): import geopandas - df = geopandas.read_file(examples.get_path('columbus.dbf')).head() + + df = geopandas.read_file(examples.get_path("columbus.dbf")).head() W = weights.Queen.from_dataframe(df) - ssq = lambda x_y: np.sum((x_y[0]-x_y[1])**2).item() - ssq.__name__ = 'sum_of_squares' - alist = adj.adjlist_apply(df[['HOVAL', 'CRIME', 'INC']], W=W, - func=ssq) - known_ssq = [1301.1639302990804, - 3163.46450914361, - 1301.1639302990804, - 499.52656498472993, - 594.518273032036, - 3163.46450914361, - 499.52656498472993, - 181.79100173844196, - 436.09336916344097, - 594.518273032036, - 181.79100173844196, - 481.89443401250094, - 436.09336916344097, - 481.89443401250094] #ugh I hate doing this, but how else? - np.testing.assert_allclose(alist.sum_of_squares.values, - np.asarray(known_ssq), - rtol=RTOL, atol=ATOL) + ssq = lambda x_y: np.sum((x_y[0] - x_y[1]) ** 2).item() + ssq.__name__ = "sum_of_squares" + alist = adj.adjlist_apply(df[["HOVAL", "CRIME", "INC"]], W=W, func=ssq) + known_ssq = [ + 1301.1639302990804, + 3163.46450914361, + 1301.1639302990804, + 499.52656498472993, + 594.518273032036, + 3163.46450914361, + 499.52656498472993, + 181.79100173844196, + 436.09336916344097, + 594.518273032036, + 181.79100173844196, + 481.89443401250094, + 436.09336916344097, + 481.89443401250094, + ] # ugh I hate doing this, but how else? + np.testing.assert_allclose( + alist.sum_of_squares.values, np.asarray(known_ssq), rtol=RTOL, atol=ATOL + ) def test_map(self): - atts = ['HOVAL', 'CRIME', 'INC'] - df = geopandas.read_file(examples.get_path('columbus.dbf')).head() + atts = ["HOVAL", "CRIME", "INC"] + df = geopandas.read_file(examples.get_path("columbus.dbf")).head() W = weights.Queen.from_dataframe(df) hoval, crime, inc = list(map(self.apply_and_compare_columbus, atts)) mapped = adj.adjlist_map(df[atts], W=W) - for name,data in zip(atts, (hoval, crime, inc)): - np.testing.assert_allclose(data, - mapped['_'.join(('subtract',name))].values) + for name, data in zip(atts, (hoval, crime, inc)): + np.testing.assert_allclose( + data, mapped["_".join(("subtract", name))].values + ) From da4174f8e31e65815bb666b7dee4b88cf0b733ee Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Tue, 11 Aug 2020 10:15:46 -0400 Subject: [PATCH 05/33] add spacing between logic blocks in adjtools.py --- libpysal/weights/adjtools.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/libpysal/weights/adjtools.py b/libpysal/weights/adjtools.py index f4f26374c..155321f12 100644 --- a/libpysal/weights/adjtools.py +++ b/libpysal/weights/adjtools.py @@ -42,7 +42,9 @@ def adjlist_apply(X, W=None, alist=None, func=np.subtract, skip_verify=False): import pandas as pd except ImportError: raise ImportError("pandas must be installed to use this function") + W, alist = _get_W_and_alist(W, alist, skip_verify=skip_verify) + if len(X.shape) > 1: if X.shape[-1] > 1: return _adjlist_mvapply( @@ -50,6 +52,7 @@ def adjlist_apply(X, W=None, alist=None, func=np.subtract, skip_verify=False): ) else: vec = np.asarray(X).flatten() + ids = np.asarray(W.id_order)[:, None] table = pd.DataFrame(ids, columns=["id"]) table = pd.concat((table, pd.DataFrame(vec[:, None], columns=("att",))), axis=1) @@ -79,9 +82,13 @@ def _adjlist_mvapply(X, W=None, alist=None, func=None, skip_verify=False): import pandas as pd except ImportError: raise ImportError("pandas must be installed to use this function") + assert len(X.shape) == 2, "data is not two-dimensional" + W, alist = _get_W_and_alist(W=W, alist=alist, skip_verify=skip_verify) + assert X.shape[0] == W.n, "number of samples in X does not match W" + try: names = X.columns.tolist() except AttributeError: @@ -128,12 +135,15 @@ def _get_W_and_alist(W, alist, skip_verify=False): if (alist is None) and (W is not None): alist = W.to_adjlist() + elif (W is None) and (alist is not None): from .weights import W W = W.from_adjlist(alist) + elif (W is None) and (alist is None): raise ValueError("Either W or Adjacency List must be provided") + elif (W is not None) and (alist is not None) and (not skip_verify): from .weights import W as W_ @@ -187,21 +197,28 @@ def adjlist_map( import pandas as pd except ImportError: raise ImportError("pandas must be installed to use this function") + if isinstance(data, pd.DataFrame): names = data.columns data = data.values else: names = [str(i) for i in range(data.shape[1])] + assert data.shape[0] == W.n, "shape of data does not match shape of adjacency" + if callable(funcs): funcs = (funcs,) + if len(funcs) == 1: funcs = [funcs[0] for _ in range(data.shape[1])] + assert data.shape[1] == len( funcs ), "shape of data does not match the number of functions provided" W, alist = _get_W_and_alist(W, alist) + fnames = set([f.__name__ for f in funcs]) + for i, (column, function) in enumerate(zip(data.T, funcs)): alist = adjlist_apply(X=column, W=W, alist=alist, skip_verify=True) alist.drop(["att_focal", "att_neighbor"], axis=1, inplace=True) @@ -209,6 +226,7 @@ def adjlist_map( columns={function.__name__: "_".join((function.__name__, names[i]))} ) fnames.update((function.__name__,)) + return alist @@ -238,6 +256,7 @@ def filter_adjlist(adjlist, focal_col="focal", neighbor_col="neighbor"): edges = adjlist.loc[:, [focal_col, neighbor_col]] undirected = set() to_remove = [] + for index, *edge in edges.itertuples(name=None): edge = tuple(edge) if edge in undirected or edge[::-1] in undirected: @@ -246,4 +265,5 @@ def filter_adjlist(adjlist, focal_col="focal", neighbor_col="neighbor"): undirected.add(edge) undirected.add(edge[::-1]) adjlist = adjlist.drop(to_remove) + return adjlist From 39033d0de3b1ba28efe6cac712ff8463486d6528 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Tue, 11 Aug 2020 10:17:06 -0400 Subject: [PATCH 06/33] add spacing between logic blocks in adjtools.py [2] --- libpysal/weights/adjtools.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libpysal/weights/adjtools.py b/libpysal/weights/adjtools.py index 155321f12..14bf60d31 100644 --- a/libpysal/weights/adjtools.py +++ b/libpysal/weights/adjtools.py @@ -69,6 +69,7 @@ def adjlist_apply(X, W=None, alist=None, func=np.subtract, skip_verify=False): alist_atts[func.__name__] = alist_atts[["att_focal", "att_neighbor"]].apply( lambda x: func(x.att_focal, x.att_neighbor), axis=1 ) + return alist_atts @@ -93,6 +94,7 @@ def _adjlist_mvapply(X, W=None, alist=None, func=None, skip_verify=False): names = X.columns.tolist() except AttributeError: names = list(map(str, list(range(X.shape[1])))) + ids = np.asarray(W.id_order)[:, None] table = pd.DataFrame(ids, columns=["id"]) table = pd.concat((table, pd.DataFrame(X, columns=names)), axis=1) @@ -105,6 +107,7 @@ def _adjlist_mvapply(X, W=None, alist=None, func=None, skip_verify=False): right_on="id", suffixes=("_focal", "_neighbor"), ) + alist_atts.drop(["id_focal", "id_neighbor"], axis=1, inplace=True) alist_atts[func.__name__] = list( map( @@ -117,6 +120,7 @@ def _adjlist_mvapply(X, W=None, alist=None, func=None, skip_verify=False): ), ) ) + return alist_atts @@ -150,6 +154,7 @@ def _get_W_and_alist(W, alist, skip_verify=False): np.testing.assert_allclose( W.sparse.toarray(), W_.from_adjlist(alist).sparse.toarray() ) + return W, alist From 67a89bfba5ee1b38bc7982c8931566b80e24ddcf Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Fri, 14 Aug 2020 17:28:44 -0400 Subject: [PATCH 07/33] updating weights/contiguity.py & weights/tests/test_contiguity.py --- libpysal/weights/contiguity.py | 468 ++++++++++++---------- libpysal/weights/tests/test_contiguity.py | 126 +++--- 2 files changed, 332 insertions(+), 262 deletions(-) diff --git a/libpysal/weights/contiguity.py b/libpysal/weights/contiguity.py index df12e106d..4c7c41ff5 100644 --- a/libpysal/weights/contiguity.py +++ b/libpysal/weights/contiguity.py @@ -1,5 +1,4 @@ import itertools - import numpy from ..cg import voronoi_frames @@ -11,37 +10,42 @@ try: from shapely.geometry import Point as shapely_point from ..cg.shapes import Point as pysal_point + point_type = (shapely_point, pysal_point) except ImportError: from ..cg.shapes import Point as point_type -WT_TYPE = {'rook': 2, 'queen': 1} # for _contW_Binning +WT_TYPE = {"rook": 2, "queen": 1} # for _contW_Binning -__author__ = "Sergio J. Rey , Levi John Wolf " +__author__ = ( + "Sergio J. Rey , Levi John Wolf " +) -__all__ = ['Rook', 'Queen', 'Voronoi'] +__all__ = ["Rook", "Queen", "Voronoi"] class Rook(W): - """ - Construct a weights object from a collection of pysal polygons that share at least one edge. + """Construct a weights object from a collection of + PySAL polygons that share at least one edge. Parameters ---------- - polygons : list - a collection of PySAL shapes to build weights from - ids : list - a list of names to use to build the weights - **kw : keyword arguments - optional arguments for :class:`pysal.weights.W` + polygons : list + A collection of PySAL shapes from which to build weights. + **kw : keyword arguments + Optional arguments for ``pysal.weights.W``. The parameter ``ids``, + a list of names to use to build the weights, should be included here. See Also - --------- - :class:`libpysal.weights.weights.W` + -------- + + libpysal.weights.weights.W + """ + def __init__(self, polygons, **kw): - criterion = 'rook' - ids = kw.pop('ids', None) + criterion = "rook" + ids = kw.pop("ids", None) polygons, backup = itertools.tee(polygons) first_shape = next(iter(backup)) if isinstance(first_shape, point_type): @@ -52,26 +56,31 @@ def __init__(self, polygons, **kw): @classmethod def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): - """ - Rook contiguity weights from a polygon shapefile. + """Rook contiguity weights from a polygon shapefile. Parameters ---------- - - shapefile : string - name of polygon shapefile including suffix. - sparse : boolean - If True return WSP instance - If False return W instance + filepath : str + The name of polygon shapefile including the file extension. + idVariable : str + The name of the attribute in the shapefile to associate + with ids in the weights. Default is ``None``. + full : bool + Write out the entire path for a shapefile (``True``) or + only the base of the shapefile without extension (``False``). + Default is ``False``. + **kwargs : keyword arguments + ``'sparse'`` should be included here. + If ``True`` return `WSP` instance. If ``False`` return `W` instance. Returns ------- - - w : W - instance of spatial weights + w : W + An instance of spatial weights. Examples -------- + >>> from libpysal.weights import Rook >>> import libpysal >>> wr=Rook.from_shapefile(libpysal.examples.get_path("columbus.shp"), "POLYID") @@ -85,15 +94,18 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): Notes ----- - Rook contiguity defines as neighbors any pair of polygons that share a - common edge in their polygon definitions. + Rook contiguity defines as neighbors any pair of polygons + that share a common edge in their polygon definitions. See Also -------- - :class:`libpysal.weights.weights.W` - :class:`libpysal.weights.contiguity.Rook` + + libpysal.weights.weights.W + libpysal.weights.contiguity.Rook + """ - sparse = kwargs.pop('sparse', False) + + sparse = kwargs.pop("sparse", False) if idVariable is not None: ids = get_ids(filepath, idVariable) else: @@ -106,22 +118,28 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): @classmethod def from_iterable(cls, iterable, sparse=False, **kwargs): - """ - Construct a weights object from a collection of arbitrary polygons. This - will cast the polygons to PySAL polygons, then build the W. + """Construct a weights object from a collection of arbitrary polygons. + This will cast the polygons to PySAL polygons, then build the `W`. Parameters ---------- - iterable : iterable - a collection of of shapes to be cast to PySAL shapes. Must - support iteration. Can be either Shapely or PySAL shapes. - **kw : keyword arguments - optional arguments for :class:`pysal.weights.W` + iterable : iterable + A collection of of shapes to be cast to PySAL shapes. Must + support iteration. Can be either Shapely or PySAL shapes. + sparse : bool + Generate ``WSP`` object. + **kwargs : keyword arguments + Optional arguments for ``pysal.weights.W``. + See Also -------- - :class:`libpysal.weights.weights.W` - :class:`libpysal.weights.contiguity.Rook` + + libpysal.weights.weights.W + libpysal.weights.weights.WSP + libpysal.weights.contiguity.Rook + """ + new_iterable = iter(iterable) w = cls(new_iterable, **kwargs) if sparse: @@ -129,46 +147,41 @@ def from_iterable(cls, iterable, sparse=False, **kwargs): return w @classmethod - def from_dataframe(cls, - df, - geom_col='geometry', - idVariable=None, - ids=None, - id_order=None, - **kwargs): - """ - Construct a weights object from a pandas dataframe with a geometry - column. This will cast the polygons to PySAL polygons, then build the W + def from_dataframe( + cls, df, geom_col="geometry", idVariable=None, ids=None, id_order=None, **kwargs + ): + """Construct a weights object from a ``pandas.DataFrame`` with a geometry + column. This will cast the polygons to PySAL polygons, then build the `W` using ids from the dataframe. Parameters ---------- - df : DataFrame - a :class: `pandas.DataFrame` containing geometries to use - for spatial weights - geom_col : string - the name of the column in `df` that contains the - geometries. Defaults to `geometry` - idVariable : string - the name of the column to use as IDs. If nothing is - provided, the dataframe index is used - ids : list - a list of ids to use to index the spatial weights object. - Order is not respected from this list. + df : pandas.DataFrame + A ``pandas.DataFrame` containing geometries to use for spatial weights. + geom_col : str + The name of the column in ``df`` that contains the + geometries. Default is ``geometry``. + idVariable : str + The name of the column to use as IDs. If nothing is provided, the + dataframe index is used. Default is ``None``. + ids : list + A list of ids to use to index the spatial weights object. + Order is not respected from this list. Default is ``None``. id_order : list - an ordered list of ids to use to index the spatial weights - object. If used, the resulting weights object will iterate - over results in the order of the names provided in this - argument. + An ordered list of ids to use to index the spatial weights object. If + used, the resulting weights object will iterate over results in the + order of the names provided in this argument. Default is ``None``. See Also -------- - :class:`libpysal.weights.weights.W` - :class:`libpysal.weights.contiguity.Rook` + + libpysal.weights.weights.W + libpysal.weights.contiguity.Rook + """ + if id_order is not None: - if id_order is True and ((idVariable is not None) or - (ids is not None)): + if id_order is True and ((idVariable is not None) or (ids is not None)): # if idVariable is None, we want ids. Otherwise, we want the # idVariable column id_order = list(df.get(idVariable, ids)) @@ -178,32 +191,33 @@ def from_dataframe(cls, ids = df.get(idVariable).tolist() elif isinstance(ids, str): ids = df.get(ids).tolist() - return cls.from_iterable(df[geom_col].tolist(), - ids=ids, - id_order=id_order, - **kwargs) + return cls.from_iterable( + df[geom_col].tolist(), ids=ids, id_order=id_order, **kwargs + ) class Queen(W): - """ - Construct a weights object from a collection of pysal polygons that share at least one vertex. + """Construct a weights object from a collection of PySAL + polygons that share at least one vertex. Parameters ---------- - polygons : list - a collection of PySAL shapes to build weights from - ids : list - a list of names to use to build the weights - **kw : keyword arguments - optional arguments for :class:`pysal.weights.W` + polygons : list + A collection of PySAL shapes from which to build weights. + **kw : keyword arguments + Optional arguments for ``pysal.weights.W``. The parameter ``ids``, + a list of names to use to build the weights, should be included here. See Also -------- - :class:`libpysal.weights.weights.W` + + libpysal.weights.weights.W + """ + def __init__(self, polygons, **kw): - criterion = 'queen' - ids = kw.pop('ids', None) + criterion = "queen" + ids = kw.pop("ids", None) polygons, backup = itertools.tee(polygons) first_shape = next(iter(backup)) if isinstance(first_shape, point_type): @@ -219,22 +233,27 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): Parameters ---------- + filepath : str + The name of polygon shapefile including the file extension. + idVariable : str + The name of the attribute in the shapefile to associate + with ids in the weights. Default is ``None``. + full : bool + Write out the entire path for a shapefile (``True``) or + only the base of the shapefile without extension (``False``). + Default is ``False``. + **kwargs : keyword arguments + ``'sparse'`` should be included here. + If ``True`` return `WSP` instance. If ``False`` return `W` instance. - shapefile : string - name of polygon shapefile including suffix. - idVariable : string - name of a column in the shapefile's DBF to use for ids. - sparse : boolean - If True return WSP instance - If False return W instance Returns ------- - - w : W - instance of spatial weights + w : W + An instance of spatial weights. Examples -------- + >>> from libpysal.weights import Queen >>> import libpysal >>> wq=Queen.from_shapefile(libpysal.examples.get_path("columbus.shp")) @@ -249,16 +268,20 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): '0.098' Notes + ----- Queen contiguity defines as neighbors any pair of polygons that share at least one vertex in their polygon definitions. See Also -------- - :class:`libpysal.weights.weights.W` - :class:`libpysal.weights.contiguity.Queen` + + libpysal.weights.weights.W + libpysal.weights.contiguity.Queen + """ - sparse = kwargs.pop('sparse', False) + + sparse = kwargs.pop("sparse", False) if idVariable is not None: ids = get_ids(filepath, idVariable) else: @@ -271,22 +294,28 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): @classmethod def from_iterable(cls, iterable, sparse=False, **kwargs): - """ - Construct a weights object from a collection of arbitrary polygons. This - will cast the polygons to PySAL polygons, then build the W. + """Construct a weights object from a collection of arbitrary polygons. + This will cast the polygons to PySAL polygons, then build the `W`. Parameters ---------- - iterable : iterable - a collection of of shapes to be cast to PySAL shapes. Must - support iteration. Contents may either be a shapely or PySAL shape. - **kw : keyword arguments - optional arguments for :class:`pysal.weights.W` + iterable : iterable + A collection of of shapes to be cast to PySAL shapes. Must + support iteration. Can be either Shapely or PySAL shapes. + sparse : bool + Generate ``WSP`` object. + **kwargs : keyword arguments + Optional arguments for ``pysal.weights.W``. + See Also - --------- - :class:`libpysal.weights.weights.W` - :class:`libpysal.weights.contiguiyt.Queen` + -------- + + libpysal.weights.weights.W + libpysal.weights.weights.WSP + libpysal.weights.contiguity.Queen + """ + new_iterable = iter(iterable) w = cls(new_iterable, **kwargs) if sparse: @@ -294,43 +323,42 @@ def from_iterable(cls, iterable, sparse=False, **kwargs): return w @classmethod - def from_dataframe(cls, df, geom_col='geometry', **kwargs): - """ - Construct a weights object from a pandas dataframe with a geometry - column. This will cast the polygons to PySAL polygons, then build the W + def from_dataframe(cls, df, geom_col="geometry", **kwargs): + """Construct a weights object from a ``pandas.DataFrame`` with a geometry + column. This will cast the polygons to PySAL polygons, then build the `W` using ids from the dataframe. Parameters ---------- - df : DataFrame - a :class: `pandas.DataFrame` containing geometries to use - for spatial weights - geom_col : string - the name of the column in `df` that contains the - geometries. Defaults to `geometry` - idVariable : string - the name of the column to use as IDs. If nothing is - provided, the dataframe index is used - ids : list - a list of ids to use to index the spatial weights object. - Order is not respected from this list. + df : pandas.DataFrame + A ``pandas.DataFrame` containing geometries to use for spatial weights. + geom_col : str + The name of the column in ``df`` that contains the + geometries. Default is ``geometry``. + idVariable : str + The name of the column to use as IDs. If nothing is provided, the + dataframe index is used. Default is ``None``. + ids : list + A list of ids to use to index the spatial weights object. + Order is not respected from this list. Default is ``None``. id_order : list - an ordered list of ids to use to index the spatial weights - object. If used, the resulting weights object will iterate - over results in the order of the names provided in this - argument. + An ordered list of ids to use to index the spatial weights object. If + used, the resulting weights object will iterate over results in the + order of the names provided in this argument. Default is ``None``. See Also -------- - :class:`libpysal.weights.weights.W` - :class:`libpysal.weights.contiguity.Queen` + + libpysal.weights.weights.W + libpysal.weights.contiguity.Queen + """ - idVariable = kwargs.pop('idVariable', None) - ids = kwargs.pop('ids', None) - id_order = kwargs.pop('id_order', None) + + idVariable = kwargs.pop("idVariable", None) + ids = kwargs.pop("ids", None) + id_order = kwargs.pop("id_order", None) if id_order is not None: - if id_order is True and ((idVariable is not None) or - (ids is not None)): + if id_order is True and ((idVariable is not None) or (ids is not None)): # if idVariable is None, we want ids. Otherwise, we want the # idVariable column ids = list(df.get(idVariable, ids)) @@ -342,37 +370,36 @@ def from_dataframe(cls, df, geom_col='geometry', **kwargs): ids = df.get(idVariable).tolist() elif isinstance(ids, str): ids = df.get(ids).tolist() - w = cls.from_iterable(df[geom_col].tolist(), - ids=ids, - id_order=id_order, - **kwargs) + w = cls.from_iterable( + df[geom_col].tolist(), ids=ids, id_order=id_order, **kwargs + ) return w -def Voronoi(points, criterion='rook', clip='ahull', **kwargs): - """ - Voronoi weights for a 2-d point set - - - Points are Voronoi neighbors if their polygons share an edge or vertex. - +def Voronoi(points, criterion="rook", clip="ahull", **kwargs): + """Voronoi weights for a 2-d point set. Points are Voronoi neighbors + if their polygons share an edge or vertex. Parameters ---------- - - points : array - (n,2) - coordinates for point locations - kwargs : arguments to pass to Rook, the underlying contiguity class. + points : array-like + An array-like (n,2) object of coordinates for point locations. + criterion : str + The weight criterion, either ``'rook'`` or ``'queen'``. Default is ``'rook'``. + clip : : str, shapely.geometry.Polygon + An overloaded option about how to clip the voronoi cells. Default is ``'ahull'``. + See ``libpysal.cg.voronoi.voronoi_frames()`` for more explanation. + **kwargs : keyword arguments + The arguments to pass to ``Rook``, the underlying contiguity class. Returns ------- - - w : W - instance of spatial weights + w : W + An instance of spatial weights. Examples -------- + >>> import numpy as np >>> from libpysal.weights import Voronoi >>> np.random.seed(12345) @@ -380,46 +407,59 @@ def Voronoi(points, criterion='rook', clip='ahull', **kwargs): >>> w = Voronoi(points) >>> w.neighbors {0: [2, 3, 4], 1: [2], 2: [0, 1, 4], 3: [0, 4], 4: [0, 2, 3]} + """ + from ..cg.voronoi import voronoi_frames + region_df, _ = voronoi_frames(points, clip=clip) - if criterion.lower() == 'queen': + if criterion.lower() == "queen": cls = Queen - elif criterion.lower() == 'rook': + elif criterion.lower() == "rook": cls = Rook else: raise ValueError( - 'Contiguity criterion {} not supported. ' - 'Only "rook" and "queen" are supported.'.format(criterion)) + "Contiguity criterion {} not supported. " + 'Only "rook" and "queen" are supported.'.format(criterion) + ) return cls.from_dataframe(region_df, **kwargs) def _from_dataframe(df, **kwargs): - """ - Construct a voronoi contiguity weight directly from a dataframe. - Note that if criterion='rook', this is identical to the delaunay - graph for the points. + """Construct Voronoi contiguity weights directly from a dataframe. - If the input dataframe is of any other geometry type than "Point", - a value error is raised. - - Arguments - --------- - df : pandas.DataFrame - dataframe containing point geometries for a - voronoi diagram. + Parameters + ---------- + df : pandas.DataFrame + A dataframe containing point geometries for a Voronoi diagram. Returns ------- - w : W - instance of spatial weights. + w : W + An instance of spatial weights. + + Notes + ----- + + If ``criterion='rook'``, this is identical to the Delaunay graph for the points. + + Raises + ------ + + NotImplementedError + If the input dataframe is of any other geometry type than ``Point``, + a ``ValueError`` is caught and raised as a ``NotImplementedError``. + """ + try: x, y = df.geometry.x.values, df.geometry.y.values except ValueError: - raise NotImplementedError('Voronoi weights are only' - ' implemented for point geometries. ' - 'You may consider using df.centroid.') + raise NotImplementedError( + "Voronoi weights are only" + " implemented for point geometries. " + "You may consider using df.centroid." + ) coords = numpy.column_stack((x, y)) return Voronoi(coords, **kwargs) @@ -428,32 +468,37 @@ def _from_dataframe(df, **kwargs): def _build(polygons, criterion="rook", ids=None): - """ - This is a developer-facing function to construct a spatial weights object. + """This is a developer-facing function to construct a spatial weights object. Parameters - --------- - polygons : list - list of pysal polygons to use to build contiguity - criterion : string - option of which kind of contiguity to build. Is either "rook" or "queen" - ids : list - list of ids to use to index the neighbor dictionary + ---------- + polygons : list + A list of PySAL polygons to use to build contiguity. + criterion : str + Option of which kind of contiguity to build, either ``'rook'`` or ``'queen'``. + Default is ``'rook'``. + ids : list + A list of ids to use to index the neighbor dictionary. Returns ------- - tuple containing (neighbors, ids), where neighbors is a dictionary - describing contiguity relations and ids is the list of ids used to index - that dictionary. - - NOTE: this is different from the prior behavior of buildContiguity, which - returned an actual weights object. Since this just dispatches for the - classes above, this returns the raw ingredients for a spatial weights - object, not the object itself. + neighbor_result : tuple + The contents are ``(neighbors, ids)``, where ``neighbors`` is + a dictionary describing contiguity relations and ``ids`` is the + list of ids used to index that dictionary. + + Notes + ----- + + This is different from the prior behavior of ``buildContiguity``, which returned an + actual weights object. Since this just dispatches for the classes above, this returns + the raw ingredients for a spatial weights object, not the object itself. + """ if ids and len(ids) != len(set(ids)): raise ValueError( - "The argument to the ids parameter contains duplicate entries.") + "The argument to the ids parameter contains duplicate entries." + ) wttype = WT_TYPE[criterion.lower()] geo = polygons @@ -463,7 +508,7 @@ def _build(polygons, criterion="rook", ids=None): neighbor_data = ContiguityWeightsLists(polygons, wttype=wttype).w neighbors = {} - #weights={} + if ids: for key in neighbor_data: ida = ids[key] @@ -475,25 +520,28 @@ def _build(polygons, criterion="rook", ids=None): else: for key in neighbor_data: neighbors[key] = set(neighbor_data[key]) - return dict( - list( - zip(list(neighbors.keys()), - list(map(list, list(neighbors.values())))))), ids + + neighbor_result = ( + dict( + list(zip(list(neighbors.keys()), list(map(list, list(neighbors.values()))))) + ), + ids, + ) + + return neighbor_result def buildContiguity(polygons, criterion="rook", ids=None): + """This is a deprecated function. It builds a contiguity `W` from the + polygons provided. As such, it is now identical to calling the class + constructors for ``Rook`` or ``Queen``. """ - This is a deprecated function. - It builds a contiguity W from the polygons provided. As such, it is now - identical to calling the class constructors for Rook or Queen. - """ - #Warn('This function is deprecated. Please use the Rook or Queen classes', + # Warn('This function is deprecated. Please use the Rook or Queen classes', # UserWarning) - if criterion.lower() == 'rook': + if criterion.lower() == "rook": return Rook(polygons, ids=ids) - elif criterion.lower() == 'queen': + elif criterion.lower() == "queen": return Queen(polygons, ids=ids) else: - raise Exception( - 'Weights criterion "{}" was not found.'.format(criterion)) + raise Exception('Weights criterion "{}" was not found.'.format(criterion)) diff --git a/libpysal/weights/tests/test_contiguity.py b/libpysal/weights/tests/test_contiguity.py index c97c121c2..7b74da04a 100644 --- a/libpysal/weights/tests/test_contiguity.py +++ b/libpysal/weights/tests/test_contiguity.py @@ -12,29 +12,36 @@ PANDAS_EXTINCT = pandas is None try: import geopandas + GEOPANDAS_EXTINCT = False except ImportError: GEOPANDAS_EXTINCT = True + class Contiguity_Mixin(object): - polygon_path = pysal_examples.get_path('columbus.shp') - point_path = pysal_examples.get_path('baltim.shp') - f = ps_open(polygon_path) # our file handler - polygons = f.read() # our iterable - f.seek(0) #go back to head of file - cls = object # class constructor - known_wi = None #index of known w entry to compare - known_w = dict() #actual w entry + polygon_path = pysal_examples.get_path("columbus.shp") + point_path = pysal_examples.get_path("baltim.shp") + f = ps_open(polygon_path) # our file handler + polygons = f.read() # our iterable + f.seek(0) # go back to head of file + cls = object # class constructor + known_wi = None # index of known w entry to compare + known_w = dict() # actual w entry known_name = known_wi known_namedw = known_w - idVariable = None # id variable from file or column + idVariable = None # id variable from file or column def setUp(self): - self.__dict__.update({k:v for k,v in list(Contiguity_Mixin.__dict__.items()) - if not k.startswith('_')}) - + self.__dict__.update( + { + k: v + for k, v in list(Contiguity_Mixin.__dict__.items()) + if not k.startswith("_") + } + ) + def runTest(self): - pass + pass def test_init(self): # basic @@ -42,22 +49,22 @@ def test_init(self): self.assertEqual(w[self.known_wi], self.known_w) # sparse - #w = self.cls(self.polygons, sparse=True) - #srowvec = ws.sparse[self.known_wi].todense().tolist()[0] - #this_w = {i:k for i,k in enumerate(srowvec) if k>0} - #self.assertEqual(this_w, self.known_w) - #ids = ps.weights2.utils.get_ids(self.polygon_path, self.idVariable) + # w = self.cls(self.polygons, sparse=True) + # srowvec = ws.sparse[self.known_wi].todense().tolist()[0] + # this_w = {i:k for i,k in enumerate(srowvec) if k>0} + # self.assertEqual(this_w, self.known_w) + # ids = ps.weights2.utils.get_ids(self.polygon_path, self.idVariable) # named ids = util.get_ids(self.polygon_path, self.idVariable) - w = self.cls(self.polygons, ids = ids) + w = self.cls(self.polygons, ids=ids) self.assertEqual(w[self.known_name], self.known_namedw) def test_from_iterable(self): w = self.cls.from_iterable(self.f) self.f.seek(0) self.assertEqual(w[self.known_wi], self.known_w) - + def test_from_shapefile(self): # basic w = self.cls.from_shapefile(self.polygon_path) @@ -66,7 +73,7 @@ def test_from_shapefile(self): # sparse ws = self.cls.from_shapefile(self.polygon_path, sparse=True) srowvec = ws.sparse[self.known_wi].todense().tolist()[0] - this_w = {i:k for i,k in enumerate(srowvec) if k>0} + this_w = {i: k for i, k in enumerate(srowvec) if k > 0} self.assertEqual(this_w, self.known_w) # named @@ -75,9 +82,9 @@ def test_from_shapefile(self): def test_from_array(self): # test named, sparse from point array - pass + pass - @ut.skipIf(PANDAS_EXTINCT, 'Missing pandas') + @ut.skipIf(PANDAS_EXTINCT, "Missing pandas") def test_from_dataframe(self): # basic df = pdio.read_files(self.polygon_path) @@ -85,68 +92,83 @@ def test_from_dataframe(self): self.assertEqual(w[self.known_wi], self.known_w) # named geometry - df.rename(columns={'geometry':'the_geom'}, inplace=True) - w = self.cls.from_dataframe(df, geom_col = 'the_geom') + df.rename(columns={"geometry": "the_geom"}, inplace=True) + w = self.cls.from_dataframe(df, geom_col="the_geom") self.assertEqual(w[self.known_wi], self.known_w) # named geometry + named obs - w = self.cls.from_dataframe(df, geom_col='the_geom', idVariable=self.idVariable) + w = self.cls.from_dataframe(df, geom_col="the_geom", idVariable=self.idVariable) self.assertEqual(w[self.known_name], self.known_namedw) class Test_Queen(ut.TestCase, Contiguity_Mixin): def setUp(self): Contiguity_Mixin.setUp(self) - + self.known_wi = 4 - self.known_w = {2: 1.0, 3: 1.0, 5: 1.0, 7: 1.0, - 8: 1.0, 10: 1.0, 14: 1.0, 15: 1.0} + self.known_w = { + 2: 1.0, + 3: 1.0, + 5: 1.0, + 7: 1.0, + 8: 1.0, + 10: 1.0, + 14: 1.0, + 15: 1.0, + } self.cls = c.Queen - self.idVariable = 'POLYID' + self.idVariable = "POLYID" self.known_name = 5 - self.known_namedw = {k+1:v for k,v in list(self.known_w.items())} + self.known_namedw = {k + 1: v for k, v in list(self.known_w.items())} - @ut.skipIf(GEOPANDAS_EXTINCT, 'Missing Geopandas') + @ut.skipIf(GEOPANDAS_EXTINCT, "Missing Geopandas") def test_linestrings(self): - import geopandas - eberly = geopandas.read_file(pysal_examples.get_path("eberly_net.shp")).iloc[0:8] - eberly_w = {0: [1,2,3], - 1: [0,4], - 2: [0,3,4,5], - 3: [0,2,7], - 4: [1,2,5], - 5: [2,4,6], - 6: [5], - 7: [3]} + import geopandas + + eberly = geopandas.read_file(pysal_examples.get_path("eberly_net.shp")).iloc[ + 0:8 + ] + eberly_w = { + 0: [1, 2, 3], + 1: [0, 4], + 2: [0, 3, 4, 5], + 3: [0, 2, 7], + 4: [1, 2, 5], + 5: [2, 4, 6], + 6: [5], + 7: [3], + } eberly_w = W(neighbors=eberly_w).sparse.toarray() computed = self.cls.from_dataframe(eberly).sparse.toarray() np.testing.assert_array_equal(eberly_w, computed) + class Test_Rook(ut.TestCase, Contiguity_Mixin): def setUp(self): Contiguity_Mixin.setUp(self) - - self.known_w = {2: 1.0, 3: 1.0, 5: 1.0, 7: 1.0, - 8: 1.0, 10: 1.0, 14: 1.0} + + self.known_w = {2: 1.0, 3: 1.0, 5: 1.0, 7: 1.0, 8: 1.0, 10: 1.0, 14: 1.0} self.known_wi = 4 self.cls = c.Rook - self.idVariable = 'POLYID' + self.idVariable = "POLYID" self.known_name = 5 - self.known_namedw = {k+1:v for k,v in list(self.known_w.items())} + self.known_namedw = {k + 1: v for k, v in list(self.known_w.items())} + class Test_Voronoi(ut.TestCase): def test_voronoiW(self): np.random.seed(12345) - points = np.random.random((5,2))*10 + 10 + points = np.random.random((5, 2)) * 10 + 10 w = c.Voronoi(points) self.assertEqual(w.n, 5) - self.assertEqual(w.neighbors, {0: [2, 3, 4], - 1: [2], 2: [0, 1, 4], - 3: [0, 4], 4: [0, 2, 3]}) + self.assertEqual( + w.neighbors, {0: [2, 3, 4], 1: [2], 2: [0, 1, 4], 3: [0, 4], 4: [0, 2, 3]} + ) + q = ut.TestLoader().loadTestsFromTestCase(Test_Queen) r = ut.TestLoader().loadTestsFromTestCase(Test_Rook) suite = ut.TestSuite([q, r]) -if __name__ == '__main__': +if __name__ == "__main__": runner = ut.TextTestRunner() runner.run(suite) From 6123e9a177f7829f7e94757c90380899673ba8c7 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Sat, 15 Aug 2020 17:58:54 -0400 Subject: [PATCH 08/33] updating weights/tests/test_* --- libpysal/weights/tests/test_Wsets.py | 44 +- libpysal/weights/tests/test_distance.py | 253 +++++---- libpysal/weights/tests/test_nx.py | 14 +- libpysal/weights/tests/test_spatial_lag.py | 62 +-- libpysal/weights/tests/test_spintW.py | 554 +++++++++++++++----- libpysal/weights/tests/test_user.py | 12 +- libpysal/weights/tests/test_util.py | 191 +++++-- libpysal/weights/tests/test_weights.py | 574 ++++++++++++++------- libpysal/weights/tests/test_weights_IO.py | 18 +- 9 files changed, 1156 insertions(+), 566 deletions(-) diff --git a/libpysal/weights/tests/test_Wsets.py b/libpysal/weights/tests/test_Wsets.py index eccf75782..6a4909323 100644 --- a/libpysal/weights/tests/test_Wsets.py +++ b/libpysal/weights/tests/test_Wsets.py @@ -1,14 +1,13 @@ -"""Unit test for set_operations module.""" +"""Unit tests for the set_operations module.""" + import unittest from ..util import lat2W, block_weights from .. import set_operations import numpy as np -class Testset_operations(unittest.TestCase): - """Unit test for set_operations module.""" +class Testset_operations(unittest.TestCase): def test_w_union(self): - """Unit test""" w1 = lat2W(4, 4) w2 = lat2W(6, 4) w3 = set_operations.w_union(w1, w2) @@ -18,7 +17,6 @@ def test_w_union(self): self.assertEqual(set(w3.neighbors[15]), set([19, 11, 14])) def test_w_intersection(self): - """Unit test""" w1 = lat2W(4, 4) w2 = lat2W(6, 4) w3 = set_operations.w_union(w1, w2) @@ -28,7 +26,6 @@ def test_w_intersection(self): self.assertEqual(set(w3.neighbors[15]), set([19, 11, 14])) def test_w_difference(self): - """Unit test""" w1 = lat2W(4, 4, rook=False) w2 = lat2W(4, 4, rook=True) w3 = set_operations.w_difference(w1, w2, constrained=False) @@ -38,18 +35,15 @@ def test_w_difference(self): self.assertEqual(set(w3.neighbors[15]), set([10])) def test_w_symmetric_difference(self): - """Unit test""" w1 = lat2W(4, 4, rook=False) w2 = lat2W(6, 4, rook=True) - w3 = set_operations.w_symmetric_difference( - w1, w2, constrained=False) + w3 = set_operations.w_symmetric_difference(w1, w2, constrained=False) self.assertNotEqual(w1[0], w3[0]) self.assertEqual(set(w1.neighbors[15]), set([10, 11, 14])) self.assertEqual(set(w2.neighbors[15]), set([11, 14, 19])) self.assertEqual(set(w3.neighbors[15]), set([10, 19])) def test_w_subset(self): - """Unit test""" w1 = lat2W(6, 4) ids = list(range(16)) w2 = set_operations.w_subset(w1, ids) @@ -58,19 +52,24 @@ def test_w_subset(self): self.assertEqual(set(w2.neighbors[15]), set([11, 14])) def test_w_clip(self): - """Unit test for w_clip""" w1 = lat2W(3, 2, rook=False) - w1.transform = 'R' - w2 = block_weights(['r1', 'r2', 'r1', 'r1', 'r1', 'r2']) - w2.transform = 'R' + w1.transform = "R" + w2 = block_weights(["r1", "r2", "r1", "r1", "r1", "r2"]) + w2.transform = "R" wcs = set_operations.w_clip(w1, w2, outSP=True) - expected_wcs = np.array([[ 0., 0.,0.33333333,0.33333333, 0.,0.], - [ 0., 0., 0., 0., 0.,0. ], - [ 0.2, 0., 0., 0.2, 0.2,0.], - [ 0.2, 0., 0.2, 0., 0.2,0.], - [ 0., 0., 0.33333333, 0.33333333, 0., 0.], - [ 0., 0., 0., 0., 0.,0.]]) - np.testing.assert_array_equal(np.around(wcs.sparse.toarray(),decimals=8), expected_wcs) + expected_wcs = np.array( + [ + [0.0, 0.0, 0.33333333, 0.33333333, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.2, 0.0, 0.0, 0.2, 0.2, 0.0], + [0.2, 0.0, 0.2, 0.0, 0.2, 0.0], + [0.0, 0.0, 0.33333333, 0.33333333, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ] + ) + np.testing.assert_array_equal( + np.around(wcs.sparse.toarray(), decimals=8), expected_wcs + ) wc = set_operations.w_clip(w1, w2, outSP=False) np.testing.assert_array_equal(wcs.sparse.toarray(), wc.full()[0]) @@ -78,6 +77,7 @@ def test_w_clip(self): suite = unittest.TestLoader().loadTestsFromTestCase(Testset_operations) -if __name__ == '__main__': + +if __name__ == "__main__": runner = unittest.TextTestRunner() runner.run(suite) diff --git a/libpysal/weights/tests/test_distance.py b/libpysal/weights/tests/test_distance.py index 455b5e294..6bbac8868 100644 --- a/libpysal/weights/tests/test_distance.py +++ b/libpysal/weights/tests/test_distance.py @@ -1,4 +1,3 @@ - from ...common import RTOL, ATOL, pandas from ...cg.kdtree import KDTree, RADIUS_EARTH_KM from ..util import get_points_array @@ -13,77 +12,81 @@ PANDAS_EXTINCT = pandas is None # All instances should test these four methods, and define their own functional -# tests based on common codepaths/estimated weights use cases. +# tests based on common codepaths/estimated weights use cases. + class Distance_Mixin(object): - polygon_path = pysal_examples.get_path('columbus.shp') - arc_path = pysal_examples.get_path('stl_hom.shp') - points = [(10, 10), (20, 10), (40, 10), - (15, 20), (30, 20), (30, 30)] - euclidean_kdt = KDTree(points, distance_metric='euclidean') - - polygon_f = psopen(polygon_path) # our file handler - poly_centroids = get_points_array(polygon_f) # our iterable - polygon_f.seek(0) #go back to head of file - + polygon_path = pysal_examples.get_path("columbus.shp") + arc_path = pysal_examples.get_path("stl_hom.shp") + points = [(10, 10), (20, 10), (40, 10), (15, 20), (30, 20), (30, 30)] + euclidean_kdt = KDTree(points, distance_metric="euclidean") + + polygon_f = psopen(polygon_path) # our file handler + poly_centroids = get_points_array(polygon_f) # our iterable + polygon_f.seek(0) # go back to head of file + arc_f = psopen(arc_path) arc_points = get_points_array(arc_f) arc_f.seek(0) - arc_kdt = KDTree(arc_points, distance_metric='Arc', - radius=cg.sphere.RADIUS_EARTH_KM) - - cls = object # class constructor - known_wi = None #index of known w entry to compare - known_w = dict() #actual w entry + arc_kdt = KDTree( + arc_points, distance_metric="Arc", radius=cg.sphere.RADIUS_EARTH_KM + ) + + cls = object # class constructor + known_wi = None # index of known w entry to compare + known_w = dict() # actual w entry known_name = known_wi - + def setUp(self): - self.__dict__.update({k:v for k,v in list(Distance_Mixin.__dict__.items()) - if not k.startswith('_')}) - + self.__dict__.update( + { + k: v + for k, v in list(Distance_Mixin.__dict__.items()) + if not k.startswith("_") + } + ) + self.test_msg = "You need to implement this test before this module will pass" + def test_init(self): # test vanilla, named - raise NotImplementedError('You need to implement this test ' - 'before this module will pass') + raise NotImplementedError(self.test_msg) def test_from_shapefile(self): # test vanilla, named, sparse - raise NotImplementedError('You need to implement this test ' - 'before this module will pass') + raise NotImplementedError(self.test_msg) def test_from_array(self): # test named, sparse - raise NotImplementedError('You need to implement this test ' - 'before this module will pass') + raise NotImplementedError(self.test_msg) def test_from_dataframe(self): - # test named, columnar, defau - raise NotImplementedError('You need to implement this test ' - 'before this module will pass') + # test named, columnar, default + raise NotImplementedError(self.test_msg) + class Test_KNN(ut.TestCase, Distance_Mixin): def setUp(self): Distance_Mixin.setUp(self) - + self.known_wi0 = 7 self.known_w0 = [3, 6, 12, 11] self.known_wi1 = 0 - self.known_w1 = [2, 1, 3 ,7] + self.known_w1 = [2, 1, 3, 7] self.known_wi2 = 4 self.known_w2 = [1, 3, 9, 12] self.known_wi3 = 40 self.known_w3 = [31, 38, 45, 49] - + ########################## # Classmethod tests # ########################## def test_init(self): w = d.KNN(self.euclidean_kdt, k=2) - self.assertEqual(w.neighbors[0], [1,3]) + self.assertEqual(w.neighbors[0], [1, 3]) - @ut.skipIf(PANDAS_EXTINCT, 'Missing pandas') + @ut.skipIf(PANDAS_EXTINCT, "Missing pandas") def test_from_dataframe(self): df = pdio.read_files(self.polygon_path) w = d.KNN.from_dataframe(df, k=4) @@ -96,7 +99,7 @@ def test_from_array(self): self.assertEqual(w.neighbors[self.known_wi1], self.known_w1) def test_from_shapefile(self): - w = d.KNN.from_shapefile(self.polygon_path, k=4) + w = d.KNN.from_shapefile(self.polygon_path, k=4) self.assertEqual(w.neighbors[self.known_wi0], self.known_w0) self.assertEqual(w.neighbors[self.known_wi1], self.known_w1) @@ -106,103 +109,109 @@ def test_from_shapefile(self): def test_reweight(self): w = d.KNN(self.points, k=2) - new_point = [(21,21)] + new_point = [(21, 21)] wnew = w.reweight(k=4, p=1, new_data=new_point, inplace=False) self.assertEqual(wnew[0], {1: 1.0, 3: 1.0, 4: 1.0, 6: 1.0}) def test_arcdata(self): - w = d.KNN.from_shapefile(self.polygon_path, k=4, - distance_metric='Arc', - radius=cg.sphere.RADIUS_EARTH_KM) + w = d.KNN.from_shapefile( + self.polygon_path, + k=4, + distance_metric="Arc", + radius=cg.sphere.RADIUS_EARTH_KM, + ) self.assertEqual(w.data.shape[1], 3) class Test_DistanceBand(ut.TestCase, Distance_Mixin): def setUp(self): Distance_Mixin.setUp(self) - self.grid_path = pysal_examples.get_path('lattice10x10.shp') + self.grid_path = pysal_examples.get_path("lattice10x10.shp") self.grid_rook_w = c.Rook.from_shapefile(self.grid_path) self.grid_f = psopen(self.grid_path) self.grid_points = get_points_array(self.grid_f) self.grid_f.seek(0) self.grid_kdt = KDTree(self.grid_points) - + ########################## # Classmethod tests # ########################## def test_init(self): w = d.DistanceBand(self.grid_kdt, 1) - for k,v in w: + for k, v in w: self.assertEqual(v, self.grid_rook_w[k]) def test_from_shapefile(self): w = d.DistanceBand.from_shapefile(self.grid_path, 1) - for k,v in w: + for k, v in w: self.assertEqual(v, self.grid_rook_w[k]) def test_from_array(self): w = d.DistanceBand.from_array(self.grid_points, 1) - for k,v in w: + for k, v in w: self.assertEqual(v, self.grid_rook_w[k]) - @ut.skipIf(PANDAS_EXTINCT, 'Missing pandas') + @ut.skipIf(PANDAS_EXTINCT, "Missing pandas") def test_from_dataframe(self): import pandas as pd + geom_series = pdio.shp.shp2series(self.grid_path) random_data = np.random.random(size=len(geom_series)) - df = pd.DataFrame({'obs':random_data, 'geometry':geom_series}) + df = pd.DataFrame({"obs": random_data, "geometry": geom_series}) w = d.DistanceBand.from_dataframe(df, 1) - for k,v in w: + for k, v in w: self.assertEqual(v, self.grid_rook_w[k]) ########################## # Function/User tests # ########################## def test_integers(self): - """ - see issue #126 - """ - grid_integers = [tuple(map(int, poly.vertices[0])) - for poly in self.grid_f] + """See issue #126.""" + grid_integers = [tuple(map(int, poly.vertices[0])) for poly in self.grid_f] self.grid_f.seek(0) grid_dbw = d.DistanceBand(grid_integers, 1) - for k,v in grid_dbw: + for k, v in grid_dbw: self.assertEqual(v, self.grid_rook_w[k]) def test_arcdist(self): arc = cg.sphere.arcdist - kdt = KDTree(self.arc_points, distance_metric='Arc', - radius=cg.sphere.RADIUS_EARTH_KM) + kdt = KDTree( + self.arc_points, distance_metric="Arc", radius=cg.sphere.RADIUS_EARTH_KM + ) npoints = self.arc_points.shape[0] - full = np.matrix([[arc(self.arc_points[i], self.arc_points[j]) - for j in range(npoints)] - for i in range(npoints)]) + full = np.matrix( + [ + [arc(self.arc_points[i], self.arc_points[j]) for j in range(npoints)] + for i in range(npoints) + ] + ) maxdist = full.max() w = d.DistanceBand(kdt, maxdist, binary=False, alpha=1.0) np.testing.assert_allclose(w.sparse.todense(), full) self.assertEqual(w.data.shape[1], 3) def test_dense(self): - w_rook = c.Rook.from_shapefile( - pysal_examples.get_path('lattice10x10.shp')) - polys = psopen(pysal_examples.get_path('lattice10x10.shp')) + w_rook = c.Rook.from_shapefile(pysal_examples.get_path("lattice10x10.shp")) + polys = psopen(pysal_examples.get_path("lattice10x10.shp")) centroids = [p.centroid for p in polys] w_db = d.DistanceBand(centroids, 1, build_sp=False) for k in w_db.id_order: np.testing.assert_equal(w_db[k], w_rook[k]) - - @ut.skipIf(PANDAS_EXTINCT, 'Missing pandas') + + @ut.skipIf(PANDAS_EXTINCT, "Missing pandas") def test_named(self): import pandas as pd + geom_series = pdio.shp.shp2series(self.grid_path) random_data = np.random.random(size=len(geom_series)) - names = [chr(x) for x in range(60,160)] - df = pd.DataFrame({'obs':random_data, 'geometry':geom_series, 'names':names}) + names = [chr(x) for x in range(60, 160)] + df = pd.DataFrame({"obs": random_data, "geometry": geom_series, "names": names}) w = d.DistanceBand.from_dataframe(df, 1, ids=df.names) + class Test_Kernel(ut.TestCase, Distance_Mixin): def setUp(self): @@ -211,37 +220,48 @@ def setUp(self): self.known_w0 = {0: 1, 1: 0.500000049999995, 3: 0.4409830615267465} self.known_wi1 = 0 - self.known_w1 = {0: 1.0, 1: 0.33333333333333337, - 3: 0.2546440075000701} - self.known_w1_bw = 15. + self.known_w1 = {0: 1.0, 1: 0.33333333333333337, 3: 0.2546440075000701} + self.known_w1_bw = 15.0 self.known_wi2 = 0 - self.known_w2 = {0: 1.0, 1: 0.59999999999999998, - 3: 0.55278640450004202, 4: 0.10557280900008403} + self.known_w2 = { + 0: 1.0, + 1: 0.59999999999999998, + 3: 0.55278640450004202, + 4: 0.10557280900008403, + } self.known_w2_bws = [25.0, 15.0, 25.0, 16.0, 14.5, 25.0] self.known_wi3 = 0 self.known_w3 = [1.0, 0.10557289844279438, 9.9999990066379496e-08] - self.known_w3_abws =[[11.180341005532938], [11.180341005532938], - [20.000002000000002], [11.180341005532938], - [14.142137037944515], [18.027758180095585]] + self.known_w3_abws = [ + [11.180341005532938], + [11.180341005532938], + [20.000002000000002], + [11.180341005532938], + [14.142137037944515], + [18.027758180095585], + ] self.known_wi4 = 0 - self.known_w4 = {0: 0.3989422804014327, - 1: 0.26741902915776961, - 3: 0.24197074871621341} + self.known_w4 = { + 0: 0.3989422804014327, + 1: 0.26741902915776961, + 3: 0.24197074871621341, + } self.known_w4_abws = self.known_w3_abws self.known_wi5 = 1 - self.known_w5 = {4: 0.0070787731484506233, - 2: 0.2052478782400463, - 3: 0.23051223027663237, - 1: 1.0} + self.known_w5 = { + 4: 0.0070787731484506233, + 2: 0.2052478782400463, + 3: 0.23051223027663237, + 1: 1.0, + } self.known_wi6 = 0 - self.known_w6 = {0: 1.0, 2: 0.03178906767736345, - 1: 9.9999990066379496e-08} - #stick answers & params here + self.known_w6 = {0: 1.0, 2: 0.03178906767736345, 1: 9.9999990066379496e-08} + # stick answers & params here ########################## # Classmethod tests # @@ -249,68 +269,75 @@ def setUp(self): def test_init(self): w = d.Kernel(self.euclidean_kdt) - for k,v in list(w[self.known_wi0].items()): + for k, v in list(w[self.known_wi0].items()): np.testing.assert_allclose(v, self.known_w0[k], rtol=RTOL) def test_from_shapefile(self): - w = d.Kernel.from_shapefile(self.polygon_path, idVariable='POLYID') - for k,v in list(w[self.known_wi5].items()): - np.testing.assert_allclose((k,v), (k,self.known_w5[k]), rtol=RTOL) - + w = d.Kernel.from_shapefile(self.polygon_path, idVariable="POLYID") + for k, v in list(w[self.known_wi5].items()): + np.testing.assert_allclose((k, v), (k, self.known_w5[k]), rtol=RTOL) + w = d.Kernel.from_shapefile(self.polygon_path, fixed=False) - for k,v in list(w[self.known_wi6].items()): - np.testing.assert_allclose((k,v), (k,self.known_w6[k]), rtol=RTOL) + for k, v in list(w[self.known_wi6].items()): + np.testing.assert_allclose((k, v), (k, self.known_w6[k]), rtol=RTOL) def test_from_array(self): w = d.Kernel.from_array(self.points) - for k,v in list(w[self.known_wi0].items()): + for k, v in list(w[self.known_wi0].items()): np.testing.assert_allclose(v, self.known_w0[k], rtol=RTOL) - - @ut.skipIf(PANDAS_EXTINCT, 'Missing pandas') + + @ut.skipIf(PANDAS_EXTINCT, "Missing pandas") def test_from_dataframe(self): df = pdio.read_files(self.polygon_path) w = d.Kernel.from_dataframe(df) - for k,v in list(w[self.known_wi5-1].items()): - np.testing.assert_allclose(v, self.known_w5[k+1], rtol=RTOL) - + for k, v in list(w[self.known_wi5 - 1].items()): + np.testing.assert_allclose(v, self.known_w5[k + 1], rtol=RTOL) + ########################## - # Function/User tests # + # Function/User tests # ########################## def test_fixed_bandwidth(self): w = d.Kernel(self.points, bandwidth=15.0) - for k,v in list(w[self.known_wi1].items()): - np.testing.assert_allclose((k,v), (k, self.known_w1[k])) - np.testing.assert_allclose(np.ones((w.n,1))*15, w.bandwidth) + for k, v in list(w[self.known_wi1].items()): + np.testing.assert_allclose((k, v), (k, self.known_w1[k])) + np.testing.assert_allclose(np.ones((w.n, 1)) * 15, w.bandwidth) w = d.Kernel(self.points, bandwidth=self.known_w2_bws) - for k,v in list(w[self.known_wi2].items()): - np.testing.assert_allclose((k,v), (k, self.known_w2[k]), rtol=RTOL) + for k, v in list(w[self.known_wi2].items()): + np.testing.assert_allclose((k, v), (k, self.known_w2[k]), rtol=RTOL) for i in range(w.n): np.testing.assert_allclose(w.bandwidth[i], self.known_w2_bws[i], rtol=RTOL) - + def test_adaptive_bandwidth(self): w = d.Kernel(self.points, fixed=False) - np.testing.assert_allclose(sorted(w[self.known_wi3].values()), - sorted(self.known_w3), rtol=RTOL) + np.testing.assert_allclose( + sorted(w[self.known_wi3].values()), sorted(self.known_w3), rtol=RTOL + ) bws = w.bandwidth.tolist() np.testing.assert_allclose(bws, self.known_w3_abws, rtol=RTOL) - w = d.Kernel(self.points, fixed=False, function='gaussian') - for k,v in list(w[self.known_wi4].items()): - np.testing.assert_allclose((k,v), (k, self.known_w4[k]), rtol=RTOL) + w = d.Kernel(self.points, fixed=False, function="gaussian") + for k, v in list(w[self.known_wi4].items()): + np.testing.assert_allclose((k, v), (k, self.known_w4[k]), rtol=RTOL) bws = w.bandwidth.tolist() np.testing.assert_allclose(bws, self.known_w4_abws, rtol=RTOL) def test_arcdistance(self): - w = d.Kernel(self.points, fixed=True, distance_metric='Arc', - radius=cg.sphere.RADIUS_EARTH_KM) + w = d.Kernel( + self.points, + fixed=True, + distance_metric="Arc", + radius=cg.sphere.RADIUS_EARTH_KM, + ) self.assertEqual(w.data.shape[1], 3) + knn = ut.TestLoader().loadTestsFromTestCase(Test_KNN) kern = ut.TestLoader().loadTestsFromTestCase(Test_Kernel) db = ut.TestLoader().loadTestsFromTestCase(Test_DistanceBand) suite = ut.TestSuite([knn, kern, db]) -if __name__ == '__main__': + +if __name__ == "__main__": runner = ut.TextTestRunner() runner.run(suite) diff --git a/libpysal/weights/tests/test_nx.py b/libpysal/weights/tests/test_nx.py index 3dbe2bede..dda7f9ae8 100644 --- a/libpysal/weights/tests/test_nx.py +++ b/libpysal/weights/tests/test_nx.py @@ -2,17 +2,19 @@ import numpy as np from ..util import lat2W from ..weights import W + try: import networkx as nx except ImportError: nx = None + @ut.skipIf(nx is None, "Missing networkx") class Test_NetworkXConverter(ut.TestCase): def setUp(self): - self.known_nx = nx.random_regular_graph(4,10,seed=8879) + self.known_nx = nx.random_regular_graph(4, 10, seed=8879) self.known_amat = np.array(nx.to_numpy_matrix(self.known_nx)) - self.known_W = lat2W(5,5) + self.known_W = lat2W(5, 5) def test_round_trip(self): W_ = W.from_networkx(self.known_nx) @@ -20,6 +22,10 @@ def test_round_trip(self): nx2 = W_.to_networkx() np.testing.assert_allclose(nx.to_numpy_matrix(nx2), self.known_amat) nxsquare = self.known_W.to_networkx() - np.testing.assert_allclose(self.known_W.sparse.toarray(), nx.to_numpy_matrix(nxsquare)) + np.testing.assert_allclose( + self.known_W.sparse.toarray(), nx.to_numpy_matrix(nxsquare) + ) W_square = W.from_networkx(nxsquare) - np.testing.assert_allclose(self.known_W.sparse.toarray(), W_square.sparse.toarray()) + np.testing.assert_allclose( + self.known_W.sparse.toarray(), W_square.sparse.toarray() + ) diff --git a/libpysal/weights/tests/test_spatial_lag.py b/libpysal/weights/tests/test_spatial_lag.py index a4d3a10db..e437c1e92 100644 --- a/libpysal/weights/tests/test_spatial_lag.py +++ b/libpysal/weights/tests/test_spatial_lag.py @@ -1,4 +1,3 @@ - import os import unittest from ..weights import W @@ -9,55 +8,60 @@ class Test_spatial_lag(unittest.TestCase): def setUp(self): - self.neighbors = {'c': ['b'], 'b': ['c', 'a'], 'a': ['b']} - self.weights = {'c': [1.0], 'b': [1.0, 1.0], 'a': [1.0]} - self.id_order = ['a', 'b', 'c'] - self.weights = {'c': [1.0], 'b': [1.0, 1.0], 'a': [1.0]} + self.neighbors = {"c": ["b"], "b": ["c", "a"], "a": ["b"]} + self.weights = {"c": [1.0], "b": [1.0, 1.0], "a": [1.0]} + self.id_order = ["a", "b", "c"] + self.weights = {"c": [1.0], "b": [1.0, 1.0], "a": [1.0]} self.w = W(self.neighbors, self.weights, self.id_order) self.y = np.array([0, 1, 2]) self.wlat = lat2W(3, 3) - self.ycat = ['a','b','a','b','c','b','c','b','c'] - self.ycat2 = ['a', 'c', 'c', 'd', 'b', 'a', 'd', 'd', 'c'] - self.ym = np.vstack((self.ycat,self.ycat2)).T + self.ycat = ["a", "b", "a", "b", "c", "b", "c", "b", "c"] + self.ycat2 = ["a", "c", "c", "d", "b", "a", "d", "d", "c"] + self.ym = np.vstack((self.ycat, self.ycat2)).T self.random_seed = 503 + def test_lag_spatial(self): yl = lag_spatial(self.w, self.y) - np.testing.assert_array_almost_equal(yl, [1., 2., 1.]) - self.w.id_order = ['b', 'c', 'a'] + np.testing.assert_array_almost_equal(yl, [1.0, 2.0, 1.0]) + self.w.id_order = ["b", "c", "a"] y = np.array([1, 2, 0]) yl = lag_spatial(self.w, y) - np.testing.assert_array_almost_equal(yl, [2., 1., 1.]) + np.testing.assert_array_almost_equal(yl, [2.0, 1.0, 1.0]) w = lat2W(3, 3) y = np.arange(9) yl = lag_spatial(w, y) - ylc = np.array([4., 6., 6., 10., 16., 14., 10., 18., 12.]) + ylc = np.array([4.0, 6.0, 6.0, 10.0, 16.0, 14.0, 10.0, 18.0, 12.0]) np.testing.assert_array_almost_equal(yl, ylc) - w.transform = 'r' + w.transform = "r" yl = lag_spatial(w, y) - ylc = np.array( - [2., 2., 3., 3.33333333, 4., - 4.66666667, 5., 6., 6.]) + ylc = np.array([2.0, 2.0, 3.0, 3.33333333, 4.0, 4.66666667, 5.0, 6.0, 6.0]) np.testing.assert_array_almost_equal(yl, ylc) - + def test_lag_categorical(self): yl = lag_categorical(self.wlat, self.ycat) np.random.seed(self.random_seed) - known = np.array(['b', 'a', 'b', 'c', 'b', 'c', 'b', 'c', 'b']) + known = np.array(["b", "a", "b", "c", "b", "c", "b", "c", "b"]) np.testing.assert_array_equal(yl, known) - ym_lag = lag_categorical(self.wlat,self.ym) - known = np.array([['b', 'c'], - ['a', 'c'], - ['b', 'c'], - ['c', 'd'], - ['b', 'd'], - ['c', 'c'], - ['b', 'd'], - ['c', 'd'], - ['b', 'd']]) + ym_lag = lag_categorical(self.wlat, self.ym) + known = np.array( + [ + ["b", "c"], + ["a", "c"], + ["b", "c"], + ["c", "d"], + ["b", "d"], + ["c", "c"], + ["b", "d"], + ["c", "d"], + ["b", "d"], + ] + ) np.testing.assert_array_equal(ym_lag, np.asarray(known)) + suite = unittest.TestLoader().loadTestsFromTestCase(Test_spatial_lag) -if __name__ == '__main__': + +if __name__ == "__main__": runner = unittest.TextTestRunner() runner.run(suite) diff --git a/libpysal/weights/tests/test_spintW.py b/libpysal/weights/tests/test_spintW.py index 0da4011e3..15b6b6660 100644 --- a/libpysal/weights/tests/test_spintW.py +++ b/libpysal/weights/tests/test_spintW.py @@ -3,171 +3,461 @@ from ..spintW import ODW, netW, mat2L, vecW from ..util import lat2W + class TestODWeights(unittest.TestCase): def setUp(self): - self.O = lat2W(2,2) - self.D = lat2W(2,2) - self.ODW = np.array( - [[ 0. , 0. , 0. , 0. , 0. , 0.25, 0.25, 0. , 0. , - 0.25, 0.25, 0. , 0. , 0. , 0. , 0. ], - [ 0. , 0. , 0. , 0. , 0.25, 0. , 0. , 0.25, 0.25, - 0. , 0. , 0.25, 0. , 0. , 0. , 0. ], - [ 0. , 0. , 0. , 0. , 0.25, 0. , 0. , 0.25, 0.25, - 0. , 0. , 0.25, 0. , 0. , 0. , 0. ], - [ 0. , 0. , 0. , 0. , 0. , 0.25, 0.25, 0. , 0. , - 0.25, 0.25, 0. , 0. , 0. , 0. , 0. ], - [ 0. , 0.25, 0.25, 0. , 0. , 0. , 0. , 0. , 0. , - 0. , 0. , 0. , 0. , 0.25, 0.25, 0. ], - [0.25, 0. , 0. , 0.25, 0. , 0. , 0. , 0. , 0. , - 0. , 0. , 0. , 0.25, 0. , 0. , 0.25], - [ 0.25, 0. , 0. , 0.25, 0. , 0. , 0. , 0. , 0. , - 0. , 0. , 0. , 0.25, 0. , 0. , 0.25], - [ 0. , 0.25, 0.25, 0. , 0. , 0. , 0. , 0. , 0. , - 0. , 0. , 0. , 0. , 0.25, 0.25, 0. ], - [ 0. , 0.25, 0.25, 0. , 0. , 0. , 0. , 0. , 0. , - 0. , 0. , 0. , 0. , 0.25, 0.25, 0. ], - [ 0.25, 0. , 0. , 0.25, 0. , 0. , 0. , 0. , 0. , - 0. , 0. , 0. , 0.25, 0. , 0. , 0.25], - [ 0.25, 0. , 0. , 0.25, 0. , 0. , 0. , 0. , 0. , - 0. , 0. , 0. , 0.25, 0. , 0. , 0.25], - [ 0. , 0.25, 0.25, 0. , 0. , 0. , 0. , 0. , 0. , - 0. , 0. , 0. , 0. , 0.25, 0.25, 0. ], - [ 0. , 0. , 0. , 0. , 0. , 0.25, 0.25, 0. , 0. , - 0.25, 0.25, 0. , 0. , 0. , 0. , 0. ], - [ 0. , 0. , 0. , 0. , 0.25, 0. , 0. , 0.25, 0.25, - 0. , 0. , 0.25, 0. , 0. , 0. , 0. ], - [ 0. , 0. , 0. , 0. , 0.25, 0. , 0. , 0.25, 0.25, - 0. , 0. , 0.25, 0. , 0. , 0. , 0. ], - [ 0. , 0. , 0. , 0. , 0. , 0.25, 0.25, 0. , 0. , - 0.25, 0.25, 0. , 0. , 0. , 0. , 0. ]]) + self.O = lat2W(2, 2) + self.D = lat2W(2, 2) + self.ODW = np.array( + [ + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + ], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + ], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + ], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + ], + [ + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + ], + [ + 0.25, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.25, + ], + [ + 0.25, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.25, + ], + [ + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + ], + [ + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + ], + [ + 0.25, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.25, + ], + [ + 0.25, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.25, + ], + [ + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + ], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + ], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + ], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + ], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.25, + 0.25, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + ], + ] + ) def test_ODW_full(self): W = ODW(self.O, self.D) np.testing.assert_allclose(self.ODW, W.full()[0]) + class TestNetW(unittest.TestCase): def setUp(self): - self.link_list = [('a', 'b'), ('a', 'c'), ('a', 'd'), - ('b', 'a'), ('b', 'c'), ('b', 'd'), - ('c', 'a'), ('c', 'b'), ('c', 'd'), - ('d', 'a'), ('d', 'b'), ('d', 'c')] - - self._all = np.array([ - [ 0., 1., 1., 1., 1., 1., 1., 1., 0., 1., 1., 0.], - [ 1., 0., 1., 1., 1., 0., 1., 1., 1., 1., 0., 1.], - [ 1., 1., 0., 1., 0., 1., 1., 0., 1., 1., 1., 1.], - [ 1., 1., 1., 0., 1., 1., 1., 1., 0., 1., 1., 0.], - [ 1., 1., 0., 1., 0., 1., 1., 1., 1., 0., 1., 1.], - [ 1., 0., 1., 1., 1., 0., 0., 1., 1., 1., 1., 1.], - [ 1., 1., 1., 1., 1., 0., 0., 1., 1., 1., 0., 1.], - [ 1., 1., 0., 1., 1., 1., 1., 0., 1., 0., 1., 1.], - [ 0., 1., 1., 0., 1., 1., 1., 1., 0., 1., 1., 1.], - [ 1., 1., 1., 1., 0., 1., 1., 0., 1., 0., 1., 1.], - [ 1., 0., 1., 1., 1., 1., 0., 1., 1., 1., 0., 1.], - [ 0., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1., 0.]]) - - - self.O = np.array([ - [ 0., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [ 1., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [ 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [ 0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0., 0.], - [ 0., 0., 0., 1., 0., 1., 0., 0., 0., 0., 0., 0.], - [ 0., 0., 0., 1., 1., 0., 0., 0., 0., 0., 0., 0.], - [ 0., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0.], - [ 0., 0., 0., 0., 0., 0., 1., 0., 1., 0., 0., 0.], - [ 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0.], - [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1.], - [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 1.], - [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 0.]]) - - - self.D = np.array([ - [ 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0.], - [ 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 1.], - [ 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0.], - [ 0., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0.], - [ 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.], - [ 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., 0., 0.], - [ 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., 0.], - [ 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.], - [ 0., 0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 0.], - [ 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 0., 0.], - [ 1., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0.], - [ 0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.]]) - - - self.OD = np.array([ - [ 0., 1., 1., 0., 0., 0., 0., 1., 0., 0., 1., 0.], - [ 1., 0., 1., 0., 1., 0., 0., 0., 0., 0., 0., 1.], - [ 1., 1., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0.], - [ 0., 0., 0., 0., 1., 1., 1., 0., 0., 1., 0., 0.], - [ 0., 1., 0., 1., 0., 1., 0., 0., 0., 0., 0., 1.], - [ 0., 0., 1., 1., 1., 0., 0., 0., 1., 0., 0., 0.], - [ 0., 0., 0., 1., 0., 0., 0., 1., 1., 1., 0., 0.], - [ 1., 0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 0.], - [ 0., 0., 1., 0., 0., 1., 1., 1., 0., 0., 0., 0.], - [ 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 1., 1.], - [ 1., 0., 0., 0., 0., 0., 0., 1., 0., 1., 0., 1.], - [ 0., 1., 0., 0., 1., 0., 0., 0., 0., 1., 1., 0.]]) - - - self.C = np.array([ - [ 0., 0., 0., 1., 1., 1., 0., 0., 0., 0., 0., 0.], - [ 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0.], - [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1.], - [ 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [ 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0.], - [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1.], - [ 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [ 0., 0., 0., 1., 1., 1., 0., 0., 0., 0., 0., 0.], - [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1.], - [ 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.], - [ 0., 0., 0., 1., 1., 1., 0., 0., 0., 0., 0., 0.], - [ 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0.]]) + self.link_list = [ + ("a", "b"), + ("a", "c"), + ("a", "d"), + ("b", "a"), + ("b", "c"), + ("b", "d"), + ("c", "a"), + ("c", "b"), + ("c", "d"), + ("d", "a"), + ("d", "b"), + ("d", "c"), + ] + + self._all = np.array( + [ + [0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0], + [1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0], + [1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0], + [1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0], + [1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0], + [1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0], + [0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0], + [1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0], + [0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0], + ] + ) + + self.O = np.array( + [ + [0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0], + ] + ) + + self.D = np.array( + [ + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0], + [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ] + ) + + self.OD = np.array( + [ + [0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0], + [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0], + [1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0], + [0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0], + [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0], + [0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0], + ] + ) + + self.C = np.array( + [ + [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0], + ] + ) self.edge_list = [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)] def test_netOD(self): - netW_OD = netW(self.link_list, share='OD') + netW_OD = netW(self.link_list, share="OD") np.testing.assert_allclose(netW_OD.full()[0], self.OD) - def test_netO(self): - netW_O = netW(self.link_list, share='O') + netW_O = netW(self.link_list, share="O") np.testing.assert_allclose(netW_O.full()[0], self.O) def test_netD(self): - netW_D = netW(self.link_list, share='D') + netW_D = netW(self.link_list, share="D") np.testing.assert_allclose(netW_D.full()[0], self.D) def test_netC(self): - netW_C = netW(self.link_list, share='C') + netW_C = netW(self.link_list, share="C") np.testing.assert_allclose(netW_C.full()[0], self.C) def test_net_all(self): - netW_all = netW(self.link_list, share='A') + netW_all = netW(self.link_list, share="A") np.testing.assert_allclose(netW_all.full()[0], self._all) def test_mat2L(self): - mat = np.array([[0,1,1],[1,0,1],[1,1,0]]) + mat = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]]) edge_list = mat2L(mat) self.assertEqual(edge_list, self.edge_list) + class TestVecW(unittest.TestCase): def setUp(self): - self.origin_x = np.array([2,6,9,2]) - self.origin_y = np.array([4,8,2,5]) - self.dest_x = np.array([9,1,6,3]) - self.dest_y = np.array([3,6,2,7]) - self.continuous = np.array([ - [ 0. , 0.09759001, 0.12598816, 0.13736056], - [ 0.09759001, 0. , 0.10783277, 0.18257419], - [ 0.12598816, 0.10783277, 0. , 0.10425721], - [ 0.13736056, 0.18257419, 0.10425721, 0. ]]) + self.origin_x = np.array([2, 6, 9, 2]) + self.origin_y = np.array([4, 8, 2, 5]) + self.dest_x = np.array([9, 1, 6, 3]) + self.dest_y = np.array([3, 6, 2, 7]) + self.continuous = np.array( + [ + [0.0, 0.09759001, 0.12598816, 0.13736056], + [0.09759001, 0.0, 0.10783277, 0.18257419], + [0.12598816, 0.10783277, 0.0, 0.10425721], + [0.13736056, 0.18257419, 0.10425721, 0.0], + ] + ) def test_vecW(self): - W = vecW(self.origin_x, self.origin_y, self.dest_x, self.dest_y, - threshold=np.inf, binary=False) + W = vecW( + self.origin_x, + self.origin_y, + self.dest_x, + self.dest_y, + threshold=np.inf, + binary=False, + ) np.testing.assert_allclose(self.continuous, W.full()[0]) - diff --git a/libpysal/weights/tests/test_user.py b/libpysal/weights/tests/test_user.py index 3d87fb52c..58f25fa15 100644 --- a/libpysal/weights/tests/test_user.py +++ b/libpysal/weights/tests/test_user.py @@ -8,20 +8,22 @@ class Testuser(unittest.TestCase): def test_min_threshold_dist_from_shapefile(self): - f = examples.get_path('columbus.shp') + f = examples.get_path("columbus.shp") min_d = user.min_threshold_dist_from_shapefile(f) self.assertAlmostEqual(min_d, 0.61886415807685413) - + def test_build_lattice_shapefile(self): of = "lattice.shp" user.build_lattice_shapefile(20, 20, of) w = Rook.from_shapefile(of) self.assertEqual(w.n, 400) - os.remove('lattice.shp') - os.remove('lattice.shx') + os.remove("lattice.shp") + os.remove("lattice.shx") + suite = unittest.TestLoader().loadTestsFromTestCase(Testuser) -if __name__ == '__main__': + +if __name__ == "__main__": runner = unittest.TextTestRunner() runner.run(suite) diff --git a/libpysal/weights/tests/test_util.py b/libpysal/weights/tests/test_util.py index a0cc69890..f8b7c03ca 100644 --- a/libpysal/weights/tests/test_util.py +++ b/libpysal/weights/tests/test_util.py @@ -12,6 +12,7 @@ try: import geopandas as gpd + HAS_GEOPANDAS = True except: HAS_GEOPANDAS = False @@ -19,10 +20,9 @@ class Testutil(unittest.TestCase): def setUp(self): - self.w = Rook.from_shapefile( - examples.get_path('10740.shp')) + self.w = Rook.from_shapefile(examples.get_path("10740.shp")) - self.rio = examples.load_example('Rio Grande do Sul') + self.rio = examples.load_example("Rio Grande do Sul") def test_lat2W(self): w9 = lat2W(3, 3) @@ -51,26 +51,61 @@ def test_block_weights(self): regimes = np.ones(25) regimes[list(range(10, 20))] = 2 regimes[list(range(21, 25))] = 3 - regimes = np.array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., - 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 1., 3., 3., - 3., 3.]) + regimes = np.array( + [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 2.0, + 2.0, + 2.0, + 2.0, + 2.0, + 2.0, + 2.0, + 2.0, + 2.0, + 2.0, + 1.0, + 3.0, + 3.0, + 3.0, + 3.0, + ] + ) w = util.block_weights(regimes) ww0 = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] self.assertEqual(w.weights[0], ww0) wn0 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 20] self.assertEqual(w.neighbors[0], wn0) - regimes = ['n', 'n', 's', 's', 'e', 'e', 'w', 'w', 'e'] + regimes = ["n", "n", "s", "s", "e", "e", "w", "w", "e"] w = util.block_weights(regimes) - wn = {0: [1], 1: [0], 2: [3], 3: [2], 4: [5, 8], 5: [4, 8], - 6: [7], 7: [6], 8: [4, 5]} + wn = { + 0: [1], + 1: [0], + 2: [3], + 3: [2], + 4: [5, 8], + 5: [4, 8], + 6: [7], + 7: [6], + 8: [4, 5], + } self.assertEqual(w.neighbors, wn) - ids = ['id-%i'%i for i in range(len(regimes))] + ids = ["id-%i" % i for i in range(len(regimes))] w = util.block_weights(regimes, ids=np.array(ids)) - w0 = {'id-1': 1.0} - self.assertEqual(w['id-0'], w0) + w0 = {"id-1": 1.0} + self.assertEqual(w["id-0"], w0) w = util.block_weights(regimes, ids=ids) - w0 = {'id-1': 1.0} - self.assertEqual(w['id-0'], w0) + w0 = {"id-1": 1.0} + self.assertEqual(w["id-0"], w0) def test_comb(self): x = list(range(4)) @@ -100,10 +135,10 @@ def test_higher_order(self): self.assertEqual(w5_20, w5_2[0]) def test_higher_order_classes(self): - wdb = DistanceBand.from_shapefile(examples.get_path('baltim.shp'), 34) - wknn = KNN.from_shapefile(examples.get_path('baltim.shp'), 10) - wrook = Rook.from_shapefile(examples.get_path('columbus.shp')) - wqueen = Queen.from_shapefile(examples.get_path('columbus.shp')) + wdb = DistanceBand.from_shapefile(examples.get_path("baltim.shp"), 34) + wknn = KNN.from_shapefile(examples.get_path("baltim.shp"), 10) + wrook = Rook.from_shapefile(examples.get_path("columbus.shp")) + wqueen = Queen.from_shapefile(examples.get_path("columbus.shp")) wsparse = wqueen.sparse ww = W(wknn.neighbors, wknn.weights) util.higher_order(wdb, 2) @@ -112,14 +147,12 @@ def test_higher_order_classes(self): util.higher_order(wqueen, 5) util.higher_order(wsparse, 2) util.higher_order(ww, 2) - ww.transform = 'r' + ww.transform = "r" wsparse_notbinary = wrook.sparse with self.assertRaises(ValueError): util.higher_order(wsparse, 2) util.higher_order(ww, 3) - - def test_shimbel(self): w5 = lat2W() w5_shimbel = util.shimbel(w5) @@ -129,14 +162,17 @@ def test_shimbel(self): self.assertEqual(w5_shimbel004, w5_shimbel[0][0:4]) def test_full(self): - neighbors = {'first': ['second'], 'second': ['first', - 'third'], 'third': ['second']} - weights = {'first': [1], 'second': [1, 1], 'third': [1]} + neighbors = { + "first": ["second"], + "second": ["first", "third"], + "third": ["second"], + } + weights = {"first": [1], "second": [1, 1], "third": [1]} w = W(neighbors, weights) wf, ids = util.full(w) - wfo = np.array([[0., 1., 0.], [1., 0., 1.], [0., 1., 0.]]) + wfo = np.array([[0.0, 1.0, 0.0], [1.0, 0.0, 1.0], [0.0, 1.0, 0.0]]) np.testing.assert_array_almost_equal(wfo, wf, decimal=8) - idso = ['first', 'second', 'third'] + idso = ["first", "second", "third"] self.assertEqual(idso, ids) def test_full2W(self): @@ -147,7 +183,7 @@ def test_full2W(self): a[i, j] = np.random.random(1) w = util.full2W(a) np.testing.assert_array_equal(w.full()[0], a) - ids = ['myID0', 'myID1', 'myID2', 'myID3'] + ids = ["myID0", "myID1", "myID2", "myID3"] w = util.full2W(a, ids=ids) np.testing.assert_array_equal(w.full()[0], a) w.full()[0] == a @@ -158,12 +194,14 @@ def test_WSP2W(self): w = util.WSP2W(wsp) self.assertEqual(w.n, 10) self.assertEqual(w[0], {1: 1, 5: 1}) - w = psopen(examples.get_path('sids2.gal'), 'r').read() + w = psopen(examples.get_path("sids2.gal"), "r").read() wsp = WSP(w.sparse, w.id_order) w = util.WSP2W(wsp) self.assertEqual(w.n, 100) - self.assertEqual(w['37135'], {'37001': 1.0, '37033': 1.0, - '37037': 1.0, '37063': 1.0, '37145': 1.0}) + self.assertEqual( + w["37135"], + {"37001": 1.0, "37033": 1.0, "37037": 1.0, "37063": 1.0, "37145": 1.0}, + ) def test_insert_diagonal(self): w1 = util.insert_diagonal(self.w) @@ -183,35 +221,39 @@ def test_remap_ids(self): self.assertEqual(wid_order, w.id_order) wneighbors0 = [2, 1] self.assertEqual(wneighbors0, w.neighbors[0]) - old_to_new = {0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f'} + old_to_new = {0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f"} w_new = util.remap_ids(w, old_to_new) - w_newid_order = ['a', 'b', 'c', 'd', 'e', 'f'] + w_newid_order = ["a", "b", "c", "d", "e", "f"] self.assertEqual(w_newid_order, w_new.id_order) - w_newdneighborsa = ['c', 'b'] - self.assertEqual(w_newdneighborsa, w_new.neighbors['a']) + w_newdneighborsa = ["c", "b"] + self.assertEqual(w_newdneighborsa, w_new.neighbors["a"]) def test_get_ids_shp(self): - polyids = util.get_ids( - examples.get_path('columbus.shp'), "POLYID") + polyids = util.get_ids(examples.get_path("columbus.shp"), "POLYID") polyids5 = [1, 2, 3, 4, 5] self.assertEqual(polyids5, polyids[:5]) - @unittest.skipIf(not HAS_GEOPANDAS, "Missing geopandas, cannot test get_ids with gdf") + @unittest.skipIf( + not HAS_GEOPANDAS, "Missing geopandas; cannot test get_ids() with gdf." + ) def test_get_ids_gdf(self): - gdf = gpd.read_file(examples.get_path('columbus.shp')) + gdf = gpd.read_file(examples.get_path("columbus.shp")) polyids = util.get_ids(gdf, "POLYID") polyids5 = [1, 2, 3, 4, 5] self.assertEqual(polyids5, polyids[:5]) def test_get_points_array_from_shapefile(self): - xy = util.get_points_array_from_shapefile( - examples.get_path('juvenile.shp')) - xy3 = np.array([[94., 93.], [80., 95.], [79., 90.]]) + xy = util.get_points_array_from_shapefile(examples.get_path("juvenile.shp")) + xy3 = np.array([[94.0, 93.0], [80.0, 95.0], [79.0, 90.0]]) np.testing.assert_array_almost_equal(xy3, xy[:3], decimal=8) - xy = util.get_points_array_from_shapefile( - examples.get_path('columbus.shp')) - xy3 = np.array([[8.82721847, 14.36907602], [8.33265837, - 14.03162401], [9.01226541, 13.81971908]]) + xy = util.get_points_array_from_shapefile(examples.get_path("columbus.shp")) + xy3 = np.array( + [ + [8.82721847, 14.36907602], + [8.33265837, 14.03162401], + [9.01226541, 13.81971908], + ] + ) np.testing.assert_array_almost_equal(xy3, xy[:3], decimal=8) def test_min_threshold_distance(self): @@ -220,40 +262,77 @@ def test_min_threshold_distance(self): y.shape = (25, 1) data = np.hstack([x, y]) mint = 1.0 - self.assertEqual( - mint, util.min_threshold_distance(data)) + self.assertEqual(mint, util.min_threshold_distance(data)) def test_attach_islands(self): - w = Rook.from_shapefile(examples.get_path('10740.shp')) - w_knn1 = KNN.from_shapefile(examples.get_path('10740.shp'), k=1) + w = Rook.from_shapefile(examples.get_path("10740.shp")) + w_knn1 = KNN.from_shapefile(examples.get_path("10740.shp"), k=1) w_attach = util.attach_islands(w, w_knn1) self.assertEqual(w_attach.islands, []) self.assertEqual(w_attach[w.islands[0]], {166: 1.0}) - @unittest.skipIf(not HAS_GEOPANDAS, "Missing geopandas, cannot test nonplanar neighbors") + @unittest.skipIf( + not HAS_GEOPANDAS, "Missing geopandas; cannot test nonplanar neighbors." + ) def test_nonplanar_neighbors(self): - df = gpd.read_file(examples.get_path('map_RS_BR.shp')) + df = gpd.read_file(examples.get_path("map_RS_BR.shp")) w = Queen.from_dataframe(df) - self.assertEqual(w.islands, [0, 4, 23, 27, 80, 94, 101, 107, 109, 119, 122, 139, 169, 175, 223, 239, 247, 253, 254, 255, 256, 261, 276, 291, 294, 303, 321, 357, 374]) + self.assertEqual( + w.islands, + [ + 0, + 4, + 23, + 27, + 80, + 94, + 101, + 107, + 109, + 119, + 122, + 139, + 169, + 175, + 223, + 239, + 247, + 253, + 254, + 255, + 256, + 261, + 276, + 291, + 294, + 303, + 321, + 357, + 374, + ], + ) wnp = nonplanar_neighbors(w, df) self.assertEqual(wnp.islands, []) self.assertEqual(w.neighbors[0], []) self.assertEqual(wnp.neighbors[0], [23, 59, 152, 239]) self.assertEqual(wnp.neighbors[23], [0, 45, 59, 107, 152, 185, 246]) - @unittest.skipIf(not HAS_GEOPANDAS, "Missing geopandas, cannot test fuzzy_contiguity") + @unittest.skipIf( + not HAS_GEOPANDAS, "Missing geopandas; cannot test fuzzy contiguity." + ) def test_fuzzy_contiguity(self): - rs = examples.get_path('map_RS_BR.shp') + rs = examples.get_path("map_RS_BR.shp") rs_df = gpd.read_file(rs) wf = fuzzy_contiguity(rs_df) self.assertEqual(wf.islands, []) self.assertEqual(set(wf.neighbors[0]), set([239, 59, 152, 23, 107])) - buff = fuzzy_contiguity(rs_df, buffering=True, buffer=.1) + buff = fuzzy_contiguity(rs_df, buffering=True, buffer=0.1) self.assertEqual(set(buff.neighbors[0]), set([119, 239, 59, 152, 23, 107])) suite = unittest.TestLoader().loadTestsFromTestCase(Testutil) -if __name__ == '__main__': + +if __name__ == "__main__": runner = unittest.TextTestRunner() runner.run(suite) diff --git a/libpysal/weights/tests/test_weights.py b/libpysal/weights/tests/test_weights.py index b21a21e7d..fd1832a57 100644 --- a/libpysal/weights/tests/test_weights.py +++ b/libpysal/weights/tests/test_weights.py @@ -13,15 +13,32 @@ class TestW(unittest.TestCase): def setUp(self): - self.w = Rook.from_shapefile(examples.get_path('10740.shp'), - silence_warnings=True) - - self.neighbors = {0: [3, 1], 1: [0, 4, 2], 2: [1, 5], 3: [0, 6, 4], - 4: [1, 3, 7, 5], 5: [2, 4, 8], 6: [3, 7], - 7: [4, 6, 8], 8: [5, 7]} - self.weights = {0: [1, 1], 1: [1, 1, 1], 2: [1, 1], 3: [1, 1, 1], - 4: [1, 1, 1, 1], 5: [1, 1, 1], 6: [1, 1], - 7: [1, 1, 1], 8: [1, 1]} + self.w = Rook.from_shapefile( + examples.get_path("10740.shp"), silence_warnings=True + ) + + self.neighbors = { + 0: [3, 1], + 1: [0, 4, 2], + 2: [1, 5], + 3: [0, 6, 4], + 4: [1, 3, 7, 5], + 5: [2, 4, 8], + 6: [3, 7], + 7: [4, 6, 8], + 8: [5, 7], + } + self.weights = { + 0: [1, 1], + 1: [1, 1, 1], + 2: [1, 1], + 3: [1, 1, 1], + 4: [1, 1, 1, 1], + 5: [1, 1, 1], + 6: [1, 1], + 7: [1, 1, 1], + 8: [1, 1], + } self.w3x3 = util.lat2W(3, 3) @@ -30,8 +47,7 @@ def test_W(self): self.assertEqual(w.pct_nonzero, 29.62962962962963) def test___getitem__(self): - self.assertEqual( - self.w[0], {1: 1.0, 4: 1.0, 101: 1.0, 85: 1.0, 5: 1.0}) + self.assertEqual(self.w[0], {1: 1.0, 4: 1.0, 101: 1.0, 85: 1.0, 5: 1.0}) def test___init__(self): w = W(self.neighbors, self.weights, silence_warnings=True) @@ -47,48 +63,80 @@ def test___iter__(self): def test_asymmetries(self): w = lat2W(3, 3) - w.transform = 'r' + w.transform = "r" result = w.asymmetry() - self.assertEqual(result, [(0, 1), (0, 3), (1, 0), (1, 2), - (1, 4), (2, 1), (2, 5), (3, 0), - (3, 4), (3, 6), (4, 1), (4, 3), - (4, 5), (4, 7), (5, 2), (5, 4), - (5, 8), (6, 3), (6, 7), (7, 4), - (7, 6), (7, 8), (8, 5), (8, 7)]) + self.assertEqual( + result, + [ + (0, 1), + (0, 3), + (1, 0), + (1, 2), + (1, 4), + (2, 1), + (2, 5), + (3, 0), + (3, 4), + (3, 6), + (4, 1), + (4, 3), + (4, 5), + (4, 7), + (5, 2), + (5, 4), + (5, 8), + (6, 3), + (6, 7), + (7, 4), + (7, 6), + (7, 8), + (8, 5), + (8, 7), + ], + ) def test_asymmetry(self): w = lat2W(3, 3) self.assertEqual(w.asymmetry(), []) - w.transform = 'r' + w.transform = "r" self.assertFalse(w.asymmetry() == []) def test_cardinalities(self): w = lat2W(3, 3) - self.assertEqual(w.cardinalities, {0: 2, 1: 3, 2: 2, 3: 3, 4: 4, 5: 3, - 6: 2, 7: 3, 8: 2}) + self.assertEqual( + w.cardinalities, {0: 2, 1: 3, 2: 2, 3: 3, 4: 4, 5: 3, 6: 2, 7: 3, 8: 2} + ) def test_diagW2(self): - NPTA3E(self.w3x3.diagW2, np.array([2., 3., 2., 3., 4., 3., 2., - 3., 2.])) + NPTA3E( + self.w3x3.diagW2, np.array([2.0, 3.0, 2.0, 3.0, 4.0, 3.0, 2.0, 3.0, 2.0]) + ) def test_diagWtW(self): - NPTA3E(self.w3x3.diagW2, np.array([2., 3., 2., 3., 4., 3., 2., - 3., 2.])) + NPTA3E( + self.w3x3.diagW2, np.array([2.0, 3.0, 2.0, 3.0, 4.0, 3.0, 2.0, 3.0, 2.0]) + ) def test_diagWtW_WW(self): - NPTA3E(self.w3x3.diagWtW_WW, np.array([4., 6., 4., 6., 8., - 6., 4., 6., 4.])) + NPTA3E( + self.w3x3.diagWtW_WW, + np.array([4.0, 6.0, 4.0, 6.0, 8.0, 6.0, 4.0, 6.0, 4.0]), + ) def test_full(self): - wf = np.array([[0., 1., 0., 1., 0., 0., 0., 0., 0.], - [1., 0., 1., 0., 1., 0., 0., 0., 0.], - [0., 1., 0., 0., 0., 1., 0., 0., 0.], - [1., 0., 0., 0., 1., 0., 1., 0., 0.], - [0., 1., 0., 1., 0., 1., 0., 1., 0.], - [0., 0., 1., 0., 1., 0., 0., 0., 1.], - [0., 0., 0., 1., 0., 0., 0., 1., 0.], - [0., 0., 0., 0., 1., 0., 1., 0., 1.], - [0., 0., 0., 0., 0., 1., 0., 1., 0.]]) + wf = np.array( + [ + [0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0], + [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0], + ] + ) ids = list(range(9)) wf1, ids1 = self.w3x3.full() @@ -96,30 +144,64 @@ def test_full(self): self.assertEqual(ids1, ids) def test_get_transform(self): - self.assertEqual(self.w3x3.transform, 'O') - self.w3x3.transform = 'r' - self.assertEqual(self.w3x3.transform, 'R') - self.w3x3.transform = 'b' + self.assertEqual(self.w3x3.transform, "O") + self.w3x3.transform = "r" + self.assertEqual(self.w3x3.transform, "R") + self.w3x3.transform = "b" def test_higher_order(self): - weights = {0: [1.0, 1.0, 1.0], 1: [1.0, 1.0, 1.0], 2: [1.0, 1.0, 1.0], - 3: [1.0, 1.0, 1.0], 4: [1.0, 1.0, 1.0, 1.0], - 5: [1.0, 1.0, 1.0], 6: [1.0, 1.0, 1.0], 7: - [1.0, 1.0, 1.0], 8: [1.0, 1.0, 1.0]} - neighbors = {0: [4, 6, 2], 1: [3, 5, 7], 2: [8, 0, 4], 3: [7, 1, 5], - 4: [8, 0, 2, 6], 5: [1, 3, 7], 6: [4, 0, 8], 7: [3, 1, 5], - 8: [6, 2, 4]} - wneighbs = {k: {neighb: weights[k][i] for i, neighb in enumerate(v)} - for k, v in list(neighbors.items())} + weights = { + 0: [1.0, 1.0, 1.0], + 1: [1.0, 1.0, 1.0], + 2: [1.0, 1.0, 1.0], + 3: [1.0, 1.0, 1.0], + 4: [1.0, 1.0, 1.0, 1.0], + 5: [1.0, 1.0, 1.0], + 6: [1.0, 1.0, 1.0], + 7: [1.0, 1.0, 1.0], + 8: [1.0, 1.0, 1.0], + } + neighbors = { + 0: [4, 6, 2], + 1: [3, 5, 7], + 2: [8, 0, 4], + 3: [7, 1, 5], + 4: [8, 0, 2, 6], + 5: [1, 3, 7], + 6: [4, 0, 8], + 7: [3, 1, 5], + 8: [6, 2, 4], + } + wneighbs = { + k: {neighb: weights[k][i] for i, neighb in enumerate(v)} + for k, v in list(neighbors.items()) + } w2 = util.higher_order(self.w3x3, 2) - test_wneighbs = {k: {ne: weights[k][i] for i, ne in enumerate(v)} - for k, v in list(w2.neighbors.items())} + test_wneighbs = { + k: {ne: weights[k][i] for i, ne in enumerate(v)} + for k, v in list(w2.neighbors.items()) + } self.assertEqual(test_wneighbs, wneighbs) def test_histogram(self): - hist = [(0, 1), (1, 1), (2, 4), (3, 20), (4, 57), (5, 44), (6, 36), - (7, 15), (8, 7), (9, 1), (10, 6), (11, 0), (12, 2), (13, 0), - (14, 0), (15, 1)] + hist = [ + (0, 1), + (1, 1), + (2, 4), + (3, 20), + (4, 57), + (5, 44), + (6, 36), + (7, 15), + (8, 7), + (9, 1), + (10, 6), + (11, 0), + (12, 2), + (13, 0), + (14, 0), + (15, 1), + ] self.assertEqual(self.w.histogram, hist) def test_id2i(self): @@ -127,18 +209,22 @@ def test_id2i(self): self.assertEqual(self.w3x3.id2i, id2i) def test_id_order_set(self): - w = W(neighbors={'a': ['b'], 'b': ['a', 'c'], 'c': ['b']}) + w = W(neighbors={"a": ["b"], "b": ["a", "c"], "c": ["b"]}) self.assertFalse(w.id_order_set) def test_islands(self): - w = W(neighbors={'a': ['b'], 'b': ['a', 'c'], 'c': - ['b'], 'd': []}, silence_warnings=True) - self.assertEqual(w.islands, ['d']) + w = W( + neighbors={"a": ["b"], "b": ["a", "c"], "c": ["b"], "d": []}, + silence_warnings=True, + ) + self.assertEqual(w.islands, ["d"]) self.assertEqual(self.w3x3.islands, []) def test_max_neighbors(self): - w = W(neighbors={'a': ['b'], 'b': ['a', 'c'], 'c': - ['b'], 'd': []}, silence_warnings=True) + w = W( + neighbors={"a": ["b"], "b": ["a", "c"], "c": ["b"], "d": []}, + silence_warnings=True, + ) self.assertEqual(w.max_neighbors, 2) self.assertEqual(self.w3x3.max_neighbors, 4) @@ -155,15 +241,17 @@ def test_n(self): self.assertEqual(w.n, 25) def test_neighbor_offsets(self): - d = {0: [3, 1], - 1: [0, 4, 2], - 2: [1, 5], - 3: [0, 6, 4], - 4: [1, 3, 7, 5], - 5: [2, 4, 8], - 6: [3, 7], - 7: [4, 6, 8], - 8: [5, 7]} + d = { + 0: [3, 1], + 1: [0, 4, 2], + 2: [1, 5], + 3: [0, 6, 4], + 4: [1, 3, 7, 5], + 5: [2, 4, 8], + 6: [3, 7], + 7: [4, 6, 8], + 8: [5, 7], + } self.assertEqual(self.w3x3.neighbor_offsets, d) @@ -172,15 +260,17 @@ def test_nonzero(self): def test_order(self): w = util.lat2W(3, 3) - o = {0: [-1, 1, 2, 1, 2, 3, 2, 3, 0], - 1: [1, -1, 1, 2, 1, 2, 3, 2, 3], - 2: [2, 1, -1, 3, 2, 1, 0, 3, 2], - 3: [1, 2, 3, -1, 1, 2, 1, 2, 3], - 4: [2, 1, 2, 1, -1, 1, 2, 1, 2], - 5: [3, 2, 1, 2, 1, -1, 3, 2, 1], - 6: [2, 3, 0, 1, 2, 3, -1, 1, 2], - 7: [3, 2, 3, 2, 1, 2, 1, -1, 1], - 8: [0, 3, 2, 3, 2, 1, 2, 1, -1]} + o = { + 0: [-1, 1, 2, 1, 2, 3, 2, 3, 0], + 1: [1, -1, 1, 2, 1, 2, 3, 2, 3], + 2: [2, 1, -1, 3, 2, 1, 0, 3, 2], + 3: [1, 2, 3, -1, 1, 2, 1, 2, 3], + 4: [2, 1, 2, 1, -1, 1, 2, 1, 2], + 5: [3, 2, 1, 2, 1, -1, 3, 2, 1], + 6: [2, 3, 0, 1, 2, 3, -1, 1, 2], + 7: [3, 2, 3, 2, 1, 2, 1, -1, 1], + 8: [0, 3, 2, 3, 2, 1, 2, 1, -1], + } self.assertEqual(util.order(w), o) def test_pct_nonzero(self): @@ -196,8 +286,9 @@ def test_s2(self): self.assertEqual(self.w3x3.s2, 272.0) def test_s2array(self): - s2a = np.array([[16.], [36.], [16.], [36.], - [64.], [36.], [16.], [36.], [16.]]) + s2a = np.array( + [[16.0], [36.0], [16.0], [36.0], [64.0], [36.0], [16.0], [36.0], [16.0]] + ) NPTA3E(self.w3x3.s2array, s2a) def test_sd(self): @@ -205,75 +296,92 @@ def test_sd(self): def test_set_transform(self): w = util.lat2W(2, 2) - self.assertEqual(w.transform, 'O') + self.assertEqual(w.transform, "O") self.assertEqual(w.weights[0], [1.0, 1.0]) - w.transform = 'r' + w.transform = "r" self.assertEqual(w.weights[0], [0.5, 0.5]) def test_shimbel(self): - d = {0: [-1, 1, 2, 1, 2, 3, 2, 3, 4], - 1: [1, -1, 1, 2, 1, 2, 3, 2, 3], - 2: [2, 1, -1, 3, 2, 1, 4, 3, 2], - 3: [1, 2, 3, -1, 1, 2, 1, 2, 3], - 4: [2, 1, 2, 1, -1, 1, 2, 1, 2], - 5: [3, 2, 1, 2, 1, -1, 3, 2, 1], - 6: [2, 3, 4, 1, 2, 3, -1, 1, 2], - 7: [3, 2, 3, 2, 1, 2, 1, -1, 1], - 8: [4, 3, 2, 3, 2, 1, 2, 1, -1]} + d = { + 0: [-1, 1, 2, 1, 2, 3, 2, 3, 4], + 1: [1, -1, 1, 2, 1, 2, 3, 2, 3], + 2: [2, 1, -1, 3, 2, 1, 4, 3, 2], + 3: [1, 2, 3, -1, 1, 2, 1, 2, 3], + 4: [2, 1, 2, 1, -1, 1, 2, 1, 2], + 5: [3, 2, 1, 2, 1, -1, 3, 2, 1], + 6: [2, 3, 4, 1, 2, 3, -1, 1, 2], + 7: [3, 2, 3, 2, 1, 2, 1, -1, 1], + 8: [4, 3, 2, 3, 2, 1, 2, 1, -1], + } self.assertEqual(util.shimbel(self.w3x3), d) def test_sparse(self): self.assertEqual(self.w3x3.sparse.nnz, 24) def test_trcW2(self): - self.assertEqual(self.w3x3.trcW2, 24.) + self.assertEqual(self.w3x3.trcW2, 24.0) def test_trcWtW(self): - self.assertEqual(self.w3x3.trcWtW, 24.) + self.assertEqual(self.w3x3.trcWtW, 24.0) def test_trcWtW_WW(self): - self.assertEqual(self.w3x3.trcWtW_WW, 48.) + self.assertEqual(self.w3x3.trcWtW_WW, 48.0) def test_symmetrize(self): - symm = self.w.symmetrize() + symm = self.w.symmetrize() np.testing.assert_allclose(symm.sparse.toarray(), self.w.sparse.toarray()) - knn = KNN.from_shapefile(examples.get_path('baltim.shp'), k=10, - silence_warnings=True) + knn = KNN.from_shapefile( + examples.get_path("baltim.shp"), k=10, silence_warnings=True + ) sknn = knn.symmetrize() - assert (not np.allclose(knn.sparse.toarray(), sknn.sparse.toarray())) + assert not np.allclose(knn.sparse.toarray(), sknn.sparse.toarray()) np.testing.assert_allclose(sknn.sparse.toarray(), sknn.sparse.toarray().T) knn.symmetrize(inplace=True) np.testing.assert_allclose(sknn.sparse.toarray(), knn.sparse.toarray()) np.testing.assert_allclose(knn.sparse.toarray().T, knn.sparse.toarray()) def test_connected_components(self): - disco = {0: [1], - 1: [0], - 2: [3], - 3: [2]} + disco = {0: [1], 1: [0], 2: [3], 3: [2]} disco = W(disco) assert disco.n_components == 2 def test_roundtrip_write(self): - self.w.to_file('./tmp.gal') - new = W.from_file('./tmp.gal') - np.testing.assert_array_equal(self.w.sparse.toarray(), - new.sparse.toarray()) + self.w.to_file("./tmp.gal") + new = W.from_file("./tmp.gal") + np.testing.assert_array_equal(self.w.sparse.toarray(), new.sparse.toarray()) + class Test_WSP_Back_To_W(unittest.TestCase): # Test to make sure we get back to the same W functionality def setUp(self): - self.w = Rook.from_shapefile(examples.get_path('10740.shp'), - silence_warnings=True) + self.w = Rook.from_shapefile( + examples.get_path("10740.shp"), silence_warnings=True + ) wsp = self.w.to_WSP() self.w = wsp.to_W(silence_warnings=True) - self.neighbors = {0: [3, 1], 1: [0, 4, 2], 2: [1, 5], 3: [0, 6, 4], - 4: [1, 3, 7, 5], 5: [2, 4, 8], 6: [3, 7], - 7: [4, 6, 8], 8: [5, 7]} - self.weights = {0: [1, 1], 1: [1, 1, 1], 2: [1, 1], 3: [1, 1, 1], - 4: [1, 1, 1, 1], 5: [1, 1, 1], 6: [1, 1], 7: [1, 1, 1], - 8: [1, 1]} + self.neighbors = { + 0: [3, 1], + 1: [0, 4, 2], + 2: [1, 5], + 3: [0, 6, 4], + 4: [1, 3, 7, 5], + 5: [2, 4, 8], + 6: [3, 7], + 7: [4, 6, 8], + 8: [5, 7], + } + self.weights = { + 0: [1, 1], + 1: [1, 1, 1], + 2: [1, 1], + 3: [1, 1, 1], + 4: [1, 1, 1, 1], + 5: [1, 1, 1], + 6: [1, 1], + 7: [1, 1, 1], + 8: [1, 1], + } self.w3x3 = util.lat2W(3, 3) w3x3 = WSP(self.w3x3.sparse, self.w3x3.id_order) @@ -284,8 +392,7 @@ def test_W(self): self.assertEqual(w.pct_nonzero, 29.62962962962963) def test___getitem__(self): - self.assertEqual( - self.w[0], {1: 1.0, 4: 1.0, 101: 1.0, 85: 1.0, 5: 1.0}) + self.assertEqual(self.w[0], {1: 1.0, 4: 1.0, 101: 1.0, 85: 1.0, 5: 1.0}) def test___init__(self): w = W(self.neighbors, self.weights, silence_warnings=True) @@ -301,47 +408,80 @@ def test___iter__(self): def test_asymmetries(self): w = util.lat2W(3, 3) - w.transform = 'r' + w.transform = "r" result = w.asymmetry() - self.assertEqual(result, [(0, 1), (0, 3), (1, 0), (1, 2), (1, 4), - (2, 1), (2, 5), (3, 0), (3, 4), (3, 6), - (4, 1), (4, 3), (4, 5), (4, 7), (5, 2), - (5, 4), (5, 8), (6, 3), (6, 7), (7, 4), - (7, 6), (7, 8), (8, 5), (8, 7)]) + self.assertEqual( + result, + [ + (0, 1), + (0, 3), + (1, 0), + (1, 2), + (1, 4), + (2, 1), + (2, 5), + (3, 0), + (3, 4), + (3, 6), + (4, 1), + (4, 3), + (4, 5), + (4, 7), + (5, 2), + (5, 4), + (5, 8), + (6, 3), + (6, 7), + (7, 4), + (7, 6), + (7, 8), + (8, 5), + (8, 7), + ], + ) def test_asymmetry(self): w = util.lat2W(3, 3) self.assertEqual(w.asymmetry(), []) - w.transform = 'r' + w.transform = "r" self.assertFalse(w.asymmetry() == []) def test_cardinalities(self): w = util.lat2W(3, 3) - self.assertEqual(w.cardinalities, {0: 2, 1: 3, 2: 2, 3: 3, 4: 4, 5: 3, - 6: 2, 7: 3, 8: 2}) + self.assertEqual( + w.cardinalities, {0: 2, 1: 3, 2: 2, 3: 3, 4: 4, 5: 3, 6: 2, 7: 3, 8: 2} + ) def test_diagW2(self): - NPTA3E(self.w3x3.diagW2, np.array([2., 3., 2., 3., 4., 3., 2., - 3., 2.])) + NPTA3E( + self.w3x3.diagW2, np.array([2.0, 3.0, 2.0, 3.0, 4.0, 3.0, 2.0, 3.0, 2.0]) + ) def test_diagWtW(self): - NPTA3E(self.w3x3.diagW2, np.array([2., 3., 2., 3., 4., 3., 2., - 3., 2.])) + NPTA3E( + self.w3x3.diagW2, np.array([2.0, 3.0, 2.0, 3.0, 4.0, 3.0, 2.0, 3.0, 2.0]) + ) def test_diagWtW_WW(self): - NPTA3E(self.w3x3.diagWtW_WW, np.array([4., 6., 4., 6., 8., - 6., 4., 6., 4.])) + NPTA3E( + self.w3x3.diagWtW_WW, + np.array([4.0, 6.0, 4.0, 6.0, 8.0, 6.0, 4.0, 6.0, 4.0]), + ) def test_full(self): - wf = np.array([[0., 1., 0., 1., 0., 0., 0., 0., 0.], - [1., 0., 1., 0., 1., 0., 0., 0., 0.], - [0., 1., 0., 0., 0., 1., 0., 0., 0.], - [1., 0., 0., 0., 1., 0., 1., 0., 0.], - [0., 1., 0., 1., 0., 1., 0., 1., 0.], - [0., 0., 1., 0., 1., 0., 0., 0., 1.], - [0., 0., 0., 1., 0., 0., 0., 1., 0.], - [0., 0., 0., 0., 1., 0., 1., 0., 1.], - [0., 0., 0., 0., 0., 1., 0., 1., 0.]]) + wf = np.array( + [ + [0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0], + [0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0], + [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0], + ] + ) ids = list(range(9)) wf1, ids1 = self.w3x3.full() @@ -349,30 +489,64 @@ def test_full(self): self.assertEqual(ids1, ids) def test_get_transform(self): - self.assertEqual(self.w3x3.transform, 'O') - self.w3x3.transform = 'r' - self.assertEqual(self.w3x3.transform, 'R') - self.w3x3.transform = 'b' + self.assertEqual(self.w3x3.transform, "O") + self.w3x3.transform = "r" + self.assertEqual(self.w3x3.transform, "R") + self.w3x3.transform = "b" def test_higher_order(self): - weights = {0: [1.0, 1.0, 1.0], 1: [1.0, 1.0, 1.0], 2: [1.0, 1.0, 1.0], - 3: [1.0, 1.0, 1.0], 4: [1.0, 1.0, 1.0, 1.0], - 5: [1.0, 1.0, 1.0], 6: [1.0, 1.0, 1.0], - 7: [1.0, 1.0, 1.0], 8: [1.0, 1.0, 1.0]} - neighbors = {0: [4, 6, 2], 1: [3, 5, 7], 2: [8, 0, 4], 3: [7, 1, 5], - 4: [8, 0, 2, 6], 5: [1, 3, 7], 6: [4, 0, 8], 7: [3, 1, 5], - 8: [6, 2, 4]} - wneighbs = {k: {neighb: weights[k][i] for i, neighb in enumerate(v)} - for k, v in list(neighbors.items())} + weights = { + 0: [1.0, 1.0, 1.0], + 1: [1.0, 1.0, 1.0], + 2: [1.0, 1.0, 1.0], + 3: [1.0, 1.0, 1.0], + 4: [1.0, 1.0, 1.0, 1.0], + 5: [1.0, 1.0, 1.0], + 6: [1.0, 1.0, 1.0], + 7: [1.0, 1.0, 1.0], + 8: [1.0, 1.0, 1.0], + } + neighbors = { + 0: [4, 6, 2], + 1: [3, 5, 7], + 2: [8, 0, 4], + 3: [7, 1, 5], + 4: [8, 0, 2, 6], + 5: [1, 3, 7], + 6: [4, 0, 8], + 7: [3, 1, 5], + 8: [6, 2, 4], + } + wneighbs = { + k: {neighb: weights[k][i] for i, neighb in enumerate(v)} + for k, v in list(neighbors.items()) + } w2 = util.higher_order(self.w3x3, 2) - test_wneighbs = {k: {ne: w2.weights[k][i] for i, ne in enumerate(v)} - for k, v in list(w2.neighbors.items())} + test_wneighbs = { + k: {ne: w2.weights[k][i] for i, ne in enumerate(v)} + for k, v in list(w2.neighbors.items()) + } self.assertEqual(test_wneighbs, wneighbs) def test_histogram(self): - hist = [(0, 1), (1, 1), (2, 4), (3, 20), (4, 57), (5, 44), (6, 36), - (7, 15), (8, 7), (9, 1), (10, 6), (11, 0), (12, 2), (13, 0), - (14, 0), (15, 1)] + hist = [ + (0, 1), + (1, 1), + (2, 4), + (3, 20), + (4, 57), + (5, 44), + (6, 36), + (7, 15), + (8, 7), + (9, 1), + (10, 6), + (11, 0), + (12, 2), + (13, 0), + (14, 0), + (15, 1), + ] self.assertEqual(self.w.histogram, hist) def test_id2i(self): @@ -380,18 +554,22 @@ def test_id2i(self): self.assertEqual(self.w3x3.id2i, id2i) def test_id_order_set(self): - w = W(neighbors={'a': ['b'], 'b': ['a', 'c'], 'c': ['b']}) + w = W(neighbors={"a": ["b"], "b": ["a", "c"], "c": ["b"]}) self.assertFalse(w.id_order_set) def test_islands(self): - w = W(neighbors={'a': ['b'], 'b': ['a', 'c'], 'c': - ['b'], 'd': []}, silence_warnings=True) - self.assertEqual(w.islands, ['d']) + w = W( + neighbors={"a": ["b"], "b": ["a", "c"], "c": ["b"], "d": []}, + silence_warnings=True, + ) + self.assertEqual(w.islands, ["d"]) self.assertEqual(self.w3x3.islands, []) def test_max_neighbors(self): - w = W(neighbors={'a': ['b'], 'b': ['a', 'c'], 'c': - ['b'], 'd': []}, silence_warnings=True) + w = W( + neighbors={"a": ["b"], "b": ["a", "c"], "c": ["b"], "d": []}, + silence_warnings=True, + ) self.assertEqual(w.max_neighbors, 2) self.assertEqual(self.w3x3.max_neighbors, 4) @@ -412,15 +590,17 @@ def test_nonzero(self): def test_order(self): w = util.lat2W(3, 3) - o = {0: [-1, 1, 2, 1, 2, 3, 2, 3, 0], - 1: [1, -1, 1, 2, 1, 2, 3, 2, 3], - 2: [2, 1, -1, 3, 2, 1, 0, 3, 2], - 3: [1, 2, 3, -1, 1, 2, 1, 2, 3], - 4: [2, 1, 2, 1, -1, 1, 2, 1, 2], - 5: [3, 2, 1, 2, 1, -1, 3, 2, 1], - 6: [2, 3, 0, 1, 2, 3, -1, 1, 2], - 7: [3, 2, 3, 2, 1, 2, 1, -1, 1], - 8: [0, 3, 2, 3, 2, 1, 2, 1, -1]} + o = { + 0: [-1, 1, 2, 1, 2, 3, 2, 3, 0], + 1: [1, -1, 1, 2, 1, 2, 3, 2, 3], + 2: [2, 1, -1, 3, 2, 1, 0, 3, 2], + 3: [1, 2, 3, -1, 1, 2, 1, 2, 3], + 4: [2, 1, 2, 1, -1, 1, 2, 1, 2], + 5: [3, 2, 1, 2, 1, -1, 3, 2, 1], + 6: [2, 3, 0, 1, 2, 3, -1, 1, 2], + 7: [3, 2, 3, 2, 1, 2, 1, -1, 1], + 8: [0, 3, 2, 3, 2, 1, 2, 1, -1], + } self.assertEqual(util.order(w), o) def test_pct_nonzero(self): @@ -436,8 +616,9 @@ def test_s2(self): self.assertEqual(self.w3x3.s2, 272.0) def test_s2array(self): - s2a = np.array([[16.], [36.], [16.], [36.], - [64.], [36.], [16.], [36.], [16.]]) + s2a = np.array( + [[16.0], [36.0], [16.0], [36.0], [64.0], [36.0], [16.0], [36.0], [16.0]] + ) NPTA3E(self.w3x3.s2array, s2a) def test_sd(self): @@ -445,34 +626,36 @@ def test_sd(self): def test_set_transform(self): w = util.lat2W(2, 2) - self.assertEqual(w.transform, 'O') + self.assertEqual(w.transform, "O") self.assertEqual(w.weights[0], [1.0, 1.0]) - w.transform = 'r' + w.transform = "r" self.assertEqual(w.weights[0], [0.5, 0.5]) def test_shimbel(self): - d = {0: [-1, 1, 2, 1, 2, 3, 2, 3, 4], - 1: [1, -1, 1, 2, 1, 2, 3, 2, 3], - 2: [2, 1, -1, 3, 2, 1, 4, 3, 2], - 3: [1, 2, 3, -1, 1, 2, 1, 2, 3], - 4: [2, 1, 2, 1, -1, 1, 2, 1, 2], - 5: [3, 2, 1, 2, 1, -1, 3, 2, 1], - 6: [2, 3, 4, 1, 2, 3, -1, 1, 2], - 7: [3, 2, 3, 2, 1, 2, 1, -1, 1], - 8: [4, 3, 2, 3, 2, 1, 2, 1, -1]} + d = { + 0: [-1, 1, 2, 1, 2, 3, 2, 3, 4], + 1: [1, -1, 1, 2, 1, 2, 3, 2, 3], + 2: [2, 1, -1, 3, 2, 1, 4, 3, 2], + 3: [1, 2, 3, -1, 1, 2, 1, 2, 3], + 4: [2, 1, 2, 1, -1, 1, 2, 1, 2], + 5: [3, 2, 1, 2, 1, -1, 3, 2, 1], + 6: [2, 3, 4, 1, 2, 3, -1, 1, 2], + 7: [3, 2, 3, 2, 1, 2, 1, -1, 1], + 8: [4, 3, 2, 3, 2, 1, 2, 1, -1], + } self.assertEqual(util.shimbel(self.w3x3), d) def test_sparse(self): self.assertEqual(self.w3x3.sparse.nnz, 24) def test_trcW2(self): - self.assertEqual(self.w3x3.trcW2, 24.) + self.assertEqual(self.w3x3.trcW2, 24.0) def test_trcWtW(self): - self.assertEqual(self.w3x3.trcWtW, 24.) + self.assertEqual(self.w3x3.trcWtW, 24.0) def test_trcWtW_WW(self): - self.assertEqual(self.w3x3.trcWtW_WW, 48.) + self.assertEqual(self.w3x3.trcWtW_WW, 48.0) class TestWSP(unittest.TestCase): @@ -486,18 +669,21 @@ def test_WSP(self): self.assertEqual(self.w.id_order, self.wsp.id_order) self.assertEqual(self.w.n, self.wsp.n) np.testing.assert_array_equal( - self.w.sparse.todense(), self.wsp.sparse.todense()) + self.w.sparse.todense(), self.wsp.sparse.todense() + ) def test_diagWtW_WW(self): - NPTA3E(self.w3x3.diagWtW_WW, np.array([4., 6., 4., 6., 8., - 6., 4., 6., 4.])) + NPTA3E( + self.w3x3.diagWtW_WW, + np.array([4.0, 6.0, 4.0, 6.0, 8.0, 6.0, 4.0, 6.0, 4.0]), + ) def test_trcWtW_WW(self): - self.assertEqual(self.w3x3.trcWtW_WW, 48.) + self.assertEqual(self.w3x3.trcWtW_WW, 48.0) def test_s0(self): self.assertEqual(self.w3x3.s0, 24.0) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/libpysal/weights/tests/test_weights_IO.py b/libpysal/weights/tests/test_weights_IO.py index 5a26ee214..cafa534ce 100644 --- a/libpysal/weights/tests/test_weights_IO.py +++ b/libpysal/weights/tests/test_weights_IO.py @@ -3,36 +3,32 @@ import tempfile, os - - - class TestWIO(unittest.TestCase): def setUp(self): - self.swmFile1 = libpysal.examples.get_path('ohio.swm') - self.swmFile2 = libpysal.examples.get_path('us48_CONTIGUITY_EDGES_ONLY.swm') - self.swmFile3 = libpysal.examples.get_path('us48_INVERSE_DISTANCE.swm') + self.swmFile1 = libpysal.examples.get_path("ohio.swm") + self.swmFile2 = libpysal.examples.get_path("us48_CONTIGUITY_EDGES_ONLY.swm") + self.swmFile3 = libpysal.examples.get_path("us48_INVERSE_DISTANCE.swm") self.files = [self.swmFile1, self.swmFile2, self.swmFile3] - def test_SWMIO(self): for file in self.files: f1 = libpysal.io.open(file) w1 = f1.read() - f = tempfile.NamedTemporaryFile(suffix='.swm') + f = tempfile.NamedTemporaryFile(suffix=".swm") fname = f.name f.close() - f2 = libpysal.io.open(fname, 'w') + f2 = libpysal.io.open(fname, "w") f2.varName = f1.varName f2.srs = f1.srs f2.write(w1) f2.close() - w2 = libpysal.io.open(fname, 'r').read() + w2 = libpysal.io.open(fname, "r").read() assert w1.pct_nonzero == w2.pct_nonzero os.remove(fname) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() From dafea7f1270fac38e7b41df2e972a522ddbe6ed0 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Sun, 16 Aug 2020 11:52:35 -0400 Subject: [PATCH 09/33] update libpysal/weights/user.py --- libpysal/weights/user.py | 119 ++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/libpysal/weights/user.py b/libpysal/weights/user.py index eff8a3193..28bb4019c 100644 --- a/libpysal/weights/user.py +++ b/libpysal/weights/user.py @@ -3,71 +3,73 @@ contiguity and distance criteria. """ -__author__ = "Sergio J. Rey " +__author__ = "Sergio J. Rey " from .util import get_points_array_from_shapefile, min_threshold_distance from ..io.fileio import FileIO as ps_open from .. import cg import numpy as np -__all__ = ['min_threshold_dist_from_shapefile', 'build_lattice_shapefile', 'spw_from_gal'] +__all__ = [ + "min_threshold_dist_from_shapefile", + "build_lattice_shapefile", + "spw_from_gal", +] + def spw_from_gal(galfile): - """ - Sparse scipy matrix for w from a gal file. + """Sparse ``scipy`` matrix for w from a ``.gal`` file. Parameters ---------- - - galfile : string - name of gal file including suffix + galfile : str + The name of a ``.gal`` file including the file extension. Returns ------- - - spw : sparse_matrix - scipy sparse matrix in CSR format - - ids : array - identifiers for rows/cols of spw + spw : libpysal.weights.weights.WSP + The sparse matrix in CSR format (``scipy.sparse.csr.csr_matrix``) can + be accessed through ``spw.sparse``. Examples -------- + >>> import libpysal >>> spw = libpysal.weights.spw_from_gal(libpysal.examples.get_path("sids2.gal")) + + The number of all stored values in ``spw``: + >>> spw.sparse.nnz 462 """ - return ps_open(galfile, 'r').read(sparse=True) + return ps_open(galfile, "r").read(sparse=True) + def min_threshold_dist_from_shapefile(shapefile, radius=None, p=2): - """ - Get the maximum nearest neighbor distance between observations in the - shapefile. + """Get the maximum nearest neighbor distance + between observations in the shapefile. Parameters ---------- - shapefile : string - shapefile name with shp suffix. - radius : float - If supplied arc_distances will be calculated - based on the given radius. p will be ignored. - p : float - Minkowski p-norm distance metric parameter: - 1<=p<=infinity - 2: Euclidean distance - 1: Manhattan distance + shapefile : str + The shapefile name including the ``.shp`` file extension. + radius : float + If supplied ``arc_distances`` will be calculated + based on the given radius and ``p`` will be ignored. + p : float + Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. + ``2`` is Euclidean distance and ``1`` is Manhattan distance. Default is ``2 ``. Returns ------- - d : float - Maximum nearest neighbor distance between the n - observations. + d : float + The maximum nearest neighbor distance between the ``n`` observations. Examples -------- + >>> import libpysal >>> md = libpysal.weights.min_threshold_dist_from_shapefile(libpysal.examples.get_path("columbus.shp")) >>> md @@ -77,12 +79,14 @@ def min_threshold_dist_from_shapefile(shapefile, radius=None, p=2): Notes ----- - Supports polygon or point shapefiles. For polygon shapefiles, distance is - based on polygon centroids. Distances are defined using coordinates in - shapefile which are assumed to be projected and not geographical - coordinates. + + This function supports polygon or point shapefiles. For polygon + shapefiles, distance is based on polygon centroids. Distances are + defined using coordinates from the shapefile which are assumed to + be projected and not geographical coordinates. """ + points = get_points_array_from_shapefile(shapefile) if radius is not None: kdt = cg.kdtree.Arc_KDTree(points, radius=radius) @@ -92,32 +96,27 @@ def min_threshold_dist_from_shapefile(shapefile, radius=None, p=2): return min_threshold_distance(points, p) -def build_lattice_shapefile(nrows, ncols, outFileName): - """ - Build a lattice shapefile with nrows rows and ncols cols. +def build_lattice_shapefile(nrows, ncols, out_file_name): + """Build a lattice shapefile with ``nrows`` rows and ``ncols`` columns. Parameters ---------- - - nrows : int - Number of rows - ncols : int - Number of cols - outFileName : str - shapefile name with shp suffix - - Returns - ------- - None + nrows : int + The number of rows. + ncols : int + The number of columns. + out_file_name : str + The shapefile name including the ``.shp`` file extension. """ - if not outFileName.endswith('.shp'): - raise ValueError("outFileName must end with .shp") - o = ps_open(outFileName, 'w') - dbf_name = outFileName.split(".")[0] + ".dbf" - d = ps_open(dbf_name, 'w') - d.header = [ 'ID' ] - d.field_spec = [ ('N', 8, 0) ] + + if not out_file_name.endswith(".shp"): + raise ValueError("``out_file_name`` must end with .shp") + o = ps_open(out_file_name, "w") + dbf_name = out_file_name.split(".")[0] + ".dbf" + d = ps_open(dbf_name, "w") + d.header = ["ID"] + d.field_spec = [("N", 8, 0)] c = 0 for i in range(ncols): for j in range(nrows): @@ -131,14 +130,18 @@ def build_lattice_shapefile(nrows, ncols, outFileName): d.close() o.close() + def _test(): import doctest + # the following line could be used to define an alternative to the '' flag - #doctest.BLANKLINE_MARKER = 'something better than ' - start_suppress = np.get_printoptions()['suppress'] + # doctest.BLANKLINE_MARKER = 'something better than ' + + start_suppress = np.get_printoptions()["suppress"] np.set_printoptions(suppress=True) doctest.testmod() np.set_printoptions(suppress=start_suppress) -if __name__ == '__main__': + +if __name__ == "__main__": _test() From bdca6df27d17e13e83db74611abb7b86fecd7351 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Sun, 16 Aug 2020 13:11:51 -0400 Subject: [PATCH 10/33] update libpysal/weights/spatial_lag.py --- libpysal/weights/contiguity.py | 8 +- libpysal/weights/spatial_lag.py | 220 +++++++++++++++++--------------- 2 files changed, 121 insertions(+), 107 deletions(-) diff --git a/libpysal/weights/contiguity.py b/libpysal/weights/contiguity.py index 4c7c41ff5..bcc6c5c7e 100644 --- a/libpysal/weights/contiguity.py +++ b/libpysal/weights/contiguity.py @@ -75,7 +75,7 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): Returns ------- - w : W + w : libpysal.weights.weights.W An instance of spatial weights. Examples @@ -248,7 +248,7 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): Returns ------- - w : W + w : libpysal.weights.weights.W An instance of spatial weights. Examples @@ -394,7 +394,7 @@ def Voronoi(points, criterion="rook", clip="ahull", **kwargs): Returns ------- - w : W + w : libpysal.weights.weights.W An instance of spatial weights. Examples @@ -435,7 +435,7 @@ def _from_dataframe(df, **kwargs): Returns ------- - w : W + w : libpysal.weights.weights.W An instance of spatial weights. Notes diff --git a/libpysal/weights/spatial_lag.py b/libpysal/weights/spatial_lag.py index 4c40ec784..1e73ae40a 100644 --- a/libpysal/weights/spatial_lag.py +++ b/libpysal/weights/spatial_lag.py @@ -1,37 +1,38 @@ +"""Spatial lag operations. """ -Spatial lag operations. -""" -__author__ = "Sergio J. Rey , David C. Folch , Levi John Wolf ," + "David C. Folch ," + "Levi John Wolf " +) +__all__ = ["lag_spatial", "lag_categorical"] import numpy as np -def lag_spatial(w, y): - """ - Spatial lag operator. - If w is row standardized, returns the average of each observation's neighbors; - if not, returns the weighted sum of each observation's neighbors. +def lag_spatial(w, y): + """A spatial lag operator. If ``w`` is row standardized, this function + returns the average of each observation's neighbors. If it is not, the + weighted sum of each observation's neighbors is returned. Parameters ---------- - - w : W - libpysal spatial weightsobject - y : array - numpy array with dimensionality conforming to w (see examples) + w : libpysal.weights.weights.W + A PySAL spatial weights object. + y : array-like + A ``numpy`` array with dimensionality conforming to ``w`` (see examples). Returns ------- - - wy : array - array of numeric values for the spatial lag + wy : numpy.ndarray + An array of numeric values for the spatial lag. Examples -------- - Setup a 9x9 binary spatial weights matrix and vector of data; compute the - spatial lag of the vector. + Setup a 9x9 binary spatial weights matrix and vector of data, + then compute the spatial lag of the vector. >>> import libpysal >>> import numpy as np @@ -41,7 +42,7 @@ def lag_spatial(w, y): >>> yl array([ 4., 6., 6., 10., 16., 14., 10., 18., 12.]) - Row standardize the weights matrix and recompute the spatial lag + Row standardize the weights matrix and recompute the spatial lag. >>> w.transform = 'r' >>> yl = libpysal.weights.lag_spatial(w, y) @@ -50,7 +51,7 @@ def lag_spatial(w, y): 4.66666667, 5. , 6. , 6. ]) - Explicitly define data vector as 9x1 and recompute the spatial lag + Explicitly define data vector as 9x1 and recompute the spatial lag. >>> y.shape = (9, 1) >>> yl = libpysal.weights.lag_spatial(w, y) @@ -66,7 +67,7 @@ def lag_spatial(w, y): [6. ]]) - Take the spatial lag of a 9x2 data matrix + Take the spatial lag of a 9x2 data matrix. >>> yr = np.arange(8, -1, -1) >>> yr.shape = (9, 1) @@ -84,48 +85,53 @@ def lag_spatial(w, y): [6. , 2. ]]) """ + return w.sparse * y -def lag_categorical(w, y, ties='tryself'): - """ - Spatial lag operator for categorical variables. - - Constructs the most common categories of neighboring observations, weighted - by their weight strength. +def lag_categorical(w, y, ties="tryself"): + """A spatial lag operator for categorical variables. This function + constructs the most common categories of neighboring observations + weighted by their weight strength. Parameters ---------- - - w : W - PySAL spatial weightsobject - y : iterable - iterable collection of categories (either int or - string) with dimensionality conforming to w (see examples) - ties : str - string describing the method to use when resolving - ties. By default, the option is "tryself", - and the category of the focal observation - is included with its neighbors to try - and break a tie. If this does not resolve the tie, - a winner is chosen randomly. To just use random choice to - break ties, pass "random" instead. + w : libpysal.weights.weights.W + PySAL spatial weights object. + y : iterable + An iterable collection of categories (either ``int`` or ``str``) + with dimensionality conforming to ``w`` (see examples). + ties : str + The method to use when resolving ties. By default, the option is ``'tryself'``, + and the category of the focal observation is included with its neighbors to try + and break a tie. If this does not resolve the tie, a winner is chosen randomly. + To just use random choice to break ties, pass ``'random'`` instead. + All supported options include: + 1. ``'tryself'``: Use the focal observation's label to tiebreak. + If this doesn't successfully break the tie, (which only occurs + if it induces a new tie), decide randomly.; + 2. ``'random'``: Resolve the tie randomly amongst winners.; + 3. ``'lowest'``: Pick the lowest-value label amongst winners.; + 4. ``'highest'``: Pick the highest-value label amongst winners. + Returns ------- - an (n x k) column vector containing the most common neighboring observation + output : numpy.ndarray + An (n x k) column vector containing the most common neighboring observation. Notes ----- - This works on any array where the number of unique elements along the column - axis is less than the number of elements in the array, for any dtype. - That means the routine should work on any dtype that np.unique() can - compare. + + This works on any array where the number of unique elements + along the column axis is less than the number of elements in + the array, for any ``dtype``. That means the routine should + work on any ``dtype`` that ``numpy.unique()`` can compare. Examples -------- - Set up a 9x9 weights matrix describing a 3x3 regular lattice. Lag one list of - categorical variables with no ties. + Set up a 9x9 weights matrix describing a 3x3 regular lattice. + Lag one list of categorical variables with no ties. >>> import libpysal >>> import numpy as np @@ -136,7 +142,7 @@ def lag_categorical(w, y, ties='tryself'): >>> np.array_equal(y_l, np.array(['b', 'a', 'b', 'c', 'b', 'c', 'b', 'c', 'b'])) True - Explicitly reshape y into a (9x1) array and calculate lag again + Explicitly reshape ``y`` into a (9x1) array and calculate lag again. >>> yvect = np.array(y).reshape(9,1) >>> yvect_l = libpysal.weights.lag_categorical(w,yvect) @@ -144,7 +150,7 @@ def lag_categorical(w, y, ties='tryself'): >>> np.array_equal(yvect_l, check) True - compute the lag of a 9x2 matrix of categories + Compute the lag of a 9x2 matrix of categories. >>> y2 = ['a', 'c', 'c', 'd', 'b', 'a', 'd', 'd', 'c'] >>> ym = np.vstack((y,y2)).T @@ -154,84 +160,92 @@ def lag_categorical(w, y, ties='tryself'): True """ + if isinstance(y, list): y = np.array(y) orig_shape = y.shape if len(orig_shape) > 1: if orig_shape[1] > 1: - return np.vstack([lag_categorical(w,col) for col in y.T]).T + return np.vstack([lag_categorical(w, col) for col in y.T]).T y = y.flatten() output = np.zeros_like(y) labels = np.unique(y) normalized_labels = np.zeros(y.shape, dtype=np.int) - for i,label in enumerate(labels): - normalized_labels[y == label] = i - for focal_name,neighbors in w: + for i, label in enumerate(labels): + normalized_labels[y == label] = i + for focal_name, neighbors in w: focal_idx = w.id2i[focal_name] neighborhood_tally = np.zeros(labels.shape) for neighb_name, weight in list(neighbors.items()): neighb_idx = w.id2i[neighb_name] neighb_label = normalized_labels[neighb_idx] neighborhood_tally[neighb_label] += weight - out_label_idx = _resolve_ties(focal_idx, normalized_labels, - neighborhood_tally, neighbors, ties, w) + out_label_idx = _resolve_ties( + focal_idx, normalized_labels, neighborhood_tally, neighbors, ties, w + ) output[focal_idx] = labels[out_label_idx] - return output.reshape(orig_shape) - -def _resolve_ties(idx,normalized_labels,tally,neighbors,method,w): - """ - Helper function to resolve ties if lag is multimodal + output = output.reshape(orig_shape) - first, if this function gets called when there's actually no tie, then the - correct value will be picked. + return output - if 'random' is selected as the method, a random tiebeaker is picked - if 'tryself' is selected, then the observation's own value will be used in - an attempt to break the tie, but if it fails, a random tiebreaker will be - selected. +def _resolve_ties(idx, normalized_labels, tally, neighbors, method, w): + """Helper function to resolve ties if lag is multimodal. First, if this function + gets called when there's actually no tie, then the correct value will be picked. + If ``'random'`` is selected as the method, a random tiebeaker is picked. If + ``'tryself'`` is selected, then the observation's own value will be used in an + attempt to break the tie, but if it fails, a random tiebreaker will be selected. - Arguments + Parameters --------- - idx : int - index (aligned with `normalized_labels`) of the - current observation being resolved. - normalized_labels : (n,) array of ints - normalized array of labels for each observation - tally : (p,) array of floats - current tally of neighbors' labels around `idx` to resolve. - neighbors : dict of (neighbor_name : weight) - the elements of the weights object, identical to w[idx] - method : string - configuration option to use a specific tiebreaking method. - supported options are: - 1. tryself: Use the focal observation's label to tiebreak. - If this doesn't successfully break the tie, - (which only occurs if it induces a new tie), - decide randomly. - 2. random: Resolve the tie randomly amongst winners. - 3. lowest: Pick the lowest-value label amongst winners. - 4. highest: Pick the highest-value label amongst winners. - w : pysal.W object - a PySAL weights object aligned with normalized_labels. + idx : int + The index (aligned with ``normalized_labels``) of + the current observation being resolved. + normalized_labels : numpy.ndarray + A `(n,)` normalized array of labels for each observation. + tally : numpy.ndarray + The current tally of `(p,)` neighbors' labels around ``idx`` to resolve. + neighbors : dict of (neighbor_name : weight) + The elements of the weights object (identical to ``w[idx]``) + in the form ``{neighbor_name : weight}``. + method : str + The configuration option to use a specific tiebreaking method. + See ``lag_categorical()`` for all supported options. + w : libpysal.weights.weights.W + A PySAL weights object aligned with ``normalized_labels``. Returns ------- - integer denoting which label to use to label the observation. + label : int + An integer denoting which label to use to label the observation. + """ - ties, = np.where(tally == tally.max()) #returns a tuple for flat arrays - if len(tally[tally==tally.max()]) <= 1: #no tie, pick the highest - return np.argmax(tally).astype(int) - elif method.lower() == 'random': #choose randomly from tally - return np.random.choice(np.squeeze(ties)).astype(int) - elif method.lower() == 'lowest': # pick lowest tied value - return ties[0].astype(int) - elif method.lower() == 'highest': #pick highest tied value - return ties[-1].astype(int) - elif method.lower() == 'tryself': # add self-label as observation, try again, random if fail + + m = method.lower() + + # returns a tuple for flat arrays + (ties,) = np.where(tally == tally.max()) + + # no tie, pick the highest + if len(tally[tally == tally.max()]) <= 1: + label = np.argmax(tally).astype(int) + # choose randomly from tally + elif m == "random": + label = np.random.choice(np.squeeze(ties)).astype(int) + # pick lowest tied value + elif m == "lowest": + label = ties[0].astype(int) + # pick highest tied value + elif m == "highest": + label = ties[-1].astype(int) + # add self-label as observation, try again, random if fail + elif m == "tryself": mean_neighbor_value = np.mean(list(neighbors.values())) tally[normalized_labels[idx]] += mean_neighbor_value - return _resolve_ties(idx,normalized_labels,tally,neighbors,'random', w) + label = _resolve_ties(idx, normalized_labels, tally, neighbors, "random", w) else: - raise KeyError('Tie-breaking method for categorical lag not recognized') + msg = "Tie-breaking method for categorical lag not recognized: %s" % m + raise KeyError(msg) + + return label From 31873264ebcfc35fcb79edc4e5a8f5d73642eb49 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Mon, 17 Aug 2020 09:24:39 -0400 Subject: [PATCH 11/33] intermediary commit for weights/distance.py --- libpysal/weights/distance.py | 553 +++++++++++++++++++++++------------ 1 file changed, 359 insertions(+), 194 deletions(-) diff --git a/libpysal/weights/distance.py b/libpysal/weights/distance.py index 2e9552cd0..9b92dec57 100644 --- a/libpysal/weights/distance.py +++ b/libpysal/weights/distance.py @@ -1,11 +1,19 @@ __all__ = ["KNN", "Kernel", "DistanceBand"] -__author__ = "Sergio J. Rey , Levi John Wolf " + +__author__ = ( + "Sergio J. Rey , Levi John Wolf " +) from ..cg.kdtree import KDTree from .weights import W, WSP -from .util import isKDTree, get_ids, get_points_array_from_shapefile,\ - get_points_array, WSP2W +from .util import ( + isKDTree, + get_ids, + get_points_array_from_shapefile, + get_points_array, + WSP2W, +) import copy from warnings import warn as Warn from scipy.spatial import distance_matrix @@ -13,45 +21,46 @@ import numpy as np -def knnW(data, k=2, p=2, ids=None, radius=None, distance_metric='euclidean'): - """ - This is deprecated. Use the pysal.weights.KNN class instead. - """ - #Warn('This function is deprecated. Please use pysal.weights.KNN', UserWarning) - return KNN(data, k=k, p=p, ids=ids, radius=radius, - distance_metric=distance_metric) +def knnW(data, k=2, p=2, ids=None, radius=None, distance_metric="euclidean"): + """This is deprecated. Use the ``pysal.weights.KNN class instead.``""" + # Warn('This function is deprecated. Please use pysal.weights.KNN', UserWarning) + return KNN(data, k=k, p=p, ids=ids, radius=radius, distance_metric=distance_metric) + class KNN(W): - """ - Creates nearest neighbor weights matrix based on k nearest - neighbors. + """Creates nearest neighbor weights matrix based on `k` nearest neighbors. Parameters ---------- - kdtree : object - PySAL KDTree or ArcKDTree where KDtree.data is array (n,k) - n observations on k characteristics used to measure - distances between the n objects - k : int - number of nearest neighbors - p : float - Minkowski p-norm distance metric parameter: - 1<=p<=infinity - 2: Euclidean distance - 1: Manhattan distance - Ignored if the KDTree is an ArcKDTree - ids : list - identifiers to attach to each observation - + kdtree : {libpysal.cg.kdtree.KDTree, libpysal.cg.kdtree.ArcKDTree} + An ``(n,k)`` array of `n` observations on `k` characteristics + used to measure distances between the `n` objects. + k : int + The number of nearest neighbors. + p : float + Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. + ``2`` is Euclidean distance and ``1`` is Manhattan distance. + This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. + ids : list + Identifiers to attach to each observation. + + radius : + -------------- see KDTree + + distance_metric : + -------------- see KDTree + + **kwargs : dict + Keyword arguments for ``libpysal.weights.weights.W``. + Returns ------- - - w : W - instance - Weights object with binary weights + w : libpysal.weights.distance.KNN + A weights object instance with binary weights. Examples -------- + >>> import libpysal >>> import numpy as np >>> points = [(10, 10), (20, 10), (40, 10), (15, 20), (30, 20), (30, 30)] @@ -59,13 +68,14 @@ class KNN(W): >>> wnn2 = libpysal.weights.KNN(kd, 2) >>> [1,3] == wnn2.neighbors[0] True + >>> wnn2 = KNN(kd,2) >>> wnn2[0] {1: 1.0, 3: 1.0} >>> wnn2[1] {0: 1.0, 3: 1.0} - now with 1 rather than 0 offset + Now with 1 rather than 0 offset: >>> wnn2 = libpysal.weights.KNN(kd, 2, ids=range(1,7)) >>> wnn2[1] @@ -82,68 +92,84 @@ class KNN(W): See Also -------- - :class:`libpysal.weights.weights.W` + + libpysal.weights.weights.W + """ - def __init__(self, data, k=2, p=2, ids=None, radius=None, - distance_metric='euclidean', **kwargs): - if radius is not None: - distance_metric='arc' + + def __init__( + self, + data, + k=2, + p=2, + ids=None, + radius=None, + distance_metric="euclidean", + **kwargs + ): + + if radius is not None: + distance_metric = "arc" + if isKDTree(data): self.kdtree = data self.data = self.kdtree.data else: self.kdtree = KDTree(data, radius=radius, distance_metric=distance_metric) self.data = self.kdtree.data - self.k = k + + self.k = k self.p = p - this_nnq = self.kdtree.query(self.data, k=k+1, p=p) + + this_nnq = self.kdtree.query(self.data, k=k + 1, p=p) to_weight = this_nnq[1] + if ids is None: ids = list(range(to_weight.shape[0])) neighbors = {} - for i,row in enumerate(to_weight): + for i, row in enumerate(to_weight): row = row.tolist() row.remove(i) row = [ids[j] for j in row] focal = ids[i] neighbors[focal] = row + W.__init__(self, neighbors, id_order=ids, **kwargs) @classmethod def from_shapefile(cls, filepath, *args, **kwargs): - """ - Nearest neighbor weights from a shapefile. + """Nearest neighbor weights from a shapefile. Parameters ---------- - data : string - shapefile containing attribute data. - k : int - number of nearest neighbors - p : float - Minkowski p-norm distance metric parameter: - 1<=p<=infinity - 2: Euclidean distance - 1: Manhattan distance - ids : list - identifiers to attach to each observation - radius : float - If supplied arc_distances will be calculated - based on the given radius. p will be ignored. + data : str + The name of polygon shapefile (including the file extension) + containing attribute data. + k : int + The number of nearest neighbors. + p : float + Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. + ``2`` is Euclidean distance and ``1`` is Manhattan distance. + This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. + ids : list + Identifiers to attach to each observation. + radius : float + If supplied arc distances will be calculated + based on the given radius and ``p`` will be ignored. Returns ------- - - w : KNN + w : libpysal.weights.distance.KNN instance; Weights object with binary weights. Examples -------- - Polygon shapefile + From a polygon shapefile: + >>> import libpysal >>> from libpysal.weights import KNN >>> wc=KNN.from_shapefile(libpysal.examples.get_path("columbus.shp")) @@ -157,8 +183,7 @@ def from_shapefile(cls, filepath, *args, **kwargs): >>> set(wc3.neighbors[2]) == set([4,3,0]) True - - Point shapefile + From a point shapefile: >>> w=KNN.from_shapefile(libpysal.examples.get_path("juvenile.shp")) >>> w.pct_nonzero @@ -174,9 +199,14 @@ def from_shapefile(cls, filepath, *args, **kwargs): See Also -------- - :class:`libpysal.weights.weights.W` + + libpysal.weights.weights.W + """ - return cls(get_points_array_from_shapefile(filepath), *args, **kwargs) + + w = cls(get_points_array_from_shapefile(filepath), *args, **kwargs) + + return w @classmethod def from_array(cls, array, *args, **kwargs): @@ -186,20 +216,20 @@ def from_array(cls, array, *args, **kwargs): Parameters ---------- - array : np.ndarray + array : np.ndarray (n, k) array representing n observations on k characteristics used to measure distances between the n objects - **kwargs : keyword arguments, see Rook + **kwargs : keyword arguments, see Rook Returns ------- - w : W - instance - Weights object with binary weights + w : libpysal.weights.distance.KNN + A binary weights instance. Examples -------- + >>> from libpysal.weights import KNN >>> points = [(10, 10), (20, 10), (40, 10), (15, 20), (30, 20), (30, 30)] >>> wnn2 = KNN.from_array(points, 2) @@ -211,7 +241,7 @@ def from_array(cls, array, *args, **kwargs): >>> wnn2[1] {0: 1.0, 3: 1.0} - now with 1 rather than 0 offset + Now with 1 rather than 0 offset: >>> wnn2 = KNN.from_array(points, 2, ids=range(1,7)) >>> wnn2[1] @@ -228,44 +258,70 @@ def from_array(cls, array, *args, **kwargs): See Also -------- - :class:`libpysal.weights.weights.W` + + libpysal.weights.weights.W + """ - return cls(array, *args, **kwargs) + + w = cls(array, *args, **kwargs) + + return w @classmethod - def from_dataframe(cls, df, geom_col='geometry', ids=None, *args, **kwargs): + def from_dataframe(cls, df, geom_col="geometry", ids=None, *args, **kwargs): """ Make KNN weights from a dataframe. Parameters ---------- - df : pandas.dataframe - a dataframe with a geometry column that can be used to - construct a W object - geom_col : string - column name of the geometry stored in df - ids : string or iterable - if string, the column name of the indices from the dataframe - if iterable, a list of ids to use for the W - if None, df.index is used. + df : pandas.DataFrame + A dataframe with a geometry column that can be used to construct a W object + geom_col : string + The column name of the geometry stored in ``df``. + ids : {str, iterable} + + if string, the column name of the indices from the dataframe + if iterable, a list of ids to use for the W + if None, df.index is used. + Returns + ------- + w : libpysal.weights.distance.KNN + A binary weights instance. + See Also -------- - :class:`libpysal.weights.weights.W` + + libpysal.weights.weights.W + """ + pts = get_points_array(df[geom_col]) + if ids is None: ids = df.index.tolist() elif isinstance(ids, str): ids = df[ids].tolist() - return cls(pts, *args, ids=ids, **kwargs) + + w = cls(pts, *args, ids=ids, **kwargs) + + return w def reweight(self, k=None, p=None, new_data=None, new_ids=None, inplace=True): - """ - Redo K-Nearest Neighbor weights construction using given parameters + """Redo `K`-nearest neighbor weights construction using given parameters. Parameters ---------- + + k : int + number of nearest neighbors + p : float + Minkowski p-norm distance metric parameter: + 1<=p<=infinity + 2: Euclidean distance + 1: Manhattan distance + Ignored if the KDTree is an ArcKDTree + new_data : np.ndarray an array containing additional data to use in the KNN weight @@ -275,23 +331,16 @@ def reweight(self, k=None, p=None, new_data=None, new_ids=None, inplace=True): inplace : bool a flag denoting whether to modify the KNN object in place or to return a new KNN object - k : int - number of nearest neighbors - p : float - Minkowski p-norm distance metric parameter: - 1<=p<=infinity - 2: Euclidean distance - 1: Manhattan distance - Ignored if the KDTree is an ArcKDTree - + Returns ------- A copy of the object using the new parameterization, or None if the object is reweighted in place. + """ - if (new_data is not None): - new_data = np.asarray(new_data).reshape(-1,2) - data = np.vstack((self.data, new_data)).reshape(-1,2) + if new_data is not None: + new_data = np.asarray(new_data).reshape(-1, 2) + data = np.vstack((self.data, new_data)).reshape(-1, 2) if new_ids is not None: ids = copy.deepcopy(self.id_order) ids.extend(list(new_ids)) @@ -302,7 +351,7 @@ def reweight(self, k=None, p=None, new_data=None, new_ids=None, inplace=True): data = self.kdtree ids = self.id_order elif (new_data is None) and (new_ids is not None): - Warn('Remapping ids must be done using w.remap_ids') + Warn("Remapping ids must be done using w.remap_ids") if k is None: k = self.k if p is None: @@ -313,14 +362,13 @@ def reweight(self, k=None, p=None, new_data=None, new_ids=None, inplace=True): else: return KNN(data, ids=ids, k=k, p=p) + class Kernel(W): - """ - Spatial weights based on kernel functions. + """Spatial weights based on kernel functions. Parameters ---------- - - data : array + data : array (n,k) or KDTree where KDtree.data is array (n,k) n observations on k characteristics used to measure distances between the n objects @@ -384,16 +432,15 @@ class Kernel(W): Attributes ---------- weights : dict - Dictionary keyed by id with a list of weights for each neighbor - + Dictionary keyed by id with a list of weights for each neighbor neighbors : dict - of lists of neighbors keyed by observation id - + of lists of neighbors keyed by observation id bandwidth : array - array of bandwidths + array of bandwidths Examples -------- + >>> from libpysal.weights import Kernel >>> points=[(10, 10), (20, 10), (40, 10), (15, 20), (30, 20), (30, 30)] >>> kw=Kernel(points) @@ -475,53 +522,72 @@ class Kernel(W): {0: [1.0, 0.35206533556593145, 0.3412334260702758], 1: [0.35206533556593145, 1.0, 0.2419707487162134, 0.3412334260702758, 0.31069657591175387], 2: [0.2419707487162134, 1.0, 0.31069657591175387], 3: [0.3412334260702758, 0.3412334260702758, 1.0, 0.3011374490937829, 0.26575287272131043], 4: [0.31069657591175387, 0.31069657591175387, 0.3011374490937829, 1.0, 0.35206533556593145], 5: [0.26575287272131043, 0.35206533556593145, 1.0]} """ - def __init__(self, data, bandwidth=None, fixed=True, k=2, - function='triangular', eps=1.0000001, ids=None, - diagonal=False, - distance_metric='euclidean', radius=None, - **kwargs): + + def __init__( + self, + data, + bandwidth=None, + fixed=True, + k=2, + function="triangular", + eps=1.0000001, + ids=None, + diagonal=False, + distance_metric="euclidean", + radius=None, + **kwargs + ): + if radius is not None: - distance_metric='arc' + distance_metric = "arc" + if isKDTree(data): self.kdtree = data self.data = self.kdtree.data data = self.data else: - self.kdtree = KDTree(data, distance_metric=distance_metric, - radius=radius) + self.kdtree = KDTree(data, distance_metric=distance_metric, radius=radius) self.data = self.kdtree.data + self.k = k + 1 self.function = function.lower() self.fixed = fixed self.eps = eps + if bandwidth: try: bandwidth = np.array(bandwidth) bandwidth.shape = (len(bandwidth), 1) except: - bandwidth = np.ones((len(data), 1), 'float') * bandwidth + bandwidth = np.ones((len(data), 1), "float") * bandwidth self.bandwidth = bandwidth else: self._set_bw() self._eval_kernel() neighbors, weights = self._k_to_W(ids) + if diagonal: for i in neighbors: weights[i][neighbors[i].index(i)] = 1.0 + W.__init__(self, neighbors, weights, ids, **kwargs) @classmethod - def from_shapefile(cls, filepath, idVariable=None, **kwargs): + def from_shapefile(cls, filepath, idVariable=None, **kwargs): """ Kernel based weights from shapefile Parameters ---------- - shapefile : string - shapefile name with shp suffix - idVariable : string - name of column in shapefile's DBF to use for ids + + cls : + + filepath : str + shapefile name with shp suffix + idVariable : str + The name of the column in shapefile's DBF to use for ids. + Returns ------- @@ -529,13 +595,18 @@ def from_shapefile(cls, filepath, idVariable=None, **kwargs): See Also --------- - :class:`libpysal.weights.weights.W` + + libpysal.weights.weights.W + """ + points = get_points_array_from_shapefile(filepath) + if idVariable is not None: ids = get_ids(filepath, idVariable) else: ids = None + return cls.from_array(points, ids=ids, **kwargs) @classmethod @@ -546,12 +617,15 @@ def from_array(cls, array, **kwargs): See Also -------- - :class:`libpysal.weights.weights.W` + + libpysal.weights.weights.W + """ + return cls(array, **kwargs) @classmethod - def from_dataframe(cls, df, geom_col='geometry', ids=None, **kwargs): + def from_dataframe(cls, df, geom_col="geometry", ids=None, **kwargs): """ Make Kernel weights from a dataframe. @@ -569,38 +643,62 @@ def from_dataframe(cls, df, geom_col='geometry', ids=None, **kwargs): See Also -------- - :class:`libpysal.weights.weights.W` + + libpysal.weights.weights.W + """ + pts = get_points_array(df[geom_col]) + if ids is None: ids = df.index.tolist() elif isinstance(ids, str): ids = df[ids].tolist() + return cls(pts, ids=ids, **kwargs) def _k_to_W(self, ids=None): + """ + + Returns + ------- + allneighbors : ... + ... + + weights : ... + ... + + """ allneighbors = {} weights = {} + if ids: ids = np.array(ids) else: ids = np.arange(len(self.data)) + for i, neighbors in enumerate(self.kernel): + if len(self.neigh[i]) == 0: allneighbors[ids[i]] = [] weights[ids[i]] = [] else: allneighbors[ids[i]] = list(ids[self.neigh[i]]) weights[ids[i]] = self.kernel[i].tolist() + return allneighbors, weights def _set_bw(self): + """ + """ + dmat, neigh = self.kdtree.query(self.data, k=self.k) + if self.fixed: # use max knn distance as bandwidth bandwidth = dmat.max() * self.eps n = len(dmat) - self.bandwidth = np.ones((n, 1), 'float') * bandwidth + self.bandwidth = np.ones((n, 1), "float") * bandwidth else: # use local max knn distance self.bandwidth = dmat.max(axis=1) * self.eps @@ -610,12 +708,17 @@ def _set_bw(self): self.neigh = nnq[1] def _eval_kernel(self): + """ + """ + # get points within bandwidth distance of each point - if not hasattr(self, 'neigh'): + if not hasattr(self, "neigh"): kdtq = self.kdtree.query_ball_point - neighbors = [kdtq(self.data[i], r=bwi[0]) for i, - bwi in enumerate(self.bandwidth)] + neighbors = [ + kdtq(self.data[i], r=bwi[0]) for i, bwi in enumerate(self.bandwidth) + ] self.neigh = neighbors + # get distances for neighbors bw = self.bandwidth @@ -629,26 +732,26 @@ def _eval_kernel(self): zi = np.array([dict(list(zip(ni, di)))[nid] for nid in nids]) / bw[i] z.append(zi) zs = z + # functions follow Anselin and Rey (2010) table 5.4 - if self.function == 'triangular': + if self.function == "triangular": self.kernel = [1 - zi for zi in zs] - elif self.function == 'uniform': + elif self.function == "uniform": self.kernel = [np.ones(zi.shape) * 0.5 for zi in zs] - elif self.function == 'quadratic': - self.kernel = [(3. / 4) * (1 - zi ** 2) for zi in zs] - elif self.function == 'quartic': - self.kernel = [(15. / 16) * (1 - zi ** 2) ** 2 for zi in zs] - elif self.function == 'gaussian': + elif self.function == "quadratic": + self.kernel = [(3.0 / 4) * (1 - zi ** 2) for zi in zs] + elif self.function == "quartic": + self.kernel = [(15.0 / 16) * (1 - zi ** 2) ** 2 for zi in zs] + elif self.function == "gaussian": c = np.pi * 2 c = c ** (-0.5) - self.kernel = [c * np.exp(-(zi ** 2) / 2.) for zi in zs] + self.kernel = [c * np.exp(-(zi ** 2) / 2.0) for zi in zs] else: - print(('Unsupported kernel function', self.function)) + print(("Unsupported kernel function", self.function)) class DistanceBand(W): - """ - Spatial weights based on distance band. + """Spatial weights based on distance band. Parameters ---------- @@ -689,13 +792,13 @@ class DistanceBand(W): Attributes ---------- weights : dict - of neighbor weights keyed by observation id - + Neighbor weights keyed by observation id. neighbors : dict - of neighbors keyed by observation id + Neighbors keyed by observation id. Examples -------- + >>> import libpysal >>> points=[(10, 10), (20, 10), (40, 10), (15, 20), (30, 20), (30, 30)] >>> wcheck = libpysal.weights.W({0: [1, 3], 1: [0, 3], 2: [], 3: [0, 1], 4: [5], 5: [4]}) @@ -713,7 +816,7 @@ class DistanceBand(W): >>> libpysal.weights.util.neighbor_equality(w, wcheck) True - inverse distance weights + Inverse distance weights: >>> w=libpysal.weights.DistanceBand(points,threshold=11.2,binary=False) @@ -724,7 +827,7 @@ class DistanceBand(W): >>> w.neighbors[0].tolist() [1, 3] - gravity weights + Gravity weights: >>> w=libpysal.weights.DistanceBand(points,threshold=11.2,binary=False,alpha=-2.) @@ -736,23 +839,35 @@ class DistanceBand(W): Notes ----- - This was initially implemented running scipy 0.8.0dev (in epd 6.1). - earlier versions of scipy (0.7.0) have a logic bug in scipy/sparse/dok.py - so serge changed line 221 of that file on sal-dev to fix the logic bug. + This was initially implemented running ``scipy v0.8.0dev`` (in epd 6.1). + Earlier versions of scipy (0.7.0) have a logic bug in ``scipy/sparse/dok.py``, + so Serge changed line 221 of that file on sal-dev to fix the logic bug. """ - def __init__(self, data, threshold, p=2, alpha=-1.0, binary=True, ids=None, - build_sp=True, silence_warnings=False, - distance_metric='euclidean', radius=None): + def __init__( + self, + data, + threshold, + p=2, + alpha=-1.0, + binary=True, + ids=None, + build_sp=True, + silence_warnings=False, + distance_metric="euclidean", + radius=None, + ): """Casting to floats is a work around for a bug in scipy.spatial. - See detail in pysal issue #126. - + --> See detail in pysal issue #126. """ + if ids is not None: ids = list(ids) + if radius is not None: - distance_metric='arc' + distance_metric = "arc" + self.p = p self.threshold = threshold self.binary = binary @@ -767,25 +882,28 @@ def __init__(self, data, threshold, p=2, alpha=-1.0, binary=True, ids=None, if self.build_sp: try: data = np.asarray(data) - if data.dtype.kind != 'f': + if data.dtype.kind != "f": data = data.astype(float) - self.kdtree = KDTree(data, - distance_metric=distance_metric, - radius=radius) + self.kdtree = KDTree( + data, distance_metric=distance_metric, radius=radius + ) self.data = self.kdtree.data except: - raise ValueError("Could not make array from data") + raise ValueError("Could not make array from data") else: self.data = data - self.kdtree = None + self.kdtree = None + self._band() neighbors, weights = self._distance_to_W(ids) - W.__init__(self, neighbors, weights, ids, silence_warnings=self.silence_warnings) + + W.__init__( + self, neighbors, weights, ids, silence_warnings=self.silence_warnings + ) @classmethod def from_shapefile(cls, filepath, threshold, idVariable=None, **kwargs): - """ - Distance-band based weights from shapefile + """Distance-band based weights from shapefile. Parameters ---------- @@ -795,71 +913,101 @@ def from_shapefile(cls, filepath, threshold, idVariable=None, **kwargs): name of column in shapefile's DBF to use for ids Returns - -------- + ------- Kernel Weights Object """ + points = get_points_array_from_shapefile(filepath) + if idVariable is not None: ids = get_ids(filepath, idVariable) else: ids = None + return cls.from_array(points, threshold, ids=ids, **kwargs) @classmethod def from_array(cls, array, threshold, **kwargs): + """Construct a DistanceBand weights from an array. + Supports all the same options as ``libpysal.weights.DistanceBand``. + + + + Returns + ------- + """ - Construct a DistanceBand weights from an array. Supports all the same options - as :class:`libpysal.weights.DistanceBand` - """ return cls(array, threshold, **kwargs) @classmethod - def from_dataframe(cls, df, threshold, geom_col='geometry', ids=None, **kwargs): + def from_dataframe(cls, df, threshold, geom_col="geometry", ids=None, **kwargs): """ Make DistanceBand weights from a dataframe. Parameters ---------- - df : pandas.dataframe - a dataframe with a geometry column that can be used to - construct a W object - geom_col : string - column name of the geometry stored in df - ids : string or iterable - if string, the column name of the indices from the dataframe + df : pandas.DataFrame + A dataframe with a geometry column that can be used + to construct a PySAL ``W`` object. + geom_col : str + The column name of the geometry stored in ``df``. + ids : {str, iterable} + If string, the column name of the indices from the dataframe + if iterable, a list of ids to use for the W + if None, df.index is used. - + + Returns + ------- + + """ + pts = get_points_array(df[geom_col]) + if ids is None: ids = df.index.tolist() elif isinstance(ids, str): ids = df[ids].tolist() + return cls(pts, threshold, ids=ids, **kwargs) def _band(self): - """Find all pairs within threshold. + """Find all pairs within threshold.""" - """ if self.build_sp: self.dmat = self.kdtree.sparse_distance_matrix( - self.kdtree, max_distance=self.threshold, p=self.p).tocsr() + self.kdtree, max_distance=self.threshold, p=self.p + ).tocsr() else: - if str(self.kdtree).split('.')[-1][0:10] == 'Arc_KDTree': - raise TypeError('Unable to calculate dense arc distance matrix;' - ' parameter "build_sp" must be set to True for arc' - ' distance type weight') + if str(self.kdtree).split(".")[-1][0:10] == "Arc_KDTree": + raise TypeError( + "Unable to calculate dense arc distance matrix;" + " parameter 'build_sp' must be set to True for arc" + " distance type weight." + ) self.dmat = self._spdistance_matrix(self.data, self.data, self.threshold) - def _distance_to_W(self, ids=None): + """ + + Returns + ------- + neighbors : + + weights : + + + """ if self.binary: - self.dmat[self.dmat>0] = 1 + self.dmat[self.dmat > 0] = 1 self.dmat.eliminate_zeros() - tempW = WSP2W(WSP(self.dmat, id_order=ids), silence_warnings=self.silence_warnings) + tempW = WSP2W( + WSP(self.dmat, id_order=ids), silence_warnings=self.silence_warnings + ) neighbors = tempW.neighbors weight_keys = list(tempW.weights.keys()) weight_vals = list(tempW.weights.values()) @@ -867,30 +1015,47 @@ def _distance_to_W(self, ids=None): return neighbors, weights else: weighted = self.dmat.power(self.alpha) - weighted[weighted==np.inf] = 0 + weighted[weighted == np.inf] = 0 weighted.eliminate_zeros() - tempW = WSP2W(WSP(weighted, id_order=ids), silence_warnings=self.silence_warnings) + tempW = WSP2W( + WSP(weighted, id_order=ids), silence_warnings=self.silence_warnings + ) neighbors = tempW.neighbors weight_keys = list(tempW.weights.keys()) weight_vals = list(tempW.weights.values()) weights = dict(list(zip(weight_keys, list(map(list, weight_vals))))) return neighbors, weights - def _spdistance_matrix(self, x,y, threshold=None): - dist = distance_matrix(x,y) + def _spdistance_matrix(self, x, y, threshold=None): + """ + + + Returns + ------- + + + """ + + dist = distance_matrix(x, y) + if threshold is not None: zeros = dist > threshold dist[zeros] = 0 + return sp.csr_matrix(dist) + def _test(): import doctest + # the following line could be used to define an alternative to the '' flag - #doctest.BLANKLINE_MARKER = 'something better than ' - start_suppress = np.get_printoptions()['suppress'] + # doctest.BLANKLINE_MARKER = 'something better than ' + + start_suppress = np.get_printoptions()["suppress"] np.set_printoptions(suppress=True) doctest.testmod() np.set_printoptions(suppress=start_suppress) -if __name__ == '__main__': + +if __name__ == "__main__": _test() From 7561db0eb22e41da152940e23e687d98c1d897be Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Thu, 20 Aug 2020 15:42:46 -0400 Subject: [PATCH 12/33] intermediary commit for weights/distance.py [1] --- libpysal/weights/distance.py | 356 ++++++++++++++++++++++------------- 1 file changed, 222 insertions(+), 134 deletions(-) diff --git a/libpysal/weights/distance.py b/libpysal/weights/distance.py index 9b92dec57..acce02826 100644 --- a/libpysal/weights/distance.py +++ b/libpysal/weights/distance.py @@ -22,8 +22,8 @@ def knnW(data, k=2, p=2, ids=None, radius=None, distance_metric="euclidean"): - """This is deprecated. Use the ``pysal.weights.KNN class instead.``""" - # Warn('This function is deprecated. Please use pysal.weights.KNN', UserWarning) + """This is deprecated. Use the ``libpysal.weights.KNN class instead.``""" + # Warn("This function is deprecated. Please use pysal.weights.KNN", UserWarning) return KNN(data, k=k, p=p, ids=ids, radius=radius, distance_metric=distance_metric) @@ -32,31 +32,32 @@ class KNN(W): Parameters ---------- - kdtree : {libpysal.cg.kdtree.KDTree, libpysal.cg.kdtree.ArcKDTree} + data : {libpysal.cg.kdtree.KDTree, libpysal.cg.kdtree.ArcKDTree} An ``(n,k)`` array of `n` observations on `k` characteristics used to measure distances between the `n` objects. k : int - The number of nearest neighbors. + The number of nearest neighbors. Default is ``2``. p : float Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. ``2`` is Euclidean distance and ``1`` is Manhattan distance. This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. + Default is ``2``. ids : list - Identifiers to attach to each observation. - - radius : - -------------- see KDTree - - distance_metric : - -------------- see KDTree - + Identifiers to attach to each observation. Default is ``None``. + radius : float + If supplied arc distances will be calculated based on the given radius + and ``p`` will be ignored. Default is ``None``. + See ``libpysal.cg.kdtree.KDTree`` for more details. + distance_metric : str + Either ``'euclidean'`` or ``'arc'``. Default is ``'euclidean'``. + See ``libpysal.cg.kdtree.KDTree`` for more details. **kwargs : dict Keyword arguments for ``libpysal.weights.weights.W``. Returns ------- w : libpysal.weights.distance.KNN - A weights object instance with binary weights. + A `k` nearest neighbors weights instance. Examples -------- @@ -144,26 +145,18 @@ def from_shapefile(cls, filepath, *args, **kwargs): Parameters ---------- - - data : str + filepath : str The name of polygon shapefile (including the file extension) containing attribute data. - k : int - The number of nearest neighbors. - p : float - Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. - ``2`` is Euclidean distance and ``1`` is Manhattan distance. - This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. - ids : list - Identifiers to attach to each observation. - radius : float - If supplied arc distances will be calculated - based on the given radius and ``p`` will be ignored. + *args : iterable + Positional arguments for ``libpysal.weights.distance.KNN``. + **kwargs : dict + Keyword arguments for ``libpysal.weights.distance.KNN``. Returns ------- w : libpysal.weights.distance.KNN - instance; Weights object with binary weights. + A `k` nearest neighbors weights instance. Examples -------- @@ -210,22 +203,22 @@ def from_shapefile(cls, filepath, *args, **kwargs): @classmethod def from_array(cls, array, *args, **kwargs): - """ - Creates nearest neighbor weights matrix based on k nearest - neighbors. + """Creates nearest neighbor weights matrix based on `k` nearest neighbors. Parameters ---------- - array : np.ndarray - (n, k) array representing n observations on - k characteristics used to measure distances - between the n objects - **kwargs : keyword arguments, see Rook + array : numpy.ndarray + ``(n, k)`` array representing `n` observations on `k` characteristics + used to measure distances between the `n` objects. + *args : iterable + Positional arguments for ``libpysal.weights.distance.KNN``. + **kwargs : dict + Keyword arguments for ``libpysal.weights.distance.KNN``. Returns ------- w : libpysal.weights.distance.KNN - A binary weights instance. + A `k` nearest neighbors weights instance. Examples -------- @@ -269,25 +262,29 @@ def from_array(cls, array, *args, **kwargs): @classmethod def from_dataframe(cls, df, geom_col="geometry", ids=None, *args, **kwargs): - """ - Make KNN weights from a dataframe. + """Make KNN weights from a dataframe. Parameters ---------- df : pandas.DataFrame - A dataframe with a geometry column that can be used to construct a W object + A dataframe with a geometry column that can be used + to construct a `W` object. geom_col : string The column name of the geometry stored in ``df``. + Default is ``geometry``. ids : {str, iterable} - - if string, the column name of the indices from the dataframe - if iterable, a list of ids to use for the W - if None, df.index is used. + If string, the column name of the indices from the dataframe. + If iterable, a list of ids to use for the `W`. + If ``None``, ``df.index`` is used. Default is ``None``. + *args : iterable + Positional arguments for ``libpysal.weights.distance.KNN``. + **kwargs : dict + Keyword arguments for ``libpysal.weights.distance.KNN``. Returns ------- w : libpysal.weights.distance.KNN - A binary weights instance. + A `k` nearest neighbors weights instance. See Also -------- @@ -312,32 +309,31 @@ def reweight(self, k=None, p=None, new_data=None, new_ids=None, inplace=True): Parameters ---------- - - k : int - number of nearest neighbors - p : float - Minkowski p-norm distance metric parameter: - 1<=p<=infinity - 2: Euclidean distance - 1: Manhattan distance - Ignored if the KDTree is an ArcKDTree - - new_data : np.ndarray - an array containing additional data to use in the KNN - weight - new_ids : list - a list aligned with new_data that provides the ids for - each new observation - inplace : bool - a flag denoting whether to modify the KNN object - in place or to return a new KNN object + k : int + The number of nearest neighbors. Default is ``None``. + p : float + Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. + ``2`` is Euclidean distance and ``1`` is Manhattan distance. + This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. + Default is ``None``. + new_data : numpy.ndarray + An array containing additional data to use in the ``KNN`` weight. + Default is ``None``. + new_ids : list + A list aligned with ``new_data`` that provides the ids + for each new observation. Default is ``None``. + inplace : bool + A flag denoting whether to modify the ``KNN`` object + in place or to return a new ``KNN`` object. Default is ``True``. Returns ------- - A copy of the object using the new parameterization, or None if the - object is reweighted in place. - + w : libpysal.weights.distance.KNN + A copy of the `k` nearest neighbors weights instance using the + new parameterization, or ``None`` if the object is reweighted in place. + """ + if new_data is not None: new_data = np.asarray(new_data).reshape(-1, 2) data = np.vstack((self.data, new_data)).reshape(-1, 2) @@ -352,15 +348,19 @@ def reweight(self, k=None, p=None, new_data=None, new_ids=None, inplace=True): ids = self.id_order elif (new_data is None) and (new_ids is not None): Warn("Remapping ids must be done using w.remap_ids") + if k is None: k = self.k if p is None: p = self.p + if inplace: self._reset() self.__init__(data, ids=ids, k=k, p=p) else: - return KNN(data, ids=ids, k=k, p=p) + w = KNN(data, ids=ids, k=k, p=p) + + return w class Kernel(W): @@ -388,8 +388,22 @@ class Kernel(W): If true, set diagonal weights = 1.0, if false (default), diagonals weights are set to value according to kernel function. - function : {'triangular','uniform','quadratic','quartic','gaussian'} - kernel function defined as follows with + + eps : float + The adjustment to ensure the `knn` distance range + is closed on the `knn`th observations. Default is ``1.0000001``. + ids : list + Identifiers to attach to each observation. Default is ``None``. + radius : float + If supplied arc distances will be calculated based on the given radius + and ``p`` will be ignored. Default is ``None``. + See ``libpysal.cg.kdtree.KDTree`` for more details. + distance_metric : str + Either ``'euclidean'`` or ``'arc'``. Default is ``'euclidean'``. + See ``libpysal.cg.kdtree.KDTree`` for more details. + function : str + Either ``'triangular'``, ``'uniform'``, ``'quadratic'``, ``'quartic'``, + or ``'gaussian'``. The kernel function is defined as follows with .. math:: @@ -425,18 +439,17 @@ class Kernel(W): K(z) = (2\\pi)^{(-1/2)} exp(-z^2 / 2) - eps : float - adjustment to ensure knn distance range is closed on the - knnth observations + **kwargs : dict + Keyword arguments for ``libpysal.weights.weights.W``. Attributes ---------- weights : dict - Dictionary keyed by id with a list of weights for each neighbor + Dictionary keyed by id with a list of weights for each neighbor. neighbors : dict - of lists of neighbors keyed by observation id - bandwidth : array - array of bandwidths + Lists of neighbors keyed by observation id. + bandwidth : array-like + An array of bandwidths. Examples -------- @@ -468,7 +481,7 @@ class Kernel(W): [15.], [15.]]) - Adaptive bandwidths user specified + Adaptive bandwidths user specified: >>> bw=[25.0,15.0,25.0,16.0,14.5,25.0] >>> kwa=Kernel(points,bandwidth=bw) @@ -484,7 +497,7 @@ class Kernel(W): [14.5], [25. ]]) - Endogenous adaptive bandwidths + Endogenous adaptive bandwidths: >>> kwea=Kernel(points,fixed=False) >>> kwea.weights[0] @@ -499,7 +512,7 @@ class Kernel(W): [14.14213704], [18.02775818]]) - Endogenous adaptive bandwidths with Gaussian kernel + Endogenous adaptive bandwidths with Gaussian kernel: >>> kweag=Kernel(points,fixed=False,function='gaussian') >>> kweag.weights[0] @@ -512,7 +525,7 @@ class Kernel(W): [14.14213704], [18.02775818]]) - Diagonals to 1.0 + Diagonals to 1.0: >>> kq = Kernel(points,function='gaussian') >>> kq.weights @@ -529,12 +542,12 @@ def __init__( bandwidth=None, fixed=True, k=2, - function="triangular", + diagonal=False, eps=1.0000001, ids=None, - diagonal=False, - distance_metric="euclidean", radius=None, + distance_metric="euclidean", + function="triangular", **kwargs ): @@ -575,8 +588,7 @@ def __init__( @classmethod def from_shapefile(cls, filepath, idVariable=None, **kwargs): - """ - Kernel based weights from shapefile + """Kernel based weights from shapefile Parameters ---------- @@ -587,7 +599,8 @@ def from_shapefile(cls, filepath, idVariable=None, **kwargs): shapefile name with shp suffix idVariable : str The name of the column in shapefile's DBF to use for ids. - + ... : .... + ............ Returns ------- @@ -615,6 +628,19 @@ def from_array(cls, array, **kwargs): Construct a Kernel weights from an array. Supports all the same options as :class:`libpysal.weights.Kernel` + + Parameters + ---------- + ... : .... + ............ + ... : .... + ............ + + Returns + ------- + Kernel Weights Object + + See Also -------- @@ -631,15 +657,23 @@ def from_dataframe(cls, df, geom_col="geometry", ids=None, **kwargs): Parameters ---------- - df : pandas.dataframe - a dataframe with a geometry column that can be used to - construct a W object - geom_col : string - column name of the geometry stored in df + df : pandas.DataFrame + A dataframe with a geometry column that can be used + to construct a PySAL ``W`` object. + geom_col : str + The column name of the geometry stored in ``df``. + Default is ``geometry``. ids : string or iterable if string, the column name of the indices from the dataframe if iterable, a list of ids to use for the W if None, df.index is used. + **kwargs : dict + ....................... + + Returns + ------- + .... : ..... + ..... Kernel Weights Object See Also -------- @@ -660,6 +694,11 @@ def from_dataframe(cls, df, geom_col="geometry", ids=None, **kwargs): def _k_to_W(self, ids=None): """ + Parameters + ---------- + ids : .... + ...... + Returns ------- allneighbors : ... @@ -689,7 +728,7 @@ def _k_to_W(self, ids=None): return allneighbors, weights def _set_bw(self): - """ + """Set binary weights?............................................... """ dmat, neigh = self.kdtree.query(self.data, k=self.k) @@ -708,7 +747,7 @@ def _set_bw(self): self.neigh = nnq[1] def _eval_kernel(self): - """ + """Evaluate kernel................................................ ? """ # get points within bandwidth distance of each point @@ -755,39 +794,42 @@ class DistanceBand(W): Parameters ---------- - - data : array - (n,k) or KDTree where KDtree.data is array (n,k) - n observations on k characteristics used to measure - distances between the n objects - threshold : float - distance band - p : float - Minkowski p-norm distance metric parameter: - 1<=p<=infinity - 2: Euclidean distance - 1: Manhattan distance - binary : boolean - If true w_{ij}=1 if d_{i,j}<=threshold, otherwise w_{i,j}=0 - If false wij=dij^{alpha} - alpha : float - distance decay parameter for weight (default -1.0) - if alpha is positive the weights will not decline with - distance. If binary is True, alpha is ignored - - ids : list - values to use for keys of the neighbors and weights dicts - - build_sp : boolean - True to build sparse distance matrix and false to build dense - distance matrix; significant speed gains may be obtained - dending on the sparsity of the of distance_matrix and - threshold that is applied - silent : boolean - By default libpysal will print a warning if the - dataset contains any disconnected observations or - islands. To silence this warning set this - parameter to True. + data : {array-like, libpysal.cg.kdtree.KDTree} + ``(n,k)`` or ``KDTree`` where ``KDtree.data`` is an ``(n,k)`` array + of `n` observations on `k` characteristics used to measure + distances between the `n` objects + threshold : float + The distance band. + p : float + Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. + ``2`` is Euclidean distance and ``1`` is Manhattan distance. + This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. + Default is ``2``. + binary : bool + If set to ``True``, :math:`w_{ij}=1 if d_{i,j}<=threshold`, + otherwise :math:`w_{i,j}=0`. If set to ``False``, :math:`wij=dij^{alpha}`. + Default is ``True``. + alpha : float + The distance decay parameter for weights. Default is ``-1.0``. + If ``alpha`` is positive the weights will not decline with distance. + If ``binary`` is set to ``True``, ``alpha`` is ignored. + ids : list + Identifiers to attach to each observation. Default is ``None``. + build_sp : boolean + Set to ``True`` to build a sparse distance matrix and ``False`` to build dense + distance matrix. Significant speed gains may be obtained depending on the + sparsity of the of distance matrix and the ``threshold`` that is applied. + Default is ``True``. + silence_warnings : bool + By default (`True``) libpysal will print a warning if the dataset contains any + disconnected observations or islands. To silence this warning set to ``True``. + radius : float + If supplied arc distances will be calculated based on the given radius + and ``p`` will be ignored. Default is ``None``. + See ``libpysal.cg.kdtree.KDTree`` for more details. + distance_metric : str + Either ``'euclidean'`` or ``'arc'``. Default is ``'euclidean'``. + See ``libpysal.cg.kdtree.KDTree`` for more details. Attributes ---------- @@ -850,13 +892,13 @@ def __init__( data, threshold, p=2, - alpha=-1.0, binary=True, + alpha=-1.0, ids=None, build_sp=True, silence_warnings=False, - distance_metric="euclidean", radius=None, + distance_metric="euclidean", ): """Casting to floats is a work around for a bug in scipy.spatial. --> See detail in pysal issue #126. @@ -907,9 +949,15 @@ def from_shapefile(cls, filepath, threshold, idVariable=None, **kwargs): Parameters ---------- - shapefile : string + shapefile : str shapefile name with shp suffix - idVariable : string + + threshold : float + The distance band. + + + + idVariable : str name of column in shapefile's DBF to use for ids Returns @@ -932,10 +980,20 @@ def from_array(cls, array, threshold, **kwargs): """Construct a DistanceBand weights from an array. Supports all the same options as ``libpysal.weights.DistanceBand``. - + Parameters + ---------- + array : ... + ...... + threshold : float + The distance band. + **kwargs : ... + ...... Returns ------- + ... : .... + ............ + """ @@ -951,18 +1009,29 @@ def from_dataframe(cls, df, threshold, geom_col="geometry", ids=None, **kwargs): df : pandas.DataFrame A dataframe with a geometry column that can be used to construct a PySAL ``W`` object. + threshold : float + The distance band. geom_col : str The column name of the geometry stored in ``df``. + Default is ``geometry``. ids : {str, iterable} If string, the column name of the indices from the dataframe if iterable, a list of ids to use for the W if None, df.index is used. + + Default is ``None``. + + + **kwargs : ... + ........ + Returns ------- - + ... : .... + ............ """ @@ -994,11 +1063,19 @@ def _band(self): def _distance_to_W(self, ids=None): """ + Parameters + ---------- + ids : ... + ........ Default is ``None``. + + Returns ------- - neighbors : + neighbors : ... + ........ - weights : + weights : dict + ........ """ @@ -1029,10 +1106,21 @@ def _distance_to_W(self, ids=None): def _spdistance_matrix(self, x, y, threshold=None): """ + Parameters + ---------- + x : ... + ........ + y : ... + ........ + threshold : ... + ........ Default is ``None``. + Returns ------- + ... : .... + ............ """ From 174051249cd9030d3f26c0b85dba8b1fb2afe4be Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Thu, 20 Aug 2020 21:19:45 -0400 Subject: [PATCH 13/33] completing update for weights/distance.py --- libpysal/weights/distance.py | 247 +++++++++++++++++------------------ 1 file changed, 122 insertions(+), 125 deletions(-) diff --git a/libpysal/weights/distance.py b/libpysal/weights/distance.py index acce02826..fc9d77b41 100644 --- a/libpysal/weights/distance.py +++ b/libpysal/weights/distance.py @@ -208,8 +208,8 @@ def from_array(cls, array, *args, **kwargs): Parameters ---------- array : numpy.ndarray - ``(n, k)`` array representing `n` observations on `k` characteristics - used to measure distances between the `n` objects. + An ``(n, k)`` array representing `n` observations on `k` + characteristics used to measure distances between the `n` objects. *args : iterable Positional arguments for ``libpysal.weights.distance.KNN``. **kwargs : dict @@ -368,27 +368,23 @@ class Kernel(W): Parameters ---------- - data : array - (n,k) or KDTree where KDtree.data is array (n,k) - n observations on k characteristics used to measure - distances between the n objects - bandwidth : float - or array-like (optional) - the bandwidth :math:`h_i` for the kernel. - fixed : binary - If true then :math:`h_i=h \\forall i`. If false then - bandwidth is adaptive across observations. - k : int - the number of nearest neighbors to use for determining - bandwidth. For fixed bandwidth, :math:`h_i=max(dknn) \\forall i` - where :math:`dknn` is a vector of k-nearest neighbor - distances (the distance to the kth nearest neighbor for each - observation). For adaptive bandwidths, :math:`h_i=dknn_i` - diagonal : boolean - If true, set diagonal weights = 1.0, if false (default), - diagonals weights are set to value according to kernel - function. - + data : {libpysal.cg.kdtree.KDTree, libpysal.cg.kdtree.ArcKDTree} + An ``(n,k)`` array of `n` observations on `k` characteristics + used to measure distances between the `n` objects. + k : int + The number of nearest neighbors to use for determining the bandwidth. For a + fixed bandwidth, :math:`h_i=max(dknn) \\forall i` where :math:`dknn` is a + vector of `k``-nearest neighbor distances (the distance to the `k`th nearest + neighbor for each observation). For adaptive bandwidths, :math:`h_i=dknn_i`. + Default is ``2``. + bandwidth : {float, array-like} + The bandwidth :math:`h_i` for the kernel. Default is ``None``. + fixed : bool + If ``True`` then :math:`h_i=h \\forall i`. If ``False`` then + bandwidth is adaptive across observations. Default is ``True``. + diagonal : bool + If ``True``, set diagonal weights to ``1.0``. If ``False`` diagonal weights + are set to values according to the kernel function. Default is ``False``. eps : float The adjustment to ensure the `knn` distance range is closed on the `knn`th observations. Default is ``1.0000001``. @@ -539,9 +535,9 @@ class Kernel(W): def __init__( self, data, + k=2, bandwidth=None, fixed=True, - k=2, diagonal=False, eps=1.0000001, ids=None, @@ -588,23 +584,22 @@ def __init__( @classmethod def from_shapefile(cls, filepath, idVariable=None, **kwargs): - """Kernel based weights from shapefile + """Construct kernel-based weights from a shapefile. Parameters ---------- - - cls : - filepath : str - shapefile name with shp suffix + The name of polygon shapefile (including the file extension) + containing attribute data. idVariable : str The name of the column in shapefile's DBF to use for ids. - ... : .... - ............ + **kwargs : dict + Keyword arguments for ``libpysal.weights.distance.Kernel``. Returns ------- - Kernel Weights Object + w : libpysal.weights.distance.Kernel + A kernel weights instance. See Also --------- @@ -620,26 +615,26 @@ def from_shapefile(cls, filepath, idVariable=None, **kwargs): else: ids = None - return cls.from_array(points, ids=ids, **kwargs) + w = cls.from_array(points, ids=ids, **kwargs) + + return w @classmethod def from_array(cls, array, **kwargs): - """ - Construct a Kernel weights from an array. Supports all the same options - as :class:`libpysal.weights.Kernel` - + """Construct kernel-based weights from an array. Parameters ---------- - ... : .... - ............ - ... : .... - ............ + array : numpy.ndarray + An ``(n, k)`` array representing `n` observations on `k` + characteristics used to measure distances between the `n` objects. + **kwargs : dict + Keyword arguments for ``libpysal.weights.distance.Kernel``. Returns ------- - Kernel Weights Object - + w : libpysal.weights.distance.Kernel + A kernel weights instance. See Also -------- @@ -648,12 +643,13 @@ def from_array(cls, array, **kwargs): """ - return cls(array, **kwargs) + w = cls(array, **kwargs) + + return w @classmethod def from_dataframe(cls, df, geom_col="geometry", ids=None, **kwargs): - """ - Make Kernel weights from a dataframe. + """Construct kernel-based weights from a dataframe. Parameters ---------- @@ -663,17 +659,17 @@ def from_dataframe(cls, df, geom_col="geometry", ids=None, **kwargs): geom_col : str The column name of the geometry stored in ``df``. Default is ``geometry``. - ids : string or iterable - if string, the column name of the indices from the dataframe - if iterable, a list of ids to use for the W - if None, df.index is used. + ids : {str, iterable} + If string, the column name of the indices from the dataframe. + If iterable, a list of ids to use for the `W`. + If ``None``, ``df.index`` is used. Default is ``None``. **kwargs : dict - ....................... + Keyword arguments for ``libpysal.weights.distance.Kernel``. Returns ------- - .... : ..... - ..... Kernel Weights Object + w : libpysal.weights.distance.Kernel + A kernel weights instance. See Also -------- @@ -689,25 +685,27 @@ def from_dataframe(cls, df, geom_col="geometry", ids=None, **kwargs): elif isinstance(ids, str): ids = df[ids].tolist() - return cls(pts, ids=ids, **kwargs) + w = cls(pts, ids=ids, **kwargs) + + return w def _k_to_W(self, ids=None): - """ + """Internal method for converting `k` neighbors to weights. Parameters ---------- - ids : .... - ...... + ids : list + See ``ids`` in ``Kernel``. Returns ------- - allneighbors : ... - ... - - weights : ... - ... + allneighbors : dict + Index lookup of all neighbors. + weights : dict + Index lookup of neighbor weights. """ + allneighbors = {} weights = {} @@ -728,8 +726,7 @@ def _k_to_W(self, ids=None): return allneighbors, weights def _set_bw(self): - """Set binary weights?............................................... - """ + """Internal method for setting binary weights.""" dmat, neigh = self.kdtree.query(self.data, k=self.k) @@ -747,8 +744,7 @@ def _set_bw(self): self.neigh = nnq[1] def _eval_kernel(self): - """Evaluate kernel................................................ ? - """ + """Internal method for evaluate the kernel function.""" # get points within bandwidth distance of each point if not hasattr(self, "neigh"): @@ -945,24 +941,24 @@ def __init__( @classmethod def from_shapefile(cls, filepath, threshold, idVariable=None, **kwargs): - """Distance-band based weights from shapefile. + """Construct a distance band weights object from a shapefile. Parameters ---------- - shapefile : str - shapefile name with shp suffix - + filepath : str + The name of polygon shapefile (including the file extension) + containing attribute data. threshold : float The distance band. - - - - idVariable : str - name of column in shapefile's DBF to use for ids + idVariable : str + The name of the column in shapefile's DBF to use for ids. + **kwargs : dict + Keyword arguments for ``libpysal.weights.distance.DistanceBand``. Returns ------- - Kernel Weights Object + w : libpysal.weights.distance.DistanceBand + A distance band weights instance. """ @@ -973,36 +969,38 @@ def from_shapefile(cls, filepath, threshold, idVariable=None, **kwargs): else: ids = None - return cls.from_array(points, threshold, ids=ids, **kwargs) + w = cls.from_array(points, threshold, ids=ids, **kwargs) + + return w @classmethod def from_array(cls, array, threshold, **kwargs): - """Construct a DistanceBand weights from an array. - Supports all the same options as ``libpysal.weights.DistanceBand``. + """Construct a distance band weights object from an array. Parameters ---------- - array : ... - ...... + array : numpy.ndarray + An ``(n, k)`` array representing `n` observations on `k` + characteristics used to measure distances between the `n` objects. threshold : float The distance band. - **kwargs : ... - ...... + **kwargs : dict + Keyword arguments for ``libpysal.weights.distance.DistanceBand``. Returns ------- - ... : .... - ............ - + w : libpysal.weights.distance.DistanceBand + A distance band weights instance. """ - return cls(array, threshold, **kwargs) + w = cls(array, threshold, **kwargs) + + return w @classmethod def from_dataframe(cls, df, threshold, geom_col="geometry", ids=None, **kwargs): - """ - Make DistanceBand weights from a dataframe. + """Construct a distance band weights object from a dataframe. Parameters ---------- @@ -1015,23 +1013,16 @@ def from_dataframe(cls, df, threshold, geom_col="geometry", ids=None, **kwargs): The column name of the geometry stored in ``df``. Default is ``geometry``. ids : {str, iterable} - If string, the column name of the indices from the dataframe - - if iterable, a list of ids to use for the W - - if None, df.index is used. - - Default is ``None``. - - - **kwargs : ... - ........ - + If string, the column name of the indices from the dataframe. + If iterable, a list of ids to use for the `W`. + If ``None``, ``df.index`` is used. Default is ``None``. + **kwargs : dict + Keyword arguments for ``libpysal.weights.distance.DistanceBand``. Returns ------- - ... : .... - ............ + w : libpysal.weights.distance.DistanceBand + A distance band weights instance. """ @@ -1042,10 +1033,12 @@ def from_dataframe(cls, df, threshold, geom_col="geometry", ids=None, **kwargs): elif isinstance(ids, str): ids = df[ids].tolist() - return cls(pts, threshold, ids=ids, **kwargs) + w = cls(pts, threshold, ids=ids, **kwargs) + + return w def _band(self): - """Find all pairs within threshold.""" + """Internal function for finding all pairs within the threshold.""" if self.build_sp: self.dmat = self.kdtree.sparse_distance_matrix( @@ -1061,24 +1054,22 @@ def _band(self): self.dmat = self._spdistance_matrix(self.data, self.data, self.threshold) def _distance_to_W(self, ids=None): - """ + """Internal method for converting distance band neighbors to weights. Parameters ---------- - ids : ... - ........ Default is ``None``. - + ids : list + See ``ids`` in ``DistanceBand``. Returns ------- - neighbors : ... - ........ - + neighbors : dict + Index lookup of all neighbors. weights : dict - ........ - + Index lookup of neighbor weights. """ + if self.binary: self.dmat[self.dmat > 0] = 1 self.dmat.eliminate_zeros() @@ -1104,23 +1095,27 @@ def _distance_to_W(self, ids=None): return neighbors, weights def _spdistance_matrix(self, x, y, threshold=None): - """ + """Internal method for converting a distance matrix into a CSR matrix. Parameters ---------- - x : ... - ........ - y : ... - ........ - threshold : ... - ........ Default is ``None``. - + x : array-like + X values. + y : array-like + Y values. + threshold : float + See ``threshold`` in ``DistanceBand``. Returns ------- + sp_mtx : scipy.sparse.csr_matrix + A Compressed Sparse Row matrix. - ... : .... - ............ + See Also + -------- + + scipy.spatial.distance_matrix + scipy.sparse.csr_matrix """ @@ -1130,7 +1125,9 @@ def _spdistance_matrix(self, x, y, threshold=None): zeros = dist > threshold dist[zeros] = 0 - return sp.csr_matrix(dist) + sp_mtx = sp.csr_matrix(dist) + + return sp_mtx def _test(): From 22cbc8a89bab2f3940461ffd52a1a77bf2a5c502 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Fri, 21 Aug 2020 10:00:45 -0400 Subject: [PATCH 14/33] updating typos in weights/distance.py and weights/contiguity.py --- libpysal/weights/contiguity.py | 170 ++++++++++++++++++++++----------- libpysal/weights/distance.py | 86 ++++++++--------- 2 files changed, 157 insertions(+), 99 deletions(-) diff --git a/libpysal/weights/contiguity.py b/libpysal/weights/contiguity.py index bcc6c5c7e..004730ea9 100644 --- a/libpysal/weights/contiguity.py +++ b/libpysal/weights/contiguity.py @@ -32,27 +32,31 @@ class Rook(W): ---------- polygons : list A collection of PySAL shapes from which to build weights. - **kw : keyword arguments - Optional arguments for ``pysal.weights.W``. The parameter ``ids``, + **kwargs : dict + Keyword arguments for ``libpysal.weights.W``. The parameter ``ids``, a list of names to use to build the weights, should be included here. See Also -------- - libpysal.weights.weights.W + libpysal.weights.W """ - def __init__(self, polygons, **kw): + def __init__(self, polygons, **kwargs): + criterion = "rook" - ids = kw.pop("ids", None) + ids = kwargs.pop("ids", None) polygons, backup = itertools.tee(polygons) first_shape = next(iter(backup)) + if isinstance(first_shape, point_type): polygons, vertices = voronoi_frames(get_points_array(polygons)) polygons = list(polygons.geometry) + neighbors, ids = _build(polygons, criterion=criterion, ids=ids) - W.__init__(self, neighbors, ids=ids, **kw) + + W.__init__(self, neighbors, ids=ids, **kwargs) @classmethod def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): @@ -69,14 +73,15 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): Write out the entire path for a shapefile (``True``) or only the base of the shapefile without extension (``False``). Default is ``False``. - **kwargs : keyword arguments - ``'sparse'`` should be included here. - If ``True`` return `WSP` instance. If ``False`` return `W` instance. + **kwargs : dict + Keyword arguments for ``libpysal.weights.Rook``. ``'sparse'`` + should be included here. If ``True`` return `WSP` instance. + If ``False`` return `W` instance. Returns ------- - w : libpysal.weights.weights.W - An instance of spatial weights. + w : libpysal.weights.Rook + A rook-style instance of spatial weights. Examples -------- @@ -100,20 +105,24 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): See Also -------- - libpysal.weights.weights.W - libpysal.weights.contiguity.Rook + libpysal.weights.W + libpysal.weights.Rook """ sparse = kwargs.pop("sparse", False) + if idVariable is not None: ids = get_ids(filepath, idVariable) else: ids = None + w = cls(FileIO(filepath), ids=ids, **kwargs) w.set_shapefile(filepath, idVariable=idVariable, full=full) + if sparse: w = w.to_WSP() + return w @classmethod @@ -128,22 +137,29 @@ def from_iterable(cls, iterable, sparse=False, **kwargs): support iteration. Can be either Shapely or PySAL shapes. sparse : bool Generate ``WSP`` object. - **kwargs : keyword arguments - Optional arguments for ``pysal.weights.W``. + **kwargs : dict + Keyword arguments for ``libpysal.weights.Rook``. + + Returns + ------- + w : libpysal.weights.Rook + A rook-style instance of spatial weights. See Also -------- - libpysal.weights.weights.W - libpysal.weights.weights.WSP - libpysal.weights.contiguity.Rook + libpysal.weights.W + libpysal.weights.WSP + libpysal.weights.Rook """ new_iterable = iter(iterable) w = cls(new_iterable, **kwargs) + if sparse: w = WSP.from_W(w) + return w @classmethod @@ -167,16 +183,23 @@ def from_dataframe( ids : list A list of ids to use to index the spatial weights object. Order is not respected from this list. Default is ``None``. - id_order : list + id_order : list An ordered list of ids to use to index the spatial weights object. If used, the resulting weights object will iterate over results in the order of the names provided in this argument. Default is ``None``. + **kwargs : dict + Keyword arguments for ``libpysal.weights.Rook``. + Returns + ------- + w : w : libpysal.weights.Rook + A rook-style instance of spatial weights. + See Also -------- - libpysal.weights.weights.W - libpysal.weights.contiguity.Rook + libpysal.weights.W + libpysal.weights.Rook """ @@ -191,10 +214,13 @@ def from_dataframe( ids = df.get(idVariable).tolist() elif isinstance(ids, str): ids = df.get(ids).tolist() - return cls.from_iterable( + + w = cls.from_iterable( df[geom_col].tolist(), ids=ids, id_order=id_order, **kwargs ) + return w + class Queen(W): """Construct a weights object from a collection of PySAL @@ -204,27 +230,31 @@ class Queen(W): ---------- polygons : list A collection of PySAL shapes from which to build weights. - **kw : keyword arguments - Optional arguments for ``pysal.weights.W``. The parameter ``ids``, + **kwargs : dict + Keyword arguments for ``pysal.weights.W``. The parameter ``ids``, a list of names to use to build the weights, should be included here. See Also -------- - libpysal.weights.weights.W + libpysal.weights.W """ - def __init__(self, polygons, **kw): + def __init__(self, polygons, **kwargs): + criterion = "queen" - ids = kw.pop("ids", None) + ids = kwargs.pop("ids", None) polygons, backup = itertools.tee(polygons) first_shape = next(iter(backup)) + if isinstance(first_shape, point_type): polygons, vertices = voronoi_frames(get_points_array(polygons)) polygons = list(polygons.geometry) + neighbors, ids = _build(polygons, criterion=criterion, ids=ids) - W.__init__(self, neighbors, ids=ids, **kw) + + W.__init__(self, neighbors, ids=ids, **kwargs) @classmethod def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): @@ -242,14 +272,15 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): Write out the entire path for a shapefile (``True``) or only the base of the shapefile without extension (``False``). Default is ``False``. - **kwargs : keyword arguments - ``'sparse'`` should be included here. - If ``True`` return `WSP` instance. If ``False`` return `W` instance. + **kwargs : dict + Keyword arguments for ``libpysal.weights.Queen``. ``'sparse'`` + should be included here. If ``True`` return `WSP` instance. + If ``False`` return `W` instance. Returns ------- - w : libpysal.weights.weights.W - An instance of spatial weights. + w : libpysal.weights.Queen + A queen-style instance of spatial weights. Examples -------- @@ -276,20 +307,24 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): See Also -------- - libpysal.weights.weights.W - libpysal.weights.contiguity.Queen + libpysal.weights.W + libpysal.weights.Queen """ sparse = kwargs.pop("sparse", False) + if idVariable is not None: ids = get_ids(filepath, idVariable) else: ids = None + w = cls(FileIO(filepath), ids=ids, **kwargs) w.set_shapefile(filepath, idVariable=idVariable, full=full) + if sparse: w = w.to_WSP() + return w @classmethod @@ -304,22 +339,29 @@ def from_iterable(cls, iterable, sparse=False, **kwargs): support iteration. Can be either Shapely or PySAL shapes. sparse : bool Generate ``WSP`` object. - **kwargs : keyword arguments - Optional arguments for ``pysal.weights.W``. + **kwargs : dict + Keyword arguments for ``libpysal.weights.Queen``. + + Returns + ------- + w : libpysal.weights.Queen + A queen-style instance of spatial weights. See Also -------- - libpysal.weights.weights.W - libpysal.weights.weights.WSP - libpysal.weights.contiguity.Queen + libpysal.weights.W + libpysal.weights.WSP + libpysal.weights.Queen """ new_iterable = iter(iterable) w = cls(new_iterable, **kwargs) + if sparse: w = WSP.from_W(w) + return w @classmethod @@ -341,22 +383,28 @@ def from_dataframe(cls, df, geom_col="geometry", **kwargs): ids : list A list of ids to use to index the spatial weights object. Order is not respected from this list. Default is ``None``. - id_order : list + id_order : list An ordered list of ids to use to index the spatial weights object. If used, the resulting weights object will iterate over results in the order of the names provided in this argument. Default is ``None``. - + + Returns + ------- + w : libpysal.weights.Queen + A queen-style instance of spatial weights. + See Also -------- - libpysal.weights.weights.W - libpysal.weights.contiguity.Queen + libpysal.weights.W + libpysal.weights.Queen """ idVariable = kwargs.pop("idVariable", None) ids = kwargs.pop("ids", None) id_order = kwargs.pop("id_order", None) + if id_order is not None: if id_order is True and ((idVariable is not None) or (ids is not None)): # if idVariable is None, we want ids. Otherwise, we want the @@ -370,9 +418,11 @@ def from_dataframe(cls, df, geom_col="geometry", **kwargs): ids = df.get(idVariable).tolist() elif isinstance(ids, str): ids = df.get(ids).tolist() + w = cls.from_iterable( df[geom_col].tolist(), ids=ids, id_order=id_order, **kwargs ) + return w @@ -383,19 +433,19 @@ def Voronoi(points, criterion="rook", clip="ahull", **kwargs): Parameters ---------- points : array-like - An array-like (n,2) object of coordinates for point locations. + An array-like ``(n,2)`` object of coordinates for point locations. criterion : str The weight criterion, either ``'rook'`` or ``'queen'``. Default is ``'rook'``. clip : : str, shapely.geometry.Polygon An overloaded option about how to clip the voronoi cells. Default is ``'ahull'``. - See ``libpysal.cg.voronoi.voronoi_frames()`` for more explanation. - **kwargs : keyword arguments - The arguments to pass to ``Rook``, the underlying contiguity class. + See ``libpysal.cg.voronoi_frames()`` for more explanation. + **kwargs : dict + Keyword arguments to pass to ``libpysal.weights.Voronoi``. Returns ------- - w : libpysal.weights.weights.W - An instance of spatial weights. + w : libpysal.weights.Voronoi + A voronoi-style instance of spatial weights. Examples -------- @@ -413,6 +463,7 @@ def Voronoi(points, criterion="rook", clip="ahull", **kwargs): from ..cg.voronoi import voronoi_frames region_df, _ = voronoi_frames(points, clip=clip) + if criterion.lower() == "queen": cls = Queen elif criterion.lower() == "rook": @@ -422,7 +473,10 @@ def Voronoi(points, criterion="rook", clip="ahull", **kwargs): "Contiguity criterion {} not supported. " 'Only "rook" and "queen" are supported.'.format(criterion) ) - return cls.from_dataframe(region_df, **kwargs) + + w = cls.from_dataframe(region_df, **kwargs) + + return w def _from_dataframe(df, **kwargs): @@ -432,11 +486,13 @@ def _from_dataframe(df, **kwargs): ---------- df : pandas.DataFrame A dataframe containing point geometries for a Voronoi diagram. + **kwargs : dict + Keyword arguments to pass to ``libpysal.weights.Voronoi``. Returns ------- - w : libpysal.weights.weights.W - An instance of spatial weights. + w : libpysal.weights.Vornoi + A voronoi-style instance of spatial weights. Notes ----- @@ -461,7 +517,10 @@ def _from_dataframe(df, **kwargs): "You may consider using df.centroid." ) coords = numpy.column_stack((x, y)) - return Voronoi(coords, **kwargs) + + w = Voronoi(coords, **kwargs) + + return w Voronoi.from_dataframe = _from_dataframe @@ -537,8 +596,7 @@ def buildContiguity(polygons, criterion="rook", ids=None): constructors for ``Rook`` or ``Queen``. """ - # Warn('This function is deprecated. Please use the Rook or Queen classes', - # UserWarning) + # Warn('This function is deprecated. Please use the Rook or Queen classes', UserWarning) if criterion.lower() == "rook": return Rook(polygons, ids=ids) elif criterion.lower() == "queen": diff --git a/libpysal/weights/distance.py b/libpysal/weights/distance.py index fc9d77b41..d7297ffa3 100644 --- a/libpysal/weights/distance.py +++ b/libpysal/weights/distance.py @@ -22,8 +22,8 @@ def knnW(data, k=2, p=2, ids=None, radius=None, distance_metric="euclidean"): - """This is deprecated. Use the ``libpysal.weights.KNN class instead.``""" - # Warn("This function is deprecated. Please use pysal.weights.KNN", UserWarning) + """This is deprecated. Use the ``libpysal.weights.KNN`` class instead.""" + # Warn("This function is deprecated. Please use libpysal.weights.KNN", UserWarning) return KNN(data, k=k, p=p, ids=ids, radius=radius, distance_metric=distance_metric) @@ -32,7 +32,7 @@ class KNN(W): Parameters ---------- - data : {libpysal.cg.kdtree.KDTree, libpysal.cg.kdtree.ArcKDTree} + data : {libpysal.cg.KDTree, libpysal.cg.ArcKDTree} An ``(n,k)`` array of `n` observations on `k` characteristics used to measure distances between the `n` objects. k : int @@ -47,16 +47,16 @@ class KNN(W): radius : float If supplied arc distances will be calculated based on the given radius and ``p`` will be ignored. Default is ``None``. - See ``libpysal.cg.kdtree.KDTree`` for more details. + See ``libpysal.cg.KDTree`` for more details. distance_metric : str Either ``'euclidean'`` or ``'arc'``. Default is ``'euclidean'``. - See ``libpysal.cg.kdtree.KDTree`` for more details. + See ``libpysal.cg.KDTree`` for more details. **kwargs : dict - Keyword arguments for ``libpysal.weights.weights.W``. + Keyword arguments for ``libpysal.weights.W``. Returns ------- - w : libpysal.weights.distance.KNN + w : libpysal.weights.KNN A `k` nearest neighbors weights instance. Examples @@ -94,7 +94,7 @@ class KNN(W): See Also -------- - libpysal.weights.weights.W + libpysal.weights.W """ @@ -149,13 +149,13 @@ def from_shapefile(cls, filepath, *args, **kwargs): The name of polygon shapefile (including the file extension) containing attribute data. *args : iterable - Positional arguments for ``libpysal.weights.distance.KNN``. + Positional arguments for ``libpysal.weights.KNN``. **kwargs : dict - Keyword arguments for ``libpysal.weights.distance.KNN``. + Keyword arguments for ``libpysal.weights.KNN``. Returns ------- - w : libpysal.weights.distance.KNN + w : libpysal.weights.KNN A `k` nearest neighbors weights instance. Examples @@ -193,7 +193,7 @@ def from_shapefile(cls, filepath, *args, **kwargs): See Also -------- - libpysal.weights.weights.W + libpysal.weights.W """ @@ -211,13 +211,13 @@ def from_array(cls, array, *args, **kwargs): An ``(n, k)`` array representing `n` observations on `k` characteristics used to measure distances between the `n` objects. *args : iterable - Positional arguments for ``libpysal.weights.distance.KNN``. + Positional arguments for ``libpysal.weights.KNN``. **kwargs : dict - Keyword arguments for ``libpysal.weights.distance.KNN``. + Keyword arguments for ``libpysal.weights.KNN``. Returns ------- - w : libpysal.weights.distance.KNN + w : libpysal.weights.KNN A `k` nearest neighbors weights instance. Examples @@ -252,7 +252,7 @@ def from_array(cls, array, *args, **kwargs): See Also -------- - libpysal.weights.weights.W + libpysal.weights.W """ @@ -277,19 +277,19 @@ def from_dataframe(cls, df, geom_col="geometry", ids=None, *args, **kwargs): If iterable, a list of ids to use for the `W`. If ``None``, ``df.index`` is used. Default is ``None``. *args : iterable - Positional arguments for ``libpysal.weights.distance.KNN``. + Positional arguments for ``libpysal.weights.KNN``. **kwargs : dict - Keyword arguments for ``libpysal.weights.distance.KNN``. + Keyword arguments for ``libpysal.weights.KNN``. Returns ------- - w : libpysal.weights.distance.KNN + w : libpysal.weights.KNN A `k` nearest neighbors weights instance. See Also -------- - libpysal.weights.weights.W + libpysal.weights.W """ @@ -328,7 +328,7 @@ def reweight(self, k=None, p=None, new_data=None, new_ids=None, inplace=True): Returns ------- - w : libpysal.weights.distance.KNN + w : libpysal.weights.KNN A copy of the `k` nearest neighbors weights instance using the new parameterization, or ``None`` if the object is reweighted in place. @@ -368,7 +368,7 @@ class Kernel(W): Parameters ---------- - data : {libpysal.cg.kdtree.KDTree, libpysal.cg.kdtree.ArcKDTree} + data : {libpysal.cg.KDTree, libpysal.cg.ArcKDTree} An ``(n,k)`` array of `n` observations on `k` characteristics used to measure distances between the `n` objects. k : int @@ -393,10 +393,10 @@ class Kernel(W): radius : float If supplied arc distances will be calculated based on the given radius and ``p`` will be ignored. Default is ``None``. - See ``libpysal.cg.kdtree.KDTree`` for more details. + See ``libpysal.cg.KDTree`` for more details. distance_metric : str Either ``'euclidean'`` or ``'arc'``. Default is ``'euclidean'``. - See ``libpysal.cg.kdtree.KDTree`` for more details. + See ``libpysal.cg.KDTree`` for more details. function : str Either ``'triangular'``, ``'uniform'``, ``'quadratic'``, ``'quartic'``, or ``'gaussian'``. The kernel function is defined as follows with @@ -436,7 +436,7 @@ class Kernel(W): K(z) = (2\\pi)^{(-1/2)} exp(-z^2 / 2) **kwargs : dict - Keyword arguments for ``libpysal.weights.weights.W``. + Keyword arguments for ``libpysal.weights.W``. Attributes ---------- @@ -594,17 +594,17 @@ def from_shapefile(cls, filepath, idVariable=None, **kwargs): idVariable : str The name of the column in shapefile's DBF to use for ids. **kwargs : dict - Keyword arguments for ``libpysal.weights.distance.Kernel``. + Keyword arguments for ``libpysal.weights.Kernel``. Returns ------- - w : libpysal.weights.distance.Kernel + w : libpysal.weights.Kernel A kernel weights instance. See Also --------- - libpysal.weights.weights.W + libpysal.weights.W """ @@ -629,17 +629,17 @@ def from_array(cls, array, **kwargs): An ``(n, k)`` array representing `n` observations on `k` characteristics used to measure distances between the `n` objects. **kwargs : dict - Keyword arguments for ``libpysal.weights.distance.Kernel``. + Keyword arguments for ``libpysal.weights.Kernel``. Returns ------- - w : libpysal.weights.distance.Kernel + w : libpysal.weights.Kernel A kernel weights instance. See Also -------- - libpysal.weights.weights.W + libpysal.weights.W """ @@ -664,17 +664,17 @@ def from_dataframe(cls, df, geom_col="geometry", ids=None, **kwargs): If iterable, a list of ids to use for the `W`. If ``None``, ``df.index`` is used. Default is ``None``. **kwargs : dict - Keyword arguments for ``libpysal.weights.distance.Kernel``. + Keyword arguments for ``libpysal.weights.Kernel``. Returns ------- - w : libpysal.weights.distance.Kernel + w : libpysal.weights.Kernel A kernel weights instance. See Also -------- - libpysal.weights.weights.W + libpysal.weights.W """ @@ -790,7 +790,7 @@ class DistanceBand(W): Parameters ---------- - data : {array-like, libpysal.cg.kdtree.KDTree} + data : {array-like, libpysal.cg.KDTree} ``(n,k)`` or ``KDTree`` where ``KDtree.data`` is an ``(n,k)`` array of `n` observations on `k` characteristics used to measure distances between the `n` objects @@ -822,10 +822,10 @@ class DistanceBand(W): radius : float If supplied arc distances will be calculated based on the given radius and ``p`` will be ignored. Default is ``None``. - See ``libpysal.cg.kdtree.KDTree`` for more details. + See ``libpysal.cg.KDTree`` for more details. distance_metric : str Either ``'euclidean'`` or ``'arc'``. Default is ``'euclidean'``. - See ``libpysal.cg.kdtree.KDTree`` for more details. + See ``libpysal.cg.KDTree`` for more details. Attributes ---------- @@ -953,11 +953,11 @@ def from_shapefile(cls, filepath, threshold, idVariable=None, **kwargs): idVariable : str The name of the column in shapefile's DBF to use for ids. **kwargs : dict - Keyword arguments for ``libpysal.weights.distance.DistanceBand``. + Keyword arguments for ``libpysal.weights.DistanceBand``. Returns ------- - w : libpysal.weights.distance.DistanceBand + w : libpysal.weights.DistanceBand A distance band weights instance. """ @@ -985,11 +985,11 @@ def from_array(cls, array, threshold, **kwargs): threshold : float The distance band. **kwargs : dict - Keyword arguments for ``libpysal.weights.distance.DistanceBand``. + Keyword arguments for ``libpysal.weights.DistanceBand``. Returns ------- - w : libpysal.weights.distance.DistanceBand + w : libpysal.weights.DistanceBand A distance band weights instance. """ @@ -1017,11 +1017,11 @@ def from_dataframe(cls, df, threshold, geom_col="geometry", ids=None, **kwargs): If iterable, a list of ids to use for the `W`. If ``None``, ``df.index`` is used. Default is ``None``. **kwargs : dict - Keyword arguments for ``libpysal.weights.distance.DistanceBand``. + Keyword arguments for ``libpysal.weights.DistanceBand``. Returns ------- - w : libpysal.weights.distance.DistanceBand + w : libpysal.weights.DistanceBand A distance band weights instance. """ From 12b66de2bd5043217adf578a3c3509863957b394 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Fri, 21 Aug 2020 10:13:56 -0400 Subject: [PATCH 15/33] updating typos in weights/_contW_lists.py and weights/adjtools.py --- libpysal/weights/_contW_lists.py | 14 +++++---- libpysal/weights/adjtools.py | 49 +++++++++++++++++--------------- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/libpysal/weights/_contW_lists.py b/libpysal/weights/_contW_lists.py index 10b46fdf5..887e23ace 100644 --- a/libpysal/weights/_contW_lists.py +++ b/libpysal/weights/_contW_lists.py @@ -16,10 +16,10 @@ def _get_verts(shape): def _get_boundary_points(shape) -> list: + """Recursively handle polygons vs. multipolygons + to extract the boundary point set from each. """ - Recursively handle polygons vs. multipolygons to - extract the boundary point set from each. - """ + if shape.type.lower() == "polygon": shape = shape.boundary return _get_boundary_points(shape) @@ -38,9 +38,8 @@ def _get_boundary_points(shape) -> list: class ContiguityWeightsLists: - """ - Contiguity for a collection of polygons using high performance - list, set, and dict containers. + """Contiguity for a collection of polygons using high + performance ``list``, ``set``, and ``dict`` containers. """ def __init__(self, collection, wttype=1): @@ -54,11 +53,13 @@ def __init__(self, collection, wttype=1): 1: Queen; 2: Rook """ + self.collection = list(collection) self.wttype = wttype self.jcontiguity() def jcontiguity(self): + numPoly = len(self.collection) w = {} @@ -119,4 +120,5 @@ def jcontiguity(self): pass else: raise Exception("Weight type {} not understood!".format(self.wttype)) + self.w = w diff --git a/libpysal/weights/adjtools.py b/libpysal/weights/adjtools.py index 14bf60d31..039108d6d 100644 --- a/libpysal/weights/adjtools.py +++ b/libpysal/weights/adjtools.py @@ -10,9 +10,9 @@ def adjlist_apply(X, W=None, alist=None, func=np.subtract, skip_verify=False): An `(N,P)`-length iterable to apply ``func`` to. If (N,1), then ``func`` must take 2 arguments and return a single reduction. If `P`>1, then ``func`` must take two `P`-length arrays and return a single reduction of them. - W : pysal.weights.W object + W : libpysal.weights.W A weights object that provides adjacency information. - alist : pandas DataFrame + alist : pandas.DataFrame A table containing an adajacency list representation of a `W` matrix. func : callable A function taking two arguments and returning a single argument. This will @@ -33,15 +33,15 @@ def adjlist_apply(X, W=None, alist=None, func=np.subtract, skip_verify=False): Returns ------- alist_atts : list - An adjacency list (or modifies alist inplace) with the - function applied to each row. + An adjacency list (or modifies ``alist`` inplace) + with the function applied to each row. """ try: import pandas as pd except ImportError: - raise ImportError("pandas must be installed to use this function") + raise ImportError("Pandas must be installed to use this function.") W, alist = _get_W_and_alist(W, alist, skip_verify=skip_verify) @@ -75,20 +75,21 @@ def adjlist_apply(X, W=None, alist=None, func=np.subtract, skip_verify=False): def _adjlist_mvapply(X, W=None, alist=None, func=None, skip_verify=False): """This function is used when ``X`` is multi-dimensional. - See ``weights.adjtools.adjlist_apply()`` for Parameters and Returns information. + See ``libpysal.weights.adjtools.adjlist_apply()`` + for parameters and returns information. """ try: import pandas as pd except ImportError: - raise ImportError("pandas must be installed to use this function") + raise ImportError("Pandas must be installed to use this function.") - assert len(X.shape) == 2, "data is not two-dimensional" + assert len(X.shape) == 2, "Data is not two-dimensional." W, alist = _get_W_and_alist(W=W, alist=alist, skip_verify=skip_verify) - assert X.shape[0] == W.n, "number of samples in X does not match W" + assert X.shape[0] == W.n, "The number of samples in X does not match W." try: names = X.columns.tolist() @@ -131,9 +132,10 @@ def _get_W_and_alist(W, alist, skip_verify=False): 3. raise ValueError if neither are provided, 4. raise AssertionError if both W and adjlist are provided and don't match. - If this completes successfully, the `W` and ``adjlist`` will both - be returned and are checked for equality. See ``weights.adjtools.adjlist_apply()`` - for Parameters and Returns information. + If this completes successfully, the `W` and ``adjlist`` + will both be returned and are checked for equality. + See ``libpysal.weights.adjtools.adjlist_apply()`` + for parameters and returns information. """ @@ -146,7 +148,7 @@ def _get_W_and_alist(W, alist, skip_verify=False): W = W.from_adjlist(alist) elif (W is None) and (alist is None): - raise ValueError("Either W or Adjacency List must be provided") + raise ValueError("Either W or Adjacency List must be provided.") elif (W is not None) and (alist is not None) and (not skip_verify): from .weights import W as W_ @@ -166,7 +168,7 @@ def adjlist_map( focal_col="focal", neighbor_col="neighbor", ): - """ Map a set of functions over a `W` or adjacency list. + """Map a set of functions over a `W` or an adjacency list. Parameters ---------- @@ -177,8 +179,8 @@ def adjlist_map( list of functions to apply to each column of `P`. This function must take two arguments, compare them, and return a value. Examples may be ``lambda x,y: x < y`` or ``np.subtract``. - W : pysal.weights.W - A pysal weights object. If not provided, one is + W : libpysal.weights.W + A PySAL weights object. If not provided, one is constructed from the given adjacency list. alist : pandas.Dataframe An adjacency list representation of a weights matrix. If not @@ -201,7 +203,7 @@ def adjlist_map( try: import pandas as pd except ImportError: - raise ImportError("pandas must be installed to use this function") + raise ImportError("Pandas must be installed to use this function.") if isinstance(data, pd.DataFrame): names = data.columns @@ -209,7 +211,9 @@ def adjlist_map( else: names = [str(i) for i in range(data.shape[1])] - assert data.shape[0] == W.n, "shape of data does not match shape of adjacency" + assert ( + data.shape[0] == W.n + ), "The shape of 'data' does not match the shape of 'adjacency'." if callable(funcs): funcs = (funcs,) @@ -219,7 +223,7 @@ def adjlist_map( assert data.shape[1] == len( funcs - ), "shape of data does not match the number of functions provided" + ), "The shape of 'data' does not match the number of functions provided." W, alist = _get_W_and_alist(W, alist) fnames = set([f.__name__ for f in funcs]) @@ -236,11 +240,10 @@ def adjlist_map( def filter_adjlist(adjlist, focal_col="focal", neighbor_col="neighbor"): - """ - This deduplicates an adjacency list by examining both ``(a,b)` and `(b,a)` + """This deduplicates an adjacency list by examining both `(a,b)` and `(b,a)` when `(a,b)` is encountered. The removal is done in order of the iteration order of the input adjacency list. So, if a special order of removal is - desired, you need to sort the list before this function. + desired, you need to sort the list before this function. Parameters ---------- @@ -254,7 +257,7 @@ def filter_adjlist(adjlist, focal_col="focal", neighbor_col="neighbor"): Returns ------- adjlist : pandas.DataFrame - An adjacency table with reversible entries removed. + An adjacency table with reversible entries removed. """ From 154216e02b5648e9d8f7d49b33a98bd4a889815a Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Fri, 21 Aug 2020 14:43:12 -0400 Subject: [PATCH 16/33] updating weights/spintW.py --- libpysal/weights/adjtools.py | 30 ++-- libpysal/weights/contiguity.py | 20 +-- libpysal/weights/distance.py | 27 ++-- libpysal/weights/spintW.py | 283 ++++++++++++++++++++------------- libpysal/weights/user.py | 2 +- 5 files changed, 208 insertions(+), 154 deletions(-) diff --git a/libpysal/weights/adjtools.py b/libpysal/weights/adjtools.py index 039108d6d..c4875ae1e 100644 --- a/libpysal/weights/adjtools.py +++ b/libpysal/weights/adjtools.py @@ -11,14 +11,15 @@ def adjlist_apply(X, W=None, alist=None, func=np.subtract, skip_verify=False): must take 2 arguments and return a single reduction. If `P`>1, then ``func`` must take two `P`-length arrays and return a single reduction of them. W : libpysal.weights.W - A weights object that provides adjacency information. + A weights object that provides adjacency information. Default is ``None``. alist : pandas.DataFrame A table containing an adajacency list representation of a `W` matrix. + Default is ``None``. func : callable A function taking two arguments and returning a single argument. This will be evaluated for every (focal, neighbor) pair, or each row of the adjacency list. If ``X`` has more than one column, this function should take two arrays - and provide a single scalar in return. + and provide a single scalar in return. Default is ``np.subtract``. Example scalars include: ``lambda x,y: x < y, np.subtract`` Example multivariates: @@ -29,6 +30,7 @@ def adjlist_apply(X, W=None, alist=None, func=np.subtract, skip_verify=False): Whether or not to skip verifying that the `W` is the same as an adjacency list. Do this if you are certain the adjacency list and ``W`` agree and would like to avoid re-instantiating a `W` from the adjacency list. + Default is ``False``. Returns ------- @@ -126,15 +128,11 @@ def _adjlist_mvapply(X, W=None, alist=None, func=None, skip_verify=False): def _get_W_and_alist(W, alist, skip_verify=False): - """ Either: - 1. compute a `W` from an alist - 2. adjacencylist from a W - 3. raise ValueError if neither are provided, - 4. raise AssertionError if both W and adjlist are provided and don't match. - - If this completes successfully, the `W` and ``adjlist`` - will both be returned and are checked for equality. - See ``libpysal.weights.adjtools.adjlist_apply()`` + """ Either (1) compute a ``W`` from an ``alist``; (2) compute an adjacency list + from a ``W``; (3) raise a ``ValueError`` if neither are provided; or (4) raise an + ``AssertionError`` if both ``W`` and ``adjlist`` are provided and don't match. + If this completes successfully, the ``W`` and ``adjlist`` will both be returned and + are checked for equality. See ``libpysal.weights.adjtools.adjlist_apply()`` for parameters and returns information. """ @@ -179,18 +177,21 @@ def adjlist_map( list of functions to apply to each column of `P`. This function must take two arguments, compare them, and return a value. Examples may be ``lambda x,y: x < y`` or ``np.subtract``. + Default is ``(np.subtract,)``. W : libpysal.weights.W A PySAL weights object. If not provided, one is - constructed from the given adjacency list. + constructed from the given adjacency list. Default is ``None``. alist : pandas.Dataframe An adjacency list representation of a weights matrix. If not provided, one is constructed from the weights object. If both are provided, they are validated against one another to ensure they - provide identical weights matrices. + provide identical weights matrices. Default is ``None``. focal_col : str The name of column in ``alist`` containing the focal observation ids. + Default is ``'focal'``. neighbor_col : str The name of column in ``alist`` containing the neighboring observation ids. + Default is ``'neighbor'``. Returns ------- @@ -250,9 +251,10 @@ def filter_adjlist(adjlist, focal_col="focal", neighbor_col="neighbor"): adjlist : pandas.DataFrame A dataframe that contains focal and neighbor columns. focal_col : str - The name of the column with the focal observation id. + The name of the column with the focal observation id. Default is ``'focal'``. neighbor_col : str The name of the column with the neighbor observation id. + Default is ``'neighbor'``. Returns ------- diff --git a/libpysal/weights/contiguity.py b/libpysal/weights/contiguity.py index 004730ea9..e931ab83d 100644 --- a/libpysal/weights/contiguity.py +++ b/libpysal/weights/contiguity.py @@ -136,7 +136,7 @@ def from_iterable(cls, iterable, sparse=False, **kwargs): A collection of of shapes to be cast to PySAL shapes. Must support iteration. Can be either Shapely or PySAL shapes. sparse : bool - Generate ``WSP`` object. + Generate ``WSP`` object. Default is ``False``. **kwargs : dict Keyword arguments for ``libpysal.weights.Rook``. @@ -176,7 +176,7 @@ def from_dataframe( A ``pandas.DataFrame` containing geometries to use for spatial weights. geom_col : str The name of the column in ``df`` that contains the - geometries. Default is ``geometry``. + geometries. Default is ``'geometry'``. idVariable : str The name of the column to use as IDs. If nothing is provided, the dataframe index is used. Default is ``None``. @@ -338,7 +338,7 @@ def from_iterable(cls, iterable, sparse=False, **kwargs): A collection of of shapes to be cast to PySAL shapes. Must support iteration. Can be either Shapely or PySAL shapes. sparse : bool - Generate ``WSP`` object. + Generate ``WSP`` object. Default is ``False``. **kwargs : dict Keyword arguments for ``libpysal.weights.Queen``. @@ -376,17 +376,7 @@ def from_dataframe(cls, df, geom_col="geometry", **kwargs): A ``pandas.DataFrame` containing geometries to use for spatial weights. geom_col : str The name of the column in ``df`` that contains the - geometries. Default is ``geometry``. - idVariable : str - The name of the column to use as IDs. If nothing is provided, the - dataframe index is used. Default is ``None``. - ids : list - A list of ids to use to index the spatial weights object. - Order is not respected from this list. Default is ``None``. - id_order : list - An ordered list of ids to use to index the spatial weights object. If - used, the resulting weights object will iterate over results in the - order of the names provided in this argument. Default is ``None``. + geometries. Default is ``'geometry'``. Returns ------- @@ -537,7 +527,7 @@ def _build(polygons, criterion="rook", ids=None): Option of which kind of contiguity to build, either ``'rook'`` or ``'queen'``. Default is ``'rook'``. ids : list - A list of ids to use to index the neighbor dictionary. + A list of ids to use to index the neighbor dictionary. Default is ``None``. Returns ------- diff --git a/libpysal/weights/distance.py b/libpysal/weights/distance.py index d7297ffa3..53c01b1fa 100644 --- a/libpysal/weights/distance.py +++ b/libpysal/weights/distance.py @@ -37,7 +37,7 @@ class KNN(W): used to measure distances between the `n` objects. k : int The number of nearest neighbors. Default is ``2``. - p : float + p : {int, float} Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. ``2`` is Euclidean distance and ``1`` is Manhattan distance. This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. @@ -271,7 +271,7 @@ def from_dataframe(cls, df, geom_col="geometry", ids=None, *args, **kwargs): to construct a `W` object. geom_col : string The column name of the geometry stored in ``df``. - Default is ``geometry``. + Default is ``'geometry'``. ids : {str, iterable} If string, the column name of the indices from the dataframe. If iterable, a list of ids to use for the `W`. @@ -311,7 +311,7 @@ def reweight(self, k=None, p=None, new_data=None, new_ids=None, inplace=True): ---------- k : int The number of nearest neighbors. Default is ``None``. - p : float + p : {int, float} Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. ``2`` is Euclidean distance and ``1`` is Manhattan distance. This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. @@ -399,7 +399,8 @@ class Kernel(W): See ``libpysal.cg.KDTree`` for more details. function : str Either ``'triangular'``, ``'uniform'``, ``'quadratic'``, ``'quartic'``, - or ``'gaussian'``. The kernel function is defined as follows with + or ``'gaussian'``. Default is ``'triangular'``. + The kernel function is defined as follows with .. math:: @@ -593,6 +594,7 @@ def from_shapefile(cls, filepath, idVariable=None, **kwargs): containing attribute data. idVariable : str The name of the column in shapefile's DBF to use for ids. + Default is ``None``. **kwargs : dict Keyword arguments for ``libpysal.weights.Kernel``. @@ -658,7 +660,7 @@ def from_dataframe(cls, df, geom_col="geometry", ids=None, **kwargs): to construct a PySAL ``W`` object. geom_col : str The column name of the geometry stored in ``df``. - Default is ``geometry``. + Default is ``'geometry'``. ids : {str, iterable} If string, the column name of the indices from the dataframe. If iterable, a list of ids to use for the `W`. @@ -695,7 +697,7 @@ def _k_to_W(self, ids=None): Parameters ---------- ids : list - See ``ids`` in ``Kernel``. + See ``ids`` in ``Kernel``. Default is ``None``. Returns ------- @@ -793,10 +795,10 @@ class DistanceBand(W): data : {array-like, libpysal.cg.KDTree} ``(n,k)`` or ``KDTree`` where ``KDtree.data`` is an ``(n,k)`` array of `n` observations on `k` characteristics used to measure - distances between the `n` objects + distances between the `n` objects. threshold : float The distance band. - p : float + p : {int, float} Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. ``2`` is Euclidean distance and ``1`` is Manhattan distance. This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. @@ -817,7 +819,7 @@ class DistanceBand(W): sparsity of the of distance matrix and the ``threshold`` that is applied. Default is ``True``. silence_warnings : bool - By default (`True``) libpysal will print a warning if the dataset contains any + By default (`False``) libpysal will print a warning if the dataset contains any disconnected observations or islands. To silence this warning set to ``True``. radius : float If supplied arc distances will be calculated based on the given radius @@ -952,6 +954,7 @@ def from_shapefile(cls, filepath, threshold, idVariable=None, **kwargs): The distance band. idVariable : str The name of the column in shapefile's DBF to use for ids. + Default is ``None``. **kwargs : dict Keyword arguments for ``libpysal.weights.DistanceBand``. @@ -1011,7 +1014,7 @@ def from_dataframe(cls, df, threshold, geom_col="geometry", ids=None, **kwargs): The distance band. geom_col : str The column name of the geometry stored in ``df``. - Default is ``geometry``. + Default is ``'geometry'``. ids : {str, iterable} If string, the column name of the indices from the dataframe. If iterable, a list of ids to use for the `W`. @@ -1059,7 +1062,7 @@ def _distance_to_W(self, ids=None): Parameters ---------- ids : list - See ``ids`` in ``DistanceBand``. + See ``ids`` in ``DistanceBand``. Default is ``None``. Returns ------- @@ -1104,7 +1107,7 @@ def _spdistance_matrix(self, x, y, threshold=None): y : array-like Y values. threshold : float - See ``threshold`` in ``DistanceBand``. + See ``threshold`` in ``DistanceBand``. Default is ``None``. Returns ------- diff --git a/libpysal/weights/spintW.py b/libpysal/weights/spintW.py index 48545bce5..1ea4fa9d1 100644 --- a/libpysal/weights/spintW.py +++ b/libpysal/weights/spintW.py @@ -1,7 +1,6 @@ """ Spatial weights for spatial interaction including contiguity OD weights (ODW), network based weights (netW), and distance-decay based vector weights (vecW). - """ __author__ = "Taylor Oshan " @@ -11,28 +10,35 @@ from .distance import DistanceBand from collections import OrderedDict -def ODW(Wo, Wd, transform='r', silence_warnings=True): - """ - Constructs an o*d by o*d origin-destination style spatial weight for o*d - flows using standard spatial weights on o origins and d destinations. Input - spatial weights must be binary or able to be sutiably transformed to binary. + +def ODW(Wo, Wd, transform="r", silence_warnings=True): + """Construct an :math:`o \cdot d \times o \cdot d` + origin-destination style spatial weight for :math:`o \cdot d` + flows using standard spatial weights on :math:`o` origins + and :math:`d` destinations. Input spatial weights must be + binary or able to be sutiably transformed to binary. Parameters ---------- - Wo : W object for origin locations - o x o spatial weight object amongst o origins - - Wd : W object for destination locations - d x d spatial weight object amongst d destinations - - transform : Transformation for standardization of final OD spatial weight; default - is 'r' for row standardized + Wo : libpysal.weights.W + A `W` object for origin locations as a :math:`o \cdot o` + spatial weight object amongst :math:`o` origins. + Wd : libpysal.weights.W + A `W` object for destination locations as a :math:`d \cdot d` + spatial weight object amongst :math:`d` destinations + transform : str + A transformation for standardization of final the + `OD` spatial weights. Default is ``'r'`` for row standardized. + silence_warnings : bool + By default (`True``) libpysal will silence a warning if the dataset contains any + disconnected observations or islands. To print this warning set to ``False``. Returns ------- - W : spatial contiguity W object for assocations between flows - o*d x o*d spatial weight object amongst o*d flows between o - origins and d destinations + Ww : libpysal.weights.WSP + A sparse spatial contiguity `W` object for assocations between flows + between :math:`o` origins and :math:`d` destinations, + :math:`o \cdot d \times o \cdot d`. Examples -------- @@ -50,53 +56,66 @@ def ODW(Wo, Wd, transform='r', silence_warnings=True): 0. , 0. , 0. , 0. , 0. ]) """ - if Wo.transform != 'b': + + if Wo.transform != "b": try: - Wo.tranform = 'b' + Wo.tranform = "b" except: - raise AttributeError('Wo is not binary and cannot be transformed to ' - 'binary. Wo must be binary or suitably transformed to binary.') - if Wd.transform != 'b': + raise AttributeError( + "Wo is not binary and cannot be transformed to " + "binary. Wo must be binary or suitably transformed to binary." + ) + + if Wd.transform != "b": try: - Wd.tranform = 'b' + Wd.tranform = "b" except: - raise AttributeError('Wd is not binary and cannot be transformed to ' - 'binary. Wd must be binary or suitably transformed to binary.') + raise AttributeError( + "Wd is not binary and cannot be transformed to " + "binary. Wd must be binary or suitably transformed to binary." + ) + Wo = Wo.sparse Wo.eliminate_zeros() Wd = Wd.sparse Wd.eliminate_zeros() - Ww = kron(Wo, Wd, format='csr') + Ww = kron(Wo, Wd, format="csr") Ww.eliminate_zeros() Ww = WSP(Ww).to_W(silence_warnings=silence_warnings) Ww.transform = transform + return Ww -def netW(link_list, share='A', transform = 'r', **kwargs): - """ - Create a network-contiguity based weight object based on different nodal - relationships encoded in a network. + +def netW(link_list, share="A", transform="r", **kwargs): + """Create a network-contiguity based weights object based + on different nodal relationships encoded in a network. Parameters ---------- - link_list : list - of tuples where each tuple is of the form (o,d) where o is an - origin id and d is a destination id - - share : string - denoting how to define the nodal relationship used to determine neighboring edges; defualt is 'A' for any shared nodes between two network edges; options include: O a shared origin node; D a shared destination node; OD; a shared origin or a shared destination node; C a shared node that is the destination of the first edge and the origin of the second edge - i.e., a directed chain is formed moving from edge one to edge two. - - transform : Transformation for standardization of final OD spatial weight; default - is 'r' for row standardized - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` - + link_list : list + Collection of tuples where each ``tuple`` is of the form :math:`(o,d)` + where :math:`o` is an origin id and :math:`d` is a destination id. + share : str + This denotes how to define the nodal relationship used to determine + neighboring edges. The default is ``'A'``, for any shared nodes between + two network edges; options include: ``'O'`` a shared origin node; ``'D'`` + a shared destination node; ``'OD'``; a shared origin or a shared + destination node; ``'C'`` a shared node that is the destination of + the first edge and the origin of the second edge - i.e., a directed + chain is formed moving from edge one to edge two. + transform : str + A transformation for standardization of final the + `OD` spatial weights. Default is ``'r'`` for row standardized. + **kwargs : dict + Optional keyword arguments arguments for ``libpysal.weights.W`` Returns ------- - W : nodal contiguity W object for networkd edges or flows - W Object representing the binary adjacency of the network edges - given a definition of nodal relationshilibpysal.weights.spintW. + netW : libpysal.weights.W + A nodal contiguity `W` object for network edges or + flows representing the binary adjacency of the network + edges given a definition of nodal relationships. Examples -------- @@ -113,87 +132,110 @@ def netW(link_list, share='A', transform = 'r', **kwargs): [('a', 'c'), ('a', 'd'), ('c', 'b'), ('c', 'a')] """ + neighbors = {} neighbors = OrderedDict() edges = link_list + for key in edges: neighbors[key] = [] + for neigh in edges: if key == neigh: continue - if share.upper() == 'OD': + if share.upper() == "OD": if key[0] == neigh[0] or key[1] == neigh[1]: neighbors[key].append(neigh) - elif share.upper() == 'O': + elif share.upper() == "O": if key[0] == neigh[0]: neighbors[key].append(neigh) - elif share.upper() == 'D': + elif share.upper() == "D": if key[1] == neigh[1]: neighbors[key].append(neigh) - elif share.upper() == 'C': + elif share.upper() == "C": if key[1] == neigh[0]: neighbors[key].append(neigh) - elif share.upper() == 'A': - if key[0] == neigh[0] or key[0] == neigh[1] or \ - key[1] == neigh[0] or key[1] == neigh[1]: + elif share.upper() == "A": + if ( + key[0] == neigh[0] + or key[0] == neigh[1] + or key[1] == neigh[0] + or key[1] == neigh[1] + ): neighbors[key].append(neigh) else: - raise AttributeError("Parameter 'share' must be 'O', 'D'," - " 'OD', or 'C'") + raise AttributeError( + "Parameter 'share' must be 'O', 'D'," " 'OD', or 'C'" + ) + netW = W(neighbors, **kwargs) netW.tranform = transform + return netW -def vecW(origin_x, origin_y, dest_x, dest_y, threshold, p=2, alpha=-1.0, - binary=True, ids=None, build_sp=False, **kwargs): - """ - Distance-based spatial weight for vectors that is computed using a + +def vecW( + origin_x, + origin_y, + dest_x, + dest_y, + threshold, + p=2, + alpha=-1.0, + binary=True, + ids=None, + build_sp=False, + **kwargs +): + """Distance-based spatial weight for vectors that is computed using a 4-dimensional distance between the origin x,y-coordinates and the - destination x,y-coordinates + destination x,y-coordinates. Parameters ---------- - origin_x : list or array - of vector origin x-coordinates - origin_y : list or array - of vector origin y-coordinates - dest_x : list or array - of vector destination x-coordinates - dest_y : list or array - of vector destination y-coordinates - threshold : float - distance band - p : float - Minkowski p-norm distance metric parameter: - 1<=p<=infinity - 2: Euclidean distance - 1: Manhattan distance - binary : boolean - If true w_{ij}=1 if d_{i,j}<=threshold, otherwise w_{i,j}=0 - If false wij=dij^{alpha} - alpha : float - distance decay parameter for weight (default -1.0) - if alpha is positive the weights will not decline with - distance. If binary is True, alpha is ignored - - ids : list - values to use for keys of the neighbors and weights dicts - build_sp : boolean - True to build sparse distance matrix and false to build dense - distance matrix; significant speed gains may be obtained - dending on the sparsity of the of distance_matrix and - threshold that is applied - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` - + origin_x : {list, numpy.ndarray} + A vector of origin x-coordinates. + origin_y : {list, numpy.ndarray} + A vector of origin y-coordinates. + dest_x : {list, numpy.ndarray} + A vector of destination x-coordinates. + dest_y : {list, numpy.ndarray} + A vector of destination y-coordinates. + threshold : float + The distance band. + p : {int, float} + Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. + ``2`` is Euclidean distance and ``1`` is Manhattan distance. + This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. + Default is ``2``. + alpha : float + The distance decay parameter for weights. Default is ``-1.0``. + If ``alpha`` is positive the weights will not decline with distance. + If ``binary`` is set to ``True``, ``alpha`` is ignored. + binary : bool + If set to ``True``, :math:`w_{ij}=1 if d_{i,j}<=threshold`, + otherwise :math:`w_{i,j}=0`. If set to ``False``, :math:`wij=dij^{alpha}`. + Default is ``True``. + ids : list + Identifiers to attach to each observation in ``neighbors`` + and ``weights``. Default is ``None``. + build_sp : boolean + Set to ``True`` to build a sparse distance matrix and ``False`` to build dense + distance matrix. Significant speed gains may be obtained depending on the + sparsity of the of distance matrix and the ``threshold`` that is applied. + Default is ``True``. + **kwargs : dict + Optional keyword arguments arguments for ``libpysal.weights.W`` Returns ------ - W : DistanceBand W object that uses 4-dimenional distances between - vectors origin and destination coordinates. + W : libpysal.weights.DistanceBand + A ``libpysal.weights.DistanceBand`` `W` object that uses 4-dimenional + distances between vectors of origin and destination coordinates. Examples -------- + >>> import libpysal >>> x1 = [5,6,3] >>> y1 = [1,8,5] @@ -207,39 +249,56 @@ def vecW(origin_x, origin_y, dest_x, dest_y, threshold, p=2, alpha=-1.0, [1, 2] """ + data = list(zip(origin_x, origin_y, dest_x, dest_y)) - W = DistanceBand(data, threshold=threshold, p=p, binary=binary, alpha=alpha, - ids=ids, build_sp=False, **kwargs) + + W = DistanceBand( + data, + threshold=threshold, + p=p, + binary=binary, + alpha=alpha, + ids=ids, + build_sp=False, + **kwargs + ) + return W + def mat2L(edge_matrix): - """ - Convert a matrix denoting network connectivity (edges or flows) to a list - denoting edges + """Convert a matrix denoting network connectivity + (edges or flows) to a list denoting edges. Parameters ---------- - edge_matrix : array - where rows denote network edge origins, columns denote - network edge destinations, and non-zero entries denote the - existence of an edge between a given origin and destination + edge_matrix : numpy.ndarray + A matrix where rows denote network edge origins, columns denote + network edge destinations, and non-zero entries denote the + existence of an edge between a given origin and destination. Returns ------- - edge_list : list - of tuples where each tuple is of the form (o,d) where o is an - origin id and d is a destination id + edge_list : list + Collection of tuples where each ``tuple`` is of the form :math:`(o,d)` + where :math:`o` is an origin id and :math:`d` is a destination id. """ - if len(edge_matrix.shape) !=2: - raise AttributeError("Matrix of network edges should be two dimensions" - "with edge origins on one axis and edge destinations on the" - "second axis with non-zero matrix entires denoting an edge" - "between and origin and destination") + + if len(edge_matrix.shape) != 2: + raise AttributeError( + "Matrix of network edges should be two dimensions" + "with edge origins on one axis and edge destinations on the" + "second axis with non-zero matrix entires denoting an edge" + "between and origin and destination" + ) + edge_list = [] rows, cols = edge_matrix.shape + for row in range(rows): for col in range(cols): if edge_matrix[row, col] != 0: - edge_list.append((row,col)) + edge_list.append((row, col)) + return edge_list diff --git a/libpysal/weights/user.py b/libpysal/weights/user.py index 28bb4019c..de6a95ed5 100644 --- a/libpysal/weights/user.py +++ b/libpysal/weights/user.py @@ -58,7 +58,7 @@ def min_threshold_dist_from_shapefile(shapefile, radius=None, p=2): radius : float If supplied ``arc_distances`` will be calculated based on the given radius and ``p`` will be ignored. - p : float + p : {int, float} Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. ``2`` is Euclidean distance and ``1`` is Manhattan distance. Default is ``2 ``. From 88035fbd8d225f3eb77f1a60640ea687a95b6766 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Sat, 22 Aug 2020 15:11:16 -0400 Subject: [PATCH 17/33] updating weights/set_operations.py --- libpysal/weights/set_operations.py | 393 +++++++++++++++-------------- 1 file changed, 207 insertions(+), 186 deletions(-) diff --git a/libpysal/weights/set_operations.py b/libpysal/weights/set_operations.py index b560fd2b9..a5eb6f36b 100644 --- a/libpysal/weights/set_operations.py +++ b/libpysal/weights/set_operations.py @@ -2,43 +2,52 @@ Set-like manipulation of weights matrices. """ -__author__ = "Sergio J. Rey , Charles Schmidt , David Folch , Dani Arribas-Bel " +__author__ = ( + "Sergio J. Rey ," + "Charles Schmidt ," + "David Folch ," + "Dani Arribas-Bel " +) import copy from .weights import W, WSP from scipy.sparse import isspmatrix_csr from numpy import ones -__all__ = ['w_union', 'w_intersection', 'w_difference', - 'w_symmetric_difference', 'w_subset', 'w_clip'] +__all__ = [ + "w_union", + "w_intersection", + "w_difference", + "w_symmetric_difference", + "w_subset", + "w_clip", +] def w_union(w1, w2, **kwargs): - """ - Returns a binary weights object, w, that includes all neighbor pairs that - exist in either w1 or w2. + """Return a binary weights object, ``w``, that includes all + neighbor pairs that exist in either ``w1`` or ``w2``. Parameters ---------- - - w1 : W - object - w2 : W - object - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` + w1 : libpysal.weights.W + A PySAL weights object. + w2 : libpysal.weights.W + A PySAL weights object. + **kwargs : dict + Keyword arguments for ``libpysal.weights.W``. Returns ------- - - w : W - object + w : libpysal.weights.W + The union of two PySAL weights objects. Notes ----- - ID comparisons are performed using ==, therefore the integer ID 2 is + + ID comparisons are performed using ``==``, therefore the integer ID 2 is equivalent to the float ID 2.0. Returns a matrix with all the unique IDs - from w1 and w2. + from ``w1`` and ``w2``. Examples -------- @@ -61,45 +70,48 @@ def w_union(w1, w2, **kwargs): [19, 11, 14] """ + neighbors = dict(list(w1.neighbors.items())) + for i in w2.neighbors: if i in neighbors: add_neigh = set(neighbors[i]).union(set(w2.neighbors[i])) neighbors[i] = list(add_neigh) else: neighbors[i] = copy.copy(w2.neighbors[i]) - return W(neighbors, **kwargs) + w = W(neighbors, **kwargs) + + return w -def w_intersection(w1, w2, w_shape='w1', **kwargs): - """ - Returns a binary weights object, w, that includes only - those neighbor pairs that exist in both w1 and w2. + +def w_intersection(w1, w2, w_shape="w1", **kwargs): + """Returns a binary weights object, ``w``, that includes only + those neighbor pairs that exist in both ``w1`` and ``w2``. Parameters ---------- - - w1 : W - object - w2 : W - object - w_shape : string - Defines the shape of the returned weights matrix. 'w1' returns a - matrix with the same IDs as w1; 'all' returns a matrix with all - the unique IDs from w1 and w2; and 'min' returns a matrix with - only the IDs occurring in both w1 and w2. - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` + w1 : libpysal.weights.W + A PySAL weights object. + w2 : libpysal.weights.W + A PySAL weights object. + w_shape : str + Defines the shape of the returned weights matrix. ``'w1'`` returns a + matrix with the same IDs as ``w1``; ``'all'`` returns a matrix with all + the unique IDs from ``w1`` and ``w2``; and ``'min'`` returns a matrix with + only the IDs occurring in both ``w1`` and ``w2``. Default is ``'w1'``. + **kwargs : dict + Keyword arguments for ``libpysal.weights.W``. Returns ------- - w : W - object + w : libpysal.weights.W + The intersection of two PySAL weights objects. Notes ----- - ID comparisons are performed using ==, therefore the integer ID 2 is + ID comparisons are performed using ``==``, therefore the integer ID 2 is equivalent to the float ID 2.0. Examples @@ -124,13 +136,12 @@ def w_intersection(w1, w2, w_shape='w1', **kwargs): """ - if w_shape == 'w1': + if w_shape == "w1": neigh_keys = list(w1.neighbors.keys()) - elif w_shape == 'all': + elif w_shape == "all": neigh_keys = set(w1.neighbors.keys()).union(set(w2.neighbors.keys())) - elif w_shape == 'min': - neigh_keys = set(w1.neighbors.keys( - )).intersection(set(w2.neighbors.keys())) + elif w_shape == "min": + neigh_keys = set(w1.neighbors.keys()).intersection(set(w2.neighbors.keys())) else: raise Exception("invalid string passed to w_shape") @@ -142,55 +153,56 @@ def w_intersection(w1, w2, w_shape='w1', **kwargs): else: neighbors[i] = [] - return W(neighbors, **kwargs) + w = W(neighbors, **kwargs) + return w -def w_difference(w1, w2, w_shape='w1', constrained=True, **kwargs): - """ - Returns a binary weights object, w, that includes only neighbor pairs - in w1 that are not in w2. The w_shape and constrained parameters - determine which pairs in w1 that are not in w2 are returned. + +def w_difference(w1, w2, w_shape="w1", constrained=True, **kwargs): + """Returns a binary weights object, ``w``, that includes + only neighbor pairs in ``w1`` that are not in ``w2``. The + ``w_shape`` and ``constrained`` parameters determine which + pairs in ``w1`` that are not in ``w2`` are returned. Parameters ---------- - - w1 : W - object - w2 : W - object - w_shape : string - Defines the shape of the returned weights matrix. 'w1' returns a - matrix with the same IDs as w1; 'all' returns a matrix with all - the unique IDs from w1 and w2; and 'min' returns a matrix with - the IDs occurring in w1 and not in w2. - constrained : boolean - If False then the full set of neighbor pairs in w1 that are - not in w2 are returned. If True then those pairs that would - not be possible if w_shape='min' are dropped. Ignored if - w_shape is set to 'min'. - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` + w1 : libpysal.weights.W + A PySAL weights object. + w2 : libpysal.weights.W + A PySAL weights object. + w_shape : str + Defines the shape of the returned weights matrix. ``'w1'`` returns a + matrix with the same IDs as ``w1``; ``'all'`` returns a matrix with all + the unique IDs from ``w1`` and ``w2``; and ``'min'`` returns a matrix with + the IDs occurring in ``w1`` and not in ``w2``. Default is ``'w1'``. + constrained : bool + If ``False`` then the full set of neighbor pairs in ``w1`` that are + not in ``w2`` are returned. If ``True`` then those pairs that would + not be possible if ``w_shape='min'`` are dropped. Default is ``True``. + Ignored if ``w_shape`` is set to ``'min'``. + **kwargs : dict + Keyword arguments for ``libpysal.weights.W``. Returns ------- - - w : W - object + w : libpysal.weights.W + The difference of two PySAL weights objects. Notes ----- - ID comparisons are performed using ==, therefore the integer ID 2 is + + ID comparisons are performed using ``==``, therefore the integer ID 2 is equivalent to the float ID 2.0. Examples -------- - Construct rook (w2) and queen (w1) weights matrices for two 4x4 regions - (16 areas). A queen matrix has all the joins a rook matrix does plus joins - between areas that share a corner. The new matrix formed by the difference - of rook from queen contains only join at corners (typically called a - bishop matrix). Note that the difference of queen from rook would result - in a weights matrix with no joins. + Construct rook (``w2``) and queen (``w1``) weights matrices for two 4x4 + regions (16 areas). A queen matrix has all the joins a rook matrix does + plus joins between areas that share a corner. The new matrix formed by + the difference of rook from queen contains only joins at corners (typically + called a bishop matrix). Note that the difference of queen from rook would + result in a weights matrix with no joins. >>> from libpysal.weights import lat2W, w_difference >>> w1 = lat2W(4,4,rook=False) @@ -207,13 +219,12 @@ def w_difference(w1, w2, w_shape='w1', constrained=True, **kwargs): """ - if w_shape == 'w1': + if w_shape == "w1": neigh_keys = list(w1.neighbors.keys()) - elif w_shape == 'all': + elif w_shape == "all": neigh_keys = set(w1.neighbors.keys()).union(set(w2.neighbors.keys())) - elif w_shape == 'min': - neigh_keys = set( - w1.neighbors.keys()).difference(set(w2.neighbors.keys())) + elif w_shape == "min": + neigh_keys = set(w1.neighbors.keys()).difference(set(w2.neighbors.keys())) if not neigh_keys: raise Exception("returned an empty weights matrix") else: @@ -223,71 +234,69 @@ def w_difference(w1, w2, w_shape='w1', constrained=True, **kwargs): for i in neigh_keys: if i in w1.neighbors: if i in w2.neighbors: - add_neigh = set(w1.neighbors[i] - ).difference(set(w2.neighbors[i])) + add_neigh = set(w1.neighbors[i]).difference(set(w2.neighbors[i])) neighbors[i] = list(add_neigh) else: neighbors[i] = copy.copy(w1.neighbors[i]) else: neighbors[i] = [] - if constrained or w_shape == 'min': - constrained_keys = set( - w1.neighbors.keys()).difference(set(w2.neighbors.keys())) + if constrained or w_shape == "min": + constrained_keys = set(w1.neighbors.keys()).difference(set(w2.neighbors.keys())) island_keys = set(neighbors.keys()).difference(constrained_keys) for i in island_keys: neighbors[i] = [] for i in constrained_keys: - neighbors[i] = list( - set(neighbors[i]).intersection(constrained_keys)) + neighbors[i] = list(set(neighbors[i]).intersection(constrained_keys)) - return W(neighbors, **kwargs) + w = W(neighbors, **kwargs) + return w -def w_symmetric_difference(w1, w2, w_shape='all', constrained=True, **kwargs): - """ - Returns a binary weights object, w, that includes only neighbor pairs - that are not shared by w1 and w2. The w_shape and constrained parameters - determine which pairs that are not shared by w1 and w2 are returned. + +def w_symmetric_difference(w1, w2, w_shape="all", constrained=True, **kwargs): + """Returns a binary weights object, ``w``, that includes only + neighbor pairs that are not shared by ``w1`` and ``w2``. The + ``w_shape`` and ``constrained`` parameters determine which + pairs that are not shared by ``w1`` and ``w2`` are returned. Parameters ---------- - - w1 : W - object - w2 : W - object - w_shape : string - Defines the shape of the returned weights matrix. 'all' returns a - matrix with all the unique IDs from w1 and w2; and 'min' returns - a matrix with the IDs not shared by w1 and w2. - constrained : boolean - If False then the full set of neighbor pairs that are not - shared by w1 and w2 are returned. If True then those pairs - that would not be possible if w_shape='min' are dropped. - Ignored if w_shape is set to 'min'. - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` + w1 : libpysal.weights.W + A PySAL weights object. + w2 : libpysal.weights.W + A PySAL weights object. + w_shape : str + Defines the shape of the returned weights matrix. ``'all'`` returns a + matrix with all the unique IDs from ``w1`` and ``w2``; and ``'min'`` + returns a matrix with the IDs not shared by ``w1`` and ``w2``. + constrained : bool + If ``False`` then the full set of neighbor pairs that are not + shared by ``w1`` and ``w2`` are returned. If ``True`` then those pairs + that would not be possible if ``w_shape='min'`` are dropped. + Default is ``True``. Ignored if ``w_shape`` is set to ``'min'``. + **kwargs : dict + Keyword arguments for ``libpysal.weights.W``. Returns ------- - - w : W - object + w : libpysal.weights.W + The symmetric difference of two PySAL weights objects. Notes ----- - ID comparisons are performed using ==, therefore the integer ID 2 is + + ID comparisons are performed using ``==``, therefore the integer ID 2 is equivalent to the float ID 2.0. Examples -------- - Construct queen weights matrix for a 4x4 (16 areas) region (w1) and a rook - matrix for a 6x4 (24 areas) region (w2). The symmetric difference of these - two matrices (with w_shape set to 'all' and constrained set to False) - contains the corner joins in the overlap area, all the joins in the - non-overlap area. + Construct a queen weights matrix for a 4x4 (16 areas) region (``w1``) + and a rook matrix for a 6x4 (24 areas) region (``w2``). The symmetric + difference of these two matrices (with ``w_shape`` set to ``'all'`` and + ``constrained`` set to ``False``) contains the corner joins in the overlap + area, all the joins in the non-overlap area. >>> from libpysal.weights import lat2W, w_symmetric_difference >>> w1 = lat2W(4,4,rook=False) @@ -304,11 +313,12 @@ def w_symmetric_difference(w1, w2, w_shape='all', constrained=True, **kwargs): """ - if w_shape == 'all': + if w_shape == "all": neigh_keys = set(w1.neighbors.keys()).union(set(w2.neighbors.keys())) - elif w_shape == 'min': - neigh_keys = set(w1.neighbors.keys( - )).symmetric_difference(set(w2.neighbors.keys())) + elif w_shape == "min": + neigh_keys = set(w1.neighbors.keys()).symmetric_difference( + set(w2.neighbors.keys()) + ) else: raise Exception("invalid string passed to w_shape") @@ -317,7 +327,8 @@ def w_symmetric_difference(w1, w2, w_shape='all', constrained=True, **kwargs): if i in w1.neighbors: if i in w2.neighbors: add_neigh = set(w1.neighbors[i]).symmetric_difference( - set(w2.neighbors[i])) + set(w2.neighbors[i]) + ) neighbors[i] = list(add_neigh) else: neighbors[i] = copy.copy(w1.neighbors[i]) @@ -326,49 +337,45 @@ def w_symmetric_difference(w1, w2, w_shape='all', constrained=True, **kwargs): else: neighbors[i] = [] - if constrained or w_shape == 'min': - constrained_keys = set( - w1.neighbors.keys()).difference(set(w2.neighbors.keys())) + if constrained or w_shape == "min": + constrained_keys = set(w1.neighbors.keys()).difference(set(w2.neighbors.keys())) island_keys = set(neighbors.keys()).difference(constrained_keys) for i in island_keys: neighbors[i] = [] for i in constrained_keys: - neighbors[i] = list( - set(neighbors[i]).intersection(constrained_keys)) + neighbors[i] = list(set(neighbors[i]).intersection(constrained_keys)) + + w = W(neighbors, **kwargs) - return W(neighbors, **kwargs) + return w def w_subset(w1, ids, **kwargs): - """ - Returns a binary weights object, w, that includes only those - observations in ids. + """Returns a binary weights object, ``w``, that includes only those + observations in passed in with the ``ids`` parameter. Parameters ---------- - - w1 : W - object - ids : list - A list containing the IDs to be include in the returned weights - object. - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` + w1 : libpysal.weights.W + A PySAL weights object. + ids : list + A list containing the IDs to be include in the returned weights object. + **kwargs : dict + Keyword arguments for ``libpysal.weights.W``. Returns ------- - - w : W - object + w : libpysal.weights.W + The subset of a PySAL weights object. Examples -------- - Construct a rook weights matrix for a 6x4 region (24 areas). By default - PySAL assigns integer IDs to the areas in a region. By passing in a list - of integers from 0 to 15, the first 16 areas are extracted from the - previous weights matrix, and only those joins relevant to the new region - are retained. + Construct a rook weights matrix for a 6x4 region (24 areas). By + default PySAL assigns integer IDs to the areas in a region. By + passing in a list of integers from 0 to 15, the first 16 areas are + extracted from the previous weights matrix, and only those joins + relevant to the new region are retained. >>> from libpysal.weights import lat2W, w_subset >>> w1 = lat2W(6,4) @@ -385,6 +392,7 @@ def w_subset(w1, ids, **kwargs): neighbors = {} ids_set = set(list(ids)) + for i in ids: if i in w1.neighbors: neigh_add = ids_set.intersection(set(w1.neighbors[i])) @@ -392,45 +400,50 @@ def w_subset(w1, ids, **kwargs): else: neighbors[i] = [] - return W(neighbors, id_order=list(ids), **kwargs) + w = W(neighbors, id_order=list(ids), **kwargs) + return w -def w_clip(w1, w2, outSP=True, **kwargs): - ''' - Clip a continuous W object (w1) with a different W object (w2) so only cells where - w2 has a non-zero value remain with non-zero values in w1. - Checks on w1 and w2 are performed to make sure they conform to the - appropriate format and, if not, they are converted. +def w_clip(w1, w2, outSP=True, **kwargs): + """Clip a continuous `W` object (``w1``) with a different `W` object + (``w2``) so only cells where ``w2`` has a non-zero value remain with + non-zero values in ``w1``. Checks on ``w1`` and ``w2`` are performed + to make sure they conform to the appropriate format and, if not, they + are converted. Parameters ---------- - w1 : W - W, scipy.sparse.csr.csr_matrix - Potentially continuous weights matrix to be clipped. The clipped - matrix wc will have at most the same elements as w1. - w2 : W - W, scipy.sparse.csr.csr_matrix - Weights matrix to use as shell to clip w1. Automatically - converted to binary format. Only non-zero elements in w2 will be - kept non-zero in wc. NOTE: assumed to be of the same shape as w1 - outSP : boolean - If True (default) return sparse version of the clipped W, if - False, return W object of the clipped matrix - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` + w1 : {libpysal.weights.W, scipy.sparse.csr.csr_matrix} + The potentially continuous weights matrix to be clipped. The clipped + matrix, ``wc``, will have at most the same elements as ``w1``. + w2 : {libpysal.weights.W, scipy.sparse.csr.csr_matrix} + The weights matrix to use as a shell to clip ``w1``. It is automatically + converted to binary format. Only non-zero elements in ``w2`` will be + kept non-zero in ``wc``. It is assumed to be of the same shape as ``w1``. + outSP : bool + If ``True`` (default) return the sparse version of the clipped `W`, if + ``False``, return a `W` object of the clipped matrix. + **kwargs : dict + Keyword arguments for ``libpysal.weights.W``. Returns ------- - wc : W - W, scipy.sparse.csr.csr_matrix - Clipped W object (sparse if outSP=Ture). It inherits ``id_order`` from w1. - + wc : {libpysal.weights.W, scipy.sparse.csr.csr_matrix} + A clipped `W` object that is sparse if ``outSP`` is set to ``True``. + It inherits ``id_order`` from ``w1``. + + Notes + ----- + + The ``w2`` parameter is assumed to be of the same shape as ``w1``. + Examples -------- + >>> from libpysal.weights import lat2W - First create a W object from a lattice using queen contiguity and + First create a `W` object from a lattice using queen contiguity and row-standardize it (note that these weights will stay when we clip the object, but they will not neccesarily represent a row-standardization anymore): @@ -440,24 +453,25 @@ def w_clip(w1, w2, outSP=True, **kwargs): We will clip that geography assuming observations 0, 2, 3 and 4 belong to one group and 1, 5 belong to another group and we don't want both groups - to interact with each other in our weights (i.e. w_ij = 0 if i and j in - different groups). For that, we use the following method: + to interact with each other in our weights (i.e. :math:`w_ij = 0` + if :math:`i` and :math:`j` are in different groups). + For that, we use the following method: >>> import libpysal >>> w2 = libpysal.weights.block_weights(['r1', 'r2', 'r1', 'r1', 'r1', 'r2']) - To illustrate that w2 will only be considered as binary even when the - object passed is not, we can row-standardize it + To illustrate that ``w2`` will only be considered as binary even when the + object passed is not, we can row-standardize it. >>> w2.transform = 'R' - The clipped object ``wc`` will contain only the spatial queen - relationships that occur within one group ('r1' or 'r2') but will have - gotten rid of those that happen across groups + The clipped object ``wc`` will contain only the spatial queen relationships + that occur within one group (``'r1'`` or ``'r2'``) but will have + gotten rid of those that happen across groups. >>> wcs = libpysal.weights.w_clip(w1, w2, outSP=True) - This will create a sparse object (recommended when n is large). + This will create a sparse object (recommended when :math:`n` is large). >>> wcs.sparse.toarray() array([[0. , 0. , 0.33333333, 0.33333333, 0. , @@ -474,8 +488,8 @@ def w_clip(w1, w2, outSP=True, **kwargs): 0. ]]) - If we wanted an original W object, we can control that with the argument - ``outSP``: + If we wanted an original `W` object, we can control + that with the argument ``outSP``: >>> wc = libpysal.weights.w_clip(w1, w2, outSP=False) >>> wc.full()[0] @@ -502,19 +516,26 @@ def w_clip(w1, w2, outSP=True, **kwargs): [ True, True, True, True, True, True], [ True, True, True, True, True, True]]) - ''' + """ from .util import WSP2W + if not w1.id_order: w1.id_order = None + id_order = w1.id_order + if not isspmatrix_csr(w1): w1 = w1.sparse + if not isspmatrix_csr(w2): w2 = w2.sparse + w2.data = ones(w2.data.shape) wc = w1.multiply(w2) wc = WSP(wc, id_order=id_order) + if not outSP: wc = WSP2W(wc, **kwargs) + return wc From c15d215fec05cae20cc8821ed445cf91a2b13693 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Sun, 23 Aug 2020 13:56:23 -0400 Subject: [PATCH 18/33] various updats for weights/{distance, weights, spintW}.py --- libpysal/weights/distance.py | 47 +-- libpysal/weights/spintW.py | 13 +- libpysal/weights/weights.py | 601 +++++++++++++++++++++-------------- 3 files changed, 392 insertions(+), 269 deletions(-) diff --git a/libpysal/weights/distance.py b/libpysal/weights/distance.py index 53c01b1fa..c0db67e26 100644 --- a/libpysal/weights/distance.py +++ b/libpysal/weights/distance.py @@ -799,14 +799,14 @@ class DistanceBand(W): threshold : float The distance band. p : {int, float} - Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. + Minkowski `p`-norm distance metric parameter where :math:`1<=\mathtt{p}<=\infty`. ``2`` is Euclidean distance and ``1`` is Manhattan distance. This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. Default is ``2``. binary : bool - If set to ``True``, :math:`w_{ij}=1 if d_{i,j}<=threshold`, - otherwise :math:`w_{i,j}=0`. If set to ``False``, :math:`wij=dij^{alpha}`. - Default is ``True``. + If set to ``True``, :math:`w_{ij}=1` if :math:`d_{i,j}<=\mathtt{threshold}`, + otherwise :math:`w_{i,j}=0`. If set to ``False``, + :math:`wij=dij^{\mathtt{alpha}}`. Default is ``True``. alpha : float The distance decay parameter for weights. Default is ``-1.0``. If ``alpha`` is positive the weights will not decline with distance. @@ -819,7 +819,7 @@ class DistanceBand(W): sparsity of the of distance matrix and the ``threshold`` that is applied. Default is ``True``. silence_warnings : bool - By default (`False``) libpysal will print a warning if the dataset contains any + By default (``False``) libpysal will print a warning if the dataset contains any disconnected observations or islands. To silence this warning set to ``True``. radius : float If supplied arc distances will be calculated based on the given radius @@ -842,26 +842,30 @@ class DistanceBand(W): >>> import libpysal >>> points=[(10, 10), (20, 10), (40, 10), (15, 20), (30, 20), (30, 30)] >>> wcheck = libpysal.weights.W({0: [1, 3], 1: [0, 3], 2: [], 3: [0, 1], 4: [5], 5: [4]}) + UserWarning: The weights matrix is not fully connected: + There are 3 disconnected components. + There is 1 island with id: 2. - WARNING: there is one disconnected observation (no neighbors) - Island id: [2] - >>> w=libpysal.weights.DistanceBand(points,threshold=11.2) + >>> w = libpysal.weights.DistanceBand(points, threshold=11.2) + UserWarning: The weights matrix is not fully connected: + There are 3 disconnected components. + There is 1 island with id: 2. - WARNING: there is one disconnected observation (no neighbors) - Island id: [2] >>> libpysal.weights.util.neighbor_equality(w, wcheck) True - >>> w=libpysal.weights.DistanceBand(points,threshold=14.2) + + >>> w = libpysal.weights.DistanceBand(points, threshold=14.2) >>> wcheck = libpysal.weights.W({0: [1, 3], 1: [0, 3, 4], 2: [4], 3: [1, 0], 4: [5, 2, 1], 5: [4]}) >>> libpysal.weights.util.neighbor_equality(w, wcheck) True Inverse distance weights: - >>> w=libpysal.weights.DistanceBand(points,threshold=11.2,binary=False) - - WARNING: there is one disconnected observation (no neighbors) - Island id: [2] + >>> w = libpysal.weights.DistanceBand(points, threshold=11.2, binary=False) + UserWarning: The weights matrix is not fully connected: + There are 3 disconnected components. + There is 1 island with id: 2. + >>> w.weights[0] [0.1, 0.08944271909999159] >>> w.neighbors[0].tolist() @@ -869,10 +873,11 @@ class DistanceBand(W): Gravity weights: - >>> w=libpysal.weights.DistanceBand(points,threshold=11.2,binary=False,alpha=-2.) - - WARNING: there is one disconnected observation (no neighbors) - Island id: [2] + >>> w = libpysal.weights.DistanceBand(points, threshold=11.2, binary=False, alpha=-2.) + UserWarning: The weights matrix is not fully connected: + There are 3 disconnected components. + There is 1 island with id: 2. + >>> w.weights[0] [0.01, 0.007999999999999998] @@ -898,8 +903,8 @@ def __init__( radius=None, distance_metric="euclidean", ): - """Casting to floats is a work around for a bug in scipy.spatial. - --> See detail in pysal issue #126. + """Casting to floats is a work around for a bug in ``scipy.spatial``. + See details in `pysal/pysal#126 `_. """ if ids is not None: diff --git a/libpysal/weights/spintW.py b/libpysal/weights/spintW.py index 1ea4fa9d1..cccf99eab 100644 --- a/libpysal/weights/spintW.py +++ b/libpysal/weights/spintW.py @@ -12,7 +12,7 @@ def ODW(Wo, Wd, transform="r", silence_warnings=True): - """Construct an :math:`o \cdot d \times o \cdot d` + """Construct an :math:`(o \cdot d)\cdot(o \cdot d)` origin-destination style spatial weight for :math:`o \cdot d` flows using standard spatial weights on :math:`o` origins and :math:`d` destinations. Input spatial weights must be @@ -30,7 +30,7 @@ def ODW(Wo, Wd, transform="r", silence_warnings=True): A transformation for standardization of final the `OD` spatial weights. Default is ``'r'`` for row standardized. silence_warnings : bool - By default (`True``) libpysal will silence a warning if the dataset contains any + By default (``True``) libpysal will silence a warning if the dataset contains any disconnected observations or islands. To print this warning set to ``False``. Returns @@ -38,7 +38,7 @@ def ODW(Wo, Wd, transform="r", silence_warnings=True): Ww : libpysal.weights.WSP A sparse spatial contiguity `W` object for assocations between flows between :math:`o` origins and :math:`d` destinations, - :math:`o \cdot d \times o \cdot d`. + :math:`(o \cdot d)\cdot(o \cdot d)`. Examples -------- @@ -119,6 +119,7 @@ def netW(link_list, share="A", transform="r", **kwargs): Examples -------- + >>> import libpysal >>> links = [('a','b'), ('a','c'), ('a','d'), ('c','d'), ('c', 'b'), ('c','a')] >>> O = libpysal.weights.netW(links, share='O') @@ -204,7 +205,7 @@ def vecW( threshold : float The distance band. p : {int, float} - Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. + Minkowski `p`-norm distance metric parameter where :math:`1<=p<=\infty`. ``2`` is Euclidean distance and ``1`` is Manhattan distance. This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. Default is ``2``. @@ -213,7 +214,7 @@ def vecW( If ``alpha`` is positive the weights will not decline with distance. If ``binary`` is set to ``True``, ``alpha`` is ignored. binary : bool - If set to ``True``, :math:`w_{ij}=1 if d_{i,j}<=threshold`, + If set to ``True``, :math:`w_{ij}=1` if :math:`d_{i,j}<=threshold`, otherwise :math:`w_{i,j}=0`. If set to ``False``, :math:`wij=dij^{alpha}`. Default is ``True``. ids : list @@ -225,7 +226,7 @@ def vecW( sparsity of the of distance matrix and the ``threshold`` that is applied. Default is ``True``. **kwargs : dict - Optional keyword arguments arguments for ``libpysal.weights.W`` + Optional keyword arguments arguments for ``libpysal.weights.W``. Returns ------ diff --git a/libpysal/weights/weights.py b/libpysal/weights/weights.py index 44917cc5e..36a31ec03 100644 --- a/libpysal/weights/weights.py +++ b/libpysal/weights/weights.py @@ -1,6 +1,7 @@ """ -Weights. +Spatial Weights. """ + __author__ = "Sergio J. Rey " import copy @@ -20,23 +21,21 @@ class W(object): - """ - Spatial weights class. Class attributes are described by their + """Spatial weights class. Class attributes are described by their docstrings. to view, use the ``help`` function. Parameters ---------- - neighbors : dict - Key is region ID, value is a list of neighbor IDS. + Key is region ID, value is a list of neighbor IDs. For example, ``{'a':['b'],'b':['a','c'],'c':['b']}``. weights : dict Key is region ID, value is a list of edge weights. If not supplied all edge weights are assumed to have a weight of 1. For example, ``{'a':[0.5],'b':[0.5,1.5],'c':[1.5]}``. id_order : list - An ordered list of ids, defines the order of observations when - iterating over ``W`` if not set, lexicographical ordering is used + An ordered list of IDs, defines the order of observations when + iterating over `W` if not set, lexicographical ordering is used to iterate and the ``id_order_set`` property will return ``False``. This can be set after creation by setting the ``id_order`` property. silence_warnings : bool @@ -45,10 +44,9 @@ class W(object): parameter to ``True``. ids : list Values to use for keys of the neighbors and weights ``dict`` objects. - + Attributes ---------- - asymmetries cardinalities component_labels @@ -116,7 +114,7 @@ class W(object): >>> round(w.trcWtW, 3) 2533.667 - Cardinality Histogram: + Cardinality histogram: >>> w.histogram [(2, 4), (3, 392), (4, 9604)] @@ -125,7 +123,6 @@ class W(object): >>> from libpysal.weights import W >>> w = W({1:[0],0:[1],2:[], 3:[]}) - UserWarning: The weights matrix is not fully connected: There are 3 disconnected components. There are 2 islands with ids: 2, 3. @@ -135,16 +132,20 @@ class W(object): def __init__( self, neighbors, weights=None, id_order=None, silence_warnings=False, ids=None ): + self.silence_warnings = silence_warnings self.transformations = {} self.neighbors = neighbors + if not weights: weights = {} for key in neighbors: weights[key] = [1.0] * len(neighbors[key]) + self.weights = weights self.transformations["O"] = self.weights.copy() # original weights self.transform = "O" + if id_order is None: self._id_order = list(self.neighbors.keys()) self._id_order.sort() @@ -152,14 +153,17 @@ def __init__( else: self._id_order = id_order self._id_order_set = True + self._reset() self._n = len(self.weights) + if not self.silence_warnings and self.n_components > 1: message = ( "The weights matrix is not fully connected: " "\n There are %d disconnected components." % self.n_components ) ni = len(self.islands) + if ni == 1: message = message + "\n There is 1 island with id: " "%s." % ( str(self.islands[0]) @@ -169,83 +173,92 @@ def __init__( ni, ", ".join(str(island) for island in self.islands), ) + warnings.warn(message) def _reset(self): """Reset properties.""" - self._cache = {} - - def to_file(self, path='', format=None): - """ - Write a weights to a file. The format is guessed automatically - from the path, but can be overridden with the format argument. - See libpysal.io.FileIO for more information. + self._cache = {} - Arguments - --------- - path : string - location to save the file - format : string - string denoting the format to write the weights to. + def to_file(self, path="", format=None): + """Write a weights object to a file. The format is inferred + from the path, but can be overridden with the format argument. + See ``libpysal.io.FileIO`` for more information. + Parameters + ---------- + path : str + The location to save the file. Default is ``''``. + format : str + The format of the weights file to write. Default is ``None``. + + See Also + -------- + + libpysal.io.FileIO - Returns - ------- - None """ - f = popen(dataPath=path, mode='w', dataFormat=format) + + f = popen(dataPath=path, mode="w", dataFormat=format) f.write(self) f.close() - @classmethod - def from_file(cls, path='', format=None): - """ - Read a weights file into a W object. + def from_file(cls, path="", format=None): + """Read a weights file into a `W` object. - Arguments - --------- - path : string - location to save the file - format : string - string denoting the format to write the weights to. + Parameters + ---------- + path : str + The location to save the file. Default is ``''``. + format : str + The format of the weights file to read. Default is ``None``. Returns ------- - W object + w : libpysal.weights.W + A PySAL `W` spatial weights object. + """ - f = popen(dataPath=path, mode='r', dataFormat=format) + + f = popen(dataPath=path, mode="r", dataFormat=format) w = f.read() f.close() + return w @classmethod def from_shapefile(cls, *args, **kwargs): + """Construct a weights object from a shapefile.""" + # we could also just "do the right thing," but I think it'd make sense to # try and get people to use `Rook.from_shapefile(shapefile)` rather than # W.from_shapefile(shapefile, type=`rook`), otherwise we'd need to build # a type dispatch table. Generic W should be for stuff we don't know # anything about. - raise NotImplementedError( - "Use type-specific constructors, like Rook," - " Queen, DistanceBand, or Kernel" + + msg = ( + "Use type-specific constructors, like Rook, Queen, DistanceBand, or Kernel." ) + raise NotImplementedError(msg) @classmethod def from_WSP(cls, WSP, silence_warnings=True): - return WSP2W(WSP, silence_warnings=silence_warnings) + """Construct a weights object from a ``WSP`` object.""" + + w = WSP2W(WSP, silence_warnings=silence_warnings) + + return w @classmethod def from_adjlist( cls, adjlist, focal_col="focal", neighbor_col="neighbor", weight_col=None ): - """ - Return an adjacency list representation of a weights object. + """Return an adjacency list representation of a weights object. Parameters ---------- - adjlist : pandas.DataFrame Adjacency list with a minimum of two columns. focal_col : str @@ -256,13 +269,23 @@ def from_adjlist( Name of the column with the weight information. If not provided and the dataframe has no column named "weight" then all weights are assumed to be 1. + + Returns + ------- + w : libpysal.weights.W + NEED....................................................................................... + """ + if weight_col is None: weight_col = "weight" + try_weightcol = getattr(adjlist, weight_col) + if try_weightcol is None: adjlist = adjlist.copy(deep=True) adjlist["weight"] = 1 + all_ids = set(adjlist[focal_col].tolist()) all_ids |= set(adjlist[neighbor_col].tolist()) grouper = adjlist.groupby(focal_col) @@ -270,7 +293,10 @@ def from_adjlist( weights = grouper[weight_col].apply(list).to_dict() neighbors.update({k: [] for k in all_ids.difference(list(neighbors.keys()))}) weights.update({k: [] for k in all_ids.difference(list(weights.keys()))}) - return cls(neighbors=neighbors, weights=weights) + + w = cls(neighbors=neighbors, weights=weights) + + return w def to_adjlist( self, @@ -279,8 +305,7 @@ def to_adjlist( neighbor_col="neighbor", weight_col="weight", ): - """ - Compute an adjacency list representation of a weights object. + """Compute an adjacency list representation of a weights object. Parameters ---------- @@ -298,21 +323,31 @@ def to_adjlist( Name of the column in which to store "destination" node ids. weight_col : str Name of the column in which to store weight information. + + Returns + ------- + NEED : NEED + NEED...................................................................................... + """ + try: import pandas as pd except ImportError: - raise ImportError("pandas must be installed to use this method") + raise ImportError("Pandas must be installed to use this method.") + n_islands = len(self.islands) + if n_islands > 0 and (not self.silence_warnings): - warnings.warn( - "{} islands in this weights matrix. Conversion to an " - "adjacency list will drop these observations!" - ) + msg = "The are %s islands in this weights matrix. " % n_islands + msg += "Conversion to an adjacency list will drop these observations!" + warnings.warn(msg) + adjlist = pd.DataFrame( ((idx, n, w) for idx, neighb in self for n, w in list(neighb.items())), columns=("focal", "neighbor", "weight"), ) + return adjtools.filter_adjlist(adjlist) if remove_symmetric else adjlist def to_networkx(self): @@ -320,144 +355,158 @@ def to_networkx(self): Returns ------- - A ``networkx`` graph representation of the ``W`` object. + netx : networkx.Graph + A ``networkx`` graph representation of the `W` object. + """ + try: import networkx as nx except ImportError: - raise ImportError("NetworkX is required to use this function.") + raise ImportError("NetworkX is required to use this method.") + G = nx.DiGraph() if len(self.asymmetries) > 0 else nx.Graph() - return nx.from_scipy_sparse_matrix(self.sparse, create_using=G) + + netx = nx.from_scipy_sparse_matrix(self.sparse, create_using=G) + + return netx @classmethod def from_networkx(cls, graph, weight_col="weight"): - """Convert a ``networkx`` graph to a PySAL ``W`` object. + """Convert a ``networkx`` graph to a PySAL `W` object. Parameters ---------- graph : networkx.Graph - The graph to convert to a ``W``. - weight_col : string + The graph to convert to a `W`. + weight_col : str If the graph is labeled, this should be the name of the field - to use as the weight for the ``W``. + to use as the weight for the `W`. Default is ``'weight'``. Returns ------- - w : libpysal.weights.W - A ``W`` object containing the same graph as the ``networkx`` graph. + w : libpysal.weights.WSP + A `WSP` object containing the same graph as the ``networkx`` graph. + """ + try: import networkx as nx except ImportError: - raise ImportError("NetworkX is required to use this function.") + raise ImportError("NetworkX is required to use this method.") + sparse_matrix = nx.to_scipy_sparse_matrix(graph) w = WSP(sparse_matrix).to_W() + return w @property def sparse(self): - """Sparse matrix object. For any matrix manipulations required for w, + """A sparse matrix object. For any matrix manipulations required for w, ``w.sparse`` should be used. This is based on ``scipy.sparse``. """ + if "sparse" not in self._cache: self._sparse = self._build_sparse() self._cache["sparse"] = self._sparse + return self._sparse @property def n_components(self): - """Store whether the adjacency matrix is fully connected. - """ + """Store whether the adjacency matrix is fully connected.""" + if "n_components" not in self._cache: self._n_components, self._component_labels = connected_components( self.sparse ) self._cache["n_components"] = self._n_components self._cache["component_labels"] = self._component_labels + return self._n_components @property def component_labels(self): - """Store the graph component in which each observation falls. - """ + """Store the graph component in which each observation falls.""" + if "component_labels" not in self._cache: self._n_components, self._component_labels = connected_components( self.sparse ) self._cache["n_components"] = self._n_components self._cache["component_labels"] = self._component_labels + return self._component_labels def _build_sparse(self): - """Construct the sparse attribute. - """ + """Construct the sparse attribute.""" row = [] col = [] data = [] id2i = self.id2i + for i, neigh_list in list(self.neighbor_offsets.items()): card = self.cardinalities[i] row.extend([id2i[i]] * card) col.extend(neigh_list) data.extend(self.weights[i]) + row = np.array(row) col = np.array(col) data = np.array(data) + s = scipy.sparse.csr_matrix((data, (row, col)), shape=(self.n, self.n)) + return s @property def id2i(self): - """Dictionary where the key is an ID and the value is that ID's - index in ``W.id_order``. + """A dictionary where the key is an ID + and the value is that ID's index in ``W.id_order``. """ + if "id2i" not in self._cache: self._id2i = {} for i, id_i in enumerate(self._id_order): self._id2i[id_i] = i self._id2i = self._id2i self._cache["id2i"] = self._id2i + return self._id2i @property def n(self): - """Number of units. - """ + """The number of units.""" + if "n" not in self._cache: self._n = len(self.neighbors) self._cache["n"] = self._n + return self._n @property def s0(self): - r"""``s0`` is defined as - - .. math:: - - s0=\sum_i \sum_j w_{i,j} + """``s0`` is defined as :math:`s0=\sum_i \sum_j w_{i,j}`.""" - """ if "s0" not in self._cache: self._s0 = self.sparse.sum() self._cache["s0"] = self._s0 + return self._s0 @property def s1(self): - r"""``s1`` is defined as - - .. math:: - - s1=1/2 \sum_i \sum_j \Big(w_{i,j} + w_{j,i}\Big)^2 - + """``s1`` is defined as :math:`s1=1/2 \sum_i \sum_j \Big(w_{i,j} + w_{j,i}\Big)^2`. """ + if "s1" not in self._cache: t = self.sparse.transpose() t = t + self.sparse t2 = t.multiply(t) # element-wise square self._s1 = t2.sum() / 2.0 self._cache["s1"] = self._s1 + return self._s1 @property @@ -466,27 +515,27 @@ def s2array(self): See Also -------- - s2 + + libpysal.weights.W.s2 """ + if "s2array" not in self._cache: s = self.sparse self._s2array = np.array(s.sum(1) + s.sum(0).transpose()) ** 2 self._cache["s2array"] = self._s2array + return self._s2array @property def s2(self): - r"""``s2`` is defined as - - .. math:: - - s2=\sum_j \Big(\sum_i w_{i,j} + \sum_i w_{j,i}\Big)^2 - + """``s2`` is defined as :math:`s2=\sum_j \Big(\sum_i w_{i,j} + \sum_i w_{j,i}\Big)^2`. """ + if "s2" not in self._cache: self._s2 = self.s2array.sum() self._cache["s2"] = self._s2 + return self._s2 @property @@ -495,12 +544,15 @@ def trcW2(self): See Also -------- - diagW2 + + libpysal.weights.W.diagW2 """ + if "trcW2" not in self._cache: self._trcW2 = self.diagW2.sum() self._cache["trcw2"] = self._trcW2 + return self._trcW2 @property @@ -509,12 +561,15 @@ def diagW2(self): See Also -------- - trcW2 + + libpysal.weights.W.trcW2 """ + if "diagw2" not in self._cache: self._diagW2 = (self.sparse * self.sparse).diagonal() self._cache["diagW2"] = self._diagW2 + return self._diagW2 @property @@ -523,12 +578,15 @@ def diagWtW(self): See Also -------- - trcWtW + + libpysal.weights.W.trcWtW """ + if "diagWtW" not in self._cache: self._diagWtW = (self.sparse.transpose() * self.sparse).diagonal() self._cache["diagWtW"] = self._diagWtW + return self._diagWtW @property @@ -537,116 +595,130 @@ def trcWtW(self): See Also -------- - diagWtW + + libpysal.weights.W.diagWtW """ + if "trcWtW" not in self._cache: self._trcWtW = self.diagWtW.sum() self._cache["trcWtW"] = self._trcWtW + return self._trcWtW @property def diagWtW_WW(self): - """Diagonal of :math:`W^{'}W + WW`. - """ + """Diagonal of :math:`W^{'}W + WW`.""" + if "diagWtW_WW" not in self._cache: wt = self.sparse.transpose() w = self.sparse self._diagWtW_WW = (wt * w + w * w).diagonal() self._cache["diagWtW_WW"] = self._diagWtW_WW + return self._diagWtW_WW @property def trcWtW_WW(self): - """Trace of :math:`W^{'}W + WW`. - """ + """Trace of :math:`W^{'}W + WW`.""" + if "trcWtW_WW" not in self._cache: self._trcWtW_WW = self.diagWtW_WW.sum() self._cache["trcWtW_WW"] = self._trcWtW_WW + return self._trcWtW_WW @property def pct_nonzero(self): - """Percentage of nonzero weights. - """ + """Percentage of nonzero weights.""" + if "pct_nonzero" not in self._cache: self._pct_nonzero = 100.0 * self.sparse.nnz / (1.0 * self._n ** 2) self._cache["pct_nonzero"] = self._pct_nonzero + return self._pct_nonzero @property def cardinalities(self): - """Number of neighbors for each observation. - """ + """Number of neighbors for each observation.""" + if "cardinalities" not in self._cache: c = {} for i in self._id_order: c[i] = len(self.neighbors[i]) self._cardinalities = c self._cache["cardinalities"] = self._cardinalities + return self._cardinalities @property def max_neighbors(self): - """Largest number of neighbors. - """ + """Largest number of neighbors.""" + if "max_neighbors" not in self._cache: self._max_neighbors = max(self.cardinalities.values()) self._cache["max_neighbors"] = self._max_neighbors + return self._max_neighbors @property def mean_neighbors(self): - """Average number of neighbors. - """ + """Average (mean) number of neighbors.""" + if "mean_neighbors" not in self._cache: self._mean_neighbors = np.mean(list(self.cardinalities.values())) self._cache["mean_neighbors"] = self._mean_neighbors + return self._mean_neighbors @property def min_neighbors(self): - """Minimum number of neighbors. - """ + """Minimum number of neighbors.""" + if "min_neighbors" not in self._cache: self._min_neighbors = min(self.cardinalities.values()) self._cache["min_neighbors"] = self._min_neighbors + return self._min_neighbors @property def nonzero(self): - """Number of nonzero weights. - """ + """Number of nonzero weights.""" + if "nonzero" not in self._cache: self._nonzero = self.sparse.nnz self._cache["nonzero"] = self._nonzero + return self._nonzero @property def sd(self): - """Standard deviation of number of neighbors. - """ + """Standard deviation of number of neighbors.""" + if "sd" not in self._cache: self._sd = np.std(list(self.cardinalities.values())) self._cache["sd"] = self._sd + return self._sd @property def asymmetries(self): - """List of id pairs with asymmetric weights. - """ + """List of id pairs with asymmetric weights.""" + if "asymmetries" not in self._cache: self._asymmetries = self.asymmetry() self._cache["asymmetries"] = self._asymmetries + return self._asymmetries @property def islands(self): - """List of ids without any neighbors. - """ + """List of ids without any neighbors.""" + if "islands" not in self._cache: self._islands = [i for i, c in list(self.cardinalities.items()) if c == 0] self._cache["islands"] = self._islands + return self._islands @property @@ -654,6 +726,7 @@ def histogram(self): """Cardinality histogram as a dictionary where key is the id and value is the number of neighbors for that unit. """ + if "histogram" not in self._cache: ct, bin = np.histogram( list(self.cardinalities.values()), @@ -661,6 +734,7 @@ def histogram(self): ) self._histogram = list(zip(bin, ct)) self._cache["histogram"] = self._histogram + return self._histogram def __getitem__(self, key): @@ -668,25 +742,26 @@ def __getitem__(self, key): Examples -------- + >>> from libpysal.weights import lat2W >>> w = lat2W() - >>> w[0] == dict({1: 1.0, 5: 1.0}) True + """ + return dict(list(zip(self.neighbors[key], self.weights[key]))) def __iter__(self): - """ - Support iteration over weights. + """Support iteration over weights. Examples -------- + >>> from libpysal.weights import lat2W - >>> w=lat2W(3,3) - >>> for i,wi in enumerate(w): - ... print(i,wi[0]) - ... + >>> w = lat2W(3, 3) + >>> for i, wi in enumerate(w): + ... print(i, wi[0]) 0 0 1 1 2 2 @@ -696,24 +771,23 @@ def __iter__(self): 6 6 7 7 8 8 - >>> + """ + for i in self._id_order: yield i, dict(list(zip(self.neighbors[i], self.weights[i]))) def remap_ids(self, new_ids): - """ - In place modification throughout ``W`` of id values from - ``w.id_order`` to ``new_ids`` in all. + """An in place modification throughout `W` of id + values from ``w.id_order`` to ``new_ids`` in all. Parameters ---------- - - new_ids : list, numpy.ndarray - Aligned list of new ids to be inserted. Note that first - element of ``new_ids`` will replace first element of - ``w.id_order``, second element of ``new_ids`` replaces second - element of ``w.id_order`` and so on. + new_ids : {list, numpy.ndarray} + An aligned list of new ids to be inserted. Note that the + first element of ``new_ids`` will replace the first element + of ``w.id_order``, the second element of ``new_ids`` replaces + the second element of ``w.id_order`` and so on. Examples -------- @@ -730,21 +804,23 @@ def remap_ids(self, new_ids): ['id0', 'id1', 'id2', 'id3', 'id4', 'id5', 'id6', 'id7', 'id8'] >>> w.neighbors['id0'] ['id3', 'id1'] + """ old_ids = self._id_order + if len(old_ids) != len(new_ids): - raise Exception( - "W.remap_ids: length of `old_ids` does not match \ - that of new_ids" - ) + msg = "'W.remap_ids': length of 'old_ids' does not match that of 'new_ids'." + raise Exception(msg) + if len(set(new_ids)) != len(new_ids): - raise Exception("W.remap_ids: list `new_ids` contains duplicates") + raise Exception("'W.remap_ids': list 'new_ids' contains duplicates.") else: new_neighbors = {} new_weights = {} old_transformations = self.transformations["O"].copy() new_transformations = {} + for o, n in zip(old_ids, new_ids): o_neighbors = self.neighbors[o] o_weights = self.weights[o] @@ -752,6 +828,7 @@ def remap_ids(self, new_ids): new_neighbors[n] = n_neighbors new_weights[n] = o_weights[:] new_transformations[n] = old_transformations[o] + self.neighbors = new_neighbors self.weights = new_weights self.transformations["O"] = new_transformations @@ -763,14 +840,13 @@ def remap_ids(self, new_ids): self._reset() def __set_id_order(self, ordered_ids): - """Set the iteration order in w. ``W`` can be iterated over. On - construction the iteration order is set to the lexicographic order of + """Set the iteration order in w. ``W`` can be iterated over. On + construction the iteration order is set to the lexicographic order of the keys in the ``w.weights`` dictionary. If a specific order is required it can be set with this method. Parameters ---------- - ordered_ids : sequence Identifiers for observations in specified order. @@ -785,10 +861,9 @@ def __set_id_order(self, ordered_ids): -------- >>> from libpysal.weights import lat2W - >>> w=lat2W(3,3) + >>> w = lat2W(3,3) >>> for i,wi in enumerate(w): ... print(i, wi[0]) - ... 0 0 1 1 2 2 @@ -798,14 +873,14 @@ def __set_id_order(self, ordered_ids): 6 6 7 7 8 8 + >>> w.id_order [0, 1, 2, 3, 4, 5, 6, 7, 8] - >>> w.id_order=range(8,-1,-1) + >>> w.id_order = range(8,-1,-1) >>> list(w.id_order) [8, 7, 6, 5, 4, 3, 2, 1, 0] >>> for i,w_i in enumerate(w): ... print(i,w_i[0]) - ... 0 8 1 7 2 6 @@ -823,34 +898,37 @@ def __set_id_order(self, ordered_ids): self._id_order_set = True self._reset() else: - raise Exception("ordered_ids do not align with W ids") + raise Exception("'ordered_ids' do not align with 'W.ids'.") def __get_id_order(self): """Returns the ids for the observations in the order in which they would be encountered if iterating over the weights. """ + return self._id_order id_order = property(__get_id_order, __set_id_order) @property def id_order_set(self): - """ Returns ``True`` if user has set ``id_order``, ``False`` if not. + """Returns ``True`` if user has set ``id_order``, ``False`` if not. Examples -------- + >>> from libpysal.weights import lat2W - >>> w=lat2W() + >>> w = lat2W() >>> w.id_order_set True + """ + return self._id_order_set @property def neighbor_offsets(self): - """ - Given the current ``id_order``, ``neighbor_offsets[id]`` is the - offsets of the id's neighbors in ``id_order``. + """Given the current ``id_order``, ``neighbor_offsets[id]`` + is the offsets of the id's neighbors in ``id_order``. Returns ------- @@ -859,16 +937,18 @@ def neighbor_offsets(self): Examples -------- + >>> from libpysal.weights import W - >>> neighbors={'c': ['b'], 'b': ['c', 'a'], 'a': ['b']} - >>> weights ={'c': [1.0], 'b': [1.0, 1.0], 'a': [1.0]} - >>> w=W(neighbors,weights) + >>> neighbors = {'c': ['b'], 'b': ['c', 'a'], 'a': ['b']} + >>> weights = {'c': [1.0], 'b': [1.0, 1.0], 'a': [1.0]} + >>> w = W(neighbors,weights) >>> w.id_order = ['a','b','c'] >>> w.neighbor_offsets['b'] [2, 0] >>> w.id_order = ['b','a','c'] >>> w.neighbor_offsets['b'] [2, 1] + """ if "neighbors_0" not in self._cache: @@ -877,9 +957,9 @@ def neighbor_offsets(self): for j, neigh_list in list(self.neighbors.items()): self.__neighbors_0[j] = [id2i[neigh] for neigh in neigh_list] self._cache["neighbors_0"] = self.__neighbors_0 - + neighbor_list = self.__neighbors_0 - + return neighbor_list def get_transform(self): @@ -893,8 +973,9 @@ def get_transform(self): Examples -------- + >>> from libpysal.weights import lat2W - >>> w=lat2W() + >>> w = lat2W() >>> w.weights[0] [1.0, 1.0] >>> w.transform @@ -908,6 +989,7 @@ def get_transform(self): See also -------- + set_transform """ @@ -932,15 +1014,15 @@ def set_transform(self, value="B"): Notes ----- - Transformations are applied only to the value of the weights at - instantiation. Chaining of transformations cannot be done on a ``W`` - instance. + Transformations are applied only to the value of the weights at instantiation. + Chaining of transformations cannot be done on a `W` instance. Examples -------- + >>> from libpysal.weights import lat2W - >>> w=lat2W() + >>> w = lat2W() >>> w.weights[0] [1.0, 1.0] >>> w.transform @@ -951,9 +1033,12 @@ def set_transform(self, value="B"): >>> w.transform='b' >>> w.weights[0] [1.0, 1.0] + """ + value = value.upper() self._transform = value + if value in self.transformations: self.weights = self.transformations[value] self._reset() @@ -1026,42 +1111,32 @@ def set_transform(self, value="B"): self.weights = original self._reset() else: - raise Exception("unsupported weights transformation") + raise Exception("Unsupported weights transformation.") transform = property(get_transform, set_transform) def asymmetry(self, intrinsic=True): - r""" - Asymmetry check. + r"""Asymmetry check. Parameters ---------- intrinsic : bool Default is ``True``. Intrinsic symmetry is defined as - - .. math:: - - w_{i,j} == w_{j,i} - - If ``intrinsic`` is ``False`` symmetry is defined as - - .. math:: - - i \in N_j \ \& \ j \in N_i - + :math:`w_{i,j} == w_{j,i}`. If ``intrinsic`` is ``False`` + symmetry is defined as :math:`i \in N_j \ \& \ j \in N_i` where :math:`N_j` is the set of neighbors for :math:`j`. Returns ------- - asymmetries : list - Empty if no asymmetries are found if asymmetries, then a + ijs : list + Empty if no asymmetries are found if asymmetries, otherwise a ``list`` of ``(i,j)`` tuples is returned. Examples -------- >>> from libpysal.weights import lat2W - >>> w=lat2W(3,3) + >>> w = lat2W(3,3) >>> w.asymmetry() [] >>> w.transform='r' @@ -1070,11 +1145,12 @@ def asymmetry(self, intrinsic=True): >>> result = w.asymmetry(intrinsic=False) >>> result [] - >>> neighbors={0:[1,2,3], 1:[1,2,3], 2:[0,1], 3:[0,1]} - >>> weights={0:[1,1,1], 1:[1,1,1], 2:[1,1], 3:[1,1]} - >>> w=W(neighbors,weights) + >>> neighbors = {0:[1,2,3], 1:[1,2,3], 2:[0,1], 3:[0,1]} + >>> weights = {0:[1,1,1], 1:[1,1,1], 2:[1,1], 3:[1,1]} + >>> w = W(neighbors,weights) >>> w.asymmetry() [(0, 1), (1, 0)] + """ if intrinsic: @@ -1086,25 +1162,41 @@ def asymmetry(self, intrinsic=True): self.transform = transform ids = np.nonzero(wd) + if len(ids[0]) == 0: - return [] + ijs = [] + return ijs else: ijs = list(zip(ids[0], ids[1])) ijs.sort() return ijs def symmetrize(self, inplace=False): - """Construct a symmetric KNN weight. This ensures that the neighbors + """Construct a symmetric ``KNN`` weight. This ensures that the neighbors of each focal observation consider the focal observation itself as - a neighbor. This returns a generic ``W`` object, since the object is no + a neighbor. This returns a generic `W` object, since the object is no longer guaranteed to have ``k`` neighbors for each observation. + + Parameters + ---------- + inplace : bool + Update the `W` object in place (``True``). Default is ``False``. + + Returns + ------- + out_W : libpysal.weights.W + A symmetrized `W` object. Default is ``False``. + If ``inplace`` is set to ``True`` the `W` object is simply updated. + """ + if not inplace: neighbors = copy.deepcopy(self.neighbors) weights = copy.deepcopy(self.weights) out_W = W(neighbors, weights, id_order=self.id_order) out_W.symmetrize(inplace=True) return out_W + else: for focal, fneighbs in list(self.neighbors.items()): for j, neighbor in enumerate(fneighbs): @@ -1131,6 +1223,7 @@ def full(self): Examples -------- + >>> from libpysal.weights import W, full >>> neighbors = {'first':['second'],'second':['first','third'],'third':['second']} >>> weights = {'first':[1],'second':[1,1],'third':[1]} @@ -1142,17 +1235,22 @@ def full(self): [0., 1., 0.]]) >>> ids ['first', 'second', 'third'] + """ + wfull = np.zeros([self.n, self.n], dtype=float) keys = list(self.neighbors.keys()) + if self.id_order: keys = self.id_order + for i, key in enumerate(keys): n_i = self.neighbors[key] w_i = self.weights[key] for j, wij in zip(n_i, w_i): c = keys.index(j) wfull[i, c] = wij + return (wfull, keys) def to_WSP(self): @@ -1160,17 +1258,17 @@ def to_WSP(self): Returns ------- - - implicit : libpysal.weights.WSP - Thin ``W`` class + w : libpysal.weights.WSP + A thin `W` class. Examples -------- + >>> from libpysal.weights import W, WSP - >>> neighbors={'first':['second'],'second':['first','third'],'third':['second']} - >>> weights={'first':[1],'second':[1,1],'third':[1]} - >>> w=W(neighbors,weights) - >>> wsp=w.to_WSP() + >>> neighbors = {'first':['second'],'second':['first','third'],'third':['second']} + >>> weights = {'first':[1],'second':[1,1],'third':[1]} + >>> w = W(neighbors,weights) + >>> wsp = w.to_WSP() >>> isinstance(wsp, WSP) True >>> wsp.n @@ -1180,10 +1278,14 @@ def to_WSP(self): See also -------- - WSP + + libpysal.weights.WSP """ - return WSP(self.sparse, self._id_order) + + w = WSP(self.sparse, self._id_order) + + return w def set_shapefile(self, shapefile, idVariable=None, full=False): """ @@ -1200,6 +1302,7 @@ def set_shapefile(self, shapefile, idVariable=None, full=False): Write out the entire path for a shapefile (``True``) or only the base of the shapefile without extension (``False``). Default is ``True``. + """ if full: @@ -1260,26 +1363,33 @@ def plot( >>> import geopandas >>> gdf = geopandas.read_file(lp.examples.get_path("columbus.shp")) >>> weights = Queen.from_dataframe(gdf) - >>> tmp = weights.plot(gdf, color='firebrickred', node_kws=dict(marker='*', color='k')) + >>> tmp = weights.plot( + ... gdf, color='firebrickred', node_kws=dict(marker='*', color='k') + ... ) + """ + try: import matplotlib.pyplot as plt except ImportError: raise ImportError( - "W.plot depends on matplotlib.pyplot, and this was" - "not able to be imported. \nInstall matplotlib to" + "'W.plot()' depends on 'matplotlib.pyplot', and this was" + "not able to be imported. \nInstall 'matplotlib' to" "plot spatial weights." ) + if ax is None: f = plt.figure() ax = plt.gca() else: f = plt.gcf() + if node_kws is not None: if "color" not in node_kws: node_kws["color"] = color else: node_kws = dict(color=color) + if edge_kws is not None: if "color" not in edge_kws: edge_kws["color"] = color @@ -1302,40 +1412,39 @@ def plot( ax.plot(*list(zip(focal, neighbor)), marker=None, **edge_kws) seen.update((idx, nidx)) seen.update((nidx, idx)) + ax.scatter( gdf.centroid.apply(lambda p: p.x), gdf.centroid.apply(lambda p: p.y), **node_kws ) + return f, ax class WSP(object): - """Thin ``W`` class for ``spreg``. + """Thin `W` class for ``spreg``. Parameters ---------- - sparse : scipy.sparse.{matrix-type} - NxN object from ``scipy.sparse`` - + An :math:`NxN` object from ``scipy.sparse``. id_order : list An ordered list of ids, assumed to match the ordering in ``sparse``. Attributes ---------- - - n : int - description - s0 : float - description - trcWtW_WW : float - description + n : int + description.................................................................................................... + s0 : float + description.................................................................................................... + trcWtW_WW : float + description.................................................................................................... Examples -------- - From GAL information + From GAL information: >>> import scipy.sparse >>> from libpysal.weights import WSP @@ -1355,38 +1464,37 @@ class WSP(object): def __init__(self, sparse, id_order=None): if not scipy.sparse.issparse(sparse): - raise ValueError("must pass a scipy sparse object") + raise ValueError("A scipy sparse object must be passed in.") + rows, cols = sparse.shape if rows != cols: - raise ValueError("Weights object must be square") + raise ValueError("The weights object must be square.") + self.sparse = sparse.tocsr() self.n = sparse.shape[0] + if id_order: if len(id_order) != self.n: - raise ValueError( - "Number of values in id_order must match shape of sparse" - ) + msg = "The Number of values in 'id_order' must match shape of 'sparse'." + raise ValueError(msg) + self.id_order = id_order self._cache = {} @property def s0(self): - r"""``s0`` is defined as: - - .. math:: - - s0=\sum_i \sum_j w_{i,j} + r"""``s0`` is defined as :math:`s0=\sum_i \sum_j w_{i,j}`.""" - """ if "s0" not in self._cache: self._s0 = self.sparse.sum() self._cache["s0"] = self._s0 + return self._s0 @property def trcWtW_WW(self): - """Trace of :math:`W^{'}W + WW`. - """ + """Trace of :math:`W^{'}W + WW`.""" + if "trcWtW_WW" not in self._cache: self._trcWtW_WW = self.diagWtW_WW.sum() self._cache["trcWtW_WW"] = self._trcWtW_WW @@ -1394,18 +1502,19 @@ def trcWtW_WW(self): @property def diagWtW_WW(self): - """Diagonal of :math:`W^{'}W + WW`. - """ + """Diagonal of :math:`W^{'}W + WW`.""" + if "diagWtW_WW" not in self._cache: wt = self.sparse.transpose() w = self.sparse self._diagWtW_WW = (wt * w + w * w).diagonal() self._cache["diagWtW_WW"] = self._diagWtW_WW + return self._diagWtW_WW @classmethod def from_W(cls, W): - """Constructs a ``WSP`` object from the ``W``'s sparse matrix. + """Constructs a `WSP` object from the `W`'s sparse matrix. Parameters ---------- @@ -1414,18 +1523,20 @@ def from_W(cls, W): Returns ------- - A ``WSP`` instance. + w : libpysal.weights.WSP + A `WSP` instance. + """ - return cls(W.sparse, id_order=W.id_order) + + w = cls(W.sparse, id_order=W.id_order) + + return w def to_W(self, silence_warnings=False): - """ - Convert a pysal WSP object (thin weights matrix) to a pysal W object. + """Convert a PySAL `WSP` object (thin weights matrix) to a pysal `W` object. Parameters ---------- - self : WSP - PySAL sparse weights object. silence_warnings : bool Switch to ``True`` to turn off print statements for every observation with islands. Default is ``False``, which does @@ -1433,27 +1544,28 @@ def to_W(self, silence_warnings=False): Returns ------- - w : W - PySAL weights object. + w : libpysal.weights.W + A PySAL spatial weights object. Examples -------- + >>> from libpysal.weights import lat2SW, WSP, WSP2W Build a 10x10 ``scipy.sparse`` matrix for a rectangular 2x5 region of cells (rook contiguity), then construct a ``libpysal`` - sparse weights object (``self``). + sparse weights object. >>> sp = lat2SW(2, 5) - >>> self = WSP(sp) - >>> self.n + >>> wsp = WSP(sp) + >>> wsp.n 10 - >>> print(self.sparse[0].todense()) + >>> print(wsp.sparse[0].todense()) [[0 1 0 0 0 1 0 0 0 0]] Convert this sparse weights object to a standard PySAL weights object. - >>> w = WSP2W(self) + >>> w = WSP2W(wsp) >>> w.n 10 >>> print(w.full()[0][0]) @@ -1465,12 +1577,15 @@ def to_W(self, silence_warnings=False): data = list(self.sparse.data) indptr = list(self.sparse.indptr) id_order = self.id_order + if id_order: # replace indices with user IDs indices = [id_order[i] for i in indices] else: id_order = list(range(self.n)) + neighbors, weights = {}, {} + start = indptr[0] for i in range(self.n): oid = id_order[i] @@ -1478,8 +1593,10 @@ def to_W(self, silence_warnings=False): neighbors[oid] = indices[start:end] weights[oid] = data[start:end] start = end + ids = copy.copy(self.id_order) w = W(neighbors, weights, ids, silence_warnings=silence_warnings) w._sparse = copy.deepcopy(self.sparse) w._cache["sparse"] = w._sparse + return w From ae5419a434152f137c17a565c807938001edb97d Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Sun, 23 Aug 2020 16:43:15 -0400 Subject: [PATCH 19/33] fixing more various typos, etc. --- libpysal/weights/contiguity.py | 15 ++--- libpysal/weights/distance.py | 88 +++++++++++++++--------------- libpysal/weights/set_operations.py | 6 +- libpysal/weights/spatial_lag.py | 29 +++++----- libpysal/weights/spintW.py | 8 +-- 5 files changed, 74 insertions(+), 72 deletions(-) diff --git a/libpysal/weights/contiguity.py b/libpysal/weights/contiguity.py index e931ab83d..ef2353a92 100644 --- a/libpysal/weights/contiguity.py +++ b/libpysal/weights/contiguity.py @@ -88,10 +88,10 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): >>> from libpysal.weights import Rook >>> import libpysal - >>> wr=Rook.from_shapefile(libpysal.examples.get_path("columbus.shp"), "POLYID") + >>> wr = Rook.from_shapefile(libpysal.examples.get_path("columbus.shp"), "POLYID") >>> "%.3f"%wr.pct_nonzero '8.330' - >>> wr=Rook.from_shapefile(libpysal.examples.get_path("columbus.shp"), sparse=True) + >>> wr = Rook.from_shapefile(libpysal.examples.get_path("columbus.shp"), sparse=True) >>> pct_sp = wr.sparse.nnz *1. / wr.n**2 >>> "%.3f"%pct_sp '0.083' @@ -173,7 +173,7 @@ def from_dataframe( Parameters ---------- df : pandas.DataFrame - A ``pandas.DataFrame` containing geometries to use for spatial weights. + A ``pandas.DataFrame`` containing geometries to use for spatial weights. geom_col : str The name of the column in ``df`` that contains the geometries. Default is ``'geometry'``. @@ -258,8 +258,7 @@ def __init__(self, polygons, **kwargs): @classmethod def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): - """ - Queen contiguity weights from a polygon shapefile. + """Queen contiguity weights from a polygon shapefile. Parameters ---------- @@ -272,7 +271,7 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): Write out the entire path for a shapefile (``True``) or only the base of the shapefile without extension (``False``). Default is ``False``. - **kwargs : dict + **kwargs : dict Keyword arguments for ``libpysal.weights.Queen``. ``'sparse'`` should be included here. If ``True`` return `WSP` instance. If ``False`` return `W` instance. @@ -373,10 +372,12 @@ def from_dataframe(cls, df, geom_col="geometry", **kwargs): Parameters ---------- df : pandas.DataFrame - A ``pandas.DataFrame` containing geometries to use for spatial weights. + A ``pandas.DataFrame`` containing geometries to use for spatial weights. geom_col : str The name of the column in ``df`` that contains the geometries. Default is ``'geometry'``. + **kwargs : dict + Keyword arguments for ``libpysal.weights.Queen``. Returns ------- diff --git a/libpysal/weights/distance.py b/libpysal/weights/distance.py index c0db67e26..378410fb0 100644 --- a/libpysal/weights/distance.py +++ b/libpysal/weights/distance.py @@ -38,10 +38,10 @@ class KNN(W): k : int The number of nearest neighbors. Default is ``2``. p : {int, float} - Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. - ``2`` is Euclidean distance and ``1`` is Manhattan distance. - This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. - Default is ``2``. + Minkowski `p`-norm distance metric parameter where + :math:`1<=\mathtt{p}<=\infty`. ``2`` is Euclidean distance and + ``1`` is Manhattan distance. This parameter is ignored if the + ``KDTree`` is an ``ArcKDTree``. Default is ``2``. ids : list Identifiers to attach to each observation. Default is ``None``. radius : float @@ -165,12 +165,12 @@ def from_shapefile(cls, filepath, *args, **kwargs): >>> import libpysal >>> from libpysal.weights import KNN - >>> wc=KNN.from_shapefile(libpysal.examples.get_path("columbus.shp")) + >>> wc = KNN.from_shapefile(libpysal.examples.get_path("columbus.shp")) >>> "%.4f"%wc.pct_nonzero '4.0816' >>> set([2,1]) == set(wc.neighbors[0]) True - >>> wc3=KNN.from_shapefile(libpysal.examples.get_path("columbus.shp"),k=3) + >>> wc3 = KNN.from_shapefile(libpysal.examples.get_path("columbus.shp"),k=3) >>> set(wc3.neighbors[0]) == set([2,1,3]) True >>> set(wc3.neighbors[2]) == set([4,3,0]) @@ -178,10 +178,10 @@ def from_shapefile(cls, filepath, *args, **kwargs): From a point shapefile: - >>> w=KNN.from_shapefile(libpysal.examples.get_path("juvenile.shp")) + >>> w = KNN.from_shapefile(libpysal.examples.get_path("juvenile.shp")) >>> w.pct_nonzero 1.1904761904761905 - >>> w1=KNN.from_shapefile(libpysal.examples.get_path("juvenile.shp"),k=1) + >>> w1 = KNN.from_shapefile(libpysal.examples.get_path("juvenile.shp"),k=1) >>> "%.3f"%w1.pct_nonzero '0.595' @@ -312,10 +312,10 @@ def reweight(self, k=None, p=None, new_data=None, new_ids=None, inplace=True): k : int The number of nearest neighbors. Default is ``None``. p : {int, float} - Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. - ``2`` is Euclidean distance and ``1`` is Manhattan distance. - This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. - Default is ``None``. + Minkowski `p`-norm distance metric parameter where + :math:`1<=\mathtt{p}<=\infty`. ``2`` is Euclidean distance and + ``1`` is Manhattan distance. This parameter is ignored if the + ``KDTree`` is an ``ArcKDTree``. Default is ``None``. new_data : numpy.ndarray An array containing additional data to use in the ``KNN`` weight. Default is ``None``. @@ -369,18 +369,18 @@ class Kernel(W): Parameters ---------- data : {libpysal.cg.KDTree, libpysal.cg.ArcKDTree} - An ``(n,k)`` array of `n` observations on `k` characteristics - used to measure distances between the `n` objects. + An :math:`(n,k)` array of :math:`n` observations on :math:`k` + characteristics used to measure distances between the :math:`n` objects. k : int The number of nearest neighbors to use for determining the bandwidth. For a - fixed bandwidth, :math:`h_i=max(dknn) \\forall i` where :math:`dknn` is a - vector of `k``-nearest neighbor distances (the distance to the `k`th nearest - neighbor for each observation). For adaptive bandwidths, :math:`h_i=dknn_i`. - Default is ``2``. + fixed bandwidth, :math:`h_i = max(dknn) \\forall i` where :math:`dknn` is a + vector of :math:`k`-nearest neighbor distances (the distance to the + :math:`k`th nearest neighbor for each observation). For adaptive bandwidths, + :math:`h_i=dknn_i`. Default is ``2``. bandwidth : {float, array-like} The bandwidth :math:`h_i` for the kernel. Default is ``None``. fixed : bool - If ``True`` then :math:`h_i=h \\forall i`. If ``False`` then + If ``True`` then :math:`h_i = h \\forall i`. If ``False`` then bandwidth is adaptive across observations. Default is ``True``. diagonal : bool If ``True``, set diagonal weights to ``1.0``. If ``False`` diagonal weights @@ -402,39 +402,39 @@ class Kernel(W): or ``'gaussian'``. Default is ``'triangular'``. The kernel function is defined as follows with - .. math:: + .. math:: - z_{i,j} = d_{i,j}/h_i + z_{i,j} = d_{i,j}/h_i - triangular + triangular - .. math:: + .. math:: - K(z) = (1 - |z|) \\ if |z| \\le 1 + K(z) = (1 - |z|) \\ if |z| \\le 1 - uniform + uniform - .. math:: + .. math:: - K(z) = 1/2 \\ if |z| \\le 1 + K(z) = 1/2 \\ if |z| \\le 1 - quadratic + quadratic - .. math:: + .. math:: - K(z) = (3/4)(1-z^2) \\ if |z| \\le 1 + K(z) = (3/4)(1-z^2) \\ if |z| \\le 1 - quartic + quartic - .. math:: + .. math:: - K(z) = (15/16)(1-z^2)^2 \\ if |z| \\le 1 + K(z) = (15/16)(1-z^2)^2 \\ if |z| \\le 1 - gaussian + gaussian - .. math:: + .. math:: - K(z) = (2\\pi)^{(-1/2)} exp(-z^2 / 2) + K(z) = (2\\pi)^{(-1/2)} exp(-z^2 / 2) **kwargs : dict Keyword arguments for ``libpysal.weights.W``. @@ -452,8 +452,8 @@ class Kernel(W): -------- >>> from libpysal.weights import Kernel - >>> points=[(10, 10), (20, 10), (40, 10), (15, 20), (30, 20), (30, 30)] - >>> kw=Kernel(points) + >>> points = [(10, 10), (20, 10), (40, 10), (15, 20), (30, 20), (30, 30)] + >>> kw = Kernel(points) >>> kw.weights[0] [1.0, 0.500000049999995, 0.4409830615267465] >>> kw.neighbors[0] @@ -465,7 +465,7 @@ class Kernel(W): [20.000002], [20.000002], [20.000002]]) - >>> kw15=Kernel(points,bandwidth=15.0) + >>> kw15 = Kernel(points,bandwidth=15.0) >>> kw15[0] {0: 1.0, 1: 0.33333333333333337, 3: 0.2546440075000701} >>> kw15.neighbors[0] @@ -480,8 +480,8 @@ class Kernel(W): Adaptive bandwidths user specified: - >>> bw=[25.0,15.0,25.0,16.0,14.5,25.0] - >>> kwa=Kernel(points,bandwidth=bw) + >>> bw = [25.0,15.0,25.0,16.0,14.5,25.0] + >>> kwa = Kernel(points,bandwidth=bw) >>> kwa.weights[0] [1.0, 0.6, 0.552786404500042, 0.10557280900008403] >>> kwa.neighbors[0] @@ -496,7 +496,7 @@ class Kernel(W): Endogenous adaptive bandwidths: - >>> kwea=Kernel(points,fixed=False) + >>> kwea = Kernel(points,fixed=False) >>> kwea.weights[0] [1.0, 0.10557289844279438, 9.99999900663795e-08] >>> kwea.neighbors[0] @@ -511,7 +511,7 @@ class Kernel(W): Endogenous adaptive bandwidths with Gaussian kernel: - >>> kweag=Kernel(points,fixed=False,function='gaussian') + >>> kweag = Kernel(points,fixed=False,function='gaussian') >>> kweag.weights[0] [0.3989422804014327, 0.2674190291577696, 0.2419707487162134] >>> kweag.bandwidth @@ -840,7 +840,7 @@ class DistanceBand(W): -------- >>> import libpysal - >>> points=[(10, 10), (20, 10), (40, 10), (15, 20), (30, 20), (30, 30)] + >>> points = [(10, 10), (20, 10), (40, 10), (15, 20), (30, 20), (30, 30)] >>> wcheck = libpysal.weights.W({0: [1, 3], 1: [0, 3], 2: [], 3: [0, 1], 4: [5], 5: [4]}) UserWarning: The weights matrix is not fully connected: There are 3 disconnected components. diff --git a/libpysal/weights/set_operations.py b/libpysal/weights/set_operations.py index a5eb6f36b..ea223151e 100644 --- a/libpysal/weights/set_operations.py +++ b/libpysal/weights/set_operations.py @@ -414,10 +414,10 @@ def w_clip(w1, w2, outSP=True, **kwargs): Parameters ---------- - w1 : {libpysal.weights.W, scipy.sparse.csr.csr_matrix} + w1 : {libpysal.weights.W, scipy.sparse.csr_matrix} The potentially continuous weights matrix to be clipped. The clipped matrix, ``wc``, will have at most the same elements as ``w1``. - w2 : {libpysal.weights.W, scipy.sparse.csr.csr_matrix} + w2 : {libpysal.weights.W, scipy.sparse.csr_matrix} The weights matrix to use as a shell to clip ``w1``. It is automatically converted to binary format. Only non-zero elements in ``w2`` will be kept non-zero in ``wc``. It is assumed to be of the same shape as ``w1``. @@ -429,7 +429,7 @@ def w_clip(w1, w2, outSP=True, **kwargs): Returns ------- - wc : {libpysal.weights.W, scipy.sparse.csr.csr_matrix} + wc : {libpysal.weights.W, scipy.sparse.csr_matrix} A clipped `W` object that is sparse if ``outSP`` is set to ``True``. It inherits ``id_order`` from ``w1``. diff --git a/libpysal/weights/spatial_lag.py b/libpysal/weights/spatial_lag.py index 1e73ae40a..917fdc93b 100644 --- a/libpysal/weights/spatial_lag.py +++ b/libpysal/weights/spatial_lag.py @@ -102,22 +102,23 @@ def lag_categorical(w, y, ties="tryself"): An iterable collection of categories (either ``int`` or ``str``) with dimensionality conforming to ``w`` (see examples). ties : str - The method to use when resolving ties. By default, the option is ``'tryself'``, - and the category of the focal observation is included with its neighbors to try - and break a tie. If this does not resolve the tie, a winner is chosen randomly. - To just use random choice to break ties, pass ``'random'`` instead. - All supported options include: - 1. ``'tryself'``: Use the focal observation's label to tiebreak. - If this doesn't successfully break the tie, (which only occurs - if it induces a new tie), decide randomly.; - 2. ``'random'``: Resolve the tie randomly amongst winners.; - 3. ``'lowest'``: Pick the lowest-value label amongst winners.; - 4. ``'highest'``: Pick the highest-value label amongst winners. + The method to use when resolving ties. By default, the option is + ``'tryself'``, and the category of the focal observation is included + with its neighbors to try and break a tie. If this does not resolve + the tie, a winner is chosen randomly. To just use random choice to + break ties, pass ``'random'`` instead. + The following are supported options + + * ``'tryself'`` -- Use the focal observation's label to tiebreak. If this doesn't successfully break the tie, which only occurs if it induces a new tie, decide randomly.; + * ``'random'`` -- Resolve the tie randomly amongst winners.; + * ``'lowest'`` -- Pick the lowest-value label amongst winners.; + * ``'highest'`` -- Pick the highest-value label amongst winners. Returns ------- output : numpy.ndarray - An (n x k) column vector containing the most common neighboring observation. + An :math:`(n \cdot k)` column vector containing + the most common neighboring observation. Notes ----- @@ -203,9 +204,9 @@ def _resolve_ties(idx, normalized_labels, tally, neighbors, method, w): The index (aligned with ``normalized_labels``) of the current observation being resolved. normalized_labels : numpy.ndarray - A `(n,)` normalized array of labels for each observation. + A :math:`(n,)` normalized array of labels for each observation. tally : numpy.ndarray - The current tally of `(p,)` neighbors' labels around ``idx`` to resolve. + The current tally of :math:`(p,)` neighbors' labels around ``idx`` to resolve. neighbors : dict of (neighbor_name : weight) The elements of the weights object (identical to ``w[idx]``) in the form ``{neighbor_name : weight}``. diff --git a/libpysal/weights/spintW.py b/libpysal/weights/spintW.py index cccf99eab..234db9f21 100644 --- a/libpysal/weights/spintW.py +++ b/libpysal/weights/spintW.py @@ -229,8 +229,8 @@ def vecW( Optional keyword arguments arguments for ``libpysal.weights.W``. Returns - ------ - W : libpysal.weights.DistanceBand + ------- + w : libpysal.weights.DistanceBand A ``libpysal.weights.DistanceBand`` `W` object that uses 4-dimenional distances between vectors of origin and destination coordinates. @@ -253,7 +253,7 @@ def vecW( data = list(zip(origin_x, origin_y, dest_x, dest_y)) - W = DistanceBand( + w = DistanceBand( data, threshold=threshold, p=p, @@ -264,7 +264,7 @@ def vecW( **kwargs ) - return W + return w def mat2L(edge_matrix): From bc1d4bbcaea69c9ce634528af9dc5b9f0a63da69 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Mon, 24 Aug 2020 14:58:47 -0400 Subject: [PATCH 20/33] updating weights/weights.py and other typos, etc. --- libpysal/weights/_contW_lists.py | 19 ++++++------ libpysal/weights/adjtools.py | 2 +- libpysal/weights/contiguity.py | 22 +++++++------- libpysal/weights/distance.py | 18 +++++------ libpysal/weights/spintW.py | 8 ++--- libpysal/weights/weights.py | 51 ++++++++++++++++---------------- 6 files changed, 60 insertions(+), 60 deletions(-) diff --git a/libpysal/weights/_contW_lists.py b/libpysal/weights/_contW_lists.py index 887e23ace..29da5dc58 100644 --- a/libpysal/weights/_contW_lists.py +++ b/libpysal/weights/_contW_lists.py @@ -40,19 +40,18 @@ def _get_boundary_points(shape) -> list: class ContiguityWeightsLists: """Contiguity for a collection of polygons using high performance ``list``, ``set``, and ``dict`` containers. + + Parameters + ---------- + collection: PySAL PolygonCollection + A collection of polygons. + wttype: int + Set to ``1`` for Queen contiguity or set to ``2`` for Rook contiguity. + Default is ``1``. + """ def __init__(self, collection, wttype=1): - """ - - Parameters - ---------- - collection: PySAL PolygonCollection - - wttype: int - 1: Queen; 2: Rook - - """ self.collection = list(collection) self.wttype = wttype diff --git a/libpysal/weights/adjtools.py b/libpysal/weights/adjtools.py index c4875ae1e..529cf2b5d 100644 --- a/libpysal/weights/adjtools.py +++ b/libpysal/weights/adjtools.py @@ -28,7 +28,7 @@ def adjlist_apply(X, W=None, alist=None, func=np.subtract, skip_verify=False): ``sklearn.metrics.euclidean_distance`` skip_verify: bool Whether or not to skip verifying that the `W` is the same as an adjacency - list. Do this if you are certain the adjacency list and ``W`` agree and + list. Do this if you are certain the adjacency list and `W` agree and would like to avoid re-instantiating a `W` from the adjacency list. Default is ``False``. diff --git a/libpysal/weights/contiguity.py b/libpysal/weights/contiguity.py index ef2353a92..779d2fa7d 100644 --- a/libpysal/weights/contiguity.py +++ b/libpysal/weights/contiguity.py @@ -60,7 +60,7 @@ def __init__(self, polygons, **kwargs): @classmethod def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): - """Rook contiguity weights from a polygon shapefile. + """`Rook` contiguity weights from a polygon shapefile. Parameters ---------- @@ -99,7 +99,7 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): Notes ----- - Rook contiguity defines as neighbors any pair of polygons + `Rook` contiguity defines as neighbors any pair of polygons that share a common edge in their polygon definitions. See Also @@ -136,7 +136,7 @@ def from_iterable(cls, iterable, sparse=False, **kwargs): A collection of of shapes to be cast to PySAL shapes. Must support iteration. Can be either Shapely or PySAL shapes. sparse : bool - Generate ``WSP`` object. Default is ``False``. + Generate a `WSP` object. Default is ``False``. **kwargs : dict Keyword arguments for ``libpysal.weights.Rook``. @@ -258,7 +258,7 @@ def __init__(self, polygons, **kwargs): @classmethod def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): - """Queen contiguity weights from a polygon shapefile. + """`Queen` contiguity weights from a polygon shapefile. Parameters ---------- @@ -286,13 +286,13 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): >>> from libpysal.weights import Queen >>> import libpysal - >>> wq=Queen.from_shapefile(libpysal.examples.get_path("columbus.shp")) + >>> wq = Queen.from_shapefile(libpysal.examples.get_path("columbus.shp")) >>> "%.3f"%wq.pct_nonzero '9.829' - >>> wq=Queen.from_shapefile(libpysal.examples.get_path("columbus.shp"),"POLYID") + >>> wq = Queen.from_shapefile(libpysal.examples.get_path("columbus.shp"),"POLYID") >>> "%.3f"%wq.pct_nonzero '9.829' - >>> wq=Queen.from_shapefile(libpysal.examples.get_path("columbus.shp"), sparse=True) + >>> wq = Queen.from_shapefile(libpysal.examples.get_path("columbus.shp"), sparse=True) >>> pct_sp = wq.sparse.nnz *1. / wq.n**2 >>> "%.3f"%pct_sp '0.098' @@ -300,7 +300,7 @@ def from_shapefile(cls, filepath, idVariable=None, full=False, **kwargs): Notes ----- - Queen contiguity defines as neighbors any pair of polygons that share at + `Queen` contiguity defines as neighbors any pair of polygons that share at least one vertex in their polygon definitions. See Also @@ -337,7 +337,7 @@ def from_iterable(cls, iterable, sparse=False, **kwargs): A collection of of shapes to be cast to PySAL shapes. Must support iteration. Can be either Shapely or PySAL shapes. sparse : bool - Generate ``WSP`` object. Default is ``False``. + Generate a `WSP` object. Default is ``False``. **kwargs : dict Keyword arguments for ``libpysal.weights.Queen``. @@ -444,7 +444,7 @@ def Voronoi(points, criterion="rook", clip="ahull", **kwargs): >>> import numpy as np >>> from libpysal.weights import Voronoi >>> np.random.seed(12345) - >>> points= np.random.random((5,2))*10 + 10 + >>> points = np.random.random((5,2))*10 + 10 >>> w = Voronoi(points) >>> w.neighbors {0: [2, 3, 4], 1: [2], 2: [0, 1, 4], 3: [0, 4], 4: [0, 2, 3]} @@ -584,7 +584,7 @@ def _build(polygons, criterion="rook", ids=None): def buildContiguity(polygons, criterion="rook", ids=None): """This is a deprecated function. It builds a contiguity `W` from the polygons provided. As such, it is now identical to calling the class - constructors for ``Rook`` or ``Queen``. + constructors for `Rook` or `Queen`. """ # Warn('This function is deprecated. Please use the Rook or Queen classes', UserWarning) diff --git a/libpysal/weights/distance.py b/libpysal/weights/distance.py index 378410fb0..b89c56f36 100644 --- a/libpysal/weights/distance.py +++ b/libpysal/weights/distance.py @@ -262,7 +262,7 @@ def from_array(cls, array, *args, **kwargs): @classmethod def from_dataframe(cls, df, geom_col="geometry", ids=None, *args, **kwargs): - """Make KNN weights from a dataframe. + """Make `KNN` weights from a dataframe. Parameters ---------- @@ -317,14 +317,14 @@ def reweight(self, k=None, p=None, new_data=None, new_ids=None, inplace=True): ``1`` is Manhattan distance. This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. Default is ``None``. new_data : numpy.ndarray - An array containing additional data to use in the ``KNN`` weight. + An array containing additional data to use in the `KNN` weight. Default is ``None``. new_ids : list A list aligned with ``new_data`` that provides the ids for each new observation. Default is ``None``. inplace : bool - A flag denoting whether to modify the ``KNN`` object - in place or to return a new ``KNN`` object. Default is ``True``. + A flag denoting whether to modify the `KNN` object + in place or to return a new `KNN` object. Default is ``True``. Returns ------- @@ -657,7 +657,7 @@ def from_dataframe(cls, df, geom_col="geometry", ids=None, **kwargs): ---------- df : pandas.DataFrame A dataframe with a geometry column that can be used - to construct a PySAL ``W`` object. + to construct a PySAL `W` object. geom_col : str The column name of the geometry stored in ``df``. Default is ``'geometry'``. @@ -697,7 +697,7 @@ def _k_to_W(self, ids=None): Parameters ---------- ids : list - See ``ids`` in ``Kernel``. Default is ``None``. + See ``ids`` in ``libpysal.weights.Kernel``. Default is ``None``. Returns ------- @@ -806,7 +806,7 @@ class DistanceBand(W): binary : bool If set to ``True``, :math:`w_{ij}=1` if :math:`d_{i,j}<=\mathtt{threshold}`, otherwise :math:`w_{i,j}=0`. If set to ``False``, - :math:`wij=dij^{\mathtt{alpha}}`. Default is ``True``. + :math:`w_{ij}=d_{ij}^{\mathtt{alpha}}`. Default is ``True``. alpha : float The distance decay parameter for weights. Default is ``-1.0``. If ``alpha`` is positive the weights will not decline with distance. @@ -1014,7 +1014,7 @@ def from_dataframe(cls, df, threshold, geom_col="geometry", ids=None, **kwargs): ---------- df : pandas.DataFrame A dataframe with a geometry column that can be used - to construct a PySAL ``W`` object. + to construct a PySAL `W` object. threshold : float The distance band. geom_col : str @@ -1067,7 +1067,7 @@ def _distance_to_W(self, ids=None): Parameters ---------- ids : list - See ``ids`` in ``DistanceBand``. Default is ``None``. + See ``ids`` in ``libpysal.weights.DistanceBand``. Default is ``None``. Returns ------- diff --git a/libpysal/weights/spintW.py b/libpysal/weights/spintW.py index 234db9f21..45a22ae93 100644 --- a/libpysal/weights/spintW.py +++ b/libpysal/weights/spintW.py @@ -205,7 +205,7 @@ def vecW( threshold : float The distance band. p : {int, float} - Minkowski `p`-norm distance metric parameter where :math:`1<=p<=\infty`. + Minkowski `p`-norm distance metric parameter where :math:`1<=\mathtt{p}<=\infty`. ``2`` is Euclidean distance and ``1`` is Manhattan distance. This parameter is ignored if the ``KDTree`` is an ``ArcKDTree``. Default is ``2``. @@ -214,9 +214,9 @@ def vecW( If ``alpha`` is positive the weights will not decline with distance. If ``binary`` is set to ``True``, ``alpha`` is ignored. binary : bool - If set to ``True``, :math:`w_{ij}=1` if :math:`d_{i,j}<=threshold`, - otherwise :math:`w_{i,j}=0`. If set to ``False``, :math:`wij=dij^{alpha}`. - Default is ``True``. + If set to ``True``, :math:`w_{ij}=1` if :math:`d_{i,j}<=\mathtt{threshold}`, + otherwise :math:`w_{i,j}=0`. If set to ``False``, + :math:`w_{ij}=d_{ij}^{\mathtt{alpha}}`. Default is ``True``. ids : list Identifiers to attach to each observation in ``neighbors`` and ``weights``. Default is ``None``. diff --git a/libpysal/weights/weights.py b/libpysal/weights/weights.py index 36a31ec03..74fe732a9 100644 --- a/libpysal/weights/weights.py +++ b/libpysal/weights/weights.py @@ -2,7 +2,7 @@ Spatial Weights. """ -__author__ = "Sergio J. Rey " +__author__ = "Sergio J. Rey " import copy from os.path import basename as BASENAME @@ -206,7 +206,7 @@ def to_file(self, path="", format=None): @classmethod def from_file(cls, path="", format=None): - """Read a weights file into a `W` object. + """Read a weights file into a `W` object. Parameters ---------- @@ -232,10 +232,10 @@ def from_file(cls, path="", format=None): def from_shapefile(cls, *args, **kwargs): """Construct a weights object from a shapefile.""" - # we could also just "do the right thing," but I think it'd make sense to - # try and get people to use `Rook.from_shapefile(shapefile)` rather than - # W.from_shapefile(shapefile, type=`rook`), otherwise we'd need to build - # a type dispatch table. Generic W should be for stuff we don't know + # we could also just 'do the right thing,' but I think it'd make sense to + # try and get people to use ``Rook.from_shapefile(shapefile)`` rather than + # `W`.from_shapefile(shapefile, type=`rook`), otherwise we'd need to build + # a type dispatch table. Generic `W` should be for stuff we don't know # anything about. msg = ( @@ -245,7 +245,7 @@ def from_shapefile(cls, *args, **kwargs): @classmethod def from_WSP(cls, WSP, silence_warnings=True): - """Construct a weights object from a ``WSP`` object.""" + """Construct a weights object from a `WSP` object.""" w = WSP2W(WSP, silence_warnings=silence_warnings) @@ -273,7 +273,7 @@ def from_adjlist( Returns ------- w : libpysal.weights.W - NEED....................................................................................... + A PySAL `W` spatial weights object. """ @@ -310,24 +310,24 @@ def to_adjlist( Parameters ---------- remove_symmetric : bool - Whether or not to remove symmetric entries. If the ``W`` + Whether or not to remove symmetric entries. If the `W` is symmetric, a standard directed adjacency list will contain both the forward and backward links by default because adjacency lists are a directed graph representation. If this is ``True``, - a ``W`` created from this adjacency list **MAY NOT BE THE SAME** - as the original ``W``. If you would like to consider (1,2) and + a `W` created from this adjacency list **MAY NOT BE THE SAME** + as the original `W`. If you would like to consider (1,2) and (2,1) as distinct links, leave this as ``False``. focal_col : str - Name of the column in which to store "source" node ids. + Name of the column in which to store 'source' node ids. neighbor_col : str - Name of the column in which to store "destination" node ids. + Name of the column in which to store 'destination' node ids. weight_col : str Name of the column in which to store weight information. Returns ------- - NEED : NEED - NEED...................................................................................... + adjlist : pandas.DataFrame + An adjacency list representation within a dataframe. """ @@ -348,7 +348,9 @@ def to_adjlist( columns=("focal", "neighbor", "weight"), ) - return adjtools.filter_adjlist(adjlist) if remove_symmetric else adjlist + adjlist = adjtools.filter_adjlist(adjlist) if remove_symmetric else adjlist + + return adjlist def to_networkx(self): """Convert a weights object to a ``networkx`` graph. @@ -840,7 +842,7 @@ def remap_ids(self, new_ids): self._reset() def __set_id_order(self, ordered_ids): - """Set the iteration order in w. ``W`` can be iterated over. On + """Set the iteration order in ``w``. `W` can be iterated over. On construction the iteration order is set to the lexicographic order of the keys in the ``w.weights`` dictionary. If a specific order is required it can be set with this method. @@ -1172,7 +1174,7 @@ def asymmetry(self, intrinsic=True): return ijs def symmetrize(self, inplace=False): - """Construct a symmetric ``KNN`` weight. This ensures that the neighbors + """Construct a symmetric `KNN` weight. This ensures that the neighbors of each focal observation consider the focal observation itself as a neighbor. This returns a generic `W` object, since the object is no longer guaranteed to have ``k`` neighbors for each observation. @@ -1254,7 +1256,7 @@ def full(self): return (wfull, keys) def to_WSP(self): - """Generate a ``WSP`` object. + """Generate a `WSP` object. Returns ------- @@ -1288,8 +1290,7 @@ def to_WSP(self): return w def set_shapefile(self, shapefile, idVariable=None, full=False): - """ - Adding metadata for writing headers of ``.gal`` and ``.gwt`` files. + """Adding metadata for writing headers of ``.gal`` and ``.gwt`` files. Parameters ---------- @@ -1321,7 +1322,7 @@ def plot( Parameters ---------- gdf : geopandas.GeoDataFrame - The original shapes whose topological relations are modelled in ``W``. + The original shapes whose topological relations are modelled in `W`. indexed_on : str Column of ``geopandas.GeoDataFrame`` that the weights object uses as an index. Default is ``None``, so the index of the @@ -1435,11 +1436,11 @@ class WSP(object): Attributes ---------- n : int - description.................................................................................................... + The number of rows in ``sparse``. s0 : float - description.................................................................................................... + :math:`s0=\sum_i \sum_j w_{i,j}`. trcWtW_WW : float - description.................................................................................................... + Trace of :math:`W^{'}W + WW`. Examples -------- From e20bc4288d3db60717a5a636350e02c8a319e7b9 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Mon, 24 Aug 2020 16:09:44 -0400 Subject: [PATCH 21/33] intermediary commit for weights/util.py --- libpysal/weights/user.py | 25 +- libpysal/weights/util.py | 784 ++++++++++++++++++++++++--------------- 2 files changed, 492 insertions(+), 317 deletions(-) diff --git a/libpysal/weights/user.py b/libpysal/weights/user.py index de6a95ed5..f18e01ea3 100644 --- a/libpysal/weights/user.py +++ b/libpysal/weights/user.py @@ -18,7 +18,7 @@ def spw_from_gal(galfile): - """Sparse ``scipy`` matrix for w from a ``.gal`` file. + """Sparse ``scipy`` matrix for a `W` from a ``.gal`` file. Parameters ---------- @@ -27,8 +27,8 @@ def spw_from_gal(galfile): Returns ------- - spw : libpysal.weights.weights.WSP - The sparse matrix in CSR format (``scipy.sparse.csr.csr_matrix``) can + spw : libpysal.weights.WSP + The sparse matrix in CSR format (``scipy.sparse.csr_matrix``) can be accessed through ``spw.sparse``. Examples @@ -44,7 +44,9 @@ def spw_from_gal(galfile): """ - return ps_open(galfile, "r").read(sparse=True) + spw = ps_open(galfile, "r").read(sparse=True) + + return spw def min_threshold_dist_from_shapefile(shapefile, radius=None, p=2): @@ -53,18 +55,18 @@ def min_threshold_dist_from_shapefile(shapefile, radius=None, p=2): Parameters ---------- - shapefile : str + shapefile : str The shapefile name including the ``.shp`` file extension. radius : float - If supplied ``arc_distances`` will be calculated - based on the given radius and ``p`` will be ignored. + If supplied ``arc_distances`` will be calculated based on the given + radius and ``p`` will be ignored. Default is ``None``. p : {int, float} - Minkowski `p`-norm distance metric parameter where ``1<=p<=infinity``. - ``2`` is Euclidean distance and ``1`` is Manhattan distance. Default is ``2 ``. + Minkowski `p`-norm distance metric parameter where :math:`1<=\mathtt{p}<=\infty`. + ``2`` is Euclidean distance and ``1`` is Manhattan distance. Default is ``2``. Returns ------- - d : float + nnd : float The maximum nearest neighbor distance between the ``n`` observations. Examples @@ -88,11 +90,14 @@ def min_threshold_dist_from_shapefile(shapefile, radius=None, p=2): """ points = get_points_array_from_shapefile(shapefile) + if radius is not None: kdt = cg.kdtree.Arc_KDTree(points, radius=radius) nn = kdt.query(kdt.data, k=2) nnd = nn[0].max(axis=0)[1] + return nnd + return min_threshold_distance(points, p) diff --git a/libpysal/weights/util.py b/libpysal/weights/util.py index ba93624e6..370bbfbbc 100644 --- a/libpysal/weights/util.py +++ b/libpysal/weights/util.py @@ -1,62 +1,81 @@ from ..io.fileio import FileIO as psopen from .weights import W, WSP from .set_operations import w_subset +from ..common import requires + +from collections import defaultdict +import copy +from itertools import tee +import numbers +import os +from warnings import warn + import numpy as np +import scipy from scipy import sparse from scipy.spatial import KDTree -import copy import scipy.spatial -import os -import scipy -from warnings import warn -import numbers -from collections import defaultdict -from itertools import tee -from ..common import requires try: import geopandas as gpd except ImportError: - warn('geopandas not available. Some functionality will be disabled.') - -__all__ = ['lat2W', 'block_weights', 'comb', 'order', 'higher_order', - 'shimbel', 'remap_ids', 'full2W', 'full', 'WSP2W', - 'insert_diagonal', 'get_ids', 'get_points_array_from_shapefile', - 'min_threshold_distance', 'lat2SW', 'w_local_cluster', - 'higher_order_sp', 'hexLat2W', 'attach_islands', - 'nonplanar_neighbors', 'fuzzy_contiguity'] + warn("geopandas not available. Some functionality will be disabled.") + +__all__ = [ + "lat2W", + "block_weights", + "comb", + "order", + "higher_order", + "shimbel", + "remap_ids", + "full2W", + "full", + "WSP2W", + "insert_diagonal", + "get_ids", + "get_points_array_from_shapefile", + "min_threshold_distance", + "lat2SW", + "w_local_cluster", + "higher_order_sp", + "hexLat2W", + "attach_islands", + "nonplanar_neighbors", + "fuzzy_contiguity", +] KDTREE_TYPES = [scipy.spatial.KDTree, scipy.spatial.cKDTree] + def hexLat2W(nrows=5, ncols=5, **kwargs): - """ - Create a W object for a hexagonal lattice. + """Create a `W` object for a hexagonal lattice. Parameters ---------- - nrows : int - number of rows - ncols : int - number of columns - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` - + nrows : int + The number of rows. + ncols : int + The number of columns. + **kwargs : dict + Optional keyword arguments for ``libpysal.weights.W``. Returns ------- - w : W - instance of spatial weights class W + w : libpysal.weights.W + An instance of spatial weights, `W`. Notes ----- - Observations are row ordered: first k observations are in row 0, next k in row 1, and so on. - - Construction is based on shifting every other column of a regular lattice - down 1/2 of a cell. + + Observations are row ordered with the first :math:`k` observations being + in row 0, the next :math:`k` in row 1, and so on. Construction is based + on shifting every other column of a regular lattice down 1/2 of a cell. Examples -------- + >>> from libpysal.weights import lat2W, hexLat2W >>> w = lat2W() >>> w.neighbors[1] @@ -68,6 +87,7 @@ def hexLat2W(nrows=5, ncols=5, **kwargs): [0, 6, 2, 5, 7] >>> wh.neighbors[21] [16, 20, 22] + """ if nrows == 1 or ncols == 1: @@ -82,8 +102,10 @@ def hexLat2W(nrows=5, ncols=5, **kwargs): c1 = ncols - 1 w = lat2W(nrows, ncols).neighbors + for i in range(n): odd = cid[i] % 2 + if odd: if rid[i] < r1: # odd col index above last row # new sw neighbor @@ -110,41 +132,43 @@ def hexLat2W(nrows=5, ncols=5, **kwargs): w[i] = w.get(i, []) + jne w[i] = w.get(i, []) + jnw + w = W(w, **kwargs) return W(w, **kwargs) -def lat2W(nrows=5, ncols=5, rook=True, id_type='int', **kwargs): - """ - Create a W object for a regular lattice. +def lat2W(nrows=5, ncols=5, rook=True, id_type="int", **kwargs): + """Create a `W` object for a regular lattice. Parameters ---------- - - nrows : int - number of rows - ncols : int - number of columns - rook : boolean - type of contiguity. Default is rook. For queen, rook =False - id_type : string - string defining the type of IDs to use in the final W object; - options are 'int' (0, 1, 2 ...; default), 'float' (0.0, - 1.0, 2.0, ...) and 'string' ('id0', 'id1', 'id2', ...) - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` - + nrows : int + The number of rows. + ncols : int + The number of columns. + rook : bool + The type of contiguity. Default is `Rook`. For `Queen` set to ``False``. + id_type : str + The type of IDs to use in the final `W` object. The options are as + follows with ``'int'`` being the default. + + * ``'int'`` -- ``(0, 1, 2, ...)`` + * ``'float'`` -- ``(0.0, 1.0, 2.0, ...)`` + * ``'string'`` -- ``('id0', 'id1', 'id2', ...)`` + + **kwargs : dict + Optional keyword arguments for ``libpysal.weights.W``. Returns ------- - - w : W - instance of spatial weights class W + w : libpysal.weights.W + An instance of spatial weights, `W`. Notes ----- - - Observations are row ordered: first k observations are in row 0, next k in row 1, and so on. + + Observations are row ordered with the first :math:`k` observations being + in row 0, the next :math:`k` in row 1, and so on. Examples -------- @@ -157,15 +181,19 @@ def lat2W(nrows=5, ncols=5, rook=True, id_type='int', **kwargs): True >>> w9[3] == {0: 1.0, 4: 1.0, 6: 1.0} True + """ + n = nrows * ncols r1 = nrows - 1 c1 = ncols - 1 - rid = [i // ncols for i in range(n)] #must be floor! + rid = [i // ncols for i in range(n)] # must be floor! cid = [i % ncols for i in range(n)] w = {} r = below = 0 + for i in range(n - 1): + if rid[i] < r1: below = rid[i] + 1 r = below * ncols + cid[i] @@ -190,14 +218,17 @@ def lat2W(nrows=5, ncols=5, rook=True, id_type='int', **kwargs): neighbors = {} weights = {} + for key in w: - weights[key] = [1.] * len(w[key]) + weights[key] = [1.0] * len(w[key]) ids = list(range(n)) - if id_type == 'string': - ids = ['id' + str(i) for i in ids] - elif id_type == 'float': - ids = [i * 1. for i in ids] - if id_type == 'string' or id_type == 'float': + + if id_type == "string": + ids = ["id" + str(i) for i in ids] + elif id_type == "float": + ids = [i * 1.0 for i in ids] + + if id_type == "string" or id_type == "float": id_dict = dict(list(zip(list(range(n)), ids))) alt_w = {} alt_weights = {} @@ -208,38 +239,38 @@ def lat2W(nrows=5, ncols=5, rook=True, id_type='int', **kwargs): alt_weights[key] = weights[i] w = alt_w weights = alt_weights - return W(w, weights, ids=ids, id_order=ids[:], **kwargs) + w = W(w, weights, ids=ids, id_order=ids[:], **kwargs) + + return w -def block_weights(regimes, ids=None, sparse=False, **kwargs): - """ - Construct spatial weights for regime neighbors. - Block contiguity structures are relevant when defining neighbor relations - based on membership in a regime. For example, all counties belonging to - the same state could be defined as neighbors, in an analysis of all - counties in the US. +def block_weights(regimes, ids=None, sparse=False, **kwargs): + """Construct spatial weights for regime neighbors. Block contiguity structures + are relevant when defining neighbor relations based on membership in a regime. + For example, all counties belonging to the same state could be defined as + neighbors, in an analysis of all counties in the US. Parameters ---------- - regimes : list, array - ids of which regime an observation belongs to - ids : list, array - Ordered sequence of IDs for the observations - sparse : boolean - If True return WSP instance - If False return W instance - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` - + regimes : {list, numpy.ndarray} + The ids of the regime to which an observation belongs. + ids : {list, numpy.ndarray} + An ordered sequence of IDs for the observations. + sparse : bool + If ``True`` return `WSP` instance, otherwise return `W` instance + (``False``). Default is ``False``. + **kwargs : dict + Optional keyword arguments for ``libpysal.weights.W``. Returns ------- - - W : spatial weights instance + w : {libpysal.weights.W, libpysal.weights.WSP} + An instance of spatial weights (`W`), or a thin version (`WSP`). Examples -------- + >>> from libpysal.weights import block_weights >>> import numpy as np >>> regimes = np.ones(25) @@ -248,57 +279,64 @@ def block_weights(regimes, ids=None, sparse=False, **kwargs): >>> regimes array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 1., 3., 3., 3., 3.]) + >>> w = block_weights(regimes) >>> w.weights[0] [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] + >>> w.neighbors[0] [1, 2, 3, 4, 5, 6, 7, 8, 9, 20] + >>> regimes = ['n','n','s','s','e','e','w','w','e'] >>> n = len(regimes) >>> w = block_weights(regimes) >>> w.neighbors == {0: [1], 1: [0], 2: [3], 3: [2], 4: [5, 8], 5: [4, 8], 6: [7], 7: [6], 8: [4, 5]} True + """ + rids = np.unique(regimes) neighbors = {} NPNZ = np.nonzero regimes = np.array(regimes) + for rid in rids: members = NPNZ(regimes == rid)[0] for member in members: neighbors[member] = members[NPNZ(members != member)[0]].tolist() + w = W(neighbors, **kwargs) + if ids is not None: w.remap_ids(ids) + if sparse: w = WSP(w.sparse, id_order=ids) + return w def comb(items, n=None): - """ - Combinations of size n taken from items + """Combinations of size :math:`n` taken from items. Parameters ---------- - items : list - items to be drawn from - n : integer - size of combinations to take from items + The items to be drawn from. + n : int + The size of combinations to take from items. - Returns + Yields ------- - implicit : generator - combinations of size n taken from items - + The combinations of size :math:`n` taken from items. + Examples -------- + >>> x = range(4) >>> for c in comb(x, 2): ... print(c) - ... [0, 1] [0, 2] [0, 3] @@ -307,52 +345,54 @@ def comb(items, n=None): [2, 3] """ + items = list(items) + if n is None: n = len(items) + for i in list(range(len(items))): - v = items[i:i + 1] + v = items[i : i + 1] + if n == 1: yield v else: - rest = items[i + 1:] + rest = items[i + 1 :] for c in comb(rest, n - 1): yield v + c def order(w, kmax=3): - """ - Determine the non-redundant order of contiguity up to a specific - order. + """Determine the non-redundant order of contiguity up to a specific order. Parameters ---------- - - w : W - spatial weights object - - kmax : int - maximum order of contiguity + w : libpysal.weights.W + An instance of spatial weights, `W`. + kmax : int + The maximum order of contiguity. Default is ``3``. Returns ------- - - info : dictionary - observation id is the key, value is a list of contiguity - orders with a negative 1 in the ith position + info : dict + The observation ID are the keys and the values are lists of contiguity + orders with a -1 in the :math:`i`-th position. Notes ----- + Implements the algorithm in :cite:`Anselin1996b`. Examples -------- + >>> from libpysal.weights import order, Rook >>> import libpysal >>> w = Rook.from_shapefile(libpysal.examples.get_path('10740.shp')) - - WARNING: there is one disconnected observation (no neighbors) - Island id: [163] + The weights matrix is not fully connected: + There are 2 disconnected components. + There is 1 island with id: 163. + >>> w3 = order(w, kmax = 3) >>> w3[1][0:5] [1, -1, 1, 2, 1] @@ -361,6 +401,7 @@ def order(w, kmax=3): ids = w.id_order info = {} + for id_ in ids: s = [0] * w.n s[ids.index(id_)] = -1 @@ -381,82 +422,99 @@ def order(w, kmax=3): s[nid] = knext k = knext info[id_] = s + return info def higher_order(w, k=2, **kwargs): - """ - Contiguity weights object of order k. + """Contiguity weights object of order :math:`k`. Parameters ---------- - - w : W - spatial weights object - k : int - order of contiguity - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` + w : libpysal.weights.W + An instance of spatial weights, `W`. + k : int + The order of contiguity. Default is ``2``. + **kwargs : dict + Optional keyword arguments for ``libpysal.weights.W``. Returns ------- - - implicit : W - spatial weights object + w : libpysal.weights.W + An instance of spatial weights, `W`. Notes ----- - Proper higher order neighbors are returned such that i and j are k-order - neighbors iff the shortest path from i-j is of length k. + + Proper higher order neighbors are returned such that :math:`i` and :math:`j` + are :math:`k`-order neighbors if and only if ('iff') the shortest path from + :math:`i`-:math:`j` is of length :math:`k`. Examples -------- + >>> from libpysal.weights import lat2W, higher_order >>> w10 = lat2W(10, 10) >>> w10_2 = higher_order(w10, 2) >>> w10_2[0] == {2: 1.0, 11: 1.0, 20: 1.0} True + >>> w5 = lat2W() >>> w5[0] == {1: 1.0, 5: 1.0} True + >>> w5[1] == {0: 1.0, 2: 1.0, 6: 1.0} True + >>> w5_2 = higher_order(w5,2) >>> w5_2[0] == {10: 1.0, 2: 1.0, 6: 1.0} True + """ - return higher_order_sp(w, k, **kwargs) + + w = higher_order_sp(w, k, **kwargs) + + return w def higher_order_sp(w, k=2, shortest_path=True, diagonal=False, **kwargs): - """ - Contiguity weights for either a sparse W or W for order k. + """Contiguity weights for either a `WSP` or `W` for order :math:`k`. Parameters ---------- - w : W - sparse_matrix, spatial weights object or - scipy.sparse.csr.csr_instance - k : int - Order of contiguity - shortest_path : boolean - True: i,j and k-order neighbors if the - shortest path for i,j is k. - False: i,j are k-order neighbors if there - is a path from i,j of length k. - diagonal : boolean - True: keep k-order (i,j) joins when i==j - False: remove k-order (i,j) joins when i==j - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` + w : {libpysal.weights.W, libpysal.weights.WSP, scipy.sparse.csr_instance} + A sparse matrix or spatial weights object. + k : int + The order of contiguity. Default is ``2``. + shortest_path : bool + Set to ``True`` to define :math:`i,j` are :math:`k`-order neighbors if the + shortest path for :math:`i,j` is :math:`k`. Set to ``False`` to define + :math:`i,j` and :math:`k`-order neighbors if there is a path from + :math:`i,j` of length :math:`k`. Default is ``True``. + diagonal : bool + Set to ``True`` to keep :math:`k`-order :math:`(i,j)` joins when :math:`i==j`. + Set to ``False`` to remove :math:`k`-order :math:`(i,j)` joins when :math:`i==j`. + Default is ``False``. + **kwargs : dict + Optional keyword arguments for ``libpysal.weights.W``. Returns ------- - wk : W - WSP, type matches type of w argument - + wk : {libpysal.weights.W, libpysal.weights.WSP} + WSP, type matches type of w argument + + Raises + ------ + ValueError + The input `W` weights are not binary. + ValueError + The input ``scipy.sparse.csr_instance`` weights are not binary. + TypeError + The input weights are in the correct format and/or are not binary. + Notes ----- + Lower order contiguities are removed. Examples @@ -466,58 +524,73 @@ def higher_order_sp(w, k=2, shortest_path=True, diagonal=False, **kwargs): >>> w25 = lat2W(5,5) >>> w25.n 25 + >>> w25[0] == {1: 1.0, 5: 1.0} True + >>> w25_2 = higher_order_sp(w25, 2) >>> w25_2[0] == {10: 1.0, 2: 1.0, 6: 1.0} True + >>> w25_2 = higher_order_sp(w25, 2, diagonal=True) >>> w25_2[0] == {0: 1.0, 10: 1.0, 2: 1.0, 6: 1.0} True + >>> w25_3 = higher_order_sp(w25, 3) >>> w25_3[0] == {15: 1.0, 3: 1.0, 11: 1.0, 7: 1.0} True + >>> w25_3 = higher_order_sp(w25, 3, shortest_path=False) >>> w25_3[0] == {1: 1.0, 3: 1.0, 5: 1.0, 7: 1.0, 11: 1.0, 15: 1.0} True """ + id_order = None + if issubclass(type(w), W) or isinstance(w, W): if np.unique(np.hstack(list(w.weights.values()))) == np.array([1.0]): id_order = w.id_order w = w.sparse else: - raise ValueError('Weights are not binary (0,1)') + raise ValueError("Weights are not binary (0,1).") elif scipy.sparse.isspmatrix_csr(w): if not np.unique(w.data) == np.array([1.0]): - raise ValueError('Sparse weights matrix is not binary (0,1) weights matrix.') + raise ValueError( + "Sparse weights matrix is not binary (0,1) weights matrix." + ) else: - raise TypeError("Weights provided are neither a binary W object nor " - "a scipy.sparse.csr_matrix") + raise TypeError( + "Weights provided are neither a binary 'W' object nor " + "a 'scipy.sparse.csr_matrix'." + ) - wk = w**k + wk = w ** k rk, ck = wk.nonzero() sk = set(zip(rk, ck)) if shortest_path: for j in range(1, k): - wj = w**j + wj = w ** j rj, cj = wj.nonzero() sj = set(zip(rj, cj)) sk.difference_update(sj) if not diagonal: - sk = set([(i,j) for i,j in sk if i!=j]) + sk = set([(i, j) for i, j in sk if i != j]) if id_order: - d = dict([(i,[]) for i in id_order]) + d = dict([(i, []) for i in id_order]) for pair in sk: k, v = pair k = id_order[k] v = id_order[v] d[k].append(v) - return W(neighbors=d, **kwargs) + + wk = W(neighbors=d, **kwargs) + + return wk + else: d = {} for pair in sk: @@ -526,25 +599,25 @@ def higher_order_sp(w, k=2, shortest_path=True, diagonal=False, **kwargs): d[k].append(v) else: d[k] = [v] - return WSP(W(neighbors=d, **kwargs).sparse) + + wk = WSP(W(neighbors=d, **kwargs).sparse) + + return wk def w_local_cluster(w): - r""" - Local clustering coefficients for each unit as a node in a graph. + r"""Local clustering coefficients for each unit as a node in a graph. Parameters ---------- - - w : W - spatial weights object + w : libpysal.weights.W + An instance of spatial weights, `W`. Returns ------- - - c : array - (w.n,1) - local clustering coefficients + c : numpy.ndarray + An array of local clustering coefficients with + dimensions :math:`(\mathtt{w}.n,1)`. Notes ----- @@ -556,12 +629,14 @@ def w_local_cluster(w): c_i = | \{w_{j,k}\} |/ (k_i(k_i - 1)): j,k \in N_i - where :math:`N_i` is the set of neighbors to :math:`i`, :math:`k_i = - |N_i|` and :math:`\{w_{j,k}\}` is the set of non-zero elements of the - weights between pairs in :math:`N_i` :cite:`Watts1998`. + where :math:`N_i` is the set of neighbors to :math:`i`, + :math:`k_i = |N_i|` and :math:`\{w_{j,k}\}` is the set + of non-zero elements of the weights between pairs in + :math:`N_i` :cite:`Watts1998`. Examples -------- + >>> from libpysal.weights import lat2W, w_local_cluster >>> w = lat2W(3,3, rook=False) >>> w_local_cluster(w) @@ -578,7 +653,7 @@ def w_local_cluster(w): """ c = np.zeros((w.n, 1), float) - w.transformation = 'b' + w.transformation = "b" for i, id in enumerate(w.id_order): ki = max(w.cardinalities[id], 1) # deal with islands Ni = w.neighbors[id] @@ -588,23 +663,23 @@ def w_local_cluster(w): def shimbel(w): - """ - Find the Shimbel matrix for first order contiguity matrix. + """Find the Shimbel matrix for first order contiguity matrix. Parameters ---------- - w : W - spatial weights object + w : libpysal.weights.W + An instance of spatial weights, `W`. Returns ------- - info : list - list of lists; one list for each observation which stores - the shortest order between it and each of the the other observations. + info : list + A list of lists; one list for each observation which stores + the shortest order between it and each of the other observations. Examples -------- + >>> from libpysal.weights import lat2W, shimbel >>> w5 = lat2W() >>> w5_shimbel = shimbel(w5) @@ -612,6 +687,7 @@ def shimbel(w): 8 >>> w5_shimbel[0][0:4] [-1, 1, 2, 3] + """ info = {} @@ -637,12 +713,12 @@ def shimbel(w): k = knext flag = s.count(0) info[i] = s + return info def full(w): - """ - Generate a full numpy array. + """Generate a full ``numpy.ndarray``. Parameters ---------- @@ -652,7 +728,7 @@ def full(w): Returns ------- (fullw, keys) : tuple - first element being the full numpy array and second element + first element being the full ``numpy.ndarray`` and second element keys being the ids associated with each row in the array. Examples @@ -668,12 +744,14 @@ def full(w): [0., 1., 0.]]) >>> ids ['first', 'second', 'third'] + """ + return w.full() + def full2W(m, ids=None, **kwargs): - ''' - Create a PySAL W object from a full array. + """Create a PySAL `W` object from a full array. Parameters ---------- @@ -681,10 +759,9 @@ def full2W(m, ids=None, **kwargs): nxn array with the full weights matrix ids : list User ids assumed to be aligned with m - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` - - + **kwargs : dict + Optional keyword arguments for ``libpysal.weights.W``. + Returns ------- w : W @@ -692,6 +769,7 @@ def full2W(m, ids=None, **kwargs): Examples -------- + >>> from libpysal.weights import full2W >>> import numpy as np @@ -724,35 +802,40 @@ def full2W(m, ids=None, **kwargs): [ True, True, True, True], [ True, True, True, True], [ True, True, True, True]]) - ''' + """ + if m.shape[0] != m.shape[1]: - raise ValueError('Your array is not square') + raise ValueError("Your array is not square") + neighbors, weights = {}, {} + for i in range(m.shape[0]): - # for i, row in enumerate(m): row = m[i] + if ids: i = ids[i] + ngh = list(row.nonzero()[0]) weights[i] = list(row[ngh]) ngh = list(ngh) + if ids: ngh = [ids[j] for j in ngh] + neighbors[i] = ngh + return W(neighbors, weights, id_order=ids, **kwargs) def WSP2W(wsp, **kwargs): - - """ - Convert a pysal WSP object (thin weights matrix) to a pysal W object. + """Convert a pysal `WSP` object (thin weights matrix) to a pysal `W` object. Parameters ---------- wsp : WSP PySAL sparse weights object - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` + **kwargs : dict + Optional keyword arguments for ``libpysal.weights.W``. Returns ------- @@ -761,6 +844,7 @@ def WSP2W(wsp, **kwargs): Examples -------- + >>> from libpysal.weights import lat2W, WSP, WSP2W Build a 10x10 scipy.sparse matrix for a rectangular 2x5 region of cells @@ -770,6 +854,7 @@ def WSP2W(wsp, **kwargs): >>> wsp = WSP(sp) >>> wsp.n 10 + >>> wsp.sparse[0].todense() matrix([[0, 1, 0, 0, 0, 1, 0, 0, 0, 0]], dtype=int8) @@ -778,56 +863,61 @@ def WSP2W(wsp, **kwargs): >>> w = WSP2W(wsp) >>> w.n 10 + >>> print(w.full()[0][0]) [0. 1. 0. 0. 0. 1. 0. 0. 0. 0.] - """ + wsp.sparse indices = wsp.sparse.indices data = wsp.sparse.data indptr = wsp.sparse.indptr id_order = wsp.id_order + if id_order: # replace indices with user IDs indices = [id_order[i] for i in indices] else: id_order = list(range(wsp.n)) + neighbors, weights = {}, {} start = indptr[0] + for i in range(wsp.n): oid = id_order[i] end = indptr[i + 1] neighbors[oid] = indices[start:end] weights[oid] = data[start:end] start = end + ids = copy.copy(wsp.id_order) w = W(neighbors, weights, ids, **kwargs) w._sparse = copy.deepcopy(wsp.sparse) - w._cache['sparse'] = w._sparse + w._cache["sparse"] = w._sparse + return w + def insert_diagonal(w, val=1.0, wsp=False): - warn('This function is deprecated. Use fill_diagonal instead.') + warn("This function is deprecated. Use fill_diagonal instead.") return fill_diagonal(w, val=val, wsp=wsp) + def fill_diagonal(w, val=1.0, wsp=False): - """ - Returns a new weights object with values inserted along the main diagonal. + """Returns a new weights object with values inserted along the main diagonal. Parameters ---------- w : W Spatial weights object - diagonal : float, int or array Defines the value(s) to which the weights matrix diagonal should be set. If a constant is passed then each element along the diagonal will get this value (default is 1.0). An array of length w.n can be passed to set explicit values to each element along the diagonal (assumed to be in the same order as w.id_order). - - wsp : boolean + wsp : bool If True return a thin weights object of the type WSP, if False return the standard W object. @@ -838,6 +928,7 @@ def fill_diagonal(w, val=1.0, wsp=False): Examples -------- + >>> from libpysal.weights import lat2W >>> import numpy as np @@ -862,6 +953,7 @@ def fill_diagonal(w, val=1.0, wsp=False): w_new = copy.deepcopy(w.sparse) w_new = w_new.tolil() + if issubclass(type(val), np.ndarray): if w.n != val.shape[0]: raise Exception("shape of w and diagonal do not match") @@ -870,7 +962,9 @@ def fill_diagonal(w, val=1.0, wsp=False): w_new.setdiag([val] * w.n) else: raise Exception("Invalid value passed to diagonal") + w_out = WSP(w_new, copy.copy(w.id_order)) + if wsp: return w_out else: @@ -878,44 +972,44 @@ def fill_diagonal(w, val=1.0, wsp=False): def remap_ids(w, old2new, id_order=[], **kwargs): - """ - Remaps the IDs in a spatial weights object. + """Remaps the IDs in a spatial weights object. Parameters ---------- w : W Spatial weights object - old2new : dictionary Dictionary where the keys are the IDs in w (i.e. "old IDs") and the values are the IDs to replace them (i.e. "new IDs") - id_order : list An ordered list of new IDs, which defines the order of observations when iterating over W. If not set then the id_order in w will be used. - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` - + **kwargs : dict + Optional keyword arguments for ``libpysal.weights.W``. Returns ------- - implicit : W Spatial weights object with new IDs Examples -------- + >>> from libpysal.weights import lat2W >>> w = lat2W(3,2) >>> w.id_order [0, 1, 2, 3, 4, 5] + >>> w.neighbors[0] [2, 1] + >>> old_to_new = {0:'a', 1:'b', 2:'c', 3:'d', 4:'e', 5:'f'} >>> w_new = remap_ids(w, old_to_new) + >>> w_new.id_order ['a', 'b', 'c', 'd', 'e', 'f'] + >>> w_new.neighbors['a'] ['c', 'b'] @@ -925,11 +1019,13 @@ def remap_ids(w, old2new, id_order=[], **kwargs): raise Exception("w must be a spatial weights object") new_neigh = {} new_weights = {} + for key, value in list(w.neighbors.items()): new_values = [old2new[i] for i in value] new_key = old2new[key] new_neigh[new_key] = new_values new_weights[new_key] = copy.copy(w.weights[key]) + if id_order: return W(new_neigh, new_weights, id_order, **kwargs) else: @@ -941,17 +1037,16 @@ def remap_ids(w, old2new, id_order=[], **kwargs): def get_ids(in_shps, idVariable): - """ - Gets the IDs from the DBF file that moves with a given shape file or - a geopandas.GeoDataFrame. + """Gets the IDs from the ``DBF`` file that moves with + a given shape file or a ``geopandas.GeoDataFrame``. Parameters ---------- - in_shps : str or geopandas.GeoDataFrame + in_shps : str or geopandas.GeoDataFrame The input geographic data. Either (1) a path to a shapefile including suffix (str); or (2) a geopandas.GeoDataFrame. - idVariable : str + idVariable : str name of a column in the shapefile's DBF or the geopandas.GeoDataFrame to use for ids. @@ -959,9 +1054,17 @@ def get_ids(in_shps, idVariable): ------- ids : list a list of IDs - + + Raises + ------ + IOError + descripiton of error........ + KeyError + descripiton of error........ + Examples -------- + >>> from libpysal.weights.util import get_ids >>> import libpysal >>> polyids = get_ids(libpysal.examples.get_path("columbus.shp"), "POLYID") @@ -985,7 +1088,7 @@ def get_ids(in_shps, idVariable): try: if type(in_shps) == str: - dbname = os.path.splitext(in_shps)[0] + '.dbf' + dbname = os.path.splitext(in_shps)[0] + ".dbf" db = psopen(dbname) cols = db.header var = db.by_col[idVariable] @@ -993,21 +1096,28 @@ def get_ids(in_shps, idVariable): else: cols = list(in_shps.columns) var = list(in_shps[idVariable]) + return var except IOError: - msg = 'The shapefile "%s" appears to be missing its DBF file. '\ - + ' The DBF file "%s" could not be found.' % (in_shps, dbname) + msg = ( + 'The shapefile "%s" appears to be missing its DBF file. ' + + ' The DBF file "%s" could not be found.' % (in_shps, dbname) + ) raise IOError(msg) + except (AttributeError, KeyError): - msg = 'The variable "%s" not found in the DBF/GDF. The the following '\ - + 'variables are present: %s.' % (idVariable, ','.join(cols)) + msg = ( + 'The variable "%s" not found in the DBF/GDF. The the following ' + + "variables are present: %s." % (idVariable, ",".join(cols)) + ) + raise KeyError(msg) def get_points_array(iterable): - """ - Gets a data array of x and y coordinates from a given iterable + """Gets a data array of `x` and `y` coordinates from a given iterable. + Parameters ---------- iterable : iterable @@ -1017,25 +1127,28 @@ def get_points_array(iterable): ------- points : array (n, 2) - a data array of x and y coordinates - + a data array of `x` and `y` coordinates + Notes ----- + If the given shape file includes polygons, - this function returns x and y coordinates of the polygons' centroids + this function returns `x` and `y` coordinates of the polygons' centroids. """ + first_choice, backup = tee(iterable) + try: data = np.vstack([np.array(shape.centroid) for shape in first_choice]) except AttributeError: data = np.vstack([shape for shape in backup]) + return data def get_points_array_from_shapefile(shapefile): - """ - Gets a data array of x and y coordinates from a given shapefile. + """Gets a data array of `x` and `y` coordinates from a given shapefile. Parameters ---------- @@ -1046,15 +1159,17 @@ def get_points_array_from_shapefile(shapefile): ------- points : array (n, 2) - a data array of x and y coordinates + a data array of `x` and `y` coordinates Notes ----- + If the given shape file includes polygons, - this function returns x and y coordinates of the polygons' centroids + this function returns `x` and `y` coordinates of the polygons' centroids Examples -------- + Point shapefile >>> import libpysal @@ -1065,7 +1180,6 @@ def get_points_array_from_shapefile(shapefile): [80., 95.], [79., 90.]]) - Polygon shapefile >>> xy = get_points_array_from_shapefile(libpysal.examples.get_path('columbus.shp')) @@ -1073,36 +1187,35 @@ def get_points_array_from_shapefile(shapefile): array([[ 8.82721847, 14.36907602], [ 8.33265837, 14.03162401], [ 9.01226541, 13.81971908]]) + """ f = psopen(shapefile) data = get_points_array(f) + return data def min_threshold_distance(data, p=2): - """ - Get the maximum nearest neighbor distance. + """Get the maximum nearest neighbor distance. Parameters ---------- - - data : array - (n,k) or KDTree where KDtree.data is array (n,k) - n observations on k attributes - p : float - Minkowski p-norm distance metric parameter: - 1<=p<=infinity - 2: Euclidean distance - 1: Manhattan distance + data : numpy.ndarray + :math:`(n,k)` or ``KDTree``` where ``KDtree.data`` is an + :math:`(n,k)` array of :math:`n` observations on :math:`k` attributes.s + p : float + Minkowski `p`-norm distance metric parameter where :math:`1<=\mathtt{p}<=\infty`. + ``2`` is Euclidean distance and ``1`` is Manhattan distance. Default is ``2``. Returns ------- - nnd : float - maximum nearest neighbor distance between the n observations + nnd : float + The maximum nearest neighbor distance between the :math:`n` observations. Examples -------- + >>> from libpysal.weights.util import min_threshold_distance >>> import numpy as np >>> x, y = np.indices((5, 5)) @@ -1113,23 +1226,24 @@ def min_threshold_distance(data, p=2): 1.0 """ + if issubclass(type(data), scipy.spatial.KDTree): kd = data data = kd.data else: kd = KDTree(data) + nn = kd.query(data, k=2, p=p) nnd = nn[0].max(axis=0)[1] + return nnd def lat2SW(nrows=3, ncols=5, criterion="rook", row_st=False): - """ - Create a sparse W matrix for a regular lattice. + """Create a sparse `W` matrix for a regular lattice. Parameters ---------- - nrows : int number of rows ncols : int @@ -1142,15 +1256,15 @@ def lat2SW(nrows=3, ncols=5, criterion="rook", row_st=False): Returns ------- - - w : scipy.sparse.dia_matrix - instance of a scipy sparse matrix + m : scipy.sparse.dia_matrix + An instance of a ``scipy`` sparse matrix. Notes ----- - Observations are row ordered: first k observations are in row 0, next k in row 1, and so on. - This method directly creates the W matrix using the strucuture of the contiguity type. + Observations are row ordered: first :math:`k` observations are in row 0, + next k in row 1, and so on. This method directly creates the `W` + matrix using the strucuture of the contiguity type. Examples -------- @@ -1164,15 +1278,19 @@ def lat2SW(nrows=3, ncols=5, criterion="rook", row_st=False): >>> w9r = lat2SW(3,3, row_st=True) >>> w9r[3,6] == 1./3 True + """ n = nrows * ncols diagonals = [] offsets = [] + if criterion == "rook" or criterion == "queen": d = np.ones((1, n)) + for i in range(ncols - 1, n, ncols): d[0, i] = 0 + diagonals.append(d) offsets.append(-1) @@ -1182,6 +1300,7 @@ def lat2SW(nrows=3, ncols=5, criterion="rook", row_st=False): if criterion == "queen" or criterion == "bishop": d = np.ones((1, n)) + for i in range(0, n, ncols): d[0, i] = 0 diagonals.append(d) @@ -1192,19 +1311,24 @@ def lat2SW(nrows=3, ncols=5, criterion="rook", row_st=False): d[0, i] = 0 diagonals.append(d) offsets.append(-(ncols + 1)) + data = np.concatenate(diagonals) offsets = np.array(offsets) m = sparse.dia_matrix((data, offsets), shape=(n, n), dtype=np.int8) m = m + m.T + if row_st: - m = sparse.spdiags(1. / m.sum(1).T, 0, *m.shape) * m + m = sparse.spdiags(1.0 / m.sum(1).T, 0, *m.shape) * m + return m def write_gal(file, k=10): - f = open(file, 'w') + + f = open(file, "w") n = k * k f.write("0 %d" % n) + for i in range(n): row = i / k col = i % k @@ -1212,11 +1336,12 @@ def write_gal(file, k=10): neighs = [j for j in neighs if j >= 0 and j < n] f.write("\n%d %d\n" % (i, len(neighs))) f.write(" ".join(map(str, neighs))) + f.close() + def neighbor_equality(w1, w2): - """ - Test if the neighbor sets are equal between two weights objects + """Test if the neighbor sets are equal between two weights objects. Parameters ---------- @@ -1239,62 +1364,70 @@ def neighbor_equality(w1, w2): Examples -------- + >>> from libpysal.weights.util import neighbor_equality >>> from libpysal.weights import lat2W, W >>> w1 = lat2W(3,3) >>> w2 = lat2W(3,3) >>> neighbor_equality(w1, w2) True + >>> w3 = lat2W(5,5) >>> neighbor_equality(w1, w3) False + >>> n4 = w1.neighbors.copy() >>> n4[0] = [1] >>> n4[1] = [4, 2] >>> w4 = W(n4) >>> neighbor_equality(w1, w4) False + >>> n5 = w1.neighbors.copy() >>> n5[0] [3, 1] + >>> n5[0] = [1, 3] >>> w5 = W(n5) >>> neighbor_equality(w1, w5) True """ + n1 = w1.neighbors n2 = w2.neighbors ids_1 = set(n1.keys()) ids_2 = set(n2.keys()) + if ids_1 != ids_2: return False + for i in ids_1: if set(w1.neighbors[i]) != set(w2.neighbors[i]): return False + return True + def isKDTree(obj): + """This is a utility function to determine whether or not an object is a + KDTree, since KDTree and cKDTree have no common parent type. """ - This is a utility function to determine whether or not an object is a - KDTree, since KDTree and cKDTree have no common parent type - """ + return any([issubclass(type(obj), KDTYPE) for KDTYPE in KDTREE_TYPES]) + def attach_islands(w, w_knn1, **kwargs): - """ - Attach nearest neighbor to islands in spatial weight w. + """Attach nearest neighbor to islands in spatial weight ``w``. Parameters ---------- - w : libpysal.weights.W pysal spatial weight object (unstandardized). w_knn1 : libpysal.weights.W Nearest neighbor pysal spatial weight object (k=1). - **kwargs : keyword arguments - optional arguments for :class:`pysal.weights.W` - + **kwargs : dict + Optional keyword arguments for ``libpysal.weights.W``. Returns ------- @@ -1303,56 +1436,63 @@ def attach_islands(w, w_knn1, **kwargs): Examples -------- + >>> from libpysal.weights import lat2W, Rook, KNN, attach_islands >>> import libpysal >>> w = Rook.from_shapefile(libpysal.examples.get_path('10740.shp')) >>> w.islands [163] + >>> w_knn1 = KNN.from_shapefile(libpysal.examples.get_path('10740.shp'),k=1) >>> w_attach = attach_islands(w, w_knn1) >>> w_attach.islands [] + >>> w_attach[w.islands[0]] {166: 1.0} """ neighbors, weights = copy.deepcopy(w.neighbors), copy.deepcopy(w.weights) + if not len(w.islands): print("There are no disconnected observations (no islands)!") return w + else: for island in w.islands: nb = w_knn1.neighbors[island][0] + if type(island) is float: nb = float(nb) + neighbors[island] = [nb] weights[island] = [1.0] neighbors[nb] = neighbors[nb] + [island] weights[nb] = weights[nb] + [1.0] + return W(neighbors, weights, id_order=w.id_order, **kwargs) -def nonplanar_neighbors(w, geodataframe, tolerance=0.001, **kwargs): - """ - Detect neighbors for non-planar polygon collections +def nonplanar_neighbors(w, geodataframe, tolerance=0.001, **kwargs): + """Detect neighbors for non-planar polygon collections. Parameters ---------- - w: pysal W + w: libpysal.weights.W A spatial weights object with reported islands - geodataframe: GeoDataframe + geodataframe: geopandas.GeoDataframe The polygon dataframe from which w was constructed. tolerance: float The percentage of the minimum horizontal or vertical extent (minextent) of the dataframe to use in defining a buffering distance to allow for fuzzy contiguity detection. The buffering distance is equal to tolerance*minextent. - **kwargs: keyword arguments - optional arguments for :class:`pysal.weights.W` + **kwargs : dict + Optional keyword arguments for ``libpysal.weights.W``. Attributes @@ -1393,17 +1533,21 @@ def nonplanar_neighbors(w, geodataframe, tolerance=0.001, **kwargs): >>> w = libpysal.weights.Queen.from_dataframe(df) >>> w.islands [0, 4, 23, 27, 80, 94, 101, 107, 109, 119, 122, 139, 169, 175, 223, 239, 247, 253, 254, 255, 256, 261, 276, 291, 294, 303, 321, 357, 374] + >>> wnp = libpysal.weights.nonplanar_neighbors(w, df) >>> wnp.islands [] + >>> w.neighbors[0] [] + >>> wnp.neighbors[0] [23, 59, 152, 239] + >>> wnp.neighbors[23] [0, 45, 59, 107, 152, 185, 246] - Also see `nonplanarweights.ipynb` + Also see `nonplanarweights.ipynb`. <----------------------------- ADD LINK ----------------------- References ---------- @@ -1414,7 +1558,10 @@ def nonplanar_neighbors(w, geodataframe, tolerance=0.001, **kwargs): """ gdf = geodataframe - assert gdf.sindex, 'GeoDataFrame must have a spatial index. Please make sure you have `libspatialindex` installed' + assert ( + gdf.sindex + ), "GeoDataFrame must have a spatial index. Please make sure you have `libspatialindex` installed" + islands = w.islands joins = copy.deepcopy(w.neighbors) candidates = gdf.geometry @@ -1423,9 +1570,16 @@ def nonplanar_neighbors(w, geodataframe, tolerance=0.001, **kwargs): # first check for intersecting polygons for island in islands: focal = gdf.iloc[island].geometry - neighbors = [j for j, candidate in enumerate(candidates) if focal.intersects(candidate) and j!= island] + neighbors = [ + j + for j, candidate in enumerate(candidates) + if focal.intersects(candidate) and j != island + ] + if len(neighbors) > 0: + for neighbor in neighbors: + if neighbor not in joins[island]: fixes[island].append(neighbor) joins[island].append(neighbor) @@ -1435,13 +1589,21 @@ def nonplanar_neighbors(w, geodataframe, tolerance=0.001, **kwargs): # if any islands remain, dilate them and check for intersection if islands: - x0,y0,x1,y1 = gdf.total_bounds - distance = tolerance * min(x1-x0, y1-y0) + x0, y0, x1, y1 = gdf.total_bounds + distance = tolerance * min(x1 - x0, y1 - y0) + for island in islands: dilated = gdf.iloc[island].geometry.buffer(distance) - neighbors = [j for j, candidate in enumerate(candidates) if dilated.intersects(candidate) and j!= island] + neighbors = [ + j + for j, candidate in enumerate(candidates) + if dilated.intersects(candidate) and j != island + ] + if len(neighbors) > 0: + for neighbor in neighbors: + if neighbor not in joins[island]: fixes[island].append(neighbor) joins[island].append(neighbor) @@ -1451,39 +1613,35 @@ def nonplanar_neighbors(w, geodataframe, tolerance=0.001, **kwargs): w = W(joins, **kwargs) w.non_planar_joins = fixes + return w -@requires('geopandas') -def fuzzy_contiguity(gdf, tolerance=0.005, buffering=False, drop=True, buffer=None, **kwargs): - """ - Fuzzy contiguity spatial weights + +@requires("geopandas") +def fuzzy_contiguity( + gdf, tolerance=0.005, buffering=False, drop=True, buffer=None, **kwargs +): + """Fuzzy contiguity spatial weights. Parameters ---------- - - gdf: GeoDataFrame - - tolerance: float + gdf : geopandas.GeoDataFrame + .......................... + tolerance : float The percentage of the length of the minimum side of the bounding rectangle for the GeoDataFrame to use in determining the buffering distance. - - buffering: boolean + buffering : bool If False (default) joins will only be detected for features that intersect (touch, contain, within). If True then features will be buffered and intersections will be based on buffered features. - - drop: boolean + drop: bool If True (default), the buffered features are removed from the GeoDataFrame. If False, buffered features are added to the GeoDataFrame. - buffer : float Specify exact buffering distance. Ignores `tolerance`. - - **kwargs: keyword arguments - optional arguments for :class:`pysal.weights.W` - + **kwargs : dict + Optional keyword arguments for ``libpysal.weights.W``. Returns ------- - - w: PySAL W + w : libpysal.weights.W Spatial weights based on fuzzy contiguity. Weights are binary. Examples @@ -1497,11 +1655,14 @@ def fuzzy_contiguity(gdf, tolerance=0.005, buffering=False, drop=True, buffer=No >>> wq = libpysal.weights.Queen.from_dataframe(rs_df) >>> len(wq.islands) 29 + >>> wq[0] {} + >>> wf = fuzzy_contiguity(rs_df) >>> wf.islands [] + >>> wf[0] == dict({239: 1.0, 59: 1.0, 152: 1.0, 23: 1.0, 107: 1.0}) True @@ -1516,9 +1677,11 @@ def fuzzy_contiguity(gdf, tolerance=0.005, buffering=False, drop=True, buffer=No >>> wf = fuzzy_contiguity(gdf) >>> wf.islands [2] + >>> wfb = fuzzy_contiguity(gdf, buffering=True) >>> wfb.islands [] + >>> wfb[2] {1: 1.0} @@ -1540,7 +1703,6 @@ def fuzzy_contiguity(gdf, tolerance=0.005, buffering=False, drop=True, buffer=No The buffering check assumes the geometry coordinates are projected. - References ---------- @@ -1548,20 +1710,26 @@ def fuzzy_contiguity(gdf, tolerance=0.005, buffering=False, drop=True, buffer=No """ + if buffering: if not buffer: # buffer each shape minx, miny, maxx, maxy = gdf.total_bounds - buffer = tolerance * 0.5 * abs(min(maxx-minx, maxy-miny)) + buffer = tolerance * 0.5 * abs(min(maxx - minx, maxy - miny)) # create new geometry column new_geometry = gdf.geometry.buffer(buffer) - gdf['_buffer'] = new_geometry + gdf["_buffer"] = new_geometry old_geometry_name = gdf.geometry.name - gdf.set_geometry('_buffer', inplace=True) - assert gdf.sindex, 'GeoDataFrame must have a spatial index. Please make sure you have `libspatialindex` installed' + gdf.set_geometry("_buffer", inplace=True) + + assert ( + gdf.sindex + ), "GeoDataFrame must have a spatial index. Please make sure you have `libspatialindex` installed" + tree = gdf.sindex neighbors = {} - n,k = gdf.shape + n, k = gdf.shape + for i in range(n): geom = gdf.geometry.iloc[i] hits = list(tree.intersection(geom.bounds)) @@ -1572,20 +1740,22 @@ def fuzzy_contiguity(gdf, tolerance=0.005, buffering=False, drop=True, buffer=No if buffering: gdf.set_geometry(old_geometry_name, inplace=True) + if drop: - gdf.drop(columns=['_buffer'], inplace=True) + gdf.drop(columns=["_buffer"], inplace=True) return W(neighbors, **kwargs) - if __name__ == "__main__": from libpysal.weights import lat2W + assert (lat2W(5, 5).sparse.todense() == lat2SW(5, 5).todense()).all() assert (lat2W(5, 3).sparse.todense() == lat2SW(5, 3).todense()).all() - assert (lat2W(5, 3, rook=False).sparse.todense() == lat2SW(5, 3, - 'queen').todense()).all() - assert (lat2W(50, 50, rook=False).sparse.todense() == lat2SW(50, - - 50, 'queen').todense()).all() + assert ( + lat2W(5, 3, rook=False).sparse.todense() == lat2SW(5, 3, "queen").todense() + ).all() + assert ( + lat2W(50, 50, rook=False).sparse.todense() == lat2SW(50, 50, "queen").todense() + ).all() From 3244f30a84b919d6a3be6da7d54325f3795465ee Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Mon, 24 Aug 2020 16:15:59 -0400 Subject: [PATCH 22/33] correction to weights/adjtools.py --- libpysal/weights/adjtools.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/libpysal/weights/adjtools.py b/libpysal/weights/adjtools.py index 529cf2b5d..2c210e0a3 100644 --- a/libpysal/weights/adjtools.py +++ b/libpysal/weights/adjtools.py @@ -7,9 +7,10 @@ def adjlist_apply(X, W=None, alist=None, func=np.subtract, skip_verify=False): Parameters ---------- X : iterable - An `(N,P)`-length iterable to apply ``func`` to. If (N,1), then ``func`` - must take 2 arguments and return a single reduction. If `P`>1, then ``func`` - must take two `P`-length arrays and return a single reduction of them. + An :math:`(N,P)`-length iterable to apply ``func`` to. If :math:`(N,1)`, + then ``func`` must take 2 arguments and return a single reduction. + If :math:`P`>1`, then ``func`` must take two :math:`P`-length arrays + and return a single reduction of them. W : libpysal.weights.W A weights object that provides adjacency information. Default is ``None``. alist : pandas.DataFrame @@ -128,10 +129,10 @@ def _adjlist_mvapply(X, W=None, alist=None, func=None, skip_verify=False): def _get_W_and_alist(W, alist, skip_verify=False): - """ Either (1) compute a ``W`` from an ``alist``; (2) compute an adjacency list - from a ``W``; (3) raise a ``ValueError`` if neither are provided; or (4) raise an - ``AssertionError`` if both ``W`` and ``adjlist`` are provided and don't match. - If this completes successfully, the ``W`` and ``adjlist`` will both be returned and + """ Either (1) compute a `W` from an ``alist``; (2) compute an adjacency list + from a `W`; (3) raise a ``ValueError`` if neither are provided; or (4) raise an + ``AssertionError`` if both `W` and ``adjlist`` are provided and don't match. + If this completes successfully, the `W` and ``adjlist`` will both be returned and are checked for equality. See ``libpysal.weights.adjtools.adjlist_apply()`` for parameters and returns information. From 1417ae78c153e8ad76984cfcd77798b1d76c1135 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Tue, 25 Aug 2020 22:07:53 -0400 Subject: [PATCH 23/33] completing docstrings for weight/util.py, etc. --- libpysal/weights/adjtools.py | 21 +- libpysal/weights/contiguity.py | 16 +- libpysal/weights/distance.py | 7 +- libpysal/weights/set_operations.py | 26 ++- libpysal/weights/spatial_lag.py | 11 +- libpysal/weights/spintW.py | 27 ++- libpysal/weights/user.py | 9 +- libpysal/weights/util.py | 342 ++++++++++++++++------------- libpysal/weights/weights.py | 60 ++++- 9 files changed, 344 insertions(+), 175 deletions(-) diff --git a/libpysal/weights/adjtools.py b/libpysal/weights/adjtools.py index 2c210e0a3..ed28cb513 100644 --- a/libpysal/weights/adjtools.py +++ b/libpysal/weights/adjtools.py @@ -39,6 +39,11 @@ def adjlist_apply(X, W=None, alist=None, func=np.subtract, skip_verify=False): An adjacency list (or modifies ``alist`` inplace) with the function applied to each row. + Raises + ------ + ImportError + Pandas must be installed to use this function. + """ try: @@ -77,9 +82,9 @@ def adjlist_apply(X, W=None, alist=None, func=np.subtract, skip_verify=False): def _adjlist_mvapply(X, W=None, alist=None, func=None, skip_verify=False): - """This function is used when ``X`` is multi-dimensional. - See ``libpysal.weights.adjtools.adjlist_apply()`` - for parameters and returns information. + """This function is used when ``X`` is multi-dimensional. See + ``libpysal.weights.adjtools.adjlist_apply()`` for + Parameters, Returns, and Raises information. """ @@ -136,6 +141,11 @@ def _get_W_and_alist(W, alist, skip_verify=False): are checked for equality. See ``libpysal.weights.adjtools.adjlist_apply()`` for parameters and returns information. + Raises + ------ + ValueError + Either W or Adjacency List must be provided. + """ if (alist is None) and (W is not None): @@ -200,6 +210,11 @@ def adjlist_map( An adjacency list (or modifies one if provided) with each function applied to the column of the data. + Raises + ------ + ImportError + Pandas must be installed to use this function. + """ try: diff --git a/libpysal/weights/contiguity.py b/libpysal/weights/contiguity.py index 779d2fa7d..ff52456e7 100644 --- a/libpysal/weights/contiguity.py +++ b/libpysal/weights/contiguity.py @@ -437,7 +437,12 @@ def Voronoi(points, criterion="rook", clip="ahull", **kwargs): ------- w : libpysal.weights.Voronoi A voronoi-style instance of spatial weights. - + + Raises + ------ + ValueError + An unsupported value of ``criterion`` was passed in. + Examples -------- @@ -492,7 +497,6 @@ def _from_dataframe(df, **kwargs): Raises ------ - NotImplementedError If the input dataframe is of any other geometry type than ``Point``, a ``ValueError`` is caught and raised as a ``NotImplementedError``. @@ -536,7 +540,12 @@ def _build(polygons, criterion="rook", ids=None): The contents are ``(neighbors, ids)``, where ``neighbors`` is a dictionary describing contiguity relations and ``ids`` is the list of ids used to index that dictionary. - + + Raises + ------ + ValueError + The argument to the ``ids`` parameter contains duplicate entries. + Notes ----- @@ -585,6 +594,7 @@ def buildContiguity(polygons, criterion="rook", ids=None): """This is a deprecated function. It builds a contiguity `W` from the polygons provided. As such, it is now identical to calling the class constructors for `Rook` or `Queen`. + """ # Warn('This function is deprecated. Please use the Rook or Queen classes', UserWarning) diff --git a/libpysal/weights/distance.py b/libpysal/weights/distance.py index b89c56f36..6b4f0a3bd 100644 --- a/libpysal/weights/distance.py +++ b/libpysal/weights/distance.py @@ -836,6 +836,11 @@ class DistanceBand(W): neighbors : dict Neighbors keyed by observation id. + Raises + ------ + Value Error + An array was unable to be instantiated with ``data``. + Examples -------- @@ -934,7 +939,7 @@ def __init__( ) self.data = self.kdtree.data except: - raise ValueError("Could not make array from data") + raise ValueError("Could not make array from data.") else: self.data = data self.kdtree = None diff --git a/libpysal/weights/set_operations.py b/libpysal/weights/set_operations.py index ea223151e..9cefc3e50 100644 --- a/libpysal/weights/set_operations.py +++ b/libpysal/weights/set_operations.py @@ -105,10 +105,14 @@ def w_intersection(w1, w2, w_shape="w1", **kwargs): Returns ------- - w : libpysal.weights.W The intersection of two PySAL weights objects. + Raises + ------ + ValueError + An invalid string value was passed to ``w_shape``. + Notes ----- ID comparisons are performed using ``==``, therefore the integer ID 2 is @@ -143,7 +147,7 @@ def w_intersection(w1, w2, w_shape="w1", **kwargs): elif w_shape == "min": neigh_keys = set(w1.neighbors.keys()).intersection(set(w2.neighbors.keys())) else: - raise Exception("invalid string passed to w_shape") + raise ValueError("Invalid string passed to w_shape.") neighbors = {} for i in neigh_keys: @@ -188,6 +192,13 @@ def w_difference(w1, w2, w_shape="w1", constrained=True, **kwargs): w : libpysal.weights.W The difference of two PySAL weights objects. + Raises + ------ + RuntimeError + An empty weights matrix was returned. + ValueError + An invalid string value was passed to ``w_shape``. + Notes ----- @@ -226,9 +237,9 @@ def w_difference(w1, w2, w_shape="w1", constrained=True, **kwargs): elif w_shape == "min": neigh_keys = set(w1.neighbors.keys()).difference(set(w2.neighbors.keys())) if not neigh_keys: - raise Exception("returned an empty weights matrix") + raise RuntimeError("Returned an empty weights matrix.") else: - raise Exception("invalid string passed to w_shape") + raise ValueError("Invalid string passed to w_shape.") neighbors = {} for i in neigh_keys: @@ -283,6 +294,11 @@ def w_symmetric_difference(w1, w2, w_shape="all", constrained=True, **kwargs): w : libpysal.weights.W The symmetric difference of two PySAL weights objects. + Raises + ------ + ValueError + An invalid string value was passed to ``w_shape``. + Notes ----- @@ -320,7 +336,7 @@ def w_symmetric_difference(w1, w2, w_shape="all", constrained=True, **kwargs): set(w2.neighbors.keys()) ) else: - raise Exception("invalid string passed to w_shape") + raise ValueError("Invalid string passed to w_shape.") neighbors = {} for i in neigh_keys: diff --git a/libpysal/weights/spatial_lag.py b/libpysal/weights/spatial_lag.py index 917fdc93b..93a000bac 100644 --- a/libpysal/weights/spatial_lag.py +++ b/libpysal/weights/spatial_lag.py @@ -165,13 +165,17 @@ def lag_categorical(w, y, ties="tryself"): if isinstance(y, list): y = np.array(y) orig_shape = y.shape + if len(orig_shape) > 1: if orig_shape[1] > 1: - return np.vstack([lag_categorical(w, col) for col in y.T]).T + output = np.vstack([lag_categorical(w, col) for col in y.T]).T + return output + y = y.flatten() output = np.zeros_like(y) labels = np.unique(y) normalized_labels = np.zeros(y.shape, dtype=np.int) + for i, label in enumerate(labels): normalized_labels[y == label] = i for focal_name, neighbors in w: @@ -221,6 +225,11 @@ def _resolve_ties(idx, normalized_labels, tally, neighbors, method, w): label : int An integer denoting which label to use to label the observation. + Raises + ------ + KeyError + The tie-breaking method for categorical lag is not recognized. + """ m = method.lower() diff --git a/libpysal/weights/spintW.py b/libpysal/weights/spintW.py index 45a22ae93..ba182fec2 100644 --- a/libpysal/weights/spintW.py +++ b/libpysal/weights/spintW.py @@ -39,7 +39,14 @@ def ODW(Wo, Wd, transform="r", silence_warnings=True): A sparse spatial contiguity `W` object for assocations between flows between :math:`o` origins and :math:`d` destinations, :math:`(o \cdot d)\cdot(o \cdot d)`. - + + Raises + ------ + AttributeError + The ``Wo`` argument is not binary. + AttributeError + The ``Wd`` argument is not binary. + Examples -------- @@ -116,7 +123,12 @@ def netW(link_list, share="A", transform="r", **kwargs): A nodal contiguity `W` object for network edges or flows representing the binary adjacency of the network edges given a definition of nodal relationships. - + + Raises + ------ + AttributeError + The ``share`` parameter must be ``'O'``, ``'D'``, ``'OD'``, or ``'C'``. + Examples -------- @@ -166,7 +178,7 @@ def netW(link_list, share="A", transform="r", **kwargs): neighbors[key].append(neigh) else: raise AttributeError( - "Parameter 'share' must be 'O', 'D'," " 'OD', or 'C'" + "Parameter 'share' must be 'O', 'D', 'OD', or 'C'." ) netW = W(neighbors, **kwargs) @@ -277,7 +289,12 @@ def mat2L(edge_matrix): A matrix where rows denote network edge origins, columns denote network edge destinations, and non-zero entries denote the existence of an edge between a given origin and destination. - + + Raises + ------ + AttributeError + The input matrix is not two dimensional. + Returns ------- edge_list : list @@ -291,7 +308,7 @@ def mat2L(edge_matrix): "Matrix of network edges should be two dimensions" "with edge origins on one axis and edge destinations on the" "second axis with non-zero matrix entires denoting an edge" - "between and origin and destination" + "between and origin and destination." ) edge_list = [] diff --git a/libpysal/weights/user.py b/libpysal/weights/user.py index f18e01ea3..7e1b78f4a 100644 --- a/libpysal/weights/user.py +++ b/libpysal/weights/user.py @@ -112,11 +112,16 @@ def build_lattice_shapefile(nrows, ncols, out_file_name): The number of columns. out_file_name : str The shapefile name including the ``.shp`` file extension. - + + Raises + ------ + ValueError + An unrecognized file extension was given. + """ if not out_file_name.endswith(".shp"): - raise ValueError("``out_file_name`` must end with .shp") + raise ValueError("'out_file_name' must end with '.shp'.") o = ps_open(out_file_name, "w") dbf_name = out_file_name.split(".")[0] + ".dbf" d = ps_open(dbf_name, "w") diff --git a/libpysal/weights/util.py b/libpysal/weights/util.py index 370bbfbbc..401697ddf 100644 --- a/libpysal/weights/util.py +++ b/libpysal/weights/util.py @@ -19,7 +19,7 @@ try: import geopandas as gpd except ImportError: - warn("geopandas not available. Some functionality will be disabled.") + warn("Geopandas not available. Some functionality will be disabled.") __all__ = [ "lat2W", @@ -33,6 +33,7 @@ "full", "WSP2W", "insert_diagonal", + "fill_diagonal", "get_ids", "get_points_array_from_shapefile", "min_threshold_distance", @@ -40,6 +41,7 @@ "w_local_cluster", "higher_order_sp", "hexLat2W", + "neighbor_equality", "attach_islands", "nonplanar_neighbors", "fuzzy_contiguity", @@ -91,8 +93,8 @@ def hexLat2W(nrows=5, ncols=5, **kwargs): """ if nrows == 1 or ncols == 1: - print("Hexagon lattice requires at least 2 rows and columns") - print("Returning a linear contiguity structure") + print("Hexagon lattice requires at least 2 rows and columns.") + print("Returning a linear contiguity structure.") return lat2W(nrows, ncols) n = nrows * ncols @@ -326,9 +328,9 @@ def comb(items, n=None): n : int The size of combinations to take from items. - Yields - ------- - implicit : generator + Yields + ------ + vc : generator The combinations of size :math:`n` taken from items. Examples @@ -355,11 +357,13 @@ def comb(items, n=None): v = items[i : i + 1] if n == 1: - yield v + vc = v + yield vc else: rest = items[i + 1 :] for c in comb(rest, n - 1): - yield v + c + vc = v + c + yield vc def order(w, kmax=3): @@ -405,8 +409,10 @@ def order(w, kmax=3): for id_ in ids: s = [0] * w.n s[ids.index(id_)] = -1 + for j in w.neighbors[id_]: s[ids.index(j)] = 1 + k = 1 while k < kmax: knext = k + 1 @@ -685,6 +691,7 @@ def shimbel(w): >>> w5_shimbel = shimbel(w5) >>> w5_shimbel[0][24] 8 + >>> w5_shimbel[0][0:4] [-1, 1, 2, 3] @@ -692,11 +699,14 @@ def shimbel(w): info = {} ids = w.id_order + for i in ids: s = [0] * w.n s[ids.index(i)] = -1 + for j in w.neighbors[i]: s[ids.index(j)] = 1 + k = 1 flag = s.count(0) while flag: @@ -722,14 +732,14 @@ def full(w): Parameters ---------- - w : W - spatial weights object + w : libpysal.weights.W + An instance of spatial weights, `W`. Returns ------- (fullw, keys) : tuple - first element being the full ``numpy.ndarray`` and second element - keys being the ids associated with each row in the array. + The first element is the full ``numpy.ndarray`` and the second element + is a list of keys that are the ids associated with each row in the array. Examples -------- @@ -742,12 +752,15 @@ def full(w): array([[0., 1., 0.], [1., 0., 1.], [0., 1., 0.]]) + >>> ids ['first', 'second', 'third'] """ - return w.full() + fullw, keys = w.full() + + return fullw, keys def full2W(m, ids=None, **kwargs): @@ -755,17 +768,22 @@ def full2W(m, ids=None, **kwargs): Parameters ---------- - m : array - nxn array with the full weights matrix - ids : list - User ids assumed to be aligned with m + m : ``numpy.ndarray``. + An :math:`n`x:math:`n` array with the full weights matrix. + ids : list + User ids assumed to be aligned with ``m``. Default is ``None``. **kwargs : dict Optional keyword arguments for ``libpysal.weights.W``. + Raises + ------ + ValueError + The input array, ``m``, is not square. + Returns ------- - w : W - PySAL weights object + w : libpysal.weights.W + An instance of spatial weights, `W`. Examples -------- @@ -805,7 +823,7 @@ def full2W(m, ids=None, **kwargs): """ if m.shape[0] != m.shape[1]: - raise ValueError("Your array is not square") + raise ValueError("Your array is not square.") neighbors, weights = {}, {} @@ -824,23 +842,25 @@ def full2W(m, ids=None, **kwargs): neighbors[i] = ngh - return W(neighbors, weights, id_order=ids, **kwargs) + w = W(neighbors, weights, id_order=ids, **kwargs) + + return w def WSP2W(wsp, **kwargs): - """Convert a pysal `WSP` object (thin weights matrix) to a pysal `W` object. + """Convert a PySAL `WSP` object (thin weights matrix) to a PySAL `W` object. Parameters ---------- - wsp : WSP - PySAL sparse weights object + wsp : libpysal.weights.WSP + An instance of sparse spatial weights, `W`. **kwargs : dict Optional keyword arguments for ``libpysal.weights.W``. Returns ------- - w : W - PySAL weights object + w : libpysal.weights.W + An instance of spatial weights, `W`. Examples -------- @@ -909,22 +929,30 @@ def fill_diagonal(w, val=1.0, wsp=False): Parameters ---------- - w : W - Spatial weights object - diagonal : float, int or array - Defines the value(s) to which the weights matrix diagonal should - be set. If a constant is passed then each element along the - diagonal will get this value (default is 1.0). An array of length - w.n can be passed to set explicit values to each element along - the diagonal (assumed to be in the same order as w.id_order). - wsp : bool - If True return a thin weights object of the type WSP, if False - return the standard W object. + w : libpysal.weights.W + An instance of spatial weights, `W`. + val : {float, int, array_like} + Defines the value(s) to which the weights matrix diagonal should + be set. If a constant is passed then each element along the + diagonal will get this value (default is ``1.0``). An array of length + ``w.n`` can be passed to set explicit values to each element along + the diagonal (assumed to be in the same order as ``w.id_order``). + wsp : bool + If ``True`` return a thin weights object of the type `WSP`, if ``False`` + return the standard `W` object. Default is ``False``. + Raises + ------ + ValueError + The shape of the spatial weights object and the + length of the diagonal are not equivalent. + ValueError + The input value for the diagonal is not valid. + Returns ------- - w : W - Spatial weights object + w_out : libpysal.weights.W + An instance of spatial weights, `W`. Examples -------- @@ -939,6 +967,7 @@ def fill_diagonal(w, val=1.0, wsp=False): >>> w_const = insert_diagonal(w) >>> w['id0'] == {'id5': 1.0, 'id1': 1.0} True + >>> w_const['id0'] == {'id5': 1.0, 'id0': 1.0, 'id1': 1.0} True @@ -956,19 +985,19 @@ def fill_diagonal(w, val=1.0, wsp=False): if issubclass(type(val), np.ndarray): if w.n != val.shape[0]: - raise Exception("shape of w and diagonal do not match") + raise ValueError("Shape of 'w' and diagonal do not match.") w_new.setdiag(val) elif isinstance(val, numbers.Number): w_new.setdiag([val] * w.n) else: - raise Exception("Invalid value passed to diagonal") + raise ValueError("Invalid value passed to diagonal.") w_out = WSP(w_new, copy.copy(w.id_order)) - if wsp: - return w_out - else: - return WSP2W(w_out) + if not wsp: + w_out = WSP2W(w_out) + + return w_out def remap_ids(w, old2new, id_order=[], **kwargs): @@ -976,22 +1005,27 @@ def remap_ids(w, old2new, id_order=[], **kwargs): Parameters ---------- - w : W - Spatial weights object - old2new : dictionary - Dictionary where the keys are the IDs in w (i.e. "old IDs") and - the values are the IDs to replace them (i.e. "new IDs") + w : libpysal.weights.W + An instance of spatial weights, `W`. + old2new : dict + Dictionary where the keys are the IDs in w (i.e. "old IDs") + and the values are the IDs to replace them (i.e. "new IDs"). id_order : list - An ordered list of new IDs, which defines the order of observations when - iterating over W. If not set then the id_order in w will be - used. + An ordered list of new IDs, which defines the order of + observations when iterating over `W`. If not set then the + id_order in ``w`` will be used. Default is ``[]``. **kwargs : dict Optional keyword arguments for ``libpysal.weights.W``. + Raises + ------ + TypeError + The object passed in as ``w`` is not a spatial weights object. + Returns ------- - implicit : W - Spatial weights object with new IDs + w : libpysal.weights.W + An instance of spatial weights, `W`, with new IDs. Examples -------- @@ -1016,7 +1050,8 @@ def remap_ids(w, old2new, id_order=[], **kwargs): """ if not isinstance(w, W): - raise Exception("w must be a spatial weights object") + raise TypeError("'w' must be a spatial weights object.") + new_neigh = {} new_weights = {} @@ -1027,13 +1062,16 @@ def remap_ids(w, old2new, id_order=[], **kwargs): new_weights[new_key] = copy.copy(w.weights[key]) if id_order: - return W(new_neigh, new_weights, id_order, **kwargs) + w = W(new_neigh, new_weights, id_order, **kwargs) + else: if w.id_order: id_order = [old2new[i] for i in w.id_order] - return W(new_neigh, new_weights, id_order, **kwargs) + w = W(new_neigh, new_weights, id_order, **kwargs) else: - return W(new_neigh, new_weights, **kwargs) + w = W(new_neigh, new_weights, **kwargs) + + return w def get_ids(in_shps, idVariable): @@ -1042,25 +1080,25 @@ def get_ids(in_shps, idVariable): Parameters ---------- - in_shps : str or geopandas.GeoDataFrame - The input geographic data. Either + in_shps : {str, geopandas.GeoDataFrame} + The input geographic data. Either (1) a path to a shapefile including suffix (str); or (2) a geopandas.GeoDataFrame. idVariable : str - name of a column in the shapefile's DBF or the - geopandas.GeoDataFrame to use for ids. + The name of a column in the shapefile's DBF or the + ``geopandas.GeoDataFrame`` to use for IDs. Returns ------- - ids : list - a list of IDs + var : list + A list of IDs. Raises ------ IOError - descripiton of error........ + No ``.dbf`` file is present in the indicated directory. KeyError - descripiton of error........ + The variable name passed in for IDs is not found. Examples -------- @@ -1120,20 +1158,19 @@ def get_points_array(iterable): Parameters ---------- - iterable : iterable - arbitrary collection of shapes that supports iteration + iterable : iterable + An arbitrary collection of shapes that supports iteration. Returns ------- - points : array - (n, 2) - a data array of `x` and `y` coordinates + data : numpy.ndarray + A data array of `x` and `y` coordinates with shape :math:`(n, 2)`. Notes ----- - If the given shape file includes polygons, - this function returns `x` and `y` coordinates of the polygons' centroids. + If the given shapefile includes polygons, this function + returns `x` and `y` coordinates of the polygons' centroids. """ @@ -1152,20 +1189,19 @@ def get_points_array_from_shapefile(shapefile): Parameters ---------- - shapefile : string - name of a shape file including suffix + shapefile : str + The name of a shapefile including the extension. Returns ------- - points : array - (n, 2) - a data array of `x` and `y` coordinates + data : numpy.ndarray + A data array of `x` and `y` coordinates with shape :math:`(n, 2)`. Notes ----- - If the given shape file includes polygons, - this function returns `x` and `y` coordinates of the polygons' centroids + If the given shape file includes polygons, this function + returns `x` and `y` coordinates of the polygons' centroids Examples -------- @@ -1244,15 +1280,16 @@ def lat2SW(nrows=3, ncols=5, criterion="rook", row_st=False): Parameters ---------- - nrows : int - number of rows - ncols : int - number of columns - rook : {"rook", "queen", "bishop"} - type of contiguity. Default is rook. - row_st : boolean - If True, the created sparse W object is row-standardized so - every row sums up to one. Defaults to False. + nrows : int + The number of rows. Default is ``3``. + ncols : int + The number of columns. Default is ``5``. + criterion : str + The type of contiguity. Default is ``'rook'``. Options + are ``'rook'``, ``'queen'``, and ``'bishop'``. + row_st : boolean + If ``True``, the created sparse `W` object is row-standardized + so every row sums up to one. Default is ``False``. Returns ------- @@ -1263,7 +1300,7 @@ def lat2SW(nrows=3, ncols=5, criterion="rook", row_st=False): ----- Observations are row ordered: first :math:`k` observations are in row 0, - next k in row 1, and so on. This method directly creates the `W` + next :math:`k` in row 1, and so on. This method directly creates the `W` matrix using the strucuture of the contiguity type. Examples @@ -1273,8 +1310,10 @@ def lat2SW(nrows=3, ncols=5, criterion="rook", row_st=False): >>> w9 = lat2SW(3,3) >>> w9[0,1] == 1 True + >>> w9[3,6] == 1 True + >>> w9r = lat2SW(3,3, row_st=True) >>> w9r[3,6] == 1./3 True @@ -1324,6 +1363,7 @@ def lat2SW(nrows=3, ncols=5, criterion="rook", row_st=False): def write_gal(file, k=10): + """Write out a ``.gal`` spatial weights file.""" f = open(file, "w") n = k * k @@ -1345,20 +1385,19 @@ def neighbor_equality(w1, w2): Parameters ---------- - - w1 : W - instance of spatial weights class W - - w2 : W - instance of spatial weights class W + w1 : libpysal.weights.W + An instance of spatial weights, `W`. + w2 : libpysal.weights.W + An instance of spatial weights, `W`. Returns ------- - Boolean - + equality : bool + Single equality evaluator. Notes ----- + Only set membership is evaluated, no check of the weight values is carried out. @@ -1394,45 +1433,49 @@ def neighbor_equality(w1, w2): """ + equality = True + n1 = w1.neighbors n2 = w2.neighbors ids_1 = set(n1.keys()) ids_2 = set(n2.keys()) if ids_1 != ids_2: - return False + equality = False - for i in ids_1: - if set(w1.neighbors[i]) != set(w2.neighbors[i]): - return False + if equality: + for i in ids_1: + if set(w1.neighbors[i]) != set(w2.neighbors[i]): + equality = False + break - return True + return equality def isKDTree(obj): """This is a utility function to determine whether or not an object is a - KDTree, since KDTree and cKDTree have no common parent type. + `KDTree`, since `KDTree` and `cKDTree` have no common parent type. """ return any([issubclass(type(obj), KDTYPE) for KDTYPE in KDTREE_TYPES]) def attach_islands(w, w_knn1, **kwargs): - """Attach nearest neighbor to islands in spatial weight ``w``. + """Attach the nearest neighbor to islands in a spatial weights object, `W`. Parameters ---------- - w : libpysal.weights.W - pysal spatial weight object (unstandardized). - w_knn1 : libpysal.weights.W - Nearest neighbor pysal spatial weight object (k=1). + w : libpysal.weights.W + A PySAL spatial weights object (unstandardized). + w_knn1 : libpysal.weights.KNN + A PySAL Nearest neighbor spatial weight object :math:`(k=1)`. **kwargs : dict Optional keyword arguments for ``libpysal.weights.W``. Returns ------- - : libpysal.weights.W - pysal spatial weight object w without islands. + w : libpysal.weights.W + A PySAL spatial weight object, `W`, without islands. Examples -------- @@ -1457,7 +1500,6 @@ def attach_islands(w, w_knn1, **kwargs): if not len(w.islands): print("There are no disconnected observations (no islands)!") - return w else: for island in w.islands: @@ -1471,7 +1513,9 @@ def attach_islands(w, w_knn1, **kwargs): neighbors[nb] = neighbors[nb] + [island] weights[nb] = weights[nb] + [1.0] - return W(neighbors, weights, id_order=w.id_order, **kwargs) + w = W(neighbors, weights, id_order=w.id_order, **kwargs) + + return w def nonplanar_neighbors(w, geodataframe, tolerance=0.001, **kwargs): @@ -1479,34 +1523,29 @@ def nonplanar_neighbors(w, geodataframe, tolerance=0.001, **kwargs): Parameters ---------- - - w: libpysal.weights.W - A spatial weights object with reported islands - - - geodataframe: geopandas.GeoDataframe - The polygon dataframe from which w was constructed. - - tolerance: float - The percentage of the minimum horizontal or vertical extent (minextent) of - the dataframe to use in defining a buffering distance to allow for fuzzy - contiguity detection. The buffering distance is equal to tolerance*minextent. + w : libpysal.weights.W + A spatial weights object with reported islands. + geodataframe : geopandas.GeoDataFrame + The polygon dataframe from which ``w`` was constructed. + tolerance : float + The percentage of the minimum horizontal or vertical extent (``minextent``) of + the dataframe to use in defining a buffering distance to allow for fuzzy + contiguity detection. The buffering distance is equal to ``tolerance*minextent``. **kwargs : dict Optional keyword arguments for ``libpysal.weights.W``. - Attributes ---------- - - non_planar_joins : dictionary - Stores the new joins detected. Key is the id of the focal unit, value is a list of neighbor ids. + non_planar_joins : dict + Stores the new joins detected. Key is the ID of the + focal unit, value is a list of neighbor IDs. Returns ------- - - w: pysal W + w : libpysal.weights.W Spatial weights object that encodes fuzzy neighbors. - This will have an attribute `non_planar_joins` to indicate what new joins were detected. + This will have an attribute `non_planar_joins` to + indicate what new joins were detected. Notes ----- @@ -1523,7 +1562,10 @@ def nonplanar_neighbors(w, geodataframe, tolerance=0.001, **kwargs): represent a hole in the containing polygon. The buffering check assumes the geometry coordinates are projected. - + + For an example see `nonplanarweights.ipynb `_. + + Examples -------- @@ -1547,13 +1589,10 @@ def nonplanar_neighbors(w, geodataframe, tolerance=0.001, **kwargs): >>> wnp.neighbors[23] [0, 45, 59, 107, 152, 185, 246] - Also see `nonplanarweights.ipynb`. <----------------------------- ADD LINK ----------------------- - References ---------- - Planar Enforcement: http://ibis.geog.ubc.ca/courses/klink/gis.notes/ncgia/u12.html#SEC12.6 - + Planar Enforcement: `see here `_. """ @@ -1626,16 +1665,21 @@ def fuzzy_contiguity( Parameters ---------- gdf : geopandas.GeoDataFrame - .......................... + A polygon dataframe. tolerance : float - The percentage of the length of the minimum side of the bounding rectangle for the GeoDataFrame to use in determining the buffering distance. + The percentage of the length of the minimum side of the bounding + rectangle for ``gdf`` to use in determining the buffering distance. + Default is ``0.005``. buffering : bool - If False (default) joins will only be detected for features that intersect (touch, contain, within). - If True then features will be buffered and intersections will be based on buffered features. - drop: bool - If True (default), the buffered features are removed from the GeoDataFrame. If False, buffered features are added to the GeoDataFrame. + If ``False`` (default) joins will only be detected for features + that intersect (touch, contain, within). If ``True`` then features + will be buffered and intersections will be based on buffered features. + drop : bool + If ``True`` (default), the buffered features are removed from ``gdf``. + If ``False``, buffered features are added to the ``gdf``. buffer : float - Specify exact buffering distance. Ignores `tolerance`. + Specify an exact buffering distance. When set ``tolerance`` is ignored. + Default is ``None``. **kwargs : dict Optional keyword arguments for ``libpysal.weights.W``. @@ -1666,7 +1710,7 @@ def fuzzy_contiguity( >>> wf[0] == dict({239: 1.0, 59: 1.0, 152: 1.0, 23: 1.0, 107: 1.0}) True - Example needing to use buffering + An example of needing to use buffering: >>> from shapely.geometry import Polygon >>> p0 = Polygon([(0,0), (10,0), (10,10)]) @@ -1699,15 +1743,15 @@ def fuzzy_contiguity( final case arises when one polygon is "inside" a second polygon but is not encoded to represent a hole in the containing polygon. - Detection of the second case will require setting buffering=True and exploring different values for tolerance. + Detection of the second case will require setting + ``buffering=True`` and exploring different values for tolerance. The buffering check assumes the geometry coordinates are projected. References ---------- - Planar Enforcement: http://ibis.geog.ubc.ca/courses/klink/gis.notes/ncgia/u12.html#SEC12.6 - + Planar Enforcement: `see here `_. """ diff --git a/libpysal/weights/weights.py b/libpysal/weights/weights.py index 74fe732a9..f898cab84 100644 --- a/libpysal/weights/weights.py +++ b/libpysal/weights/weights.py @@ -230,7 +230,14 @@ def from_file(cls, path="", format=None): @classmethod def from_shapefile(cls, *args, **kwargs): - """Construct a weights object from a shapefile.""" + """Construct a weights object from a shapefile. + + Raises + ------ + NotImplementedError + Use type-specific constructors, like Rook, Queen, DistanceBand, or Kernel. + + """ # we could also just 'do the right thing,' but I think it'd make sense to # try and get people to use ``Rook.from_shapefile(shapefile)`` rather than @@ -324,6 +331,11 @@ def to_adjlist( weight_col : str Name of the column in which to store weight information. + Raises + ------ + ImportError + Pandas must be installed to use this function. + Returns ------- adjlist : pandas.DataFrame @@ -360,6 +372,11 @@ def to_networkx(self): netx : networkx.Graph A ``networkx`` graph representation of the `W` object. + Raises + ------ + ImportError + NetworkX must be installed to use this function. + """ try: @@ -390,6 +407,11 @@ def from_networkx(cls, graph, weight_col="weight"): w : libpysal.weights.WSP A `WSP` object containing the same graph as the ``networkx`` graph. + Raises + ------ + ImportError + NetworkX must be installed to use this function. + """ try: @@ -790,7 +812,14 @@ def remap_ids(self, new_ids): first element of ``new_ids`` will replace the first element of ``w.id_order``, the second element of ``new_ids`` replaces the second element of ``w.id_order`` and so on. - + + Raises + ------ + ValueError + The length of ``old_ids`` does not match that of ``new_ids``. + ValueError + The list ``new_ids`` contains duplicates. + Examples -------- @@ -851,7 +880,12 @@ def __set_id_order(self, ordered_ids): ---------- ordered_ids : sequence Identifiers for observations in specified order. - + + Raises + ------ + ValueError + The ``ordered_ids`` argument does not align with ``W.ids``. + Notes ----- @@ -900,7 +934,7 @@ def __set_id_order(self, ordered_ids): self._id_order_set = True self._reset() else: - raise Exception("'ordered_ids' do not align with 'W.ids'.") + raise ValueError("'ordered_ids' do not align with 'W.ids'.") def __get_id_order(self): """Returns the ids for the observations in the order in which they @@ -1349,6 +1383,11 @@ def plot( ax : matplotlib.axes.Axes Axis on which the plot is made. + Raises + ------ + ImportError + Matplotlib must be installed to use this function. + Notes ----- If you'd like to overlay the actual shapes from the @@ -1441,7 +1480,16 @@ class WSP(object): :math:`s0=\sum_i \sum_j w_{i,j}`. trcWtW_WW : float Trace of :math:`W^{'}W + WW`. - + + Raises + ------ + ValueError + A scipy sparse object must be passed in. + ValueError + The weights object must be square. + ValueError + The number of values in ``id_order`` must match shape of ``sparse``. + Examples -------- @@ -1476,7 +1524,7 @@ def __init__(self, sparse, id_order=None): if id_order: if len(id_order) != self.n: - msg = "The Number of values in 'id_order' must match shape of 'sparse'." + msg = "The number of values in 'id_order' must match shape of 'sparse'." raise ValueError(msg) self.id_order = id_order From 0af1af1f66bbd43b0ecb3a0a676fdf6954a6acc9 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Tue, 25 Aug 2020 22:08:21 -0400 Subject: [PATCH 24/33] adding undocumented functions to api.rst --- docsrc/api.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docsrc/api.rst b/docsrc/api.rst index 47200fc25..a303d1d23 100644 --- a/docsrc/api.rst +++ b/docsrc/api.rst @@ -65,6 +65,7 @@ Weights Util Classes and Functions libpysal.weights.WSP2W libpysal.weights.get_ids libpysal.weights.get_points_array_from_shapefile + libpysal.weights.fill_diagonal Weights user Classes and Functions ++++++++++++++++++++++++++++++++++ @@ -83,6 +84,7 @@ Weights user Classes and Functions libpysal.weights.min_threshold_dist_from_shapefile libpysal.weights.build_lattice_shapefile libpysal.weights.spw_from_gal + libpysal.weights.neighbor_equality Set Theoretic Weights From 4d76107a18fed488148c040218deb27f2c553de3 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Wed, 26 Aug 2020 09:19:59 -0400 Subject: [PATCH 25/33] blackening cg/tests/* --- libpysal/cg/tests/test_ashapes.py | 2 +- libpysal/cg/tests/test_geoJSON.py | 45 +- libpysal/cg/tests/test_locators.py | 8 +- .../cg/tests/test_polygonQuadTreeStructure.py | 2570 ++++++++++++----- libpysal/cg/tests/test_rtree.py | 10 +- libpysal/cg/tests/test_segmentLocator.py | 37 +- libpysal/cg/tests/test_shapes.py | 555 ++-- libpysal/cg/tests/test_sphere.py | 160 +- libpysal/cg/tests/test_standalone.py | 326 ++- libpysal/cg/tests/test_voronoi.py | 29 +- 10 files changed, 2476 insertions(+), 1266 deletions(-) diff --git a/libpysal/cg/tests/test_ashapes.py b/libpysal/cg/tests/test_ashapes.py index c366268dd..a55f099c9 100644 --- a/libpysal/cg/tests/test_ashapes.py +++ b/libpysal/cg/tests/test_ashapes.py @@ -15,7 +15,7 @@ this_directory = os.path.dirname(__file__) -@skipIf(GEOPANDAS_EXTINCT, "Geopandas is missing, so test will not run") +@skipIf(GEOPANDAS_EXTINCT, "Geopandas is missing, so test will not run.") class Test_Alpha_Shapes(TestCase): def setUp(self): eberly = geopandas.read_file(get_path("eberly_net.shp")) diff --git a/libpysal/cg/tests/test_geoJSON.py b/libpysal/cg/tests/test_geoJSON.py index fc7530d20..d0a929a01 100644 --- a/libpysal/cg/tests/test_geoJSON.py +++ b/libpysal/cg/tests/test_geoJSON.py @@ -5,53 +5,54 @@ import unittest - class test_MultiPloygon(unittest.TestCase): - def test___init__1(self): + """Tests conversion of polygons with multiple + shells to geoJSON multipolygons. and back. """ - Tests conversion of polygons with multiple shells to - geoJSON multipolygons. and back. - """ - ncovr = pysal_examples.load_example('NCOVR') - shp = psopen(pysal_examples.get_path("NAT.shp"),'r') + ncovr = pysal_examples.load_example("NCOVR") + shp = psopen(pysal_examples.get_path("NAT.shp"), "r") multipolygons = [p for p in shp if len(p.parts) > 1] geoJSON = [p.__geo_interface__ for p in multipolygons] for poly in multipolygons: json = poly.__geo_interface__ shape = asShape(json) - self.assertEqual(json['type'],'MultiPolygon') + self.assertEqual(json["type"], "MultiPolygon") self.assertEqual(str(shape.holes), str(poly.holes)) self.assertEqual(str(shape.parts), str(poly.parts)) + + class test_MultiLineString(unittest.TestCase): def test_multipart_chain(self): - vertices = [[Point((0, 0)), Point((1, 0)), Point((1, 5))], - [Point((-5, -5)), Point((-5, 0)), Point((0, 0))]] + vertices = [ + [Point((0, 0)), Point((1, 0)), Point((1, 5))], + [Point((-5, -5)), Point((-5, 0)), Point((0, 0))], + ] - #part A + # part A chain0 = Chain(vertices[0]) - #part B + # part B chain1 = Chain(vertices[1]) - #part A and B + # part A and B chain2 = Chain(vertices) json = chain0.__geo_interface__ - self.assertEqual(json['type'], 'LineString') - self.assertEqual(len(json['coordinates']), 3) + self.assertEqual(json["type"], "LineString") + self.assertEqual(len(json["coordinates"]), 3) json = chain1.__geo_interface__ - self.assertEqual(json['type'], 'LineString') - self.assertEqual(len(json['coordinates']), 3) + self.assertEqual(json["type"], "LineString") + self.assertEqual(len(json["coordinates"]), 3) json = chain2.__geo_interface__ - self.assertEqual(json['type'], 'MultiLineString') - self.assertEqual(len(json['coordinates']), 2) + self.assertEqual(json["type"], "MultiLineString") + self.assertEqual(len(json["coordinates"]), 2) chain3 = asShape(json) self.assertEqual(chain2.parts, chain3.parts) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() - #runner = unittest.TextTestRunner() - #runner.run(suite) + # runner = unittest.TextTestRunner() + # runner.run(suite) diff --git a/libpysal/cg/tests/test_locators.py b/libpysal/cg/tests/test_locators.py index 67cc4edce..b9574bd75 100644 --- a/libpysal/cg/tests/test_locators.py +++ b/libpysal/cg/tests/test_locators.py @@ -1,12 +1,13 @@ - """locators Unittest.""" + from ..shapes import * from ..locators import * import unittest class PolygonLocator_Tester(unittest.TestCase): - """setup class for unit tests.""" + """Setup class for unit tests.""" + def setUp(self): p1 = Polygon([Point((0, 1)), Point((4, 5)), Point((5, 1))]) p2 = Polygon([Point((3, 9)), Point((6, 7)), Point((1, 1))]) @@ -55,12 +56,13 @@ def test_overlapping(self): res = self.pl2.overlapping(qr) self.assertEqual(len(res), 4) + suite = unittest.TestSuite() test_classes = [PolygonLocator_Tester] for i in test_classes: a = unittest.TestLoader().loadTestsFromTestCase(i) suite.addTest(a) -if __name__ == '__main__': +if __name__ == "__main__": runner = unittest.TextTestRunner() runner.run(suite) diff --git a/libpysal/cg/tests/test_polygonQuadTreeStructure.py b/libpysal/cg/tests/test_polygonQuadTreeStructure.py index 7b2614c4d..2441715f6 100644 --- a/libpysal/cg/tests/test_polygonQuadTreeStructure.py +++ b/libpysal/cg/tests/test_polygonQuadTreeStructure.py @@ -1,781 +1,1813 @@ """locators Unittest.""" + from ..polygonQuadTreeStructure import QuadTreeStructureSingleRing from ..shapes import Ring import unittest + class TestQuadTreeStructureSingleRing(unittest.TestCase): def test_QuadTreeStructureSingleRing(self): + """Tests if the class could successfully + determine if a point is inside of a polygon. + """ - Tests if the class could successfully determine if a point is inside of a polygon - """ - ring_texas = Ring([ - (-105.99835968, 31.3938179016), (-106.212753296, 31.4781284332), (-106.383041382, 31.7337627411), - (-106.538970947, 31.7861976624), (-106.614440918, 31.8177280426), (-106.615577698, 31.8446350098), - (-106.643531799, 31.8951015472), (-106.633201599, 31.9139976501), (-106.63205719, 31.9721183777), - (-106.649513245, 31.9802284241), (-106.623077393, 32.0009880066), (-106.377845764, 32.0006446838), - (-106.002708435, 32.0015525818), (-104.921798706, 32.0042686462), (-104.850563049, 32.0031509399), - (-104.018814087, 32.0072784424), (-103.980895996, 32.0058898926), (-103.728973389, 32.0061035156), - (-103.332092285, 32.0041542053), (-103.05796814, 32.0018997192), (-103.05519104, 32.0849952698), - (-103.059547424, 32.5154304504), (-103.048835754, 32.9535331726), (-103.042602539, 33.3777275085), - (-103.038238525, 33.5657424927), (-103.03276062, 33.8260879517), (-103.029144287, 34.3077430725), - (-103.022155762, 34.7452659607), (-103.024749756, 34.964717865), (-103.025650024, 35.1772079468), - (-103.02179718, 35.6236038208), (-103.022117615, 35.7422866821), (-103.02355957, 36.0560264587), - (-103.026802063, 36.4915657043), (-102.996917725, 36.4923439026), (-102.165222168, 36.4902076721), - (-102.034210205, 36.4929542542), (-101.620315552, 36.4920043945), (-101.089668274, 36.4880218506), - (-100.95690918, 36.4896087646), (-100.549415588, 36.4894485474), (-100.006866455, 36.4938774109), - (-100.001144409, 36.4925193787), (-99.9971542358, 36.0575485229), (-99.9977264404, 35.8837928772), - (-100.0, 35.6188087463), (-99.994354248, 35.424571991), (-99.9971847534, 35.182182312), - (-99.9960708618, 35.03099823), (-99.998878479, 34.7471847534), (-99.99609375, 34.5623207092), - (-99.9720993042, 34.5618629456), (-99.9447402954, 34.5795707703), (-99.9319076538, 34.5791091919), - (-99.8805999756, 34.5481758118), (-99.8605728149, 34.5186271667), (-99.8299331665, 34.5017776489), - (-99.7776870728, 34.4439926147), (-99.6849060059, 34.3774452209), (-99.6014480591, 34.3685569763), - (-99.5852203369, 34.3848571777), (-99.5778503418, 34.4089126587), (-99.5538635254, 34.4151802063), - (-99.5021362305, 34.4040679932), (-99.4794387817, 34.3835220337), (-99.4383773804, 34.3647041321), - (-99.4099578857, 34.3691062927), (-99.3941574097, 34.3967437744), (-99.392791748, 34.4289932251), - (-99.3642044067, 34.4501953125), (-99.3232955933, 34.4127082825), (-99.2671737671, 34.3982849121), - (-99.2541046143, 34.3682136536), (-99.2054901123, 34.331993103), (-99.1963043213, 34.3051223755), - (-99.2045974731, 34.255645752), (-99.1904830933, 34.2237358093), (-99.1761550903, 34.2127304077), - (-99.1279449463, 34.2014694214), (-99.0784301758, 34.2083587646), (-99.0352172852, 34.1989212036), - (-98.9961929321, 34.2094955444), (-98.952507019, 34.1945648193), (-98.8913421631, 34.1608200073), - (-98.8110656738, 34.1459350586), (-98.7785339355, 34.1319618225), (-98.705291748, 34.1307144165), - (-98.6822128296, 34.1499977112), (-98.6617202759, 34.1470375061), (-98.6259918213, 34.1584358215), - (-98.6072463989, 34.1513977051), (-98.5763320923, 34.1419296265), (-98.5575790405, 34.1053352356), - (-98.4995193481, 34.0664138794), (-98.4481887817, 34.0543746948), (-98.4213409424, 34.0658302307), - (-98.4071350098, 34.0824546814), (-98.390953064, 34.0872306824), (-98.3842544556, 34.1157798767), - (-98.350402832, 34.1421203613), (-98.3204879761, 34.1394195557), (-98.2770004272, 34.1228713989), - (-98.1728439331, 34.1153678894), (-98.1368637085, 34.1384315491), (-98.1148681641, 34.1489868164), - (-98.0941238403, 34.1345558167), (-98.1106872559, 34.0698204041), (-98.0862045288, 34.0053138733), - (-98.055557251, 33.9897994995), (-98.0234909058, 33.9869842529), (-97.9826812744, 34.001285553), - (-97.9502258301, 33.9711608887), (-97.9477539062, 33.9597511292), (-97.9629974365, 33.9486503601), - (-97.9506835938, 33.9325180054), (-97.9761276245, 33.9120521545), (-97.9763793945, 33.9025039673), - (-97.9547348022, 33.883480072), (-97.9090652466, 33.8740234375), (-97.8697509766, 33.8551139832), - (-97.8525466919, 33.8570709229), (-97.7902069092, 33.8904571533), (-97.756362915, 33.9320983887), - (-97.729019165, 33.9392929077), (-97.7042617798, 33.9715461731), (-97.6710662842, 33.9886131287), - (-97.6001815796, 33.9694366455), (-97.5923538208, 33.9178848267), (-97.575668335, 33.9025306702), - (-97.5545883179, 33.9039039612), (-97.5182037354, 33.9167709351), (-97.4775314331, 33.9077072144), - (-97.4627609253, 33.902381897), (-97.4570617676, 33.8904304504), (-97.4527359009, 33.8362121582), - (-97.410118103, 33.8207092285), (-97.363319397, 33.8310241699), (-97.3418045044, 33.8619155884), - (-97.314956665, 33.8703918457), (-97.3140869141, 33.8958396912), (-97.272277832, 33.8725738525), - (-97.2639083862, 33.8587303162), (-97.2506866455, 33.8729705811), (-97.2460632324, 33.8942375183), - (-97.2113342285, 33.9056892395), (-97.1877670288, 33.8992042542), (-97.1641693115, 33.8631477356), - (-97.1685943604, 33.8477935791), (-97.1950149536, 33.8361587524), (-97.2083206177, 33.8196487427), - (-97.189163208, 33.7527694702), (-97.1524734497, 33.7286682129), (-97.115562439, 33.725933075), - (-97.0904998779, 33.7316703796), (-97.0834655762, 33.7424125671), (-97.0876693726, 33.8075714111), - (-97.0500259399, 33.8234481812), (-97.0782470703, 33.8378105164), (-97.0821762085, 33.8511009216), - (-97.0708999634, 33.8567276001), (-97.0255966187, 33.8405609131), (-97.0058517456, 33.8505134583), - (-96.9877090454, 33.8764228821), (-96.9878616333, 33.9442024231), (-96.9681854248, 33.9373207092), - (-96.9362030029, 33.9478492737), (-96.9295654297, 33.9617729187), (-96.8984527588, 33.9500274658), - (-96.882850647, 33.9245910645), (-96.8789367676, 33.8840026855), (-96.8610153198, 33.8616790771), - (-96.8440093994, 33.8580322266), (-96.8141174316, 33.8717689514), (-96.7975921631, 33.8699493408), - (-96.7488250732, 33.8317375183), (-96.7116775513, 33.8338699341), (-96.6933822632, 33.8479042053), - (-96.6777038574, 33.9043235779), (-96.6662368774, 33.9135437012), (-96.584487915, 33.8961448669), - (-96.6141662598, 33.8628997803), (-96.6011962891, 33.842956543), (-96.5621337891, 33.8254203796), - (-96.5105743408, 33.8156852722), (-96.5007476807, 33.7880897522), (-96.4873733521, 33.7781295776), - (-96.4194641113, 33.7883262634), (-96.3708190918, 33.7403945923), (-96.3475875854, 33.7055282593), - (-96.3162765503, 33.7018013), (-96.3007888794, 33.714050293), (-96.289680481, 33.761932373), - (-96.2780761719, 33.7733879089), (-96.2125473022, 33.756690979), (-96.1870269775, 33.7585830688), - (-96.1688156128, 33.7693557739), (-96.161315918, 33.7982292175), (-96.141418457, 33.8203201294), - (-96.1545181274, 33.8239440918), (-96.1807250977, 33.8084335327), (-96.1831283569, 33.8157920837), - (-96.1692047119, 33.8289833069), (-96.1489639282, 33.8355903625), (-96.1094436646, 33.8292579651), - (-96.0915222168, 33.8445777893), (-96.0479736328, 33.8412780762), (-96.0267486572, 33.8560218811), - (-96.0140686035, 33.8442077637), (-96.0017929077, 33.8569793701), (-96.0026168823, 33.8733901978), - (-95.9942092896, 33.875377655), (-95.977394104, 33.8579521179), (-95.9587631226, 33.8650398254), - (-95.943069458, 33.8899726868), (-95.9330749512, 33.8905296326), (-95.8465576172, 33.8410377502), - (-95.8259735107, 33.8430252075), (-95.7954788208, 33.8646736145), (-95.7685165405, 33.8514022827), - (-95.764251709, 33.8790054321), (-95.7606964111, 33.8934402466), (-95.7468643188, 33.9033966064), - (-95.6997070312, 33.8948249817), (-95.6334915161, 33.9201049805), (-95.6129837036, 33.9202384949), - (-95.6148300171, 33.9366912842), (-95.6060714722, 33.9445533752), (-95.5627746582, 33.9360733032), - (-95.5463180542, 33.9040336609), (-95.5195770264, 33.9066429138), (-95.5267333984, 33.8978157043), - (-95.547492981, 33.893157959), (-95.5440368652, 33.8857421875), (-95.5128860474, 33.8977355957), - (-95.4988555908, 33.8817176819), (-95.4681243896, 33.8864326477), (-95.4516067505, 33.8657531738), - (-95.330039978, 33.8709182739), (-95.336227417, 33.8971138), (-95.3019561768, 33.8866233826), - (-95.2864303589, 33.8869018555), (-95.2773513794, 33.9179382324), (-95.2636184692, 33.8978004456), - (-95.2509918213, 33.9050216675), (-95.2512893677, 33.9364433289), (-95.2340393066, 33.9648628235), - (-95.1483154297, 33.9435462952), (-95.1279678345, 33.9408683777), (-95.1266784668, 33.9171447754), - (-95.1192245483, 33.9122810364), (-95.0953598022, 33.9217376709), (-95.0822677612, 33.9184532166), - (-95.0897140503, 33.8969154358), (-95.0836029053, 33.8884620667), (-95.0634765625, 33.9176483154), - (-95.0631408691, 33.8966941833), (-95.0428619385, 33.8844451904), (-95.037361145, 33.8664512634), - (-95.0127716064, 33.8699455261), (-94.9892807007, 33.8561820984), (-94.9687042236, 33.8662147522), - (-94.9599075317, 33.8480758667), (-94.9398880005, 33.8408241272), (-94.9403991699, 33.8158073425), - (-94.9182357788, 33.8161964417), (-94.9085464478, 33.803478241), (-94.9138793945, 33.7895965576), - (-94.8816375732, 33.7749633789), (-94.8578796387, 33.7493209839), (-94.8191604614, 33.7494049072), - (-94.8032226562, 33.7395820618), (-94.7835083008, 33.7532615662), (-94.764175415, 33.7528419495), - (-94.7820281982, 33.7422676086), (-94.7831573486, 33.7336654663), (-94.7497711182, 33.73670578), - (-94.7627182007, 33.716796875), (-94.7421112061, 33.7190475464), (-94.7544784546, 33.7077713013), - (-94.7416534424, 33.7012672424), (-94.6909866333, 33.6902885437), (-94.6684570312, 33.6965370178), - (-94.6554794312, 33.6922912598), (-94.6443252563, 33.6776504517), (-94.6679534912, 33.671459198), - (-94.6694259644, 33.6660614014), (-94.6585388184, 33.6637382507), (-94.6387634277, 33.6701049805), - (-94.6317367554, 33.6838989258), (-94.600944519, 33.6656074524), (-94.585105896, 33.678981781), - (-94.5785064697, 33.6704711914), (-94.5607223511, 33.671913147), (-94.5652084351, 33.6630134583), - (-94.5851593018, 33.6621322632), (-94.5883865356, 33.6554489136), (-94.576461792, 33.6521568298), - (-94.5454177856, 33.6616210938), (-94.5419311523, 33.6482467651), (-94.5621948242, 33.642829895), - (-94.5621490479, 33.6355361938), (-94.5501937866, 33.6326942444), (-94.5179901123, 33.6430091858), - (-94.5250549316, 33.6210212708), (-94.510559082, 33.6308097839), (-94.5006103516, 33.623046875), - (-94.4764862061, 33.6319656372), (-94.4359130859, 33.6364440918), (-94.4363327026, 33.6168441772), - (-94.4515533447, 33.604347229), (-94.4433288574, 33.5965042114), (-94.4284667969, 33.5971412659), - (-94.4065704346, 33.5734863281), (-94.3934173584, 33.5749588013), (-94.3791122437, 33.5933265686), - (-94.3706283569, 33.5900421143), (-94.3723068237, 33.5726623535), (-94.3952636719, 33.5603027344), - (-94.3707580566, 33.5476837158), (-94.3287506104, 33.573135376), (-94.3023834229, 33.5569343567), - (-94.2988204956, 33.5798530579), (-94.2789840698, 33.5893325806), (-94.2720794678, 33.5846061707), - (-94.2745437622, 33.5617370605), (-94.2372360229, 33.5924224854), (-94.2230377197, 33.5857200623), - (-94.2353668213, 33.5615348816), (-94.2108840942, 33.5579872131), (-94.2053451538, 33.5850791931), - (-94.1595153809, 33.5937728882), (-94.155166626, 33.5670852661), (-94.0987014771, 33.5729980469), - (-94.0866546631, 33.5839538574), (-94.0614318848, 33.5772132874), (-94.0359268188, 33.5559120178), - (-94.0365066528, 33.270324707), (-94.0387496948, 33.0232887268), (-94.0416030884, 32.8823471069), - (-94.0401992798, 32.6948127747), (-94.0352325439, 32.3892250061), (-94.0347671509, 32.1994476318), - (-94.0350646973, 31.994512558), (-94.0098876953, 31.9891338348), (-94.0043945312, 31.9779415131), - (-93.9772109985, 31.9461593628), (-93.9699859619, 31.9231643677), (-93.9357299805, 31.9094562531), - (-93.9179229736, 31.909702301), (-93.9234619141, 31.8925933838), (-93.8992614746, 31.8944549561), - (-93.8925247192, 31.8700656891), (-93.8812637329, 31.8714199066), (-93.8774032593, 31.850112915), - (-93.8648223877, 31.8172721863), (-93.8343276978, 31.8020172119), (-93.8220672607, 31.7746372223), - (-93.831161499, 31.7532806396), (-93.8099899292, 31.7303524017), (-93.8149490356, 31.7123508453), - (-93.8087692261, 31.7075653076), (-93.7922668457, 31.7113952637), (-93.8118438721, 31.6745662689), - (-93.806427002, 31.6537666321), (-93.8147277832, 31.6479663849), (-93.8195877075, 31.6180915833), - (-93.8355789185, 31.6151885986), (-93.8326187134, 31.5901832581), (-93.8163223267, 31.5771102905), - (-93.8105163574, 31.5590629578), (-93.780128479, 31.5337352753), (-93.7633056641, 31.5307235718), - (-93.747543335, 31.5377178192), (-93.7316589355, 31.5218772888), (-93.7057952881, 31.5205688477), - (-93.7189941406, 31.4954032898), (-93.7504348755, 31.4905567169), (-93.7512435913, 31.4855003357), - (-93.7267837524, 31.4594745636), (-93.6984176636, 31.4614582062), (-93.7019271851, 31.4462509155), - (-93.6870040894, 31.4381313324), (-93.6961288452, 31.4277362823), (-93.694442749, 31.4159221649), - (-93.6874923706, 31.406129837), (-93.6640167236, 31.3983287811), (-93.6610717773, 31.3723945618), - (-93.6348571777, 31.3738269806), (-93.6770401001, 31.3283863068), (-93.6815872192, 31.3126792908), - (-93.6561279297, 31.2866706848), (-93.6455917358, 31.2902622223), (-93.6308288574, 31.2739028931), - (-93.6164550781, 31.2758045197), (-93.6118774414, 31.2700328827), (-93.611000061, 31.2421875), - (-93.5905456543, 31.2296867371), (-93.6029205322, 31.1990661621), (-93.5939407349, 31.1801986694), - (-93.5769424438, 31.1721401215), (-93.5505905151, 31.1909294128), (-93.5289230347, 31.1857738495), - (-93.5269317627, 31.1780757904), (-93.5370178223, 31.1763401031), (-93.5283279419, 31.1629428864), - (-93.5441894531, 31.1591663361), (-93.5375061035, 31.132440567), (-93.5280914307, 31.1259250641), - (-93.5350875854, 31.116071701), (-93.556678772, 31.1093425751), (-93.5599822998, 31.1005363464), - (-93.5431213379, 31.094751358), (-93.5441055298, 31.0823726654), (-93.516998291, 31.0746707916), - (-93.5257415771, 31.0569801331), (-93.5072174072, 31.0389080048), (-93.5471191406, 31.0141410828), - (-93.5649414062, 31.0180625916), (-93.5678939819, 31.0129241943), (-93.5708465576, 30.9972705841), - (-93.5609512329, 30.991689682), (-93.5724563599, 30.9761772156), (-93.5486755371, 30.9701900482), - (-93.5373382568, 30.9568843842), (-93.5321884155, 30.9607315063), (-93.5256195068, 30.9358196259), - (-93.5299835205, 30.9269714355), (-93.549621582, 30.9248847961), (-93.5465164185, 30.9053344727), - (-93.5644760132, 30.9019317627), (-93.5684967041, 30.8862342834), (-93.5608444214, 30.8718795776), - (-93.5528030396, 30.8602828979), (-93.566444397, 30.8451480865), (-93.5556411743, 30.8423423767), - (-93.5506820679, 30.8283443451), (-93.5818710327, 30.8020401001), (-93.5851745605, 30.7721843719), - (-93.6184539795, 30.7457885742), (-93.6076507568, 30.7320098877), (-93.6177902222, 30.73254776), - (-93.612411499, 30.7103290558), (-93.6176071167, 30.6868019104), (-93.6599884033, 30.6728591919), - (-93.6779708862, 30.6396923065), (-93.6928787231, 30.6400413513), (-93.6845855713, 30.62342453), - (-93.6926956177, 30.6157951355), (-93.671585083, 30.5978317261), (-93.6934204102, 30.5988349915), - (-93.7178115845, 30.5873794556), (-93.717880249, 30.5681533813), (-93.7353057861, 30.5455169678), - (-93.7054595947, 30.522857666), (-93.7146377563, 30.5051136017), (-93.7072753906, 30.4962406158), - (-93.7148513794, 30.4886283875), (-93.6979751587, 30.4700469971), (-93.7034225464, 30.46251297), - (-93.6965713501, 30.4426326752), (-93.721534729, 30.4329795837), (-93.7425613403, 30.4088230133), - (-93.7549438477, 30.3817882538), (-93.747833252, 30.3674106598), (-93.7593383789, 30.35414505), - (-93.7591781616, 30.3408718109), (-93.7297744751, 30.3049163818), (-93.6992111206, 30.2973880768), - (-93.707359314, 30.2393722534), (-93.71484375, 30.2203063965), (-93.7043609619, 30.1808605194), - (-93.6961669922, 30.1756763458), (-93.6996612549, 30.1508083344), (-93.6831436157, 30.1482315063), - (-93.6859588623, 30.1412525177), (-93.698638916, 30.1412258148), (-93.6969223022, 30.1179294586), - (-93.7083816528, 30.1147403717), (-93.7158584595, 30.0956687927), (-93.7124786377, 30.0605201721), - (-93.7602005005, 30.0059642792), (-93.8572769165, 29.9906539917), (-93.8563308716, 29.9646015167), - (-93.9517669678, 29.8183631897), (-93.8349609375, 29.6745700836), (-94.0654067993, 29.6740760803), - (-94.3569946289, 29.5599002838), (-94.3770065308, 29.5519695282), (-94.6825180054, 29.4329051971), - (-94.7665481567, 29.363992691), (-94.7852478027, 29.3832607269), (-94.6819152832, 29.4751110077), - (-94.5726928711, 29.5330524445), (-94.5012817383, 29.5175228119), (-94.4697952271, 29.5567798615), - (-94.5108108521, 29.5451469421), (-94.5336990356, 29.5539836884), (-94.5644378662, 29.5789985657), - (-94.7880859375, 29.5385570526), (-94.7064208984, 29.6585159302), (-94.7002792358, 29.7545681), - (-94.7357254028, 29.7929859161), (-94.8294143677, 29.7598571777), (-94.8871612549, 29.6685390472), - (-94.9325866699, 29.6822090149), (-95.0882644653, 29.8039798737), (-95.040397644, 29.7115783691), - (-94.9893341064, 29.6797008514), (-95.0141220093, 29.5592651367), (-94.9111557007, 29.500333786), - (-94.9828109741, 29.46052742), (-94.9437561035, 29.4646816254), (-94.952507019, 29.4242343903), - (-94.913444519, 29.4201126099), (-94.9169921875, 29.4478225708), (-94.8911361694, 29.3993244171), - (-94.8153533936, 29.3709316254), (-94.8914718628, 29.3938312531), (-94.8987884521, 29.3087749481), - (-94.951133728, 29.3259220123), (-95.066368103, 29.1958770752), (-95.1605224609, 29.2000312805), - (-95.1647796631, 29.1175479889), (-95.1973419189, 29.105222702), (-95.2484054565, 28.9783916473), - (-95.5265808105, 28.8032417297), (-95.6830291748, 28.7269535065), (-95.6713180542, 28.7526817322), - (-95.7863540649, 28.7388706207), (-95.9373092651, 28.690454483), (-95.9561462402, 28.6226730347), - (-95.7021484375, 28.7189865112), (-96.2065811157, 28.4883861542), (-95.991645813, 28.5964241028), - (-95.9837493896, 28.6531333923), (-96.2375869751, 28.5713214874), (-96.2390289307, 28.5971164703), - (-96.1574707031, 28.6112308502), (-96.2404556274, 28.6348590851), (-96.1510620117, 28.7626724243), - (-96.2121734619, 28.6867198944), (-96.2859725952, 28.6617240906), (-96.2703781128, 28.7089805603), - (-96.3261566162, 28.6340885162), (-96.3641586304, 28.617980957), (-96.3917770386, 28.6702518463), - (-96.3927307129, 28.7260284424), (-96.4270858765, 28.7120132446), (-96.4496765137, 28.7550354004), - (-96.432258606, 28.6972484589), (-96.4033966064, 28.719493866), (-96.4187850952, 28.6386642456), - (-96.3753967285, 28.6100883484), (-96.4912033081, 28.5569438934), (-96.4371566772, 28.5969905853), - (-96.4543838501, 28.6559333801), (-96.4832687378, 28.5980548859), (-96.5118942261, 28.6081809998), - (-96.5117340088, 28.6495418549), (-96.5703964233, 28.6362667084), (-96.5705566406, 28.6918411255), - (-96.5722122192, 28.8081741333), (-96.5764846802, 28.6906890869), (-96.5914993286, 28.7173595428), - (-96.6465148926, 28.7141418457), (-96.6600112915, 28.6790752411), (-96.6067047119, 28.6236343384), - (-96.6103439331, 28.5589408875), (-96.5667037964, 28.574098587), (-96.486579895, 28.5062217712), - (-96.5631942749, 28.4696273804), (-96.5185012817, 28.4608268738), (-96.4765014648, 28.4994544983), - (-96.3907241821, 28.4340591431), (-96.6613082886, 28.30626297), (-96.7023620605, 28.3401966095), - (-96.7038116455, 28.3958854675), (-96.7407684326, 28.4034576416), (-96.7870941162, 28.4775066376), - (-96.8238754272, 28.449640274), (-96.7883377075, 28.4462547302), (-96.7591018677, 28.4109115601), - (-96.7753601074, 28.3916301727), (-96.8534927368, 28.4049968719), (-96.788230896, 28.3524703979), - (-96.7862701416, 28.3128585815), (-96.7933349609, 28.2713718414), (-96.7779312134, 28.2293491364), - (-96.8036880493, 28.2114467621), (-96.9509048462, 28.1143550873), (-96.9127197266, 28.2567977905), - (-96.9753036499, 28.2107505798), (-96.9410705566, 28.1867713928), (-96.9751052856, 28.1150455475), - (-97.0336151123, 28.1373977661), (-97.0235671997, 28.1997966766), (-97.1318359375, 28.1304264069), - (-97.1354141235, 28.1618099213), (-97.1679916382, 28.1594600677), (-97.1570587158, 28.1163806915), - (-97.2602844238, 28.0647239685), (-97.2412338257, 28.0486526489), (-97.2702941895, 28.025932312), - (-97.2362136841, 28.0405197144), (-97.1230773926, 28.054265976), (-97.0264053345, 28.107749939), - (-97.0238037109, 28.020236969), (-97.1146240234, 27.9153862), (-97.1954650879, 27.8122196198), - (-97.2470245361, 27.8223190308), (-97.2133407593, 27.8311100006), (-97.2834854126, 27.8711452484), - (-97.3610458374, 27.8399543762), (-97.3456192017, 27.8731784821), (-97.4793548584, 27.8529624939), - (-97.4966812134, 27.8754692078), (-97.521697998, 27.8636264801), (-97.4995346069, 27.8432426453), - (-97.4798126221, 27.8202819824), (-97.3885421753, 27.8314266205), (-97.3965606689, 27.7708396912), - (-97.3177947998, 27.7122249603), (-97.3495101929, 27.7153282166), (-97.3200149536, 27.6906337738), - (-97.3533630371, 27.6408004761), (-97.3992156982, 27.6331863403), (-97.3475036621, 27.631439209), - (-97.309211731, 27.707862854), (-97.2497940063, 27.6888313293), (-97.3314590454, 27.5623207092), - (-97.4122619629, 27.321023941), (-97.5004348755, 27.3196678162), (-97.5075378418, 27.4392147064), - (-97.5283813477, 27.3441009521), (-97.600112915, 27.3001346588), (-97.7500762939, 27.4196662903), - (-97.6800079346, 27.2943725586), (-97.7847442627, 27.2877197266), (-97.5481567383, 27.2302074432), - (-97.4272155762, 27.2651329041), (-97.5035018921, 27.0815410614), (-97.4789962769, 26.9965076447), - (-97.5685653687, 26.9778575897), (-97.558052063, 26.8460521698), (-97.4955749512, 26.7937812805), - (-97.4516983032, 26.6009845734), (-97.4258575439, 26.5182247162), (-97.4747085571, 26.4768047333), - (-97.4211883545, 26.3850593567), (-97.3686981201, 26.3590602875), (-97.3533630371, 26.1824493408), - (-97.2531204224, 26.068315506), (-97.2763214111, 26.0022754669), (-97.2130966187, 26.0090675354), - (-97.1722259521, 25.9545688629), (-97.307144165, 25.9651241302), (-97.30443573, 25.9386634827), - (-97.3809890747, 25.9170207977), (-97.3856430054, 25.8453617096), (-97.4343490601, 25.8451976776), - (-97.5900878906, 25.9332313538), (-97.5749359131, 25.9541721344), (-97.6129226685, 25.9620018005), - (-97.6479721069, 26.0234451294), (-97.8674316406, 26.0601406097), (-98.0400695801, 26.0593948364), - (-98.0763473511, 26.0346260071), (-98.0832138062, 26.0657577515), (-98.2006912231, 26.0553760529), - (-98.2919464111, 26.0981044769), (-98.2713546753, 26.1208953857), (-98.2922744751, 26.1328086853), - (-98.3279342651, 26.1116466522), (-98.3471908569, 26.1586799622), (-98.3845214844, 26.1560306549), - (-98.4533920288, 26.220911026), (-98.4885177612, 26.201543808), (-98.5999679565, 26.2604541779), - (-98.6779174805, 26.2420558929), (-98.8198318481, 26.3750705719), (-98.9088973999, 26.3603286743), - (-98.9392700195, 26.3953094482), (-99.1067276001, 26.4195308685), (-99.1014709473, 26.4883403778), - (-99.1686782837, 26.5457286835), (-99.1658172607, 26.5798892975), (-99.2855224609, 26.8573608398), - (-99.3905181885, 26.9466304779), (-99.3927154541, 26.9955501556), (-99.4550628662, 27.0286483765), - (-99.4371566772, 27.1991977692), (-99.4652709961, 27.2698841095), (-99.543586731, 27.3186531067), - (-99.4904937744, 27.4907550812), (-99.5267410278, 27.504283905), (-99.5491867065, 27.6126270294), - (-99.7144927979, 27.6615581512), (-99.8157272339, 27.7801074982), (-99.8747329712, 27.7976856232), - (-99.9418563843, 27.9868812561), (-99.993309021, 28.0034599304), (-100.096923828, 28.1542816162), - (-100.214073181, 28.2019348145), (-100.223464966, 28.2414569855), (-100.297920227, 28.2803535461), - (-100.292892456, 28.3203601837), (-100.351570129, 28.3941822052), (-100.37677002, 28.4786510468), - (-100.345802307, 28.5008106232), (-100.419532776, 28.5441913605), (-100.403175354, 28.5897331238), - (-100.497909546, 28.660987854), (-100.589790344, 28.8942222595), (-100.647224426, 28.9223499298), - (-100.668769836, 29.080072403), (-100.768608093, 29.1665706635), (-100.796989441, 29.2425022125), - (-101.009056091, 29.373254776), (-101.067359924, 29.4735527039), (-101.261428833, 29.526473999), - (-101.254585266, 29.6287498474), (-101.308929443, 29.580909729), (-101.305862427, 29.652431488), - (-101.368400574, 29.6571617126), (-101.416099548, 29.7454338074), (-101.401275635, 29.7699050903), - (-101.448425293, 29.7605857849), (-101.470466614, 29.788690567), (-101.538345337, 29.7630176544), - (-101.543952942, 29.8101196289), (-101.581489563, 29.7651500702), (-101.639671326, 29.7569599152), - (-101.759094238, 29.7871665955), (-101.805206299, 29.7799987793), (-101.819099426, 29.814125061), - (-101.924224854, 29.7885017395), (-101.973320007, 29.8187732697), (-102.063995361, 29.784570694), - (-102.324333191, 29.880115509), (-102.36756134, 29.8452892303), (-102.384796143, 29.7679462433), - (-102.503097534, 29.7854557037), (-102.551948547, 29.7495002747), (-102.576499939, 29.7782478333), - (-102.637611389, 29.7323379517), (-102.676361084, 29.7442245483), (-102.804725647, 29.5301456451), - (-102.82220459, 29.4118442535), (-102.883010864, 29.3533706665), (-102.908325195, 29.269203186), - (-102.866172791, 29.2290363312), (-102.988098145, 29.1908626556), (-103.153465271, 28.9786815643), - (-103.266586304, 29.0074539185), (-103.280349731, 28.9863739014), (-103.335517883, 29.0503387451), - (-103.375450134, 29.0321083069), (-103.474075317, 29.0721340179), (-103.526237488, 29.1466464996), - (-103.720314026, 29.1906318665), (-103.739852905, 29.230348587), (-103.782157898, 29.2297954559), - (-103.76776123, 29.2812404633), (-103.786994934, 29.2672595978), (-104.045631409, 29.328119278), - (-104.164382935, 29.4007148743), (-104.204734802, 29.484041214), (-104.377593994, 29.550611496), - (-104.535247803, 29.6794662476), (-104.577560425, 29.8079357147), (-104.674369812, 29.9092826843), - (-104.696495056, 30.057302475), (-104.674758911, 30.1489639282), (-104.702613831, 30.238489151), - (-104.813957214, 30.3504695892), (-104.806472778, 30.3764476776), (-104.852996826, 30.3922634125), - (-104.890678406, 30.5705566406), (-104.986930847, 30.6413249969), (-104.997543335, 30.6843338013), - (-105.060562134, 30.6878700256), (-105.21434021, 30.8120861053), (-105.25818634, 30.7976531982), - (-105.287597656, 30.831949234), (-105.313781738, 30.8165073395), (-105.390312195, 30.8530807495), - (-105.409065247, 30.9025096893), (-105.554382324, 30.9982852936), (-105.603218079, 31.0864276886), - (-105.769729614, 31.1707801819), (-105.99835968, 31.3938179016) - ]) + ring_texas = Ring( + [ + (-105.99835968, 31.3938179016), + (-106.212753296, 31.4781284332), + (-106.383041382, 31.7337627411), + (-106.538970947, 31.7861976624), + (-106.614440918, 31.8177280426), + (-106.615577698, 31.8446350098), + (-106.643531799, 31.8951015472), + (-106.633201599, 31.9139976501), + (-106.63205719, 31.9721183777), + (-106.649513245, 31.9802284241), + (-106.623077393, 32.0009880066), + (-106.377845764, 32.0006446838), + (-106.002708435, 32.0015525818), + (-104.921798706, 32.0042686462), + (-104.850563049, 32.0031509399), + (-104.018814087, 32.0072784424), + (-103.980895996, 32.0058898926), + (-103.728973389, 32.0061035156), + (-103.332092285, 32.0041542053), + (-103.05796814, 32.0018997192), + (-103.05519104, 32.0849952698), + (-103.059547424, 32.5154304504), + (-103.048835754, 32.9535331726), + (-103.042602539, 33.3777275085), + (-103.038238525, 33.5657424927), + (-103.03276062, 33.8260879517), + (-103.029144287, 34.3077430725), + (-103.022155762, 34.7452659607), + (-103.024749756, 34.964717865), + (-103.025650024, 35.1772079468), + (-103.02179718, 35.6236038208), + (-103.022117615, 35.7422866821), + (-103.02355957, 36.0560264587), + (-103.026802063, 36.4915657043), + (-102.996917725, 36.4923439026), + (-102.165222168, 36.4902076721), + (-102.034210205, 36.4929542542), + (-101.620315552, 36.4920043945), + (-101.089668274, 36.4880218506), + (-100.95690918, 36.4896087646), + (-100.549415588, 36.4894485474), + (-100.006866455, 36.4938774109), + (-100.001144409, 36.4925193787), + (-99.9971542358, 36.0575485229), + (-99.9977264404, 35.8837928772), + (-100.0, 35.6188087463), + (-99.994354248, 35.424571991), + (-99.9971847534, 35.182182312), + (-99.9960708618, 35.03099823), + (-99.998878479, 34.7471847534), + (-99.99609375, 34.5623207092), + (-99.9720993042, 34.5618629456), + (-99.9447402954, 34.5795707703), + (-99.9319076538, 34.5791091919), + (-99.8805999756, 34.5481758118), + (-99.8605728149, 34.5186271667), + (-99.8299331665, 34.5017776489), + (-99.7776870728, 34.4439926147), + (-99.6849060059, 34.3774452209), + (-99.6014480591, 34.3685569763), + (-99.5852203369, 34.3848571777), + (-99.5778503418, 34.4089126587), + (-99.5538635254, 34.4151802063), + (-99.5021362305, 34.4040679932), + (-99.4794387817, 34.3835220337), + (-99.4383773804, 34.3647041321), + (-99.4099578857, 34.3691062927), + (-99.3941574097, 34.3967437744), + (-99.392791748, 34.4289932251), + (-99.3642044067, 34.4501953125), + (-99.3232955933, 34.4127082825), + (-99.2671737671, 34.3982849121), + (-99.2541046143, 34.3682136536), + (-99.2054901123, 34.331993103), + (-99.1963043213, 34.3051223755), + (-99.2045974731, 34.255645752), + (-99.1904830933, 34.2237358093), + (-99.1761550903, 34.2127304077), + (-99.1279449463, 34.2014694214), + (-99.0784301758, 34.2083587646), + (-99.0352172852, 34.1989212036), + (-98.9961929321, 34.2094955444), + (-98.952507019, 34.1945648193), + (-98.8913421631, 34.1608200073), + (-98.8110656738, 34.1459350586), + (-98.7785339355, 34.1319618225), + (-98.705291748, 34.1307144165), + (-98.6822128296, 34.1499977112), + (-98.6617202759, 34.1470375061), + (-98.6259918213, 34.1584358215), + (-98.6072463989, 34.1513977051), + (-98.5763320923, 34.1419296265), + (-98.5575790405, 34.1053352356), + (-98.4995193481, 34.0664138794), + (-98.4481887817, 34.0543746948), + (-98.4213409424, 34.0658302307), + (-98.4071350098, 34.0824546814), + (-98.390953064, 34.0872306824), + (-98.3842544556, 34.1157798767), + (-98.350402832, 34.1421203613), + (-98.3204879761, 34.1394195557), + (-98.2770004272, 34.1228713989), + (-98.1728439331, 34.1153678894), + (-98.1368637085, 34.1384315491), + (-98.1148681641, 34.1489868164), + (-98.0941238403, 34.1345558167), + (-98.1106872559, 34.0698204041), + (-98.0862045288, 34.0053138733), + (-98.055557251, 33.9897994995), + (-98.0234909058, 33.9869842529), + (-97.9826812744, 34.001285553), + (-97.9502258301, 33.9711608887), + (-97.9477539062, 33.9597511292), + (-97.9629974365, 33.9486503601), + (-97.9506835938, 33.9325180054), + (-97.9761276245, 33.9120521545), + (-97.9763793945, 33.9025039673), + (-97.9547348022, 33.883480072), + (-97.9090652466, 33.8740234375), + (-97.8697509766, 33.8551139832), + (-97.8525466919, 33.8570709229), + (-97.7902069092, 33.8904571533), + (-97.756362915, 33.9320983887), + (-97.729019165, 33.9392929077), + (-97.7042617798, 33.9715461731), + (-97.6710662842, 33.9886131287), + (-97.6001815796, 33.9694366455), + (-97.5923538208, 33.9178848267), + (-97.575668335, 33.9025306702), + (-97.5545883179, 33.9039039612), + (-97.5182037354, 33.9167709351), + (-97.4775314331, 33.9077072144), + (-97.4627609253, 33.902381897), + (-97.4570617676, 33.8904304504), + (-97.4527359009, 33.8362121582), + (-97.410118103, 33.8207092285), + (-97.363319397, 33.8310241699), + (-97.3418045044, 33.8619155884), + (-97.314956665, 33.8703918457), + (-97.3140869141, 33.8958396912), + (-97.272277832, 33.8725738525), + (-97.2639083862, 33.8587303162), + (-97.2506866455, 33.8729705811), + (-97.2460632324, 33.8942375183), + (-97.2113342285, 33.9056892395), + (-97.1877670288, 33.8992042542), + (-97.1641693115, 33.8631477356), + (-97.1685943604, 33.8477935791), + (-97.1950149536, 33.8361587524), + (-97.2083206177, 33.8196487427), + (-97.189163208, 33.7527694702), + (-97.1524734497, 33.7286682129), + (-97.115562439, 33.725933075), + (-97.0904998779, 33.7316703796), + (-97.0834655762, 33.7424125671), + (-97.0876693726, 33.8075714111), + (-97.0500259399, 33.8234481812), + (-97.0782470703, 33.8378105164), + (-97.0821762085, 33.8511009216), + (-97.0708999634, 33.8567276001), + (-97.0255966187, 33.8405609131), + (-97.0058517456, 33.8505134583), + (-96.9877090454, 33.8764228821), + (-96.9878616333, 33.9442024231), + (-96.9681854248, 33.9373207092), + (-96.9362030029, 33.9478492737), + (-96.9295654297, 33.9617729187), + (-96.8984527588, 33.9500274658), + (-96.882850647, 33.9245910645), + (-96.8789367676, 33.8840026855), + (-96.8610153198, 33.8616790771), + (-96.8440093994, 33.8580322266), + (-96.8141174316, 33.8717689514), + (-96.7975921631, 33.8699493408), + (-96.7488250732, 33.8317375183), + (-96.7116775513, 33.8338699341), + (-96.6933822632, 33.8479042053), + (-96.6777038574, 33.9043235779), + (-96.6662368774, 33.9135437012), + (-96.584487915, 33.8961448669), + (-96.6141662598, 33.8628997803), + (-96.6011962891, 33.842956543), + (-96.5621337891, 33.8254203796), + (-96.5105743408, 33.8156852722), + (-96.5007476807, 33.7880897522), + (-96.4873733521, 33.7781295776), + (-96.4194641113, 33.7883262634), + (-96.3708190918, 33.7403945923), + (-96.3475875854, 33.7055282593), + (-96.3162765503, 33.7018013), + (-96.3007888794, 33.714050293), + (-96.289680481, 33.761932373), + (-96.2780761719, 33.7733879089), + (-96.2125473022, 33.756690979), + (-96.1870269775, 33.7585830688), + (-96.1688156128, 33.7693557739), + (-96.161315918, 33.7982292175), + (-96.141418457, 33.8203201294), + (-96.1545181274, 33.8239440918), + (-96.1807250977, 33.8084335327), + (-96.1831283569, 33.8157920837), + (-96.1692047119, 33.8289833069), + (-96.1489639282, 33.8355903625), + (-96.1094436646, 33.8292579651), + (-96.0915222168, 33.8445777893), + (-96.0479736328, 33.8412780762), + (-96.0267486572, 33.8560218811), + (-96.0140686035, 33.8442077637), + (-96.0017929077, 33.8569793701), + (-96.0026168823, 33.8733901978), + (-95.9942092896, 33.875377655), + (-95.977394104, 33.8579521179), + (-95.9587631226, 33.8650398254), + (-95.943069458, 33.8899726868), + (-95.9330749512, 33.8905296326), + (-95.8465576172, 33.8410377502), + (-95.8259735107, 33.8430252075), + (-95.7954788208, 33.8646736145), + (-95.7685165405, 33.8514022827), + (-95.764251709, 33.8790054321), + (-95.7606964111, 33.8934402466), + (-95.7468643188, 33.9033966064), + (-95.6997070312, 33.8948249817), + (-95.6334915161, 33.9201049805), + (-95.6129837036, 33.9202384949), + (-95.6148300171, 33.9366912842), + (-95.6060714722, 33.9445533752), + (-95.5627746582, 33.9360733032), + (-95.5463180542, 33.9040336609), + (-95.5195770264, 33.9066429138), + (-95.5267333984, 33.8978157043), + (-95.547492981, 33.893157959), + (-95.5440368652, 33.8857421875), + (-95.5128860474, 33.8977355957), + (-95.4988555908, 33.8817176819), + (-95.4681243896, 33.8864326477), + (-95.4516067505, 33.8657531738), + (-95.330039978, 33.8709182739), + (-95.336227417, 33.8971138), + (-95.3019561768, 33.8866233826), + (-95.2864303589, 33.8869018555), + (-95.2773513794, 33.9179382324), + (-95.2636184692, 33.8978004456), + (-95.2509918213, 33.9050216675), + (-95.2512893677, 33.9364433289), + (-95.2340393066, 33.9648628235), + (-95.1483154297, 33.9435462952), + (-95.1279678345, 33.9408683777), + (-95.1266784668, 33.9171447754), + (-95.1192245483, 33.9122810364), + (-95.0953598022, 33.9217376709), + (-95.0822677612, 33.9184532166), + (-95.0897140503, 33.8969154358), + (-95.0836029053, 33.8884620667), + (-95.0634765625, 33.9176483154), + (-95.0631408691, 33.8966941833), + (-95.0428619385, 33.8844451904), + (-95.037361145, 33.8664512634), + (-95.0127716064, 33.8699455261), + (-94.9892807007, 33.8561820984), + (-94.9687042236, 33.8662147522), + (-94.9599075317, 33.8480758667), + (-94.9398880005, 33.8408241272), + (-94.9403991699, 33.8158073425), + (-94.9182357788, 33.8161964417), + (-94.9085464478, 33.803478241), + (-94.9138793945, 33.7895965576), + (-94.8816375732, 33.7749633789), + (-94.8578796387, 33.7493209839), + (-94.8191604614, 33.7494049072), + (-94.8032226562, 33.7395820618), + (-94.7835083008, 33.7532615662), + (-94.764175415, 33.7528419495), + (-94.7820281982, 33.7422676086), + (-94.7831573486, 33.7336654663), + (-94.7497711182, 33.73670578), + (-94.7627182007, 33.716796875), + (-94.7421112061, 33.7190475464), + (-94.7544784546, 33.7077713013), + (-94.7416534424, 33.7012672424), + (-94.6909866333, 33.6902885437), + (-94.6684570312, 33.6965370178), + (-94.6554794312, 33.6922912598), + (-94.6443252563, 33.6776504517), + (-94.6679534912, 33.671459198), + (-94.6694259644, 33.6660614014), + (-94.6585388184, 33.6637382507), + (-94.6387634277, 33.6701049805), + (-94.6317367554, 33.6838989258), + (-94.600944519, 33.6656074524), + (-94.585105896, 33.678981781), + (-94.5785064697, 33.6704711914), + (-94.5607223511, 33.671913147), + (-94.5652084351, 33.6630134583), + (-94.5851593018, 33.6621322632), + (-94.5883865356, 33.6554489136), + (-94.576461792, 33.6521568298), + (-94.5454177856, 33.6616210938), + (-94.5419311523, 33.6482467651), + (-94.5621948242, 33.642829895), + (-94.5621490479, 33.6355361938), + (-94.5501937866, 33.6326942444), + (-94.5179901123, 33.6430091858), + (-94.5250549316, 33.6210212708), + (-94.510559082, 33.6308097839), + (-94.5006103516, 33.623046875), + (-94.4764862061, 33.6319656372), + (-94.4359130859, 33.6364440918), + (-94.4363327026, 33.6168441772), + (-94.4515533447, 33.604347229), + (-94.4433288574, 33.5965042114), + (-94.4284667969, 33.5971412659), + (-94.4065704346, 33.5734863281), + (-94.3934173584, 33.5749588013), + (-94.3791122437, 33.5933265686), + (-94.3706283569, 33.5900421143), + (-94.3723068237, 33.5726623535), + (-94.3952636719, 33.5603027344), + (-94.3707580566, 33.5476837158), + (-94.3287506104, 33.573135376), + (-94.3023834229, 33.5569343567), + (-94.2988204956, 33.5798530579), + (-94.2789840698, 33.5893325806), + (-94.2720794678, 33.5846061707), + (-94.2745437622, 33.5617370605), + (-94.2372360229, 33.5924224854), + (-94.2230377197, 33.5857200623), + (-94.2353668213, 33.5615348816), + (-94.2108840942, 33.5579872131), + (-94.2053451538, 33.5850791931), + (-94.1595153809, 33.5937728882), + (-94.155166626, 33.5670852661), + (-94.0987014771, 33.5729980469), + (-94.0866546631, 33.5839538574), + (-94.0614318848, 33.5772132874), + (-94.0359268188, 33.5559120178), + (-94.0365066528, 33.270324707), + (-94.0387496948, 33.0232887268), + (-94.0416030884, 32.8823471069), + (-94.0401992798, 32.6948127747), + (-94.0352325439, 32.3892250061), + (-94.0347671509, 32.1994476318), + (-94.0350646973, 31.994512558), + (-94.0098876953, 31.9891338348), + (-94.0043945312, 31.9779415131), + (-93.9772109985, 31.9461593628), + (-93.9699859619, 31.9231643677), + (-93.9357299805, 31.9094562531), + (-93.9179229736, 31.909702301), + (-93.9234619141, 31.8925933838), + (-93.8992614746, 31.8944549561), + (-93.8925247192, 31.8700656891), + (-93.8812637329, 31.8714199066), + (-93.8774032593, 31.850112915), + (-93.8648223877, 31.8172721863), + (-93.8343276978, 31.8020172119), + (-93.8220672607, 31.7746372223), + (-93.831161499, 31.7532806396), + (-93.8099899292, 31.7303524017), + (-93.8149490356, 31.7123508453), + (-93.8087692261, 31.7075653076), + (-93.7922668457, 31.7113952637), + (-93.8118438721, 31.6745662689), + (-93.806427002, 31.6537666321), + (-93.8147277832, 31.6479663849), + (-93.8195877075, 31.6180915833), + (-93.8355789185, 31.6151885986), + (-93.8326187134, 31.5901832581), + (-93.8163223267, 31.5771102905), + (-93.8105163574, 31.5590629578), + (-93.780128479, 31.5337352753), + (-93.7633056641, 31.5307235718), + (-93.747543335, 31.5377178192), + (-93.7316589355, 31.5218772888), + (-93.7057952881, 31.5205688477), + (-93.7189941406, 31.4954032898), + (-93.7504348755, 31.4905567169), + (-93.7512435913, 31.4855003357), + (-93.7267837524, 31.4594745636), + (-93.6984176636, 31.4614582062), + (-93.7019271851, 31.4462509155), + (-93.6870040894, 31.4381313324), + (-93.6961288452, 31.4277362823), + (-93.694442749, 31.4159221649), + (-93.6874923706, 31.406129837), + (-93.6640167236, 31.3983287811), + (-93.6610717773, 31.3723945618), + (-93.6348571777, 31.3738269806), + (-93.6770401001, 31.3283863068), + (-93.6815872192, 31.3126792908), + (-93.6561279297, 31.2866706848), + (-93.6455917358, 31.2902622223), + (-93.6308288574, 31.2739028931), + (-93.6164550781, 31.2758045197), + (-93.6118774414, 31.2700328827), + (-93.611000061, 31.2421875), + (-93.5905456543, 31.2296867371), + (-93.6029205322, 31.1990661621), + (-93.5939407349, 31.1801986694), + (-93.5769424438, 31.1721401215), + (-93.5505905151, 31.1909294128), + (-93.5289230347, 31.1857738495), + (-93.5269317627, 31.1780757904), + (-93.5370178223, 31.1763401031), + (-93.5283279419, 31.1629428864), + (-93.5441894531, 31.1591663361), + (-93.5375061035, 31.132440567), + (-93.5280914307, 31.1259250641), + (-93.5350875854, 31.116071701), + (-93.556678772, 31.1093425751), + (-93.5599822998, 31.1005363464), + (-93.5431213379, 31.094751358), + (-93.5441055298, 31.0823726654), + (-93.516998291, 31.0746707916), + (-93.5257415771, 31.0569801331), + (-93.5072174072, 31.0389080048), + (-93.5471191406, 31.0141410828), + (-93.5649414062, 31.0180625916), + (-93.5678939819, 31.0129241943), + (-93.5708465576, 30.9972705841), + (-93.5609512329, 30.991689682), + (-93.5724563599, 30.9761772156), + (-93.5486755371, 30.9701900482), + (-93.5373382568, 30.9568843842), + (-93.5321884155, 30.9607315063), + (-93.5256195068, 30.9358196259), + (-93.5299835205, 30.9269714355), + (-93.549621582, 30.9248847961), + (-93.5465164185, 30.9053344727), + (-93.5644760132, 30.9019317627), + (-93.5684967041, 30.8862342834), + (-93.5608444214, 30.8718795776), + (-93.5528030396, 30.8602828979), + (-93.566444397, 30.8451480865), + (-93.5556411743, 30.8423423767), + (-93.5506820679, 30.8283443451), + (-93.5818710327, 30.8020401001), + (-93.5851745605, 30.7721843719), + (-93.6184539795, 30.7457885742), + (-93.6076507568, 30.7320098877), + (-93.6177902222, 30.73254776), + (-93.612411499, 30.7103290558), + (-93.6176071167, 30.6868019104), + (-93.6599884033, 30.6728591919), + (-93.6779708862, 30.6396923065), + (-93.6928787231, 30.6400413513), + (-93.6845855713, 30.62342453), + (-93.6926956177, 30.6157951355), + (-93.671585083, 30.5978317261), + (-93.6934204102, 30.5988349915), + (-93.7178115845, 30.5873794556), + (-93.717880249, 30.5681533813), + (-93.7353057861, 30.5455169678), + (-93.7054595947, 30.522857666), + (-93.7146377563, 30.5051136017), + (-93.7072753906, 30.4962406158), + (-93.7148513794, 30.4886283875), + (-93.6979751587, 30.4700469971), + (-93.7034225464, 30.46251297), + (-93.6965713501, 30.4426326752), + (-93.721534729, 30.4329795837), + (-93.7425613403, 30.4088230133), + (-93.7549438477, 30.3817882538), + (-93.747833252, 30.3674106598), + (-93.7593383789, 30.35414505), + (-93.7591781616, 30.3408718109), + (-93.7297744751, 30.3049163818), + (-93.6992111206, 30.2973880768), + (-93.707359314, 30.2393722534), + (-93.71484375, 30.2203063965), + (-93.7043609619, 30.1808605194), + (-93.6961669922, 30.1756763458), + (-93.6996612549, 30.1508083344), + (-93.6831436157, 30.1482315063), + (-93.6859588623, 30.1412525177), + (-93.698638916, 30.1412258148), + (-93.6969223022, 30.1179294586), + (-93.7083816528, 30.1147403717), + (-93.7158584595, 30.0956687927), + (-93.7124786377, 30.0605201721), + (-93.7602005005, 30.0059642792), + (-93.8572769165, 29.9906539917), + (-93.8563308716, 29.9646015167), + (-93.9517669678, 29.8183631897), + (-93.8349609375, 29.6745700836), + (-94.0654067993, 29.6740760803), + (-94.3569946289, 29.5599002838), + (-94.3770065308, 29.5519695282), + (-94.6825180054, 29.4329051971), + (-94.7665481567, 29.363992691), + (-94.7852478027, 29.3832607269), + (-94.6819152832, 29.4751110077), + (-94.5726928711, 29.5330524445), + (-94.5012817383, 29.5175228119), + (-94.4697952271, 29.5567798615), + (-94.5108108521, 29.5451469421), + (-94.5336990356, 29.5539836884), + (-94.5644378662, 29.5789985657), + (-94.7880859375, 29.5385570526), + (-94.7064208984, 29.6585159302), + (-94.7002792358, 29.7545681), + (-94.7357254028, 29.7929859161), + (-94.8294143677, 29.7598571777), + (-94.8871612549, 29.6685390472), + (-94.9325866699, 29.6822090149), + (-95.0882644653, 29.8039798737), + (-95.040397644, 29.7115783691), + (-94.9893341064, 29.6797008514), + (-95.0141220093, 29.5592651367), + (-94.9111557007, 29.500333786), + (-94.9828109741, 29.46052742), + (-94.9437561035, 29.4646816254), + (-94.952507019, 29.4242343903), + (-94.913444519, 29.4201126099), + (-94.9169921875, 29.4478225708), + (-94.8911361694, 29.3993244171), + (-94.8153533936, 29.3709316254), + (-94.8914718628, 29.3938312531), + (-94.8987884521, 29.3087749481), + (-94.951133728, 29.3259220123), + (-95.066368103, 29.1958770752), + (-95.1605224609, 29.2000312805), + (-95.1647796631, 29.1175479889), + (-95.1973419189, 29.105222702), + (-95.2484054565, 28.9783916473), + (-95.5265808105, 28.8032417297), + (-95.6830291748, 28.7269535065), + (-95.6713180542, 28.7526817322), + (-95.7863540649, 28.7388706207), + (-95.9373092651, 28.690454483), + (-95.9561462402, 28.6226730347), + (-95.7021484375, 28.7189865112), + (-96.2065811157, 28.4883861542), + (-95.991645813, 28.5964241028), + (-95.9837493896, 28.6531333923), + (-96.2375869751, 28.5713214874), + (-96.2390289307, 28.5971164703), + (-96.1574707031, 28.6112308502), + (-96.2404556274, 28.6348590851), + (-96.1510620117, 28.7626724243), + (-96.2121734619, 28.6867198944), + (-96.2859725952, 28.6617240906), + (-96.2703781128, 28.7089805603), + (-96.3261566162, 28.6340885162), + (-96.3641586304, 28.617980957), + (-96.3917770386, 28.6702518463), + (-96.3927307129, 28.7260284424), + (-96.4270858765, 28.7120132446), + (-96.4496765137, 28.7550354004), + (-96.432258606, 28.6972484589), + (-96.4033966064, 28.719493866), + (-96.4187850952, 28.6386642456), + (-96.3753967285, 28.6100883484), + (-96.4912033081, 28.5569438934), + (-96.4371566772, 28.5969905853), + (-96.4543838501, 28.6559333801), + (-96.4832687378, 28.5980548859), + (-96.5118942261, 28.6081809998), + (-96.5117340088, 28.6495418549), + (-96.5703964233, 28.6362667084), + (-96.5705566406, 28.6918411255), + (-96.5722122192, 28.8081741333), + (-96.5764846802, 28.6906890869), + (-96.5914993286, 28.7173595428), + (-96.6465148926, 28.7141418457), + (-96.6600112915, 28.6790752411), + (-96.6067047119, 28.6236343384), + (-96.6103439331, 28.5589408875), + (-96.5667037964, 28.574098587), + (-96.486579895, 28.5062217712), + (-96.5631942749, 28.4696273804), + (-96.5185012817, 28.4608268738), + (-96.4765014648, 28.4994544983), + (-96.3907241821, 28.4340591431), + (-96.6613082886, 28.30626297), + (-96.7023620605, 28.3401966095), + (-96.7038116455, 28.3958854675), + (-96.7407684326, 28.4034576416), + (-96.7870941162, 28.4775066376), + (-96.8238754272, 28.449640274), + (-96.7883377075, 28.4462547302), + (-96.7591018677, 28.4109115601), + (-96.7753601074, 28.3916301727), + (-96.8534927368, 28.4049968719), + (-96.788230896, 28.3524703979), + (-96.7862701416, 28.3128585815), + (-96.7933349609, 28.2713718414), + (-96.7779312134, 28.2293491364), + (-96.8036880493, 28.2114467621), + (-96.9509048462, 28.1143550873), + (-96.9127197266, 28.2567977905), + (-96.9753036499, 28.2107505798), + (-96.9410705566, 28.1867713928), + (-96.9751052856, 28.1150455475), + (-97.0336151123, 28.1373977661), + (-97.0235671997, 28.1997966766), + (-97.1318359375, 28.1304264069), + (-97.1354141235, 28.1618099213), + (-97.1679916382, 28.1594600677), + (-97.1570587158, 28.1163806915), + (-97.2602844238, 28.0647239685), + (-97.2412338257, 28.0486526489), + (-97.2702941895, 28.025932312), + (-97.2362136841, 28.0405197144), + (-97.1230773926, 28.054265976), + (-97.0264053345, 28.107749939), + (-97.0238037109, 28.020236969), + (-97.1146240234, 27.9153862), + (-97.1954650879, 27.8122196198), + (-97.2470245361, 27.8223190308), + (-97.2133407593, 27.8311100006), + (-97.2834854126, 27.8711452484), + (-97.3610458374, 27.8399543762), + (-97.3456192017, 27.8731784821), + (-97.4793548584, 27.8529624939), + (-97.4966812134, 27.8754692078), + (-97.521697998, 27.8636264801), + (-97.4995346069, 27.8432426453), + (-97.4798126221, 27.8202819824), + (-97.3885421753, 27.8314266205), + (-97.3965606689, 27.7708396912), + (-97.3177947998, 27.7122249603), + (-97.3495101929, 27.7153282166), + (-97.3200149536, 27.6906337738), + (-97.3533630371, 27.6408004761), + (-97.3992156982, 27.6331863403), + (-97.3475036621, 27.631439209), + (-97.309211731, 27.707862854), + (-97.2497940063, 27.6888313293), + (-97.3314590454, 27.5623207092), + (-97.4122619629, 27.321023941), + (-97.5004348755, 27.3196678162), + (-97.5075378418, 27.4392147064), + (-97.5283813477, 27.3441009521), + (-97.600112915, 27.3001346588), + (-97.7500762939, 27.4196662903), + (-97.6800079346, 27.2943725586), + (-97.7847442627, 27.2877197266), + (-97.5481567383, 27.2302074432), + (-97.4272155762, 27.2651329041), + (-97.5035018921, 27.0815410614), + (-97.4789962769, 26.9965076447), + (-97.5685653687, 26.9778575897), + (-97.558052063, 26.8460521698), + (-97.4955749512, 26.7937812805), + (-97.4516983032, 26.6009845734), + (-97.4258575439, 26.5182247162), + (-97.4747085571, 26.4768047333), + (-97.4211883545, 26.3850593567), + (-97.3686981201, 26.3590602875), + (-97.3533630371, 26.1824493408), + (-97.2531204224, 26.068315506), + (-97.2763214111, 26.0022754669), + (-97.2130966187, 26.0090675354), + (-97.1722259521, 25.9545688629), + (-97.307144165, 25.9651241302), + (-97.30443573, 25.9386634827), + (-97.3809890747, 25.9170207977), + (-97.3856430054, 25.8453617096), + (-97.4343490601, 25.8451976776), + (-97.5900878906, 25.9332313538), + (-97.5749359131, 25.9541721344), + (-97.6129226685, 25.9620018005), + (-97.6479721069, 26.0234451294), + (-97.8674316406, 26.0601406097), + (-98.0400695801, 26.0593948364), + (-98.0763473511, 26.0346260071), + (-98.0832138062, 26.0657577515), + (-98.2006912231, 26.0553760529), + (-98.2919464111, 26.0981044769), + (-98.2713546753, 26.1208953857), + (-98.2922744751, 26.1328086853), + (-98.3279342651, 26.1116466522), + (-98.3471908569, 26.1586799622), + (-98.3845214844, 26.1560306549), + (-98.4533920288, 26.220911026), + (-98.4885177612, 26.201543808), + (-98.5999679565, 26.2604541779), + (-98.6779174805, 26.2420558929), + (-98.8198318481, 26.3750705719), + (-98.9088973999, 26.3603286743), + (-98.9392700195, 26.3953094482), + (-99.1067276001, 26.4195308685), + (-99.1014709473, 26.4883403778), + (-99.1686782837, 26.5457286835), + (-99.1658172607, 26.5798892975), + (-99.2855224609, 26.8573608398), + (-99.3905181885, 26.9466304779), + (-99.3927154541, 26.9955501556), + (-99.4550628662, 27.0286483765), + (-99.4371566772, 27.1991977692), + (-99.4652709961, 27.2698841095), + (-99.543586731, 27.3186531067), + (-99.4904937744, 27.4907550812), + (-99.5267410278, 27.504283905), + (-99.5491867065, 27.6126270294), + (-99.7144927979, 27.6615581512), + (-99.8157272339, 27.7801074982), + (-99.8747329712, 27.7976856232), + (-99.9418563843, 27.9868812561), + (-99.993309021, 28.0034599304), + (-100.096923828, 28.1542816162), + (-100.214073181, 28.2019348145), + (-100.223464966, 28.2414569855), + (-100.297920227, 28.2803535461), + (-100.292892456, 28.3203601837), + (-100.351570129, 28.3941822052), + (-100.37677002, 28.4786510468), + (-100.345802307, 28.5008106232), + (-100.419532776, 28.5441913605), + (-100.403175354, 28.5897331238), + (-100.497909546, 28.660987854), + (-100.589790344, 28.8942222595), + (-100.647224426, 28.9223499298), + (-100.668769836, 29.080072403), + (-100.768608093, 29.1665706635), + (-100.796989441, 29.2425022125), + (-101.009056091, 29.373254776), + (-101.067359924, 29.4735527039), + (-101.261428833, 29.526473999), + (-101.254585266, 29.6287498474), + (-101.308929443, 29.580909729), + (-101.305862427, 29.652431488), + (-101.368400574, 29.6571617126), + (-101.416099548, 29.7454338074), + (-101.401275635, 29.7699050903), + (-101.448425293, 29.7605857849), + (-101.470466614, 29.788690567), + (-101.538345337, 29.7630176544), + (-101.543952942, 29.8101196289), + (-101.581489563, 29.7651500702), + (-101.639671326, 29.7569599152), + (-101.759094238, 29.7871665955), + (-101.805206299, 29.7799987793), + (-101.819099426, 29.814125061), + (-101.924224854, 29.7885017395), + (-101.973320007, 29.8187732697), + (-102.063995361, 29.784570694), + (-102.324333191, 29.880115509), + (-102.36756134, 29.8452892303), + (-102.384796143, 29.7679462433), + (-102.503097534, 29.7854557037), + (-102.551948547, 29.7495002747), + (-102.576499939, 29.7782478333), + (-102.637611389, 29.7323379517), + (-102.676361084, 29.7442245483), + (-102.804725647, 29.5301456451), + (-102.82220459, 29.4118442535), + (-102.883010864, 29.3533706665), + (-102.908325195, 29.269203186), + (-102.866172791, 29.2290363312), + (-102.988098145, 29.1908626556), + (-103.153465271, 28.9786815643), + (-103.266586304, 29.0074539185), + (-103.280349731, 28.9863739014), + (-103.335517883, 29.0503387451), + (-103.375450134, 29.0321083069), + (-103.474075317, 29.0721340179), + (-103.526237488, 29.1466464996), + (-103.720314026, 29.1906318665), + (-103.739852905, 29.230348587), + (-103.782157898, 29.2297954559), + (-103.76776123, 29.2812404633), + (-103.786994934, 29.2672595978), + (-104.045631409, 29.328119278), + (-104.164382935, 29.4007148743), + (-104.204734802, 29.484041214), + (-104.377593994, 29.550611496), + (-104.535247803, 29.6794662476), + (-104.577560425, 29.8079357147), + (-104.674369812, 29.9092826843), + (-104.696495056, 30.057302475), + (-104.674758911, 30.1489639282), + (-104.702613831, 30.238489151), + (-104.813957214, 30.3504695892), + (-104.806472778, 30.3764476776), + (-104.852996826, 30.3922634125), + (-104.890678406, 30.5705566406), + (-104.986930847, 30.6413249969), + (-104.997543335, 30.6843338013), + (-105.060562134, 30.6878700256), + (-105.21434021, 30.8120861053), + (-105.25818634, 30.7976531982), + (-105.287597656, 30.831949234), + (-105.313781738, 30.8165073395), + (-105.390312195, 30.8530807495), + (-105.409065247, 30.9025096893), + (-105.554382324, 30.9982852936), + (-105.603218079, 31.0864276886), + (-105.769729614, 31.1707801819), + (-105.99835968, 31.3938179016), + ] + ) qtssr_texas = QuadTreeStructureSingleRing(ring_texas) points = [ - [-96.83201838665211, 30.43633054583931, True], [-94.97179271816618, 30.46609834459278, True], - [-96.87911915799319, 29.002783392404453, True], [-104.03817768478906, 26.080885130466065, False], - [-101.05107033713017, 26.110092672074, False], [-101.90984946051287, 30.2673660562615, True], - [-104.2666097673043, 26.638892954348243, False], [-99.00209313228278, 29.312408039204637, True], - [-96.76821760997925, 26.53415999513266, False], [-103.60734801725461, 25.989562175464958, False], - [-105.52663947372852, 35.59616356954213, False], [-99.73190928401823, 36.34065757158246, False], - [-94.28455658519579, 30.510396248421543, True], [-98.287902523108, 32.42446924695663, True], - [-98.36319041112684, 26.836620743656326, True], [-100.58823300352246, 28.287422480123738, False], - [-106.09575378559609, 29.647073739534953, False], [-96.03197805726944, 34.67673925196511, False], - [-106.13189503506499, 28.293328347459486, False], [-105.6163099497765, 34.642876249285806, False], - [-102.1699840394609, 32.64792829807019, True], [-102.19213396201079, 31.137813785847744, True], - [-96.8832056156457, 33.584067733298724, True], [-105.47157574361026, 33.89108923934286, False], - [-101.53889206881809, 27.73095057942401, False], [-95.0730796759137, 26.09464277833887, False], - [-96.45795032815394, 30.22238046821291, True], [-99.47479689046119, 27.519862201810025, True], - [-104.95530088407097, 34.46894379454478, False], [-99.64935392553515, 27.166719715093752, False], - [-101.8740013838435, 35.11537675606412, True], [-99.07039344683601, 33.64951303142618, True], - [-106.60595918307385, 29.64471250102584, False], [-97.20030616424087, 32.31820772186446, True], - [-96.23784236099425, 27.638993220475054, False], [-105.93765675735541, 35.89412147712274, False], - [-97.17726496423283, 28.55515333886519, True], [-101.41972932590615, 26.609185193916016, False], - [-101.84272804452128, 31.047193869577697, True], [-95.92485433657428, 33.9254034938182, False], - [-98.09279175575493, 31.486295362051568, True], [-100.77512720701931, 34.5423023937584, True], - [-105.99024440024378, 32.444411669816, False], [-101.4742991680627, 36.37442042029491, True], - [-99.91406680649878, 30.44981434916269, True], [-106.59351726840352, 27.830675040946844, False], - [-100.50108029331473, 28.08692086210316, False], [-106.22266727042927, 28.257938478160245, False], - [-99.16766510789698, 34.05594801235725, True], [-98.81327349833023, 34.53578673189781, False], - [-95.75835434832825, 35.97127466623442, False], [-95.87872844572516, 34.1792497663974, False], - [-97.67138045660111, 28.85228239075667, True], [-103.7493116963675, 29.910102031100415, True], - [-97.9552991722866, 35.50168101442732, False], [-101.99173352350167, 33.16196427356835, True], - [-99.69108575797604, 26.58570279334035, False], [-96.1383698450465, 30.01380467777652, True], - [-96.65345956191142, 28.13722809686032, False], [-102.19812919352621, 27.653939350819577, False], - [-106.17852559635664, 34.30676459237532, False], [-97.12171059588019, 32.98547742358788, True], - [-97.63495481018265, 31.052912731492313, True], [-103.04179423578454, 32.565022698580854, True], - [-99.7663361476778, 33.93929224333747, True], [-93.96695034930963, 33.64792052285788, False], - [-106.43489898416725, 27.00081934085768, False], [-98.61144241454609, 26.091546414019007, False], - [-97.62297026159148, 29.91547499019296, True], [-102.81093959843548, 31.775967846663903, True], - [-98.92280973197468, 34.706031694183864, False], [-105.82454817097681, 33.60768142292876, False], - [-104.1703913008495, 26.30423978112628, False], [-97.03821689475404, 25.891523519897085, False], - [-95.96948383690238, 28.06242640122303, False], [-96.20743197977514, 32.96986917934692, True], - [-101.84781525566628, 30.91982689926289, True], [-96.31807840035235, 31.527410535260906, True], - [-100.8781500555231, 34.8227970125657, True], [-94.26355042343864, 30.94843753499924, True], - [-97.3429146102502, 34.73686841121185, False], [-98.07753472284375, 29.552925455931927, True], - [-105.04690710593536, 27.132667240961553, False], [-99.87850355720977, 32.06195375035851, True], - [-96.53670297300587, 26.908808751199025, False], [-101.25665086243849, 34.48897040535931, True], - [-97.62841641211763, 34.510828859444, False], [-98.26146628715239, 29.30444627019776, True], - [-94.0424931438063, 34.024181902771446, False], [-95.32764174246178, 29.026516144561093, True], - [-94.79487469005772, 34.064778827088944, False], [-96.8217807635728, 32.9449205143214, True], - [-102.65783000325504, 31.187142262599163, True], [-102.73499640672506, 28.241956569934032, False], - [-106.57423647984751, 26.21365468361891, False], [-101.18188053173569, 35.856064735642605, True], - [-103.87152954646331, 26.239969281258677, False], [-105.21761650869897, 27.276124133480618, False], - [-96.58824456554876, 36.01267677418892, False], [-99.10920560787146, 32.43164577872433, True], - [-101.93795697189412, 31.620064217834788, True], [-105.78051688469152, 34.39769579781999, False], - [-98.13520908953838, 28.15597786510518, True], [-103.44661785399717, 34.94625679402945, False], - [-93.5839550242031, 27.799377395246868, False], [-103.09029902940127, 33.20610297263985, False], - [-97.23285674761303, 28.03274042046823, True], [-95.34132538021933, 25.846590059853146, False], - [-101.75354725398458, 29.976564072236787, True], [-102.8028884021146, 36.14653256975264, True], - [-105.23453544451979, 35.083822581361105, False], [-95.02779486726217, 28.039905574389728, False], - [-105.19965155638648, 29.896855738726387, False], [-105.04729708179732, 34.59962724881616, False], - [-93.62110240741019, 32.29972687153207, False], [-101.91706806513982, 32.22042821274782, True], - [-98.7507642387652, 28.373512157202324, True], [-98.67837171745543, 29.31049604748274, True], - [-101.4835726546693, 35.24105797268996, True], [-99.61534262189332, 34.664146656697135, False], - [-102.77162178290662, 31.477015077025758, True], [-98.14965646419336, 35.506326174334816, False], - [-100.35687795352196, 36.428658182990375, True], [-93.75583478299006, 33.78374602526648, False], - [-97.35272483145133, 36.14322026779398, False], [-104.13566843512568, 32.58780129504587, False], - [-105.80951615950367, 30.548722565553994, False], [-106.36813961730384, 35.58713162312485, False], - [-97.66809642868546, 34.13059807529114, False], [-97.61612010157164, 26.31880576825889, True], - [-105.10689839575184, 27.13050661175676, False], [-94.42639614961973, 28.61525052001613, False], - [-101.0258419742403, 29.912378788493154, True], [-100.85598848851129, 27.83703095635782, False], - [-94.6443199603266, 29.977477497837175, True], [-101.16783935327581, 32.03976620169659, True], - [-95.78686711235261, 34.37099939418539, False], [-100.59275869451973, 34.36381573902874, True], - [-104.2707359051878, 33.43707298914277, False], [-102.69754715561436, 30.324718877958027, True], - [-100.31772708579322, 36.03146883077079, True], [-93.84018528740714, 36.306249385961586, False], - [-97.81378337323478, 25.848331371979896, False], [-104.37867619764027, 31.688609367477817, True], - [-100.28100102840853, 36.18155424506788, True], [-106.48975872243628, 34.10471027956753, False], - [-101.4453862294541, 28.535362387187064, False], [-100.05248503701085, 35.161646972337195, True], - [-101.37197503092796, 27.85006417239032, False], [-95.00012601768513, 36.45104201985982, False], - [-94.1618133883979, 34.189947778971074, False], [-100.9407537928131, 27.309175834366915, False], - [-104.72991115996922, 27.3139116441178, False], [-98.59532436543168, 26.287325902961726, True], - [-94.46884442680549, 35.68627263027676, False], [-105.31712494151611, 35.84472703216106, False], - [-104.64932323670762, 28.699942778858926, False], [-100.34867007633471, 29.705706918470952, True], - [-93.69561095836606, 32.456548936430266, False], [-98.12884276179193, 28.360889737226003, True], - [-96.77862792020258, 28.586529000239178, True], [-106.1873382768634, 29.404306118606442, False], - [-105.67789642420705, 31.716310454187514, True], [-96.00775542585319, 28.568996970414524, False], - [-98.0904391114445, 33.41233020042991, True], [-93.82743788526335, 27.5659388943208, False], - [-98.96755649758798, 31.32502880958104, True], [-96.55622361353764, 35.016203517682726, False], - [-99.90454174449499, 34.88971778656889, False], [-93.92308221299953, 26.93602671682176, False], - [-103.02775965216946, 27.526954568149137, False], [-101.70211609579965, 33.221705586015986, True], - [-94.65140320779773, 30.12411725325112, True], [-105.48158850264392, 25.966083503119812, False], - [-95.86092717890419, 30.257238607458746, True], [-94.78025397771191, 26.33573351642334, False], - [-102.42306855603515, 31.528148123221925, True], [-101.11557151275859, 28.23290994001276, False], - [-99.23764635246289, 34.03712892161272, True], [-97.18358117231713, 30.819652476530774, True], - [-95.52114900196523, 33.71837984822948, True], [-94.80576475349369, 32.52595208303153, True], - [-105.53353349524157, 33.10076381764996, False], [-104.45067861203503, 29.792014903132056, True], - [-100.81552999585323, 31.13371713587459, True], [-98.10244603287715, 27.062939382372114, True], - [-102.8183262272355, 31.63644201139868, True], [-105.48022791238954, 33.47054002907317, False], - [-94.54700162806725, 33.3634517345777, True], [-103.52394717869927, 29.289585818953316, True], - [-100.88024068589486, 28.00254322622369, False], [-105.13298501311634, 33.585842169513036, False], - [-95.47745420853485, 34.33206192097545, False], [-93.75279262948008, 31.85387584406531, False], - [-105.6408652118954, 33.38704981277846, False], [-102.68527628279271, 26.856854067490247, False], - [-105.22798572254378, 34.52742324390015, False], [-94.25227485978478, 36.11993169557898, False], - [-95.31071217313071, 30.450554401067357, True], [-104.32309381721137, 33.941981631972624, False], - [-93.68344966428387, 28.25720980205315, False], [-103.11762381325748, 33.05126426779246, False], - [-95.17637994183367, 36.37368703601287, False], [-101.09466869744112, 29.378868777118257, False], - [-102.3244768566622, 29.25373813014635, False], [-105.6081974667763, 35.02231128438977, False], - [-101.45129562213593, 27.250895271383474, False], [-97.77527080825567, 29.233601979476123, True], - [-101.2708728240608, 30.929678610371226, True], [-101.17628088744974, 30.807932586287112, True], - [-98.94314936681077, 28.962959058638244, True], [-97.5297094048325, 27.24629690610976, False], - [-105.24649559460273, 31.68285402434288, True], [-95.38165074760121, 30.678277565962645, True], - [-101.5405440205774, 35.75938014879534, True], [-106.30162345059492, 32.35717712927482, False], - [-95.82698786035043, 31.471380696415473, True], [-97.58132229591222, 26.76823235412943, True], - [-96.24754364311642, 29.148074667141763, True], [-101.60752835018079, 28.02378609076799, False], - [-93.66280978876574, 34.36888263435238, False], [-97.04083533693894, 32.07693083540003, True], - [-94.41534967058817, 34.30307828414252, False], [-94.51223027590078, 26.101565901379825, False], - [-95.37877455769014, 32.91182889409107, True], [-97.02169748832533, 34.77231790184875, False], - [-99.56938377454452, 27.98323188041011, True], [-104.71416427480177, 31.671590498127077, True], - [-104.48037657157643, 28.020929009314624, False], [-101.46582277815409, 29.245030464171972, False], - [-94.33289936897319, 29.433780707715577, False], [-102.65873114765344, 32.579375713461324, True], - [-94.71197474752655, 29.919750259392565, True], [-100.56224887904757, 30.870441757396232, True], - [-95.7680416447194, 33.381874464032556, True], [-101.30709319439809, 31.415698736346386, True], - [-94.17706664281508, 30.20919168191223, True], [-105.1986310512293, 32.2767819705482, False], - [-96.64520698321878, 32.53545316641808, True], [-101.27894696079863, 27.434282641130807, False], - [-103.96823391756168, 36.206818702766164, False], [-102.61287825142863, 31.52883149992497, True], - [-97.43688838995867, 35.35809331749178, False], [-106.3345282700814, 26.77898252396381, False], - [-97.84563076948461, 28.326830633856254, True], [-97.10812201190659, 27.354066620755486, False], - [-102.06625125192836, 31.959360067966703, True], [-94.77064907103171, 32.49772073548828, True], - [-103.7070672713304, 26.993474458082765, False], [-104.63528508881744, 31.77175026283308, True], - [-106.20330061641941, 28.71581603545491, False], [-102.93466353815762, 36.42712467119802, True], - [-97.02548398973192, 26.47462461285108, False], [-97.92517027601997, 33.53006657932378, True], - [-104.89676410831059, 32.03756980285575, False], [-95.02216317823846, 28.250092458391666, False], - [-102.63731145928554, 30.78785522057966, True], [-101.9626891052094, 28.607495532359852, False], - [-103.43019339494695, 33.757210152142996, False], [-95.52042729675107, 34.824713652167745, False], - [-105.6806104213336, 28.64547500599634, False], [-105.01509606690256, 26.998326186544013, False], - [-104.83729623744878, 28.111330049442206, False], [-99.30736722245813, 27.44271254179918, True], - [-96.19450914007797, 26.5070841846474, False], [-96.93250711894682, 27.465441701127965, False], - [-98.05769301004028, 33.34316222525198, True], [-101.70993094422322, 35.378824775340604, True], - [-97.78065357281011, 30.791192214255453, True], [-103.85602296523946, 33.58813675270333, False], - [-96.16754367241163, 27.82134326033623, False], [-93.84173858221502, 28.644678311847933, False], - [-95.66361201153609, 33.1782437005781, True], [-96.18610593628335, 35.66871035791395, False], - [-100.05778168598985, 34.34775700022905, True], [-105.3070646714683, 26.129506259811194, False], - [-101.60474509803998, 28.301826427498735, False], [-94.05455083451157, 35.62737695624984, False], - [-93.77022392951797, 33.35636018468855, False], [-99.9983492940202, 28.616796438006656, True], - [-103.65327712041633, 35.68635542708576, False], [-94.18836108420525, 33.519234035329205, True], - [-101.16524711825508, 35.21736886367663, True], [-106.63756507835402, 31.512711118486916, False], - [-94.64418283641962, 32.24310968022579, True], [-98.48901518901792, 26.92275583383609, True], - [-105.71495874564097, 30.37875836321371, False], [-103.59489508163908, 34.07565890645147, False], - [-104.58617756264167, 34.7175307481461, False], [-94.94979738923941, 33.53027390298981, True], - [-104.79237901545315, 33.643949242871386, False], [-103.6261849261433, 26.37203494674699, False], - [-104.12072798260623, 34.037343854052246, False], [-99.3137876161763, 28.786746195806515, True], - [-96.90701800480272, 26.83567175899895, False], [-105.91609848895729, 27.17006217234686, False], - [-94.88867966602324, 29.0650764347025, False], [-99.09445254927051, 29.165294618392306, True], - [-99.59646096585762, 31.547978010714818, True], [-100.35308616902222, 27.998071701955347, False], - [-103.65741111414567, 29.929101550941933, True], [-95.56003645452608, 33.73354146467783, True], - [-101.98380104745135, 27.741893994563817, False], [-100.28801029632858, 26.08376297941383, False], - [-100.82876490121433, 27.203171781033483, False], [-99.00038368647441, 30.227734562748875, True], - [-93.99273175213636, 33.172989756016705, False], [-103.28907886290975, 30.68354258877181, True], - [-101.58265515547387, 34.05717966984305, True], [-93.61002560748278, 33.95079910028842, False], - [-104.1322621794693, 31.037441969505064, True], [-94.90790561010023, 33.6667146192015, True], - [-99.72772840949955, 28.57426803892259, True], [-96.76750823367423, 34.238866572662864, False], - [-99.11285733031394, 29.297129143702183, True], [-95.34529541329582, 34.50122580606273, False], - [-106.2015311076861, 33.04546069130247, False], [-106.6471763299225, 35.60222852490276, False], - [-101.63138803719991, 27.93647861077147, False], [-99.67286004973847, 29.000600155646495, True], - [-102.69661571801251, 30.438208192282016, True], [-96.47920563132686, 28.187453937378827, False], - [-99.15701789001724, 28.754330049547363, True], [-103.63208677741055, 31.506299316322448, True], - [-104.6247250325306, 34.777168030468545, False], [-94.50362320672285, 28.725533817363363, False], - [-98.8088667387509, 29.719091220812984, True], [-104.66480293721597, 31.36766582422802, True], - [-100.80243453385943, 29.56664951795082, True], [-104.3184052935018, 28.368505467940153, False], - [-94.99687127078006, 29.056199158312214, False], [-99.18151980311957, 31.331306403933503, True], - [-106.58306812961843, 33.83560030586354, False], [-102.34808248763466, 30.93213593104124, True], - [-96.87905436496129, 31.63403473842953, True], [-101.88482545812973, 31.346536386676735, True], - [-102.08612983365585, 33.94091937937585, True], [-97.59374438050571, 30.710749425390954, True], - [-98.468759043241, 34.887864549188656, False], [-104.5600126294423, 30.769523820716284, True], - [-105.63321064215583, 31.706723345643436, True], [-103.39803684928096, 31.205852663870417, True], - [-103.74991694223343, 34.47243593420573, False], [-103.55735795090966, 29.368257266446264, True], - [-100.53250901956653, 34.61994711930259, True], [-106.42290807028131, 29.47435749957574, False], - [-100.61630496584333, 28.492897242114427, False], [-95.45897223676457, 34.99538998873486, False], - [-100.80796423924814, 27.597779160448237, False], [-95.59903305812487, 33.53765968472729, True], - [-102.71817656483864, 33.67774382239573, True], [-100.0519819831278, 31.720152603322795, True], - [-101.86801670742807, 29.37988286278217, False], [-93.7251604108502, 33.63648932111699, False], - [-93.50731366944763, 31.66131539976032, False], [-93.91408986252142, 31.91658143504109, False], - [-99.1976965958281, 31.846542049263352, True], [-96.70181855090833, 27.823299476943752, False], - [-99.05025751191671, 30.14722485144382, True], [-101.91786175888922, 33.662393020783334, True], - [-97.18908214543966, 26.584467319788136, False], [-102.09768463635325, 27.745869776526586, False], - [-105.03784779065376, 33.35089012727154, False], [-99.75443466868418, 32.340335779846406, True], - [-102.53950776524619, 32.48219468369184, True], [-97.38249374177907, 31.70583501812896, True], - [-100.23917371385507, 34.15552160975429, True], [-100.64584910978431, 33.08767497994276, True], - [-95.54547414843782, 28.573393441826422, False], [-98.4400667166874, 29.169883280280335, True], - [-101.85722555245049, 29.99952272227504, True], [-103.40127887731586, 35.954389606004995, False], - [-106.49277204369605, 26.3739019521645, False], [-96.42482710450412, 25.935501811569573, False], - [-96.49468755444158, 26.21429802291354, False], [-103.26280296149277, 36.42505933494076, False], - [-103.23186132072851, 26.981602236536546, False], [-95.43868017247715, 26.795551518098133, False], - [-104.95759598697946, 29.274620743768665, False], [-97.4627043038694, 31.264418590173566, True], - [-99.40922174677335, 30.859355223178202, True], [-97.62450333069754, 31.860943195654983, True], - [-103.28423668185653, 31.64625933078107, True], [-97.34347509751296, 32.39948448790623, True], - [-104.78394662628456, 32.19069943780818, False], [-94.27543944274844, 28.45111869438522, False], - [-105.41902431151755, 34.098705479256964, False], [-105.84856227788723, 34.401910854922086, False], - [-105.76944559045042, 27.541157331410925, False], [-105.96625908843005, 34.60984823477864, False], - [-100.45522585613661, 35.82958048104291, True], [-95.8227101752813, 26.40044772412707, False], - [-98.14190854947867, 28.173726785556475, True], [-103.41079917326785, 33.17358912330205, False], - [-106.21380947469507, 30.673049362200253, False], [-98.6181240681384, 31.57826052940466, True], - [-105.52347449914703, 28.45331024338554, False], [-105.87199783454764, 29.269133017660426, False], - [-95.72117392272776, 32.84521613150032, True], [-103.98103037156038, 29.39830154631861, True], - [-98.84272856308685, 27.010603508995832, True], [-103.48528511610583, 32.292795962096, False], - [-101.10764837358862, 34.44067771878632, True], [-99.90899218640438, 33.864602563342665, True], - [-99.99425094006477, 29.632299612876448, True], [-104.19954114103936, 29.481293826903272, True], - [-99.1617923467798, 27.24969812956527, True], [-93.94985771467012, 33.20509824852493, False], - [-98.74589172711391, 26.59828162369087, True], [-100.74104498226976, 28.090884473461635, False], - [-102.0941642949128, 29.40228872741179, False], [-103.71745434686355, 36.07717165565938, False], - [-103.85504023598908, 35.797619278303934, False], [-98.3178181998277, 31.80247171895742, True], - [-100.46831716774525, 26.395663970107364, False], [-96.6262432609301, 33.43258969257319, True], - [-102.12872561318167, 30.9414684197057, True], [-96.10044267209415, 31.617205739677686, True], - [-102.76040876099727, 30.02029055974793, True], [-100.28567173102832, 33.71564104661008, True], - [-101.87455244042499, 35.45783748840937, True], [-103.80647075850555, 33.75958813400894, False], - [-104.07465598182498, 29.829473718048853, True], [-96.80679641483542, 34.068109206459674, False], - [-100.21922411687524, 35.55827610846517, True], [-102.18536576459445, 35.60838848797885, True], - [-100.74818339320018, 32.46468922651053, True], [-98.89381745119253, 29.84927086885224, True], - [-95.57772284816139, 34.03118772897132, False], [-102.19208387081574, 35.47422893590955, True], - [-99.31176771742929, 28.159649775138398, True], [-95.08438010592444, 30.864384073337725, True], - [-105.62858370011037, 36.04041710371988, False], [-99.50704661690466, 26.530069415053166, False], - [-102.21328017280045, 28.123746616410187, False], [-93.82909083931759, 31.8346812474552, False], - [-99.68311577900606, 33.37514101639904, True], [-93.88914553766337, 34.40543275268286, False], - [-95.49031947037534, 36.15787562082431, False], [-94.04958126924683, 25.895718131012476, False], - [-102.31183630727872, 26.195612345971853, False], [-99.74217764726765, 34.4866834996357, False], - [-101.2273998455989, 31.012399597950438, True], [-93.7170401640232, 26.52296130239048, False], - [-106.31856530763194, 30.254195818383003, False], [-93.54539750125572, 31.968279205890525, False], - [-102.06176434040876, 35.96267638467066, True], [-102.21150807644786, 31.276020170678756, True], - [-97.59134352614593, 27.758428418449625, True], [-105.6285777230377, 36.20035827010385, False], - [-99.56855691529327, 27.565199944571845, False], [-93.95512959128524, 35.50257083074824, False], - [-104.2068757288861, 25.983428900973824, False], [-104.1534590142076, 29.90389615149271, True], - [-104.75891565225955, 26.17708478969608, False], [-96.34136547606607, 26.58259616840622, False], - [-94.35505605596222, 31.363945882683545, True], [-100.75357998539742, 27.70797766258346, False], - [-96.34206621365894, 29.195743358202087, True], [-96.44632535331706, 33.578167176287465, True], - [-100.52062683482386, 28.935836235311534, True], [-103.59488192282778, 31.145329272683313, True], - [-96.21513910038252, 26.71734433601959, False], [-97.74524155506616, 35.72544238150706, False], - [-98.15072688652688, 29.65447185320712, True], [-102.94259657821668, 28.995860793431966, False], - [-98.37049290964605, 28.992239589640803, True], [-101.9333665893231, 32.042668019418784, True], - [-100.31205895270178, 28.542423199402116, True], [-102.7842119789965, 31.351234994650703, True], - [-102.82118189131612, 25.936779514285522, False], [-105.06566909806352, 34.429600751175066, False], - [-103.95414080936615, 30.5173184712075, True], [-96.41063716230997, 35.68425390782344, False], - [-94.82668338005425, 34.10595925830741, False], [-105.42861829324059, 26.89985572905742, False], - [-94.86003017402527, 32.72190652290611, True], [-96.69442705925057, 29.12593879172168, True], - [-100.57898366779487, 35.697109012290476, True], [-105.77290660716999, 34.23468690232633, False], - [-104.3849259560512, 31.337110429238777, True], [-95.21459964105358, 33.00732978946704, True], - [-95.7332150421658, 27.704415504038284, False], [-104.50875122680036, 34.91526292458323, False], - [-102.80833873160228, 31.875937580897503, True], [-104.53224880900562, 25.940603993388773, False], - [-99.6884270626213, 31.04708806366109, True], [-95.76317485487704, 35.45517519387816, False], - [-101.84342021439619, 35.834756908559946, True], [-104.04702349088515, 26.09752753366655, False], - [-103.48123590122003, 34.55695985365465, False], [-101.90736610359222, 26.198871101374856, False], - [-95.80852972334627, 26.4884711642595, False], [-106.49602165329638, 31.587480730479957, False], - [-99.11540612418378, 28.76377389466793, True], [-98.81124442220992, 27.408954979897345, True], - [-103.54081324519612, 28.238918914656047, False], [-102.8589030540497, 33.69752159861657, True], - [-100.38962812985066, 36.259455441754405, True], [-100.60205967959344, 27.45237069505359, False], - [-104.82116135978534, 33.90726029831255, False], [-95.25951953419671, 36.427532407586796, False], - [-93.75127522006922, 31.774003703956872, False], [-106.50710079332187, 32.31418914724039, False], - [-95.38754195058371, 27.686986329488576, False], [-103.48158704856718, 28.05982044881246, False], - [-94.40123367677305, 32.13802908979801, True], [-98.37091796124368, 31.394805069115378, True], - [-99.35199253682242, 29.364231843209286, True], [-100.6371809469257, 29.485414607179138, True], - [-101.65847488152119, 33.055932161413, True], [-104.75073650330286, 35.477180164221664, False], - [-100.70424003073441, 33.33233065367354, True], [-104.9829081950403, 33.49495185425366, False], - [-96.28014091653374, 30.765109918972172, True], [-102.51312299795521, 34.973490202758846, True], - [-106.4316820557197, 28.835022330467574, False], [-98.37035640871174, 29.15982666730043, True], - [-103.81743393736278, 33.33070497195332, False], [-102.38044273457957, 27.128328755756332, False], - [-105.83310224715474, 32.69799499508526, False], [-104.47875333696165, 33.08218160363957, False], - [-105.08315111607386, 27.5499535808647, False], [-102.50315445382816, 32.952707100728176, True], - [-106.34702977388169, 28.350530651265025, False], [-100.7324706006535, 28.110070428450136, False], - [-98.89057718530617, 30.501635222091867, True], [-93.7973707158511, 30.332449685439883, True], - [-100.50817745675471, 35.933620707046416, True], [-93.62331898394628, 28.011415151343442, False], - [-102.40168198643366, 28.038346452109714, False], [-98.76386270364078, 32.824244011949546, True], - [-98.63890075873523, 28.426721830307706, True], [-103.06753906192446, 27.182546407018886, False], - [-103.5149916483893, 32.22702185824897, False], [-96.92704846269056, 33.56573720072518, True], - [-98.59191146293448, 26.717454841499933, True], [-98.21728189059557, 30.499991381612325, True], - [-98.96569953382873, 34.09349857812645, True], [-98.81645597171263, 31.538513035336027, True], - [-103.99810746775411, 34.572232011339544, False], [-97.1971994402299, 33.01231694977086, True], - [-97.35525400352937, 27.593348250877636, True], [-95.61047936363322, 32.915418129253844, True], - [-105.77325410471146, 32.220934743164854, False], [-100.31130282858123, 31.053207323928227, True], - [-94.27435346222804, 29.717111545751255, True], [-99.36518149272568, 29.81842676057489, True], - [-102.4595465810425, 29.78052913717334, True], [-104.93563833675945, 36.1553390615043, False], - [-96.67331825738593, 33.795256849340824, True], [-104.13912762449462, 28.90285335911741, False], - [-99.85650514030851, 27.082576996579743, False], [-95.46433062216431, 26.31189218916502, False], - [-100.84562917395462, 26.46339943905897, False], [-94.75077980314441, 30.746795428982928, True], - [-98.38741630868877, 32.8939361913369, True], [-101.50339387491944, 33.409893596597634, True], - [-102.8838322429055, 33.550509813464714, True], [-94.4241089162888, 29.987534160102904, True], - [-96.97724045199526, 29.413237018802626, True], [-103.26463086689401, 35.69959960363469, False], - [-104.22583858596425, 35.32625561149897, False], [-102.28802601849752, 35.31386927276366, True], - [-101.0349738411682, 25.896953488838804, False], [-103.9839821999459, 33.2679016367904, False], - [-105.01149642855964, 34.24130005689306, False], [-104.86876474825357, 35.97880497285345, False], - [-98.95300311760788, 28.23951953232239, True], [-106.44607553528402, 28.99701327445984, False], - [-97.15665622011281, 33.802655749490576, False], [-104.88728768211863, 26.19423408399297, False], - [-98.2868092522052, 26.77958013643795, True], [-96.30475465159665, 25.886256031524383, False], - [-93.96185258306615, 30.262131379160092, True], [-95.98431071502645, 34.074476976239524, False], - [-99.34592870499611, 31.77796555029789, True], [-101.23448890900838, 35.02325060675137, True], - [-96.60311950987519, 26.520767851753252, False], [-103.30098695011493, 33.95932432365544, False], - [-97.20833874248723, 30.443771327358455, True], [-100.49276808544384, 30.563391121718663, True], - [-106.00844672588474, 27.67304591354748, False], [-94.68045487444002, 28.380941225789602, False], - [-106.32581808918103, 27.457360510530357, False], [-103.90944733413775, 33.75181119086094, False], - [-96.62055409685594, 30.911602145241257, True], [-105.08332076154659, 35.84175548182445, False], - [-106.46727840740928, 30.97311215593337, False], [-98.24608713290009, 26.87548489704331, True], - [-99.99394976092111, 35.372523026676305, False], [-101.04275569324, 26.378571750355146, False], - [-99.49808015597847, 29.0197284725707, True], [-100.04278394100143, 31.138487871928806, True], - [-93.77580092670675, 28.679720562816012, False], [-105.69434962210484, 35.48380415396366, False], - [-94.76833576200043, 34.77440737306939, False], [-93.55059692835445, 32.93611160385383, False], - [-96.16637525156335, 35.14861120144762, False], [-102.57674991354982, 33.57167753159161, True], - [-104.50984249707894, 27.845184825434426, False], [-102.46681259074438, 32.876290517890325, True], - [-103.79635335230255, 34.68136206176115, False], [-97.37101719424614, 34.331617955613964, False], - [-93.78338486646257, 29.1736195506313, False], [-98.653709287249, 30.255774672109183, True], - [-104.78162076886001, 36.14023266162845, False], [-95.30745598333526, 26.15568755210897, False], - [-96.19795914229807, 29.783289215723965, True], [-93.97200790057319, 30.34872478231179, True], - [-102.75807129557354, 32.02568271699399, True], [-95.63613363239972, 30.17575679938203, True], - [-95.74058254060239, 27.830132693387228, False], [-97.28993038017775, 33.34058580949793, True], - [-94.782421253055, 26.37055251439716, False], [-99.01579664350437, 33.29078588254949, True], - [-97.2079339911563, 27.123479789817104, False], [-96.66399830213123, 35.57917944458788, False], - [-104.00378871415248, 33.03909098032557, False], [-98.18333736354995, 33.40304952690905, True], - [-99.51409889042824, 28.352125049514296, True], [-96.39878389524632, 33.80913094147424, False], - [-97.95457727578372, 31.304986453423908, True], [-105.41519123044006, 31.17900852073914, True], - [-102.55823249490591, 28.227952215383333, False], [-105.22740902391834, 28.00912246742774, False], - [-105.4327613051658, 30.42094293937215, False], [-99.8642636892311, 34.741799628413524, False], - [-104.07069003771045, 30.561044257056018, True], [-105.71580939231154, 34.73255277750661, False], - [-98.92331393934845, 34.757305489096225, False], [-95.1451471669803, 35.59262098012566, False], - [-100.78929431916742, 35.83001258653612, True], [-106.15538551006625, 33.88884085609275, False], - [-97.78557980515986, 33.9896913074551, False], [-94.43404183378551, 31.976310799398128, True], - [-102.77500581970122, 30.636815025506447, True], [-105.67967176568628, 33.47402254839546, False], - [-98.79160014376332, 32.79018812677753, True], [-102.84347322545034, 28.136203054123584, False], - [-95.24784148052099, 34.22424254638486, False], [-106.31929728808254, 30.9825641984661, False], - [-102.43296109133702, 32.532070674152855, True], [-105.92145087338875, 30.381356965114943, False], - [-106.45282535347631, 35.54158851537021, False], [-95.04777932048988, 35.82906359730707, False], - [-97.42369763037011, 34.37608618524585, False], [-104.57070459903527, 33.70875609054547, False], - [-97.41632281711793, 32.92982657064401, True], [-94.66570627408036, 27.27007338775074, False], - [-99.1743643002243, 27.247623481915483, True], [-104.68950862955154, 29.870865904293723, False], - [-98.10816880312582, 28.162238589797898, True], [-93.64216163895259, 26.298614364258444, False], - [-95.19102534541413, 33.389356010635154, True], [-96.62070599059086, 30.74805978317854, True], - [-99.3386516464026, 27.906200841991534, True], [-100.8007152801228, 32.84683422029997, True], - [-106.20302284683326, 32.380361469176044, False], [-94.23655356205708, 26.324497474351357, False], - [-99.65954455695915, 35.474000933282724, False], [-94.31553240129878, 35.66875101369414, False], - [-101.83519143957733, 25.873081120803842, False], [-99.97028199969046, 34.58909164776538, False], - [-105.71652597027627, 31.489327979058995, True], [-105.79973754647258, 28.65472050301513, False], - [-104.61884498465494, 31.131621852439807, True], [-102.57111696065307, 30.114279991888235, True], - [-103.32297212967914, 35.236882653404706, False], [-100.4249645463674, 35.01412558025736, True], - [-96.2664164425719, 27.27790820412447, False], [-95.49119347312927, 34.04880310368103, False], - [-94.76867790780145, 30.68409383373247, True], [-97.33084948761977, 35.987707032142886, False], - [-98.9375962329848, 27.590161796852268, True], [-100.24496746645461, 35.14003067100538, True], - [-97.82265827697529, 29.12546410605067, True], [-103.60831990499707, 34.420657352069206, False], - [-96.6211125945082, 33.06459177171514, True], [-99.74211708967393, 29.148959136838407, True], - [-101.74939764896713, 34.474532089579476, True], [-94.34764438114355, 30.357142981363083, True], - [-106.52546379307893, 30.041326961454715, False], [-101.2986640335862, 26.37652905898862, False], - [-106.4580153687541, 29.87759744762145, False], [-100.89335102135293, 35.52499965140305, True], - [-100.8248639994296, 33.40975770839312, True], [-103.93212196016398, 32.909497641552065, False], - [-94.67932056126476, 35.15626837470808, False], [-106.02052521025293, 29.597198775376654, False], - [-98.2893867410877, 29.93112729119841, True], [-99.89450058695091, 27.938220198829537, True], - [-97.3506130624274, 29.943100560338415, True], [-102.20272670412375, 26.935152987528188, False], - [-102.76414059461521, 27.054007916590372, False], [-106.0041901594681, 34.50108734620676, False], - [-99.34960356247895, 30.765691636568544, True], [-93.90991359101918, 29.52415548605938, False], - [-102.45068333144832, 29.498640082146757, False], [-93.52649708202873, 33.25384150278302, False], - [-98.11359820029512, 32.661143218490004, True], [-93.64776248509527, 30.4684682460894, False], - [-99.4498229748733, 27.335197692456124, True], [-106.3098030711422, 28.114811474983753, False], - [-103.19744279212784, 29.761699794107464, True], [-98.04521749365168, 29.23374276253473, True], - [-96.02905435107336, 30.127228292010795, True], [-98.51879935475141, 29.632065468973558, True], - [-97.51304854653931, 31.509276424390762, True], [-103.29390200939619, 27.318075011416106, False], - [-99.9824973213597, 30.110443559920434, True], [-99.50915156948314, 29.258458344281003, True], - [-96.41505277249307, 27.81615410871556, False], [-104.15751695040154, 28.634566430199957, False], - [-99.54546678775662, 30.628758593109584, True], [-94.501671157283, 26.690164729805314, False], - [-99.5998715300248, 29.29963433926709, True], [-96.19407966991072, 26.583996951962845, False], - [-94.18130561957514, 34.39183349747304, False], [-99.35457098320595, 29.19418372510661, True], - [-103.2439668700228, 28.7415827836878, False], [-95.94946899471344, 26.094846437871187, False], - [-96.29555855830898, 35.61463847091715, False], [-100.90818350967113, 32.81839948197739, True], - [-103.19547298017221, 33.780381119513564, False], [-103.7311831715452, 34.51901410810771, False], - [-104.9802204382533, 27.04133234378543, False], [-97.04516124978682, 26.74522583795225, False], - [-99.54055674299795, 30.957712411033505, True], [-100.75291255499768, 34.98555626576464, True], - [-103.08923082250261, 33.115219018353876, False], [-94.38102432053655, 33.1179055259896, True], - [-102.03982643927137, 29.9838170230828, True], [-105.81904014957023, 28.79765172366808, False], - [-96.65811064697854, 29.033441999230575, True], [-94.79391199397416, 32.26686772352405, True], - [-94.05970729814709, 27.578098867203245, False], [-101.34732336260434, 31.621990539913554, True], - [-104.3518957862908, 29.119881150805877, False], [-94.62935978446168, 30.4280012647268, True], - [-94.4545353979626, 35.54695331314244, False], [-95.54556621919373, 36.13412073236616, False], - [-95.35828131810126, 29.196029446685102, True], [-94.03367199299583, 29.998249298603696, True], - [-106.36092437901962, 35.88480338683122, False], [-104.36739535175568, 34.44871919970392, False], - [-101.23847775114616, 33.38670941589996, True], [-99.14984412490651, 33.42882053133658, True], - [-103.57375420847947, 36.10822666498764, False], [-105.16720721276594, 35.376479016301516, False], - [-97.83947903342955, 27.52109204805256, True], [-98.71405542652877, 35.7689674767476, False], - [-103.06998061485356, 34.82880997682268, False], [-105.28204362921868, 28.400435333576176, False], - [-94.15911792478674, 26.17595690168265, False], [-96.8089922559183, 28.576299745236, True], - [-94.55012730211439, 35.965323310709934, False], [-102.35052435497836, 35.54979117991739, True], - [-104.37005549234762, 27.1898216329847, False], [-95.68880266269171, 28.848538713723595, True], - [-99.48796021732129, 33.879870610429656, True], [-101.00413095653694, 34.33763822793282, True], - [-106.07730385091368, 29.134515491377414, False], [-103.45299952582074, 27.571405818685125, False], - [-102.68769661036903, 35.0508397444369, True], [-102.12226125487027, 31.490531154286373, True], - [-99.20157845594305, 31.551754104915446, True], [-102.55860273418811, 34.074770794654526, True], - [-106.13595097934105, 30.66643783693412, False], [-106.62769073938416, 30.65343353159766, False], - [-106.40940133512453, 29.82237466871204, False], [-102.1529566504516, 35.06622589749327, True], - [-102.80724491463563, 34.7087445437067, True], [-97.64939497688633, 30.67928132241753, True], - [-96.36801227272899, 27.90369756441311, False], [-94.09540454906752, 26.5340311979023, False], - [-100.95868651879503, 29.489244579528528, True], [-99.00791521500773, 33.15713091296997, True], - [-94.56949551831565, 29.097678237118387, False], [-104.30210679432342, 27.480997154392185, False], - [-102.89216000521263, 28.618705789328487, False], [-96.97440058322412, 33.50865616454956, True], - [-98.99276448264528, 33.16482908208919, True], [-104.83376414397748, 30.866597558655094, True], - [-105.87523535009888, 26.94560046391572, False], [-99.29766049432588, 32.83368387034709, True], - [-102.51690788569256, 33.11843291048262, True], [-95.8185042576338, 29.933635250495907, True], - [-95.64639798097132, 29.81408328213472, True], [-95.27363238555648, 26.04686490238641, False], - [-97.49469880046678, 28.7839351111114, True], [-102.55955572766781, 32.24579142286663, True], - [-102.3259956134002, 29.68040823171017, False], [-97.73863157435005, 29.996562088019196, True], - [-104.68631893564948, 32.01946765618769, False], [-95.04016163980175, 33.10637289096444, True], - [-104.60749147021224, 34.68835575652551, False], [-103.65636297920052, 30.591031777106217, True], - [-102.77020591146633, 28.84679685398533, False], [-93.68494562344958, 30.545382533660963, False], - [-105.28442207266875, 35.066432824429576, False], [-96.25390521489425, 26.494829177985668, False], - [-98.82250217338702, 27.932953025264148, True], [-101.74188578040746, 34.64776869635973, True], - [-106.04404882060925, 28.594522513035013, False], [-93.67216964673989, 29.251042306696004, False], - [-99.78759245690895, 33.06425606417031, True], [-98.40506092257863, 30.372595091544586, True], - [-102.29051625332913, 32.71012257838102, True], [-103.71880332727147, 32.69104459346947, False], - [-97.0545647773379, 30.76611952136734, True], [-96.38483633115239, 27.604532811233028, False], - [-106.26518681815887, 30.011599887072457, False], [-96.29869292442771, 28.668280098851437, False], - [-101.81643591481892, 26.171067127237418, False], [-99.84342847150265, 26.017698064471553, False], - [-104.11331752582073, 29.654316762816297, True], [-99.97301004853021, 27.730105527140825, False], - [-106.31647836713242, 32.17703231405249, False], [-105.53511133815337, 26.702440097120153, False], - [-96.73532103327318, 35.78642691945332, False], [-99.1113903696459, 34.40215160301135, False], - [-105.57490309093876, 36.00074957169259, False], [-96.91408056656418, 32.50309840505817, True], - [-97.12004011524724, 26.947775539446376, False], [-104.31177621433217, 28.977811712282257, False], - [-94.2821165561003, 30.71331882514141, True], [-98.29146997158631, 26.800066299911094, True], - [-103.116290122766, 31.46742167424961, True], [-98.56811800705276, 29.888220960970425, True], - [-98.08987891135445, 27.80577602559695, True], [-95.34430060625397, 35.0872381937164, False], - [-97.29977470332764, 28.838558342329026, True], [-99.29340339460651, 32.1659999533891, True], - [-98.45946176172794, 35.06767544568288, False], [-97.28077383253662, 30.77329811793486, True], - [-103.2046478070315, 33.95113909648831, False], [-97.41533999785908, 32.08647437970406, True], - [-106.24449926545937, 26.923081786350345, False], [-97.29470564456045, 36.18937223503928, False], - [-97.66122972103732, 34.56870670469234, False], [-102.35020419742744, 27.349690061784784, False], - [-93.77154189004054, 33.43617694637923, False], [-101.35103101345337, 29.463692272605073, False], - [-101.62926979404627, 32.803732493055875, True], [-94.90780742661717, 27.023519182303016, False], - [-106.20392742378257, 32.97382450322062, False], [-100.31113315995306, 29.431049347619815, True], - [-101.26240055732195, 36.148817757576396, True], [-98.42727511780691, 27.460837740553018, True], - [-98.57534771820664, 35.599414672044254, False], [-97.62020154014516, 26.369709082738453, True], - [-103.2986841163323, 32.6157912851791, False], [-93.93578337162835, 34.486048529590185, False], - [-93.64615854059947, 28.5736836659404, False], [-96.99391123102941, 28.896993703684423, True], - [-102.45723986918175, 35.7010832684699, True], [-105.25314374574465, 26.38581255615052, False], - [-100.2188547460063, 28.291673320576088, True], [-100.7877537462725, 34.49341196472571, True], - [-106.45021897380575, 26.72924721086809, False], [-105.02103129126913, 30.757125052138996, True], - [-98.53860937298697, 32.45578723025558, True], [-99.57879415297293, 33.96386507864504, True], - [-93.7378357236408, 27.9626758279968, False], [-100.63489750669079, 35.38304941928768, True], - [-106.59421404071085, 30.052955341162452, False], [-106.59172119923385, 35.689363691993364, False], - [-97.34149801358657, 32.67880291850629, True], [-94.73963101360403, 27.850819649646564, False], - [-100.94403277172583, 33.474538412919046, True], [-99.27226009251935, 27.468315184289615, True], - [-100.4539129843546, 34.591160165118865, True], [-106.44494557869739, 28.035999053683224, False], - [-94.23984184184754, 29.557288201626776, False], [-99.03587934877508, 26.59598428059316, True], - [-99.74353348891233, 32.82386415542986, True], [-101.86634664448142, 35.898545381710065, True], - [-104.82904170889573, 35.68593461490256, False], [-102.883660082079, 25.927764876566876, False], - [-95.10167954830852, 29.132555432953012, False], [-104.16716765381528, 32.54432440506069, False], - [-95.8001698490917, 30.554039020076594, True], [-98.25774265085879, 30.67468293710671, True], - [-104.14549182637904, 36.13448211420454, False], [-100.79273106998009, 27.687205570475957, False], - [-97.9031540972447, 34.04156602854073, False], [-99.48655188258273, 36.01928708820145, False], - [-103.78885086741725, 26.649573815350735, False], [-97.54897028080592, 26.611495630090587, True], - [-106.18533271033974, 26.521041809084632, False], [-102.04736124316179, 35.80143440960038, True], - [-103.53014416761859, 35.94251011771925, False], [-106.37072741181773, 33.56020830971726, False], - [-100.5087744739241, 33.68691572355519, True], [-103.14684873740707, 30.33159482347187, True], - [-101.04498614381122, 34.01935100090506, True], [-99.23406810345234, 36.47780260248638, False], - [-96.38309061733807, 28.10204238295031, False], [-106.04814322602003, 30.009165824440764, False], - [-99.09937261770088, 36.27541704541078, False], [-94.9236280439113, 26.908216725432283, False], - [-96.03138446605873, 29.48956628516225, True], [-101.79748242792357, 36.2396677016486, True], - [-96.7954152001291, 28.981691254375008, True], [-95.95383982488345, 33.80047001504878, True], - [-103.89568070095685, 26.027352965725004, False], [-104.33069038138268, 36.46092136221017, False], - [-99.40487331382435, 33.362496703874385, True], [-98.0390467871466, 27.286158481267943, True], - [-103.93051493329094, 36.21025032666974, False], [-100.89036166271175, 29.431712359982495, True], - [-103.01093811284001, 28.951789475547162, False], [-105.6925789844033, 26.67611273491844, False], - [-100.63232815476351, 35.18180560295863, True], [-95.84068243438212, 26.566440681951057, False], - [-101.69166851142406, 27.58570691504354, False], [-96.39192036000713, 30.76580153991847, True], - [-99.60575587904657, 32.844854917935244, True], [-99.88359268023875, 26.427305650516256, False], - [-102.49870407406512, 35.670387195714135, True], [-99.02838249064023, 34.35433748751913, False], - [-93.69511330973809, 28.964334494491123, False], [-102.77438414412866, 34.77587690396507, True], - [-97.20637129262126, 28.62351031644859, True], [-97.20760493091282, 29.66509727888832, True], - [-97.78279263302204, 28.650053248724216, True], [-105.10373645902229, 30.10605688088612, False], - [-102.29320671455936, 34.913227698065626, True], [-102.08456452797166, 35.04888378920539, True], - [-97.684013792577, 35.91610190976283, False], [-94.66766997135741, 28.388037123223707, False], - [-93.51699445904997, 30.745955788402362, False], [-100.56415276925733, 27.122598143422042, False], - [-105.51909002313077, 30.081775705006855, False], [-95.57290373716107, 26.724672719886662, False], - [-103.50352867348445, 32.004206585428776, True], [-102.8747549124684, 32.8979077719816, True], - [-94.54178731632837, 28.121556170094824, False], [-105.1852492618114, 34.57852818651511, False], - [-101.56833897599279, 32.394936636984475, True], [-102.06612007055514, 30.0654558718264, True], - [-104.27037065814437, 26.539037110206124, False], [-96.84449118726283, 34.303252013399494, False], - [-100.21448448808589, 31.640102623719255, True], [-105.11541407363484, 32.59032999242083, False], - [-102.47865996013053, 36.106193753181586, True], [-102.1982854302976, 30.87389402879236, True], - [-103.75935920410515, 34.164405398206384, False], [-100.26058874460439, 29.97152621245525, True], - [-99.98584687760041, 29.138422331875738, True], [-98.8136163575852, 31.471193466135066, True], - [-98.2634703260477, 34.441857423457066, False], [-101.31389165577758, 26.67722696068761, False], - [-105.25939274478021, 33.42397682936403, False], [-95.42401575533701, 36.38213160460099, False], - [-93.7911156924766, 26.16919479726154, False], [-106.0793837632488, 32.0426040667038, False], - [-101.67576371230456, 26.55813999428948, False], [-105.29328020590933, 33.317961711071874, False], - [-106.6282733323151, 26.640651252579072, False], [-100.97693002993205, 29.778473204159543, True], - [-99.81270323613123, 28.521657399940562, True], [-106.35993676923691, 32.47765856951956, False], - [-98.6318807925877, 27.313103487591995, True], [-105.60269402006043, 33.9266565877851, False], - [-96.50522425362368, 33.17862810486471, True], [-99.36585657273703, 27.918462376378407, True], - [-102.71165541572611, 27.26977019467567, False], [-105.26203795210641, 33.50921690053216, False], - [-105.45340320249285, 28.841949098625847, False], [-97.67986158008269, 31.45298937828147, True], - [-95.42927279657806, 30.012853799790946, True], [-94.61186352922475, 30.990817003676423, True], - [-98.75882027588506, 33.21933322666259, True], [-104.55525321655045, 36.35184775125859, False], - [-97.39087680042095, 29.262712771446328, True], [-95.32479451597851, 32.558534263953774, True], - [-96.17652314621081, 34.39156129414083, False], [-99.95318514227759, 29.206707699022903, True], - [-105.32756527254323, 29.177591819396113, False], [-104.63110828519505, 35.14036164405956, False], - [-101.75109732251778, 30.720543376919306, True], [-98.43013838260205, 28.14394272715192, True], - [-97.0734335255207, 28.750007474412804, True], [-103.21154248047685, 32.22703974346086, False], - [-102.39810543632244, 32.819942719538474, True], [-103.36259451330584, 27.176657526494928, False], - [-103.66650593230085, 36.025520559005244, False], [-101.63539174258722, 27.803279475131447, False], - [-96.34811589893562, 31.14578417358513, True], [-98.94976448687677, 31.767797366700666, True], - [-97.38029827656692, 36.27728736453143, False], [-95.93103949592769, 28.74450380455483, True], - [-98.8151117128684, 34.70259233670797, False], [-96.14623746963383, 30.48841309287015, True], - [-101.46340647601903, 30.19511307273737, True], [-100.63602533061145, 31.749219881531914, True], - [-101.63505098125265, 33.69021849156649, True], [-94.71406829659445, 31.90061330594235, True] + [-96.83201838665211, 30.43633054583931, True], + [-94.97179271816618, 30.46609834459278, True], + [-96.87911915799319, 29.002783392404453, True], + [-104.03817768478906, 26.080885130466065, False], + [-101.05107033713017, 26.110092672074, False], + [-101.90984946051287, 30.2673660562615, True], + [-104.2666097673043, 26.638892954348243, False], + [-99.00209313228278, 29.312408039204637, True], + [-96.76821760997925, 26.53415999513266, False], + [-103.60734801725461, 25.989562175464958, False], + [-105.52663947372852, 35.59616356954213, False], + [-99.73190928401823, 36.34065757158246, False], + [-94.28455658519579, 30.510396248421543, True], + [-98.287902523108, 32.42446924695663, True], + [-98.36319041112684, 26.836620743656326, True], + [-100.58823300352246, 28.287422480123738, False], + [-106.09575378559609, 29.647073739534953, False], + [-96.03197805726944, 34.67673925196511, False], + [-106.13189503506499, 28.293328347459486, False], + [-105.6163099497765, 34.642876249285806, False], + [-102.1699840394609, 32.64792829807019, True], + [-102.19213396201079, 31.137813785847744, True], + [-96.8832056156457, 33.584067733298724, True], + [-105.47157574361026, 33.89108923934286, False], + [-101.53889206881809, 27.73095057942401, False], + [-95.0730796759137, 26.09464277833887, False], + [-96.45795032815394, 30.22238046821291, True], + [-99.47479689046119, 27.519862201810025, True], + [-104.95530088407097, 34.46894379454478, False], + [-99.64935392553515, 27.166719715093752, False], + [-101.8740013838435, 35.11537675606412, True], + [-99.07039344683601, 33.64951303142618, True], + [-106.60595918307385, 29.64471250102584, False], + [-97.20030616424087, 32.31820772186446, True], + [-96.23784236099425, 27.638993220475054, False], + [-105.93765675735541, 35.89412147712274, False], + [-97.17726496423283, 28.55515333886519, True], + [-101.41972932590615, 26.609185193916016, False], + [-101.84272804452128, 31.047193869577697, True], + [-95.92485433657428, 33.9254034938182, False], + [-98.09279175575493, 31.486295362051568, True], + [-100.77512720701931, 34.5423023937584, True], + [-105.99024440024378, 32.444411669816, False], + [-101.4742991680627, 36.37442042029491, True], + [-99.91406680649878, 30.44981434916269, True], + [-106.59351726840352, 27.830675040946844, False], + [-100.50108029331473, 28.08692086210316, False], + [-106.22266727042927, 28.257938478160245, False], + [-99.16766510789698, 34.05594801235725, True], + [-98.81327349833023, 34.53578673189781, False], + [-95.75835434832825, 35.97127466623442, False], + [-95.87872844572516, 34.1792497663974, False], + [-97.67138045660111, 28.85228239075667, True], + [-103.7493116963675, 29.910102031100415, True], + [-97.9552991722866, 35.50168101442732, False], + [-101.99173352350167, 33.16196427356835, True], + [-99.69108575797604, 26.58570279334035, False], + [-96.1383698450465, 30.01380467777652, True], + [-96.65345956191142, 28.13722809686032, False], + [-102.19812919352621, 27.653939350819577, False], + [-106.17852559635664, 34.30676459237532, False], + [-97.12171059588019, 32.98547742358788, True], + [-97.63495481018265, 31.052912731492313, True], + [-103.04179423578454, 32.565022698580854, True], + [-99.7663361476778, 33.93929224333747, True], + [-93.96695034930963, 33.64792052285788, False], + [-106.43489898416725, 27.00081934085768, False], + [-98.61144241454609, 26.091546414019007, False], + [-97.62297026159148, 29.91547499019296, True], + [-102.81093959843548, 31.775967846663903, True], + [-98.92280973197468, 34.706031694183864, False], + [-105.82454817097681, 33.60768142292876, False], + [-104.1703913008495, 26.30423978112628, False], + [-97.03821689475404, 25.891523519897085, False], + [-95.96948383690238, 28.06242640122303, False], + [-96.20743197977514, 32.96986917934692, True], + [-101.84781525566628, 30.91982689926289, True], + [-96.31807840035235, 31.527410535260906, True], + [-100.8781500555231, 34.8227970125657, True], + [-94.26355042343864, 30.94843753499924, True], + [-97.3429146102502, 34.73686841121185, False], + [-98.07753472284375, 29.552925455931927, True], + [-105.04690710593536, 27.132667240961553, False], + [-99.87850355720977, 32.06195375035851, True], + [-96.53670297300587, 26.908808751199025, False], + [-101.25665086243849, 34.48897040535931, True], + [-97.62841641211763, 34.510828859444, False], + [-98.26146628715239, 29.30444627019776, True], + [-94.0424931438063, 34.024181902771446, False], + [-95.32764174246178, 29.026516144561093, True], + [-94.79487469005772, 34.064778827088944, False], + [-96.8217807635728, 32.9449205143214, True], + [-102.65783000325504, 31.187142262599163, True], + [-102.73499640672506, 28.241956569934032, False], + [-106.57423647984751, 26.21365468361891, False], + [-101.18188053173569, 35.856064735642605, True], + [-103.87152954646331, 26.239969281258677, False], + [-105.21761650869897, 27.276124133480618, False], + [-96.58824456554876, 36.01267677418892, False], + [-99.10920560787146, 32.43164577872433, True], + [-101.93795697189412, 31.620064217834788, True], + [-105.78051688469152, 34.39769579781999, False], + [-98.13520908953838, 28.15597786510518, True], + [-103.44661785399717, 34.94625679402945, False], + [-93.5839550242031, 27.799377395246868, False], + [-103.09029902940127, 33.20610297263985, False], + [-97.23285674761303, 28.03274042046823, True], + [-95.34132538021933, 25.846590059853146, False], + [-101.75354725398458, 29.976564072236787, True], + [-102.8028884021146, 36.14653256975264, True], + [-105.23453544451979, 35.083822581361105, False], + [-95.02779486726217, 28.039905574389728, False], + [-105.19965155638648, 29.896855738726387, False], + [-105.04729708179732, 34.59962724881616, False], + [-93.62110240741019, 32.29972687153207, False], + [-101.91706806513982, 32.22042821274782, True], + [-98.7507642387652, 28.373512157202324, True], + [-98.67837171745543, 29.31049604748274, True], + [-101.4835726546693, 35.24105797268996, True], + [-99.61534262189332, 34.664146656697135, False], + [-102.77162178290662, 31.477015077025758, True], + [-98.14965646419336, 35.506326174334816, False], + [-100.35687795352196, 36.428658182990375, True], + [-93.75583478299006, 33.78374602526648, False], + [-97.35272483145133, 36.14322026779398, False], + [-104.13566843512568, 32.58780129504587, False], + [-105.80951615950367, 30.548722565553994, False], + [-106.36813961730384, 35.58713162312485, False], + [-97.66809642868546, 34.13059807529114, False], + [-97.61612010157164, 26.31880576825889, True], + [-105.10689839575184, 27.13050661175676, False], + [-94.42639614961973, 28.61525052001613, False], + [-101.0258419742403, 29.912378788493154, True], + [-100.85598848851129, 27.83703095635782, False], + [-94.6443199603266, 29.977477497837175, True], + [-101.16783935327581, 32.03976620169659, True], + [-95.78686711235261, 34.37099939418539, False], + [-100.59275869451973, 34.36381573902874, True], + [-104.2707359051878, 33.43707298914277, False], + [-102.69754715561436, 30.324718877958027, True], + [-100.31772708579322, 36.03146883077079, True], + [-93.84018528740714, 36.306249385961586, False], + [-97.81378337323478, 25.848331371979896, False], + [-104.37867619764027, 31.688609367477817, True], + [-100.28100102840853, 36.18155424506788, True], + [-106.48975872243628, 34.10471027956753, False], + [-101.4453862294541, 28.535362387187064, False], + [-100.05248503701085, 35.161646972337195, True], + [-101.37197503092796, 27.85006417239032, False], + [-95.00012601768513, 36.45104201985982, False], + [-94.1618133883979, 34.189947778971074, False], + [-100.9407537928131, 27.309175834366915, False], + [-104.72991115996922, 27.3139116441178, False], + [-98.59532436543168, 26.287325902961726, True], + [-94.46884442680549, 35.68627263027676, False], + [-105.31712494151611, 35.84472703216106, False], + [-104.64932323670762, 28.699942778858926, False], + [-100.34867007633471, 29.705706918470952, True], + [-93.69561095836606, 32.456548936430266, False], + [-98.12884276179193, 28.360889737226003, True], + [-96.77862792020258, 28.586529000239178, True], + [-106.1873382768634, 29.404306118606442, False], + [-105.67789642420705, 31.716310454187514, True], + [-96.00775542585319, 28.568996970414524, False], + [-98.0904391114445, 33.41233020042991, True], + [-93.82743788526335, 27.5659388943208, False], + [-98.96755649758798, 31.32502880958104, True], + [-96.55622361353764, 35.016203517682726, False], + [-99.90454174449499, 34.88971778656889, False], + [-93.92308221299953, 26.93602671682176, False], + [-103.02775965216946, 27.526954568149137, False], + [-101.70211609579965, 33.221705586015986, True], + [-94.65140320779773, 30.12411725325112, True], + [-105.48158850264392, 25.966083503119812, False], + [-95.86092717890419, 30.257238607458746, True], + [-94.78025397771191, 26.33573351642334, False], + [-102.42306855603515, 31.528148123221925, True], + [-101.11557151275859, 28.23290994001276, False], + [-99.23764635246289, 34.03712892161272, True], + [-97.18358117231713, 30.819652476530774, True], + [-95.52114900196523, 33.71837984822948, True], + [-94.80576475349369, 32.52595208303153, True], + [-105.53353349524157, 33.10076381764996, False], + [-104.45067861203503, 29.792014903132056, True], + [-100.81552999585323, 31.13371713587459, True], + [-98.10244603287715, 27.062939382372114, True], + [-102.8183262272355, 31.63644201139868, True], + [-105.48022791238954, 33.47054002907317, False], + [-94.54700162806725, 33.3634517345777, True], + [-103.52394717869927, 29.289585818953316, True], + [-100.88024068589486, 28.00254322622369, False], + [-105.13298501311634, 33.585842169513036, False], + [-95.47745420853485, 34.33206192097545, False], + [-93.75279262948008, 31.85387584406531, False], + [-105.6408652118954, 33.38704981277846, False], + [-102.68527628279271, 26.856854067490247, False], + [-105.22798572254378, 34.52742324390015, False], + [-94.25227485978478, 36.11993169557898, False], + [-95.31071217313071, 30.450554401067357, True], + [-104.32309381721137, 33.941981631972624, False], + [-93.68344966428387, 28.25720980205315, False], + [-103.11762381325748, 33.05126426779246, False], + [-95.17637994183367, 36.37368703601287, False], + [-101.09466869744112, 29.378868777118257, False], + [-102.3244768566622, 29.25373813014635, False], + [-105.6081974667763, 35.02231128438977, False], + [-101.45129562213593, 27.250895271383474, False], + [-97.77527080825567, 29.233601979476123, True], + [-101.2708728240608, 30.929678610371226, True], + [-101.17628088744974, 30.807932586287112, True], + [-98.94314936681077, 28.962959058638244, True], + [-97.5297094048325, 27.24629690610976, False], + [-105.24649559460273, 31.68285402434288, True], + [-95.38165074760121, 30.678277565962645, True], + [-101.5405440205774, 35.75938014879534, True], + [-106.30162345059492, 32.35717712927482, False], + [-95.82698786035043, 31.471380696415473, True], + [-97.58132229591222, 26.76823235412943, True], + [-96.24754364311642, 29.148074667141763, True], + [-101.60752835018079, 28.02378609076799, False], + [-93.66280978876574, 34.36888263435238, False], + [-97.04083533693894, 32.07693083540003, True], + [-94.41534967058817, 34.30307828414252, False], + [-94.51223027590078, 26.101565901379825, False], + [-95.37877455769014, 32.91182889409107, True], + [-97.02169748832533, 34.77231790184875, False], + [-99.56938377454452, 27.98323188041011, True], + [-104.71416427480177, 31.671590498127077, True], + [-104.48037657157643, 28.020929009314624, False], + [-101.46582277815409, 29.245030464171972, False], + [-94.33289936897319, 29.433780707715577, False], + [-102.65873114765344, 32.579375713461324, True], + [-94.71197474752655, 29.919750259392565, True], + [-100.56224887904757, 30.870441757396232, True], + [-95.7680416447194, 33.381874464032556, True], + [-101.30709319439809, 31.415698736346386, True], + [-94.17706664281508, 30.20919168191223, True], + [-105.1986310512293, 32.2767819705482, False], + [-96.64520698321878, 32.53545316641808, True], + [-101.27894696079863, 27.434282641130807, False], + [-103.96823391756168, 36.206818702766164, False], + [-102.61287825142863, 31.52883149992497, True], + [-97.43688838995867, 35.35809331749178, False], + [-106.3345282700814, 26.77898252396381, False], + [-97.84563076948461, 28.326830633856254, True], + [-97.10812201190659, 27.354066620755486, False], + [-102.06625125192836, 31.959360067966703, True], + [-94.77064907103171, 32.49772073548828, True], + [-103.7070672713304, 26.993474458082765, False], + [-104.63528508881744, 31.77175026283308, True], + [-106.20330061641941, 28.71581603545491, False], + [-102.93466353815762, 36.42712467119802, True], + [-97.02548398973192, 26.47462461285108, False], + [-97.92517027601997, 33.53006657932378, True], + [-104.89676410831059, 32.03756980285575, False], + [-95.02216317823846, 28.250092458391666, False], + [-102.63731145928554, 30.78785522057966, True], + [-101.9626891052094, 28.607495532359852, False], + [-103.43019339494695, 33.757210152142996, False], + [-95.52042729675107, 34.824713652167745, False], + [-105.6806104213336, 28.64547500599634, False], + [-105.01509606690256, 26.998326186544013, False], + [-104.83729623744878, 28.111330049442206, False], + [-99.30736722245813, 27.44271254179918, True], + [-96.19450914007797, 26.5070841846474, False], + [-96.93250711894682, 27.465441701127965, False], + [-98.05769301004028, 33.34316222525198, True], + [-101.70993094422322, 35.378824775340604, True], + [-97.78065357281011, 30.791192214255453, True], + [-103.85602296523946, 33.58813675270333, False], + [-96.16754367241163, 27.82134326033623, False], + [-93.84173858221502, 28.644678311847933, False], + [-95.66361201153609, 33.1782437005781, True], + [-96.18610593628335, 35.66871035791395, False], + [-100.05778168598985, 34.34775700022905, True], + [-105.3070646714683, 26.129506259811194, False], + [-101.60474509803998, 28.301826427498735, False], + [-94.05455083451157, 35.62737695624984, False], + [-93.77022392951797, 33.35636018468855, False], + [-99.9983492940202, 28.616796438006656, True], + [-103.65327712041633, 35.68635542708576, False], + [-94.18836108420525, 33.519234035329205, True], + [-101.16524711825508, 35.21736886367663, True], + [-106.63756507835402, 31.512711118486916, False], + [-94.64418283641962, 32.24310968022579, True], + [-98.48901518901792, 26.92275583383609, True], + [-105.71495874564097, 30.37875836321371, False], + [-103.59489508163908, 34.07565890645147, False], + [-104.58617756264167, 34.7175307481461, False], + [-94.94979738923941, 33.53027390298981, True], + [-104.79237901545315, 33.643949242871386, False], + [-103.6261849261433, 26.37203494674699, False], + [-104.12072798260623, 34.037343854052246, False], + [-99.3137876161763, 28.786746195806515, True], + [-96.90701800480272, 26.83567175899895, False], + [-105.91609848895729, 27.17006217234686, False], + [-94.88867966602324, 29.0650764347025, False], + [-99.09445254927051, 29.165294618392306, True], + [-99.59646096585762, 31.547978010714818, True], + [-100.35308616902222, 27.998071701955347, False], + [-103.65741111414567, 29.929101550941933, True], + [-95.56003645452608, 33.73354146467783, True], + [-101.98380104745135, 27.741893994563817, False], + [-100.28801029632858, 26.08376297941383, False], + [-100.82876490121433, 27.203171781033483, False], + [-99.00038368647441, 30.227734562748875, True], + [-93.99273175213636, 33.172989756016705, False], + [-103.28907886290975, 30.68354258877181, True], + [-101.58265515547387, 34.05717966984305, True], + [-93.61002560748278, 33.95079910028842, False], + [-104.1322621794693, 31.037441969505064, True], + [-94.90790561010023, 33.6667146192015, True], + [-99.72772840949955, 28.57426803892259, True], + [-96.76750823367423, 34.238866572662864, False], + [-99.11285733031394, 29.297129143702183, True], + [-95.34529541329582, 34.50122580606273, False], + [-106.2015311076861, 33.04546069130247, False], + [-106.6471763299225, 35.60222852490276, False], + [-101.63138803719991, 27.93647861077147, False], + [-99.67286004973847, 29.000600155646495, True], + [-102.69661571801251, 30.438208192282016, True], + [-96.47920563132686, 28.187453937378827, False], + [-99.15701789001724, 28.754330049547363, True], + [-103.63208677741055, 31.506299316322448, True], + [-104.6247250325306, 34.777168030468545, False], + [-94.50362320672285, 28.725533817363363, False], + [-98.8088667387509, 29.719091220812984, True], + [-104.66480293721597, 31.36766582422802, True], + [-100.80243453385943, 29.56664951795082, True], + [-104.3184052935018, 28.368505467940153, False], + [-94.99687127078006, 29.056199158312214, False], + [-99.18151980311957, 31.331306403933503, True], + [-106.58306812961843, 33.83560030586354, False], + [-102.34808248763466, 30.93213593104124, True], + [-96.87905436496129, 31.63403473842953, True], + [-101.88482545812973, 31.346536386676735, True], + [-102.08612983365585, 33.94091937937585, True], + [-97.59374438050571, 30.710749425390954, True], + [-98.468759043241, 34.887864549188656, False], + [-104.5600126294423, 30.769523820716284, True], + [-105.63321064215583, 31.706723345643436, True], + [-103.39803684928096, 31.205852663870417, True], + [-103.74991694223343, 34.47243593420573, False], + [-103.55735795090966, 29.368257266446264, True], + [-100.53250901956653, 34.61994711930259, True], + [-106.42290807028131, 29.47435749957574, False], + [-100.61630496584333, 28.492897242114427, False], + [-95.45897223676457, 34.99538998873486, False], + [-100.80796423924814, 27.597779160448237, False], + [-95.59903305812487, 33.53765968472729, True], + [-102.71817656483864, 33.67774382239573, True], + [-100.0519819831278, 31.720152603322795, True], + [-101.86801670742807, 29.37988286278217, False], + [-93.7251604108502, 33.63648932111699, False], + [-93.50731366944763, 31.66131539976032, False], + [-93.91408986252142, 31.91658143504109, False], + [-99.1976965958281, 31.846542049263352, True], + [-96.70181855090833, 27.823299476943752, False], + [-99.05025751191671, 30.14722485144382, True], + [-101.91786175888922, 33.662393020783334, True], + [-97.18908214543966, 26.584467319788136, False], + [-102.09768463635325, 27.745869776526586, False], + [-105.03784779065376, 33.35089012727154, False], + [-99.75443466868418, 32.340335779846406, True], + [-102.53950776524619, 32.48219468369184, True], + [-97.38249374177907, 31.70583501812896, True], + [-100.23917371385507, 34.15552160975429, True], + [-100.64584910978431, 33.08767497994276, True], + [-95.54547414843782, 28.573393441826422, False], + [-98.4400667166874, 29.169883280280335, True], + [-101.85722555245049, 29.99952272227504, True], + [-103.40127887731586, 35.954389606004995, False], + [-106.49277204369605, 26.3739019521645, False], + [-96.42482710450412, 25.935501811569573, False], + [-96.49468755444158, 26.21429802291354, False], + [-103.26280296149277, 36.42505933494076, False], + [-103.23186132072851, 26.981602236536546, False], + [-95.43868017247715, 26.795551518098133, False], + [-104.95759598697946, 29.274620743768665, False], + [-97.4627043038694, 31.264418590173566, True], + [-99.40922174677335, 30.859355223178202, True], + [-97.62450333069754, 31.860943195654983, True], + [-103.28423668185653, 31.64625933078107, True], + [-97.34347509751296, 32.39948448790623, True], + [-104.78394662628456, 32.19069943780818, False], + [-94.27543944274844, 28.45111869438522, False], + [-105.41902431151755, 34.098705479256964, False], + [-105.84856227788723, 34.401910854922086, False], + [-105.76944559045042, 27.541157331410925, False], + [-105.96625908843005, 34.60984823477864, False], + [-100.45522585613661, 35.82958048104291, True], + [-95.8227101752813, 26.40044772412707, False], + [-98.14190854947867, 28.173726785556475, True], + [-103.41079917326785, 33.17358912330205, False], + [-106.21380947469507, 30.673049362200253, False], + [-98.6181240681384, 31.57826052940466, True], + [-105.52347449914703, 28.45331024338554, False], + [-105.87199783454764, 29.269133017660426, False], + [-95.72117392272776, 32.84521613150032, True], + [-103.98103037156038, 29.39830154631861, True], + [-98.84272856308685, 27.010603508995832, True], + [-103.48528511610583, 32.292795962096, False], + [-101.10764837358862, 34.44067771878632, True], + [-99.90899218640438, 33.864602563342665, True], + [-99.99425094006477, 29.632299612876448, True], + [-104.19954114103936, 29.481293826903272, True], + [-99.1617923467798, 27.24969812956527, True], + [-93.94985771467012, 33.20509824852493, False], + [-98.74589172711391, 26.59828162369087, True], + [-100.74104498226976, 28.090884473461635, False], + [-102.0941642949128, 29.40228872741179, False], + [-103.71745434686355, 36.07717165565938, False], + [-103.85504023598908, 35.797619278303934, False], + [-98.3178181998277, 31.80247171895742, True], + [-100.46831716774525, 26.395663970107364, False], + [-96.6262432609301, 33.43258969257319, True], + [-102.12872561318167, 30.9414684197057, True], + [-96.10044267209415, 31.617205739677686, True], + [-102.76040876099727, 30.02029055974793, True], + [-100.28567173102832, 33.71564104661008, True], + [-101.87455244042499, 35.45783748840937, True], + [-103.80647075850555, 33.75958813400894, False], + [-104.07465598182498, 29.829473718048853, True], + [-96.80679641483542, 34.068109206459674, False], + [-100.21922411687524, 35.55827610846517, True], + [-102.18536576459445, 35.60838848797885, True], + [-100.74818339320018, 32.46468922651053, True], + [-98.89381745119253, 29.84927086885224, True], + [-95.57772284816139, 34.03118772897132, False], + [-102.19208387081574, 35.47422893590955, True], + [-99.31176771742929, 28.159649775138398, True], + [-95.08438010592444, 30.864384073337725, True], + [-105.62858370011037, 36.04041710371988, False], + [-99.50704661690466, 26.530069415053166, False], + [-102.21328017280045, 28.123746616410187, False], + [-93.82909083931759, 31.8346812474552, False], + [-99.68311577900606, 33.37514101639904, True], + [-93.88914553766337, 34.40543275268286, False], + [-95.49031947037534, 36.15787562082431, False], + [-94.04958126924683, 25.895718131012476, False], + [-102.31183630727872, 26.195612345971853, False], + [-99.74217764726765, 34.4866834996357, False], + [-101.2273998455989, 31.012399597950438, True], + [-93.7170401640232, 26.52296130239048, False], + [-106.31856530763194, 30.254195818383003, False], + [-93.54539750125572, 31.968279205890525, False], + [-102.06176434040876, 35.96267638467066, True], + [-102.21150807644786, 31.276020170678756, True], + [-97.59134352614593, 27.758428418449625, True], + [-105.6285777230377, 36.20035827010385, False], + [-99.56855691529327, 27.565199944571845, False], + [-93.95512959128524, 35.50257083074824, False], + [-104.2068757288861, 25.983428900973824, False], + [-104.1534590142076, 29.90389615149271, True], + [-104.75891565225955, 26.17708478969608, False], + [-96.34136547606607, 26.58259616840622, False], + [-94.35505605596222, 31.363945882683545, True], + [-100.75357998539742, 27.70797766258346, False], + [-96.34206621365894, 29.195743358202087, True], + [-96.44632535331706, 33.578167176287465, True], + [-100.52062683482386, 28.935836235311534, True], + [-103.59488192282778, 31.145329272683313, True], + [-96.21513910038252, 26.71734433601959, False], + [-97.74524155506616, 35.72544238150706, False], + [-98.15072688652688, 29.65447185320712, True], + [-102.94259657821668, 28.995860793431966, False], + [-98.37049290964605, 28.992239589640803, True], + [-101.9333665893231, 32.042668019418784, True], + [-100.31205895270178, 28.542423199402116, True], + [-102.7842119789965, 31.351234994650703, True], + [-102.82118189131612, 25.936779514285522, False], + [-105.06566909806352, 34.429600751175066, False], + [-103.95414080936615, 30.5173184712075, True], + [-96.41063716230997, 35.68425390782344, False], + [-94.82668338005425, 34.10595925830741, False], + [-105.42861829324059, 26.89985572905742, False], + [-94.86003017402527, 32.72190652290611, True], + [-96.69442705925057, 29.12593879172168, True], + [-100.57898366779487, 35.697109012290476, True], + [-105.77290660716999, 34.23468690232633, False], + [-104.3849259560512, 31.337110429238777, True], + [-95.21459964105358, 33.00732978946704, True], + [-95.7332150421658, 27.704415504038284, False], + [-104.50875122680036, 34.91526292458323, False], + [-102.80833873160228, 31.875937580897503, True], + [-104.53224880900562, 25.940603993388773, False], + [-99.6884270626213, 31.04708806366109, True], + [-95.76317485487704, 35.45517519387816, False], + [-101.84342021439619, 35.834756908559946, True], + [-104.04702349088515, 26.09752753366655, False], + [-103.48123590122003, 34.55695985365465, False], + [-101.90736610359222, 26.198871101374856, False], + [-95.80852972334627, 26.4884711642595, False], + [-106.49602165329638, 31.587480730479957, False], + [-99.11540612418378, 28.76377389466793, True], + [-98.81124442220992, 27.408954979897345, True], + [-103.54081324519612, 28.238918914656047, False], + [-102.8589030540497, 33.69752159861657, True], + [-100.38962812985066, 36.259455441754405, True], + [-100.60205967959344, 27.45237069505359, False], + [-104.82116135978534, 33.90726029831255, False], + [-95.25951953419671, 36.427532407586796, False], + [-93.75127522006922, 31.774003703956872, False], + [-106.50710079332187, 32.31418914724039, False], + [-95.38754195058371, 27.686986329488576, False], + [-103.48158704856718, 28.05982044881246, False], + [-94.40123367677305, 32.13802908979801, True], + [-98.37091796124368, 31.394805069115378, True], + [-99.35199253682242, 29.364231843209286, True], + [-100.6371809469257, 29.485414607179138, True], + [-101.65847488152119, 33.055932161413, True], + [-104.75073650330286, 35.477180164221664, False], + [-100.70424003073441, 33.33233065367354, True], + [-104.9829081950403, 33.49495185425366, False], + [-96.28014091653374, 30.765109918972172, True], + [-102.51312299795521, 34.973490202758846, True], + [-106.4316820557197, 28.835022330467574, False], + [-98.37035640871174, 29.15982666730043, True], + [-103.81743393736278, 33.33070497195332, False], + [-102.38044273457957, 27.128328755756332, False], + [-105.83310224715474, 32.69799499508526, False], + [-104.47875333696165, 33.08218160363957, False], + [-105.08315111607386, 27.5499535808647, False], + [-102.50315445382816, 32.952707100728176, True], + [-106.34702977388169, 28.350530651265025, False], + [-100.7324706006535, 28.110070428450136, False], + [-98.89057718530617, 30.501635222091867, True], + [-93.7973707158511, 30.332449685439883, True], + [-100.50817745675471, 35.933620707046416, True], + [-93.62331898394628, 28.011415151343442, False], + [-102.40168198643366, 28.038346452109714, False], + [-98.76386270364078, 32.824244011949546, True], + [-98.63890075873523, 28.426721830307706, True], + [-103.06753906192446, 27.182546407018886, False], + [-103.5149916483893, 32.22702185824897, False], + [-96.92704846269056, 33.56573720072518, True], + [-98.59191146293448, 26.717454841499933, True], + [-98.21728189059557, 30.499991381612325, True], + [-98.96569953382873, 34.09349857812645, True], + [-98.81645597171263, 31.538513035336027, True], + [-103.99810746775411, 34.572232011339544, False], + [-97.1971994402299, 33.01231694977086, True], + [-97.35525400352937, 27.593348250877636, True], + [-95.61047936363322, 32.915418129253844, True], + [-105.77325410471146, 32.220934743164854, False], + [-100.31130282858123, 31.053207323928227, True], + [-94.27435346222804, 29.717111545751255, True], + [-99.36518149272568, 29.81842676057489, True], + [-102.4595465810425, 29.78052913717334, True], + [-104.93563833675945, 36.1553390615043, False], + [-96.67331825738593, 33.795256849340824, True], + [-104.13912762449462, 28.90285335911741, False], + [-99.85650514030851, 27.082576996579743, False], + [-95.46433062216431, 26.31189218916502, False], + [-100.84562917395462, 26.46339943905897, False], + [-94.75077980314441, 30.746795428982928, True], + [-98.38741630868877, 32.8939361913369, True], + [-101.50339387491944, 33.409893596597634, True], + [-102.8838322429055, 33.550509813464714, True], + [-94.4241089162888, 29.987534160102904, True], + [-96.97724045199526, 29.413237018802626, True], + [-103.26463086689401, 35.69959960363469, False], + [-104.22583858596425, 35.32625561149897, False], + [-102.28802601849752, 35.31386927276366, True], + [-101.0349738411682, 25.896953488838804, False], + [-103.9839821999459, 33.2679016367904, False], + [-105.01149642855964, 34.24130005689306, False], + [-104.86876474825357, 35.97880497285345, False], + [-98.95300311760788, 28.23951953232239, True], + [-106.44607553528402, 28.99701327445984, False], + [-97.15665622011281, 33.802655749490576, False], + [-104.88728768211863, 26.19423408399297, False], + [-98.2868092522052, 26.77958013643795, True], + [-96.30475465159665, 25.886256031524383, False], + [-93.96185258306615, 30.262131379160092, True], + [-95.98431071502645, 34.074476976239524, False], + [-99.34592870499611, 31.77796555029789, True], + [-101.23448890900838, 35.02325060675137, True], + [-96.60311950987519, 26.520767851753252, False], + [-103.30098695011493, 33.95932432365544, False], + [-97.20833874248723, 30.443771327358455, True], + [-100.49276808544384, 30.563391121718663, True], + [-106.00844672588474, 27.67304591354748, False], + [-94.68045487444002, 28.380941225789602, False], + [-106.32581808918103, 27.457360510530357, False], + [-103.90944733413775, 33.75181119086094, False], + [-96.62055409685594, 30.911602145241257, True], + [-105.08332076154659, 35.84175548182445, False], + [-106.46727840740928, 30.97311215593337, False], + [-98.24608713290009, 26.87548489704331, True], + [-99.99394976092111, 35.372523026676305, False], + [-101.04275569324, 26.378571750355146, False], + [-99.49808015597847, 29.0197284725707, True], + [-100.04278394100143, 31.138487871928806, True], + [-93.77580092670675, 28.679720562816012, False], + [-105.69434962210484, 35.48380415396366, False], + [-94.76833576200043, 34.77440737306939, False], + [-93.55059692835445, 32.93611160385383, False], + [-96.16637525156335, 35.14861120144762, False], + [-102.57674991354982, 33.57167753159161, True], + [-104.50984249707894, 27.845184825434426, False], + [-102.46681259074438, 32.876290517890325, True], + [-103.79635335230255, 34.68136206176115, False], + [-97.37101719424614, 34.331617955613964, False], + [-93.78338486646257, 29.1736195506313, False], + [-98.653709287249, 30.255774672109183, True], + [-104.78162076886001, 36.14023266162845, False], + [-95.30745598333526, 26.15568755210897, False], + [-96.19795914229807, 29.783289215723965, True], + [-93.97200790057319, 30.34872478231179, True], + [-102.75807129557354, 32.02568271699399, True], + [-95.63613363239972, 30.17575679938203, True], + [-95.74058254060239, 27.830132693387228, False], + [-97.28993038017775, 33.34058580949793, True], + [-94.782421253055, 26.37055251439716, False], + [-99.01579664350437, 33.29078588254949, True], + [-97.2079339911563, 27.123479789817104, False], + [-96.66399830213123, 35.57917944458788, False], + [-104.00378871415248, 33.03909098032557, False], + [-98.18333736354995, 33.40304952690905, True], + [-99.51409889042824, 28.352125049514296, True], + [-96.39878389524632, 33.80913094147424, False], + [-97.95457727578372, 31.304986453423908, True], + [-105.41519123044006, 31.17900852073914, True], + [-102.55823249490591, 28.227952215383333, False], + [-105.22740902391834, 28.00912246742774, False], + [-105.4327613051658, 30.42094293937215, False], + [-99.8642636892311, 34.741799628413524, False], + [-104.07069003771045, 30.561044257056018, True], + [-105.71580939231154, 34.73255277750661, False], + [-98.92331393934845, 34.757305489096225, False], + [-95.1451471669803, 35.59262098012566, False], + [-100.78929431916742, 35.83001258653612, True], + [-106.15538551006625, 33.88884085609275, False], + [-97.78557980515986, 33.9896913074551, False], + [-94.43404183378551, 31.976310799398128, True], + [-102.77500581970122, 30.636815025506447, True], + [-105.67967176568628, 33.47402254839546, False], + [-98.79160014376332, 32.79018812677753, True], + [-102.84347322545034, 28.136203054123584, False], + [-95.24784148052099, 34.22424254638486, False], + [-106.31929728808254, 30.9825641984661, False], + [-102.43296109133702, 32.532070674152855, True], + [-105.92145087338875, 30.381356965114943, False], + [-106.45282535347631, 35.54158851537021, False], + [-95.04777932048988, 35.82906359730707, False], + [-97.42369763037011, 34.37608618524585, False], + [-104.57070459903527, 33.70875609054547, False], + [-97.41632281711793, 32.92982657064401, True], + [-94.66570627408036, 27.27007338775074, False], + [-99.1743643002243, 27.247623481915483, True], + [-104.68950862955154, 29.870865904293723, False], + [-98.10816880312582, 28.162238589797898, True], + [-93.64216163895259, 26.298614364258444, False], + [-95.19102534541413, 33.389356010635154, True], + [-96.62070599059086, 30.74805978317854, True], + [-99.3386516464026, 27.906200841991534, True], + [-100.8007152801228, 32.84683422029997, True], + [-106.20302284683326, 32.380361469176044, False], + [-94.23655356205708, 26.324497474351357, False], + [-99.65954455695915, 35.474000933282724, False], + [-94.31553240129878, 35.66875101369414, False], + [-101.83519143957733, 25.873081120803842, False], + [-99.97028199969046, 34.58909164776538, False], + [-105.71652597027627, 31.489327979058995, True], + [-105.79973754647258, 28.65472050301513, False], + [-104.61884498465494, 31.131621852439807, True], + [-102.57111696065307, 30.114279991888235, True], + [-103.32297212967914, 35.236882653404706, False], + [-100.4249645463674, 35.01412558025736, True], + [-96.2664164425719, 27.27790820412447, False], + [-95.49119347312927, 34.04880310368103, False], + [-94.76867790780145, 30.68409383373247, True], + [-97.33084948761977, 35.987707032142886, False], + [-98.9375962329848, 27.590161796852268, True], + [-100.24496746645461, 35.14003067100538, True], + [-97.82265827697529, 29.12546410605067, True], + [-103.60831990499707, 34.420657352069206, False], + [-96.6211125945082, 33.06459177171514, True], + [-99.74211708967393, 29.148959136838407, True], + [-101.74939764896713, 34.474532089579476, True], + [-94.34764438114355, 30.357142981363083, True], + [-106.52546379307893, 30.041326961454715, False], + [-101.2986640335862, 26.37652905898862, False], + [-106.4580153687541, 29.87759744762145, False], + [-100.89335102135293, 35.52499965140305, True], + [-100.8248639994296, 33.40975770839312, True], + [-103.93212196016398, 32.909497641552065, False], + [-94.67932056126476, 35.15626837470808, False], + [-106.02052521025293, 29.597198775376654, False], + [-98.2893867410877, 29.93112729119841, True], + [-99.89450058695091, 27.938220198829537, True], + [-97.3506130624274, 29.943100560338415, True], + [-102.20272670412375, 26.935152987528188, False], + [-102.76414059461521, 27.054007916590372, False], + [-106.0041901594681, 34.50108734620676, False], + [-99.34960356247895, 30.765691636568544, True], + [-93.90991359101918, 29.52415548605938, False], + [-102.45068333144832, 29.498640082146757, False], + [-93.52649708202873, 33.25384150278302, False], + [-98.11359820029512, 32.661143218490004, True], + [-93.64776248509527, 30.4684682460894, False], + [-99.4498229748733, 27.335197692456124, True], + [-106.3098030711422, 28.114811474983753, False], + [-103.19744279212784, 29.761699794107464, True], + [-98.04521749365168, 29.23374276253473, True], + [-96.02905435107336, 30.127228292010795, True], + [-98.51879935475141, 29.632065468973558, True], + [-97.51304854653931, 31.509276424390762, True], + [-103.29390200939619, 27.318075011416106, False], + [-99.9824973213597, 30.110443559920434, True], + [-99.50915156948314, 29.258458344281003, True], + [-96.41505277249307, 27.81615410871556, False], + [-104.15751695040154, 28.634566430199957, False], + [-99.54546678775662, 30.628758593109584, True], + [-94.501671157283, 26.690164729805314, False], + [-99.5998715300248, 29.29963433926709, True], + [-96.19407966991072, 26.583996951962845, False], + [-94.18130561957514, 34.39183349747304, False], + [-99.35457098320595, 29.19418372510661, True], + [-103.2439668700228, 28.7415827836878, False], + [-95.94946899471344, 26.094846437871187, False], + [-96.29555855830898, 35.61463847091715, False], + [-100.90818350967113, 32.81839948197739, True], + [-103.19547298017221, 33.780381119513564, False], + [-103.7311831715452, 34.51901410810771, False], + [-104.9802204382533, 27.04133234378543, False], + [-97.04516124978682, 26.74522583795225, False], + [-99.54055674299795, 30.957712411033505, True], + [-100.75291255499768, 34.98555626576464, True], + [-103.08923082250261, 33.115219018353876, False], + [-94.38102432053655, 33.1179055259896, True], + [-102.03982643927137, 29.9838170230828, True], + [-105.81904014957023, 28.79765172366808, False], + [-96.65811064697854, 29.033441999230575, True], + [-94.79391199397416, 32.26686772352405, True], + [-94.05970729814709, 27.578098867203245, False], + [-101.34732336260434, 31.621990539913554, True], + [-104.3518957862908, 29.119881150805877, False], + [-94.62935978446168, 30.4280012647268, True], + [-94.4545353979626, 35.54695331314244, False], + [-95.54556621919373, 36.13412073236616, False], + [-95.35828131810126, 29.196029446685102, True], + [-94.03367199299583, 29.998249298603696, True], + [-106.36092437901962, 35.88480338683122, False], + [-104.36739535175568, 34.44871919970392, False], + [-101.23847775114616, 33.38670941589996, True], + [-99.14984412490651, 33.42882053133658, True], + [-103.57375420847947, 36.10822666498764, False], + [-105.16720721276594, 35.376479016301516, False], + [-97.83947903342955, 27.52109204805256, True], + [-98.71405542652877, 35.7689674767476, False], + [-103.06998061485356, 34.82880997682268, False], + [-105.28204362921868, 28.400435333576176, False], + [-94.15911792478674, 26.17595690168265, False], + [-96.8089922559183, 28.576299745236, True], + [-94.55012730211439, 35.965323310709934, False], + [-102.35052435497836, 35.54979117991739, True], + [-104.37005549234762, 27.1898216329847, False], + [-95.68880266269171, 28.848538713723595, True], + [-99.48796021732129, 33.879870610429656, True], + [-101.00413095653694, 34.33763822793282, True], + [-106.07730385091368, 29.134515491377414, False], + [-103.45299952582074, 27.571405818685125, False], + [-102.68769661036903, 35.0508397444369, True], + [-102.12226125487027, 31.490531154286373, True], + [-99.20157845594305, 31.551754104915446, True], + [-102.55860273418811, 34.074770794654526, True], + [-106.13595097934105, 30.66643783693412, False], + [-106.62769073938416, 30.65343353159766, False], + [-106.40940133512453, 29.82237466871204, False], + [-102.1529566504516, 35.06622589749327, True], + [-102.80724491463563, 34.7087445437067, True], + [-97.64939497688633, 30.67928132241753, True], + [-96.36801227272899, 27.90369756441311, False], + [-94.09540454906752, 26.5340311979023, False], + [-100.95868651879503, 29.489244579528528, True], + [-99.00791521500773, 33.15713091296997, True], + [-94.56949551831565, 29.097678237118387, False], + [-104.30210679432342, 27.480997154392185, False], + [-102.89216000521263, 28.618705789328487, False], + [-96.97440058322412, 33.50865616454956, True], + [-98.99276448264528, 33.16482908208919, True], + [-104.83376414397748, 30.866597558655094, True], + [-105.87523535009888, 26.94560046391572, False], + [-99.29766049432588, 32.83368387034709, True], + [-102.51690788569256, 33.11843291048262, True], + [-95.8185042576338, 29.933635250495907, True], + [-95.64639798097132, 29.81408328213472, True], + [-95.27363238555648, 26.04686490238641, False], + [-97.49469880046678, 28.7839351111114, True], + [-102.55955572766781, 32.24579142286663, True], + [-102.3259956134002, 29.68040823171017, False], + [-97.73863157435005, 29.996562088019196, True], + [-104.68631893564948, 32.01946765618769, False], + [-95.04016163980175, 33.10637289096444, True], + [-104.60749147021224, 34.68835575652551, False], + [-103.65636297920052, 30.591031777106217, True], + [-102.77020591146633, 28.84679685398533, False], + [-93.68494562344958, 30.545382533660963, False], + [-105.28442207266875, 35.066432824429576, False], + [-96.25390521489425, 26.494829177985668, False], + [-98.82250217338702, 27.932953025264148, True], + [-101.74188578040746, 34.64776869635973, True], + [-106.04404882060925, 28.594522513035013, False], + [-93.67216964673989, 29.251042306696004, False], + [-99.78759245690895, 33.06425606417031, True], + [-98.40506092257863, 30.372595091544586, True], + [-102.29051625332913, 32.71012257838102, True], + [-103.71880332727147, 32.69104459346947, False], + [-97.0545647773379, 30.76611952136734, True], + [-96.38483633115239, 27.604532811233028, False], + [-106.26518681815887, 30.011599887072457, False], + [-96.29869292442771, 28.668280098851437, False], + [-101.81643591481892, 26.171067127237418, False], + [-99.84342847150265, 26.017698064471553, False], + [-104.11331752582073, 29.654316762816297, True], + [-99.97301004853021, 27.730105527140825, False], + [-106.31647836713242, 32.17703231405249, False], + [-105.53511133815337, 26.702440097120153, False], + [-96.73532103327318, 35.78642691945332, False], + [-99.1113903696459, 34.40215160301135, False], + [-105.57490309093876, 36.00074957169259, False], + [-96.91408056656418, 32.50309840505817, True], + [-97.12004011524724, 26.947775539446376, False], + [-104.31177621433217, 28.977811712282257, False], + [-94.2821165561003, 30.71331882514141, True], + [-98.29146997158631, 26.800066299911094, True], + [-103.116290122766, 31.46742167424961, True], + [-98.56811800705276, 29.888220960970425, True], + [-98.08987891135445, 27.80577602559695, True], + [-95.34430060625397, 35.0872381937164, False], + [-97.29977470332764, 28.838558342329026, True], + [-99.29340339460651, 32.1659999533891, True], + [-98.45946176172794, 35.06767544568288, False], + [-97.28077383253662, 30.77329811793486, True], + [-103.2046478070315, 33.95113909648831, False], + [-97.41533999785908, 32.08647437970406, True], + [-106.24449926545937, 26.923081786350345, False], + [-97.29470564456045, 36.18937223503928, False], + [-97.66122972103732, 34.56870670469234, False], + [-102.35020419742744, 27.349690061784784, False], + [-93.77154189004054, 33.43617694637923, False], + [-101.35103101345337, 29.463692272605073, False], + [-101.62926979404627, 32.803732493055875, True], + [-94.90780742661717, 27.023519182303016, False], + [-106.20392742378257, 32.97382450322062, False], + [-100.31113315995306, 29.431049347619815, True], + [-101.26240055732195, 36.148817757576396, True], + [-98.42727511780691, 27.460837740553018, True], + [-98.57534771820664, 35.599414672044254, False], + [-97.62020154014516, 26.369709082738453, True], + [-103.2986841163323, 32.6157912851791, False], + [-93.93578337162835, 34.486048529590185, False], + [-93.64615854059947, 28.5736836659404, False], + [-96.99391123102941, 28.896993703684423, True], + [-102.45723986918175, 35.7010832684699, True], + [-105.25314374574465, 26.38581255615052, False], + [-100.2188547460063, 28.291673320576088, True], + [-100.7877537462725, 34.49341196472571, True], + [-106.45021897380575, 26.72924721086809, False], + [-105.02103129126913, 30.757125052138996, True], + [-98.53860937298697, 32.45578723025558, True], + [-99.57879415297293, 33.96386507864504, True], + [-93.7378357236408, 27.9626758279968, False], + [-100.63489750669079, 35.38304941928768, True], + [-106.59421404071085, 30.052955341162452, False], + [-106.59172119923385, 35.689363691993364, False], + [-97.34149801358657, 32.67880291850629, True], + [-94.73963101360403, 27.850819649646564, False], + [-100.94403277172583, 33.474538412919046, True], + [-99.27226009251935, 27.468315184289615, True], + [-100.4539129843546, 34.591160165118865, True], + [-106.44494557869739, 28.035999053683224, False], + [-94.23984184184754, 29.557288201626776, False], + [-99.03587934877508, 26.59598428059316, True], + [-99.74353348891233, 32.82386415542986, True], + [-101.86634664448142, 35.898545381710065, True], + [-104.82904170889573, 35.68593461490256, False], + [-102.883660082079, 25.927764876566876, False], + [-95.10167954830852, 29.132555432953012, False], + [-104.16716765381528, 32.54432440506069, False], + [-95.8001698490917, 30.554039020076594, True], + [-98.25774265085879, 30.67468293710671, True], + [-104.14549182637904, 36.13448211420454, False], + [-100.79273106998009, 27.687205570475957, False], + [-97.9031540972447, 34.04156602854073, False], + [-99.48655188258273, 36.01928708820145, False], + [-103.78885086741725, 26.649573815350735, False], + [-97.54897028080592, 26.611495630090587, True], + [-106.18533271033974, 26.521041809084632, False], + [-102.04736124316179, 35.80143440960038, True], + [-103.53014416761859, 35.94251011771925, False], + [-106.37072741181773, 33.56020830971726, False], + [-100.5087744739241, 33.68691572355519, True], + [-103.14684873740707, 30.33159482347187, True], + [-101.04498614381122, 34.01935100090506, True], + [-99.23406810345234, 36.47780260248638, False], + [-96.38309061733807, 28.10204238295031, False], + [-106.04814322602003, 30.009165824440764, False], + [-99.09937261770088, 36.27541704541078, False], + [-94.9236280439113, 26.908216725432283, False], + [-96.03138446605873, 29.48956628516225, True], + [-101.79748242792357, 36.2396677016486, True], + [-96.7954152001291, 28.981691254375008, True], + [-95.95383982488345, 33.80047001504878, True], + [-103.89568070095685, 26.027352965725004, False], + [-104.33069038138268, 36.46092136221017, False], + [-99.40487331382435, 33.362496703874385, True], + [-98.0390467871466, 27.286158481267943, True], + [-103.93051493329094, 36.21025032666974, False], + [-100.89036166271175, 29.431712359982495, True], + [-103.01093811284001, 28.951789475547162, False], + [-105.6925789844033, 26.67611273491844, False], + [-100.63232815476351, 35.18180560295863, True], + [-95.84068243438212, 26.566440681951057, False], + [-101.69166851142406, 27.58570691504354, False], + [-96.39192036000713, 30.76580153991847, True], + [-99.60575587904657, 32.844854917935244, True], + [-99.88359268023875, 26.427305650516256, False], + [-102.49870407406512, 35.670387195714135, True], + [-99.02838249064023, 34.35433748751913, False], + [-93.69511330973809, 28.964334494491123, False], + [-102.77438414412866, 34.77587690396507, True], + [-97.20637129262126, 28.62351031644859, True], + [-97.20760493091282, 29.66509727888832, True], + [-97.78279263302204, 28.650053248724216, True], + [-105.10373645902229, 30.10605688088612, False], + [-102.29320671455936, 34.913227698065626, True], + [-102.08456452797166, 35.04888378920539, True], + [-97.684013792577, 35.91610190976283, False], + [-94.66766997135741, 28.388037123223707, False], + [-93.51699445904997, 30.745955788402362, False], + [-100.56415276925733, 27.122598143422042, False], + [-105.51909002313077, 30.081775705006855, False], + [-95.57290373716107, 26.724672719886662, False], + [-103.50352867348445, 32.004206585428776, True], + [-102.8747549124684, 32.8979077719816, True], + [-94.54178731632837, 28.121556170094824, False], + [-105.1852492618114, 34.57852818651511, False], + [-101.56833897599279, 32.394936636984475, True], + [-102.06612007055514, 30.0654558718264, True], + [-104.27037065814437, 26.539037110206124, False], + [-96.84449118726283, 34.303252013399494, False], + [-100.21448448808589, 31.640102623719255, True], + [-105.11541407363484, 32.59032999242083, False], + [-102.47865996013053, 36.106193753181586, True], + [-102.1982854302976, 30.87389402879236, True], + [-103.75935920410515, 34.164405398206384, False], + [-100.26058874460439, 29.97152621245525, True], + [-99.98584687760041, 29.138422331875738, True], + [-98.8136163575852, 31.471193466135066, True], + [-98.2634703260477, 34.441857423457066, False], + [-101.31389165577758, 26.67722696068761, False], + [-105.25939274478021, 33.42397682936403, False], + [-95.42401575533701, 36.38213160460099, False], + [-93.7911156924766, 26.16919479726154, False], + [-106.0793837632488, 32.0426040667038, False], + [-101.67576371230456, 26.55813999428948, False], + [-105.29328020590933, 33.317961711071874, False], + [-106.6282733323151, 26.640651252579072, False], + [-100.97693002993205, 29.778473204159543, True], + [-99.81270323613123, 28.521657399940562, True], + [-106.35993676923691, 32.47765856951956, False], + [-98.6318807925877, 27.313103487591995, True], + [-105.60269402006043, 33.9266565877851, False], + [-96.50522425362368, 33.17862810486471, True], + [-99.36585657273703, 27.918462376378407, True], + [-102.71165541572611, 27.26977019467567, False], + [-105.26203795210641, 33.50921690053216, False], + [-105.45340320249285, 28.841949098625847, False], + [-97.67986158008269, 31.45298937828147, True], + [-95.42927279657806, 30.012853799790946, True], + [-94.61186352922475, 30.990817003676423, True], + [-98.75882027588506, 33.21933322666259, True], + [-104.55525321655045, 36.35184775125859, False], + [-97.39087680042095, 29.262712771446328, True], + [-95.32479451597851, 32.558534263953774, True], + [-96.17652314621081, 34.39156129414083, False], + [-99.95318514227759, 29.206707699022903, True], + [-105.32756527254323, 29.177591819396113, False], + [-104.63110828519505, 35.14036164405956, False], + [-101.75109732251778, 30.720543376919306, True], + [-98.43013838260205, 28.14394272715192, True], + [-97.0734335255207, 28.750007474412804, True], + [-103.21154248047685, 32.22703974346086, False], + [-102.39810543632244, 32.819942719538474, True], + [-103.36259451330584, 27.176657526494928, False], + [-103.66650593230085, 36.025520559005244, False], + [-101.63539174258722, 27.803279475131447, False], + [-96.34811589893562, 31.14578417358513, True], + [-98.94976448687677, 31.767797366700666, True], + [-97.38029827656692, 36.27728736453143, False], + [-95.93103949592769, 28.74450380455483, True], + [-98.8151117128684, 34.70259233670797, False], + [-96.14623746963383, 30.48841309287015, True], + [-101.46340647601903, 30.19511307273737, True], + [-100.63602533061145, 31.749219881531914, True], + [-101.63505098125265, 33.69021849156649, True], + [-94.71406829659445, 31.90061330594235, True], ] for p in points: - assert p[2] == qtssr_texas.contains_point((p[0], p[1])) \ No newline at end of file + assert p[2] == qtssr_texas.contains_point((p[0], p[1])) diff --git a/libpysal/cg/tests/test_rtree.py b/libpysal/cg/tests/test_rtree.py index a67134352..cb4df5c05 100644 --- a/libpysal/cg/tests/test_rtree.py +++ b/libpysal/cg/tests/test_rtree.py @@ -1,11 +1,12 @@ - """pyrtree Unittest.""" + from ..rtree import RTree, Rect import unittest class Pyrtree_Tester(unittest.TestCase): - """setup class for unit tests.""" + """Setup class for unit tests.""" + def setUp(self): k = 10 w = 20 @@ -36,8 +37,7 @@ def test_rtree(self): self.assertEqual(res, [0, 1, 10, 11]) # vertices are shared by all coincident rectangles - res = [r.leaf_obj( - ) for r in t.query_point((20.0, 20.0)) if r.is_leaf()] + res = [r.leaf_obj() for r in t.query_point((20.0, 20.0)) if r.is_leaf()] self.assertEqual(len(res), 4) res = [r.leaf_obj() for r in t.query_point((21, 20)) if r.is_leaf()] @@ -63,6 +63,6 @@ def test_rtree(self): a = unittest.TestLoader().loadTestsFromTestCase(i) suite.addTest(a) -if __name__ == '__main__': +if __name__ == "__main__": runner = unittest.TextTestRunner() runner.run(suite) diff --git a/libpysal/cg/tests/test_segmentLocator.py b/libpysal/cg/tests/test_segmentLocator.py index 4bea86773..97c46f2b5 100644 --- a/libpysal/cg/tests/test_segmentLocator.py +++ b/libpysal/cg/tests/test_segmentLocator.py @@ -1,11 +1,13 @@ """Segment Locator Unittest.""" + from ..shapes import * from ..segmentLocator import * import unittest class SegmentGrid_Tester(unittest.TestCase): - """setup class for unit tests.""" + """Setup class for unit tests.""" + def setUp(self): # 10x10 grid with four line segments, one for each edge of the grid. self.grid = SegmentGrid(Rectangle(0, 0, 10, 10), 1) @@ -15,26 +17,25 @@ def setUp(self): self.grid.add(LineSegment(Point((10.0, 0.0)), Point((0.0, 0.0))), 3) def test_nearest_1(self): - self.assertEqual([0, 1, 2, 3], self.grid.nearest(Point(( - 5.0, 5.0)))) # Center + self.assertEqual([0, 1, 2, 3], self.grid.nearest(Point((5.0, 5.0)))) # Center + self.assertEqual([0], self.grid.nearest(Point((0.0, 5.0)))) # Left Edge + self.assertEqual([1], self.grid.nearest(Point((5.0, 10.0)))) # Top Edge + self.assertEqual([2], self.grid.nearest(Point((10.0, 5.0)))) # Right Edge + self.assertEqual([3], self.grid.nearest(Point((5.0, 0.0)))) # Bottom Edge + + def test_nearest_2(self): self.assertEqual( - [0], self.grid.nearest(Point((0.0, 5.0)))) # Left Edge + [0, 1, 3], self.grid.nearest(Point((-100000.0, 5.0))) + ) # Left Edge self.assertEqual( - [1], self.grid.nearest(Point((5.0, 10.0)))) # Top Edge + [1, 2, 3], self.grid.nearest(Point((100000.0, 5.0))) + ) # Right Edge self.assertEqual( - [2], self.grid.nearest(Point((10.0, 5.0)))) # Right Edge + [0, 2, 3], self.grid.nearest(Point((5.0, -100000.0))) + ) # Bottom Edge self.assertEqual( - [3], self.grid.nearest(Point((5.0, 0.0)))) # Bottom Edge - - def test_nearest_2(self): - self.assertEqual([0, 1, 3], self.grid.nearest(Point((- - 100000.0, 5.0)))) # Left Edge - self.assertEqual([1, 2, 3], self.grid.nearest(Point(( - 100000.0, 5.0)))) # Right Edge - self.assertEqual([0, 2, 3], self.grid.nearest(Point((5.0, - -100000.0)))) # Bottom Edge - self.assertEqual([0, 1, 2], self.grid.nearest(Point((5.0, - 100000.0)))) # Top Edge + [0, 1, 2], self.grid.nearest(Point((5.0, 100000.0))) + ) # Top Edge suite = unittest.TestSuite() @@ -43,6 +44,6 @@ def test_nearest_2(self): a = unittest.TestLoader().loadTestsFromTestCase(i) suite.addTest(a) -if __name__ == '__main__': +if __name__ == "__main__": runner = unittest.TextTestRunner() runner.run(suite) diff --git a/libpysal/cg/tests/test_shapes.py b/libpysal/cg/tests/test_shapes.py index 0e97c51d4..4eded147c 100644 --- a/libpysal/cg/tests/test_shapes.py +++ b/libpysal/cg/tests/test_shapes.py @@ -4,57 +4,60 @@ class test_Point(unittest.TestCase): - def test___init__1(self): - """ - Tests whether points are created without issue. + """Tests whether points are created without issue. Test tag: #tests#Point.__init__ + """ for l in [(-5.0, 10.0), (0.0, -6.0), (float(1e300), float(-1e300))]: p = Point(l) def test___str__1(self): - """ - Tests whether the string produced is valid for corner cases. + """Tests whether the string produced is valid for corner cases. Test tag: #tests#Point__str__ + """ for l in [(-5, 10), (0, -6.0), (float(1e300), -1e300)]: p = Point(l) - self.assertEqual(str(p), str((float(l[0]), float( - l[1])))) # Recast to floats like point does + self.assertEqual( + str(p), str((float(l[0]), float(l[1]))) + ) # Recast to floats like point does class test_LineSegment(unittest.TestCase): - def test_is_ccw1(self): - """ - Test corner cases for horizontal segment starting at origin. + """Test corner cases for horizontal segment starting at origin. Test tag: #tests#LineSegment.is_ccw + """ ls = LineSegment(Point((0, 0)), Point((5, 0))) - self.assertFalse(ls.is_ccw( - Point((10, 0)))) # At positive boundary beyond segment + self.assertFalse( + ls.is_ccw(Point((10, 0))) + ) # At positive boundary beyond segment self.assertFalse(ls.is_ccw(Point((3, 0)))) # On segment - self.assertFalse(ls.is_ccw( - Point((-10, 0)))) # At negative boundary beyond segment + self.assertFalse( + ls.is_ccw(Point((-10, 0))) + ) # At negative boundary beyond segment self.assertFalse(ls.is_ccw(Point((0, 0)))) # Endpoint of segment self.assertFalse(ls.is_ccw(Point((5, 0)))) # Endpoint of segment def test_is_ccw2(self): - """ - Test corner cases for vertical segment ending at origin. + """Test corner cases for vertical segment ending at origin. Test tag: #tests#LineSegment.is_ccw + """ ls = LineSegment(Point((0, -5)), Point((0, 0))) - self.assertFalse(ls.is_ccw( - Point((0, 10)))) # At positive boundary beyond segment + self.assertFalse( + ls.is_ccw(Point((0, 10))) + ) # At positive boundary beyond segment self.assertFalse(ls.is_ccw(Point((0, -3)))) # On segment - self.assertFalse(ls.is_ccw( - Point((0, -10)))) # At negative boundary beyond segment + self.assertFalse( + ls.is_ccw(Point((0, -10))) + ) # At negative boundary beyond segment self.assertFalse(ls.is_ccw(Point((0, -5)))) # Endpoint of segment self.assertFalse(ls.is_ccw(Point((0, 0)))) # Endpoint of segment @@ -65,64 +68,72 @@ def test_is_ccw3(self): Test tag: #tests#LineSegment.is_ccw """ ls = LineSegment(Point((0, 1)), Point((5, 6))) - self.assertFalse(ls.is_ccw( - Point((10, 11)))) # At positive boundary beyond segment + self.assertFalse( + ls.is_ccw(Point((10, 11))) + ) # At positive boundary beyond segment self.assertFalse(ls.is_ccw(Point((3, 4)))) # On segment - self.assertFalse(ls.is_ccw( - Point((-10, -9)))) # At negative boundary beyond segment + self.assertFalse( + ls.is_ccw(Point((-10, -9))) + ) # At negative boundary beyond segment self.assertFalse(ls.is_ccw(Point((0, 1)))) # Endpoint of segment self.assertFalse(ls.is_ccw(Point((5, 6)))) # Endpoint of segment def test_is_cw1(self): - """ - Test corner cases for horizontal segment starting at origin. + """Test corner cases for horizontal segment starting at origin. Test tag: #tests#LineSegment.is_cw + """ ls = LineSegment(Point((0, 0)), Point((5, 0))) - self.assertFalse(ls.is_cw( - Point((10, 0)))) # At positive boundary beyond segment + self.assertFalse( + ls.is_cw(Point((10, 0))) + ) # At positive boundary beyond segment self.assertFalse(ls.is_cw(Point((3, 0)))) # On segment - self.assertFalse(ls.is_cw( - Point((-10, 0)))) # At negative boundary beyond segment + self.assertFalse( + ls.is_cw(Point((-10, 0))) + ) # At negative boundary beyond segment self.assertFalse(ls.is_cw(Point((0, 0)))) # Endpoint of segment self.assertFalse(ls.is_cw(Point((5, 0)))) # Endpoint of segment def test_is_cw2(self): - """ - Test corner cases for vertical segment ending at origin. + """Test corner cases for vertical segment ending at origin. Test tag: #tests#LineSegment.is_cw + """ ls = LineSegment(Point((0, -5)), Point((0, 0))) - self.assertFalse(ls.is_cw( - Point((0, 10)))) # At positive boundary beyond segment + self.assertFalse( + ls.is_cw(Point((0, 10))) + ) # At positive boundary beyond segment self.assertFalse(ls.is_cw(Point((0, -3)))) # On segment - self.assertFalse(ls.is_cw( - Point((0, -10)))) # At negative boundary beyond segment + self.assertFalse( + ls.is_cw(Point((0, -10))) + ) # At negative boundary beyond segment self.assertFalse(ls.is_cw(Point((0, -5)))) # Endpoint of segment self.assertFalse(ls.is_cw(Point((0, 0)))) # Endpoint of segment def test_is_cw3(self): - """ - Test corner cases for non-axis-aligned segment not through origin. + """Test corner cases for non-axis-aligned segment not through origin. Test tag: #tests#LineSegment.is_cw + """ ls = LineSegment(Point((0, 1)), Point((5, 6))) - self.assertFalse(ls.is_cw( - Point((10, 11)))) # At positive boundary beyond segment + self.assertFalse( + ls.is_cw(Point((10, 11))) + ) # At positive boundary beyond segment self.assertFalse(ls.is_cw(Point((3, 4)))) # On segment - self.assertFalse(ls.is_cw( - Point((-10, -9)))) # At negative boundary beyond segment + self.assertFalse( + ls.is_cw(Point((-10, -9))) + ) # At negative boundary beyond segment self.assertFalse(ls.is_cw(Point((0, 1)))) # Endpoint of segment self.assertFalse(ls.is_cw(Point((5, 6)))) # Endpoint of segment def test_get_swap1(self): - """ - Tests corner cases. + """Tests corner cases. Test tag: #tests#LineSegment.get_swap + """ ls = LineSegment(Point((0, 0)), Point((10, 0))) swap = ls.get_swap() @@ -145,10 +156,10 @@ def test_get_swap1(self): self.assertEqual(ls.p2, swap.p1) def test_bounding_box(self): - """ - Tests corner cases. + """Tests corner cases. Test tag: #tests#LineSegment.bounding_box + """ ls = LineSegment(Point((0, 0)), Point((0, 10))) self.assertEqual(ls.bounding_box.left, 0) @@ -169,10 +180,10 @@ def test_bounding_box(self): self.assertEqual(ls.bounding_box.upper, 0) def test_len1(self): - """ - Tests corner cases. + """Tests corner cases. Test tag: #tests#LineSegment.len + """ ls = LineSegment(Point((0, 0)), Point((0, 0))) self.assertEqual(ls.len, 0) @@ -181,55 +192,52 @@ def test_len1(self): self.assertEqual(ls.len, 3) def test_line1(self): - """ - Tests corner cases. + """Tests corner cases. Test tag: #tests#LineSegment.line + """ import math + ls = LineSegment(Point((0, 0)), Point((1, 0))) self.assertEqual(ls.line.m, 0) self.assertEqual(ls.line.b, 0) ls = LineSegment(Point((0, 0)), Point((0, 1))) - self.assertEqual(ls.line.m, float('inf')) + self.assertEqual(ls.line.m, float("inf")) self.assertTrue(math.isnan(ls.line.b)) ls = LineSegment(Point((0, 0)), Point((0, -1))) - self.assertEqual(ls.line.m, float('inf')) + self.assertEqual(ls.line.m, float("inf")) self.assertTrue(math.isnan(ls.line.b)) ls = LineSegment(Point((0, 0)), Point((0, 0))) self.assertEqual(ls.line, None) - ls = LineSegment(Point((5,0)), Point((10,0))) - ls1 = LineSegment(Point((5,0)), Point((10,1))) + ls = LineSegment(Point((5, 0)), Point((10, 0))) + ls1 = LineSegment(Point((5, 0)), Point((10, 1))) self.assertTrue(ls.intersect(ls1)) - ls2 = LineSegment(Point((5,1)), Point((10,1))) + ls2 = LineSegment(Point((5, 1)), Point((10, 1))) self.assertFalse(ls.intersect(ls2)) - ls2 = LineSegment(Point((7,-1)), Point((7,2))) + ls2 = LineSegment(Point((7, -1)), Point((7, 2))) self.assertTrue(ls.intersect(ls2)) - - - class test_Line(unittest.TestCase): - def test___init__1(self): - """ - Tests a variety of generic cases. + """Tests a variety of generic cases. Test tag: #tests#Line.__init__ + """ for m, b in [(4, 0.0), (-140, 5), (0, 0)]: l = Line(m, b) def test_y1(self): - """ - Tests a variety of generic and special cases (+-infinity). + """Tests a variety of generic and special cases (+-infinity). Test tag: #tests#Line.y + """ l = Line(0, 0) self.assertEqual(l.y(0), 0) @@ -247,13 +255,13 @@ def test_y1(self): self.assertEqual(l.y(1e600), -1e600) def test_x1(self): - """ - Tests a variety of generic and special cases (+-infinity). + """Tests a variety of generic and special cases (+-infinity). Test tag: #tests#Line.x + """ l = Line(0, 0) - #self.assertEquals(l.x(0), 0) + # self.assertEquals(l.x(0), 0) with self.assertRaises(ArithmeticError): l.x(0) with self.assertRaises(ArithmeticError): @@ -273,64 +281,80 @@ def test_x1(self): class test_Ray(unittest.TestCase): - def test___init__1(self): - """ - Tests generic cases. + """Tests generic cases. - #tests#Ray.__init__ + Test tag: #tests#Ray.__init__ + """ r = Ray(Point((0, 0)), Point((1, 1))) r = Ray(Point((8, -3)), Point((-5, 9))) class test_Chain(unittest.TestCase): - def test___init__1(self): - """ - Generic testing that no exception is thrown. + """Generic testing that no exception is thrown. Test tag: #tests#Chain.__init__ + """ c = Chain([Point((0, 0))]) c = Chain([[Point((0, 0)), Point((1, 1))], [Point((2, 5))]]) def test_vertices1(self): - """ - Testing for repeated vertices and multiple parts. + """Testing for repeated vertices and multiple parts. Test tag: #tests#Chain.vertices + """ - vertices = [Point((0, 0)), Point((1, 1)), Point((2, 5)), - Point((0, 0)), Point((1, 1)), Point((2, 5))] + vertices = [ + Point((0, 0)), + Point((1, 1)), + Point((2, 5)), + Point((0, 0)), + Point((1, 1)), + Point((2, 5)), + ] self.assertEqual(Chain(vertices).vertices, vertices) - vertices = [[Point((0, 0)), Point((1, 1)), Point((2, 5))], - [Point((0, 0)), Point((1, 1)), Point((2, 5))]] + vertices = [ + [Point((0, 0)), Point((1, 1)), Point((2, 5))], + [Point((0, 0)), Point((1, 1)), Point((2, 5))], + ] self.assertEqual(Chain(vertices).vertices, vertices[0] + vertices[1]) def test_parts1(self): - """ - Generic testing of parts functionality. + """Generic testing of parts functionality. Test tag: #tests#Chain.parts + """ - vertices = [Point((0, 0)), Point((1, 1)), Point((2, 5)), - Point((0, 0)), Point((1, 1)), Point((2, 5))] + vertices = [ + Point((0, 0)), + Point((1, 1)), + Point((2, 5)), + Point((0, 0)), + Point((1, 1)), + Point((2, 5)), + ] self.assertEqual(Chain(vertices).parts, [vertices]) - vertices = [[Point((0, 0)), Point((1, 1)), Point((2, 5))], - [Point((0, 0)), Point((1, 1)), Point((2, 5))]] + vertices = [ + [Point((0, 0)), Point((1, 1)), Point((2, 5))], + [Point((0, 0)), Point((1, 1)), Point((2, 5))], + ] self.assertEqual(Chain(vertices).parts, vertices) def test_bounding_box1(self): - """ - Test correctness with multiple parts. + """Test correctness with multiple parts. Test tag: #tests#Chain.bounding_box + """ - vertices = [[Point((0, 0)), Point((1, 1)), Point((2, 6))], - [Point((-5, -5)), Point((0, 0)), Point((2, 5))]] + vertices = [ + [Point((0, 0)), Point((1, 1)), Point((2, 6))], + [Point((-5, -5)), Point((0, 0)), Point((2, 5))], + ] bb = Chain(vertices).bounding_box self.assertEqual(bb.left, -5) self.assertEqual(bb.lower, -5) @@ -338,23 +362,24 @@ def test_bounding_box1(self): self.assertEqual(bb.upper, 6) def test_len1(self): - """ - Test correctness with multiple parts and zero-length point-to-point distances. + """Test correctness with multiple parts and zero-length point-to-point distances. Test tag: #tests#Chain.len + """ - vertices = [[Point((0, 0)), Point((1, 0)), Point((1, 5))], - [Point((-5, -5)), Point((-5, 0)), Point((0, 0)), Point((0, 0))]] + vertices = [ + [Point((0, 0)), Point((1, 0)), Point((1, 5))], + [Point((-5, -5)), Point((-5, 0)), Point((0, 0)), Point((0, 0))], + ] self.assertEqual(Chain(vertices).len, 6 + 10) class test_Polygon(unittest.TestCase): - def test___init__1(self): - """ - Test various input configurations (list vs. lists of lists, holes) + """Test various input configurations (list vs. lists of lists, holes) #tests#Polygon.__init__ + """ # Input configurations tested (in order of test): # one part, no holes @@ -363,99 +388,113 @@ def test___init__1(self): # multi part, one hole # one part, multi holes # multi part, multi holes - p = Polygon([Point( - (0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))]) + p = Polygon([Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))]) p = Polygon( - [[Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))]]) + [ + [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], + [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))], + ] + ) p = Polygon( [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - holes=[Point((2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))]) + holes=[Point((2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], + ) p = Polygon( - [[Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - [Point( - ( - 30, 30)), Point( - (40, 30)), Point((40, 40)), Point((30, 40))]], - holes=[Point((2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))]) + [ + [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], + [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))], + ], + holes=[Point((2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], + ) p = Polygon( [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - holes=[[Point( - (2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], - [Point((6, 6)), Point((6, 8)), Point((8, 8)), Point((8, 6))]]) + holes=[ + [Point((2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], + [Point((6, 6)), Point((6, 8)), Point((8, 8)), Point((8, 6))], + ], + ) p = Polygon( - [[Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - [Point( - ( - 30, 30)), Point( - (40, 30)), Point((40, 40)), Point((30, 40))]], - holes=[[Point( - (2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], - [Point((6, 6)), Point((6, 8)), Point((8, 8)), Point((8, 6))]]) + [ + [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], + [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))], + ], + holes=[ + [Point((2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], + [Point((6, 6)), Point((6, 8)), Point((8, 8)), Point((8, 6))], + ], + ) def test_area1(self): - """ - Test multiple parts. + """Test multiple parts. Test tag: #tests#Polygon.area + """ p = Polygon( - [[Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))]]) + [ + [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], + [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))], + ] + ) self.assertEqual(p.area, 200) def test_area2(self): - """ - Test holes. + """Test holes. Test tag: #tests#Polygon.area + """ p = Polygon( [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - holes=[Point((2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))]) + holes=[Point((2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], + ) self.assertEqual(p.area, 100 - 4) p = Polygon( [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - holes=[[Point( - (2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], - [Point((6, 6)), Point((6, 8)), Point((8, 8)), Point((8, 6))]]) + holes=[ + [Point((2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], + [Point((6, 6)), Point((6, 8)), Point((8, 8)), Point((8, 6))], + ], + ) self.assertEqual(p.area, 100 - (4 + 4)) p = Polygon( - [[Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - [Point( - ( - 30, 30)), Point( - (40, 30)), Point((40, 40)), Point((30, 40))]], - holes=[[Point( - (2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], - [Point((36, 36)), Point((36, 38)), Point((38, 38)), Point((38, 36))]]) + [ + [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], + [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))], + ], + holes=[ + [Point((2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], + [Point((36, 36)), Point((36, 38)), Point((38, 38)), Point((38, 36))], + ], + ) self.assertEqual(p.area, 200 - (4 + 4)) def test_area4(self): - """ - Test polygons with vertices in both orders (cw, ccw). + """Test polygons with vertices in both orders (cw, ccw). Test tag: #tests#Polygon.area + """ - p = Polygon([Point( - (0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))]) + p = Polygon([Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))]) self.assertEqual(p.area, 100) - p = Polygon([Point( - (0, 0)), Point((0, 10)), Point((10, 10)), Point((10, 0))]) + p = Polygon([Point((0, 0)), Point((0, 10)), Point((10, 10)), Point((10, 0))]) self.assertEqual(p.area, 100) def test_bounding_box1(self): - """ - Test polygons with multiple parts. + """Test polygons with multiple parts. Test tag: #tests#Polygon.bounding_box + """ p = Polygon( - [[Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))]]) + [ + [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], + [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))], + ] + ) bb = p.bounding_box self.assertEqual(bb.left, 0) self.assertEqual(bb.lower, 0) @@ -463,141 +502,190 @@ def test_bounding_box1(self): self.assertEqual(bb.upper, 40) def test_centroid1(self): - """ - Test polygons with multiple parts of the same size. + """Test polygons with multiple parts of the same size. Test tag: #tests#Polygon.centroid + """ p = Polygon( - [[Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))]]) + [ + [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], + [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))], + ] + ) c = p.centroid self.assertEqual(c[0], 20) self.assertEqual(c[1], 20) def test_centroid2(self): - """ - Test polygons with multiple parts of different size. + """Test polygons with multiple parts of different size. Test tag: #tests#Polygon.centroid + """ p = Polygon( - [[Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - [Point((30, 30)), Point((35, 30)), Point((35, 35)), Point((30, 35))]]) + [ + [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], + [Point((30, 30)), Point((35, 30)), Point((35, 35)), Point((30, 35))], + ] + ) c = p.centroid self.assertEqual(c[0], 10.5) self.assertEqual(c[1], 10.5) def test_holes1(self): - """ - Test for correct vertex values/order. + """Test for correct vertex values/order. Test tag: #tests#Polygon.holes + """ p = Polygon( [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - holes=[Point((2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))]) + holes=[Point((2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], + ) self.assertEqual(len(p.holes), 1) e_holes = [Point((2, 2)), Point((2, 4)), Point((4, 4)), Point((4, 2))] - self.assertTrue(p.holes[0] in [e_holes, [e_holes[-1]] + e_holes[:3], - e_holes[-2:] + e_holes[:2], e_holes[-3:] + [e_holes[0]]]) + self.assertTrue( + p.holes[0] + in [ + e_holes, + [e_holes[-1]] + e_holes[:3], + e_holes[-2:] + e_holes[:2], + e_holes[-3:] + [e_holes[0]], + ] + ) def test_holes2(self): - """ - Test for multiple holes. + """Test for multiple holes. Test tag: #tests#Polygon.holes + """ p = Polygon( [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - holes=[[Point( - (2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], - [Point((6, 6)), Point((6, 8)), Point((8, 8)), Point((8, 6))]]) + holes=[ + [Point((2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], + [Point((6, 6)), Point((6, 8)), Point((8, 8)), Point((8, 6))], + ], + ) holes = p.holes self.assertEqual(len(holes), 2) def test_parts1(self): - """ - Test for correct vertex values/order. + """Test for correct vertex values/order. Test tag: #tests#Polygon.parts + """ p = Polygon( - [[Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - [Point((30, 30)), Point((40, 30)), Point((30, 40))]]) + [ + [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], + [Point((30, 30)), Point((40, 30)), Point((30, 40))], + ] + ) self.assertEqual(len(p.parts), 2) - part1 = [Point( - (0, 0)), Point((0, 10)), Point((10, 10)), Point((10, 0))] + part1 = [Point((0, 0)), Point((0, 10)), Point((10, 10)), Point((10, 0))] part2 = [Point((30, 30)), Point((30, 40)), Point((40, 30))] if len(p.parts[0]) == 4: - self.assertTrue(p.parts[0] in [part1, part1[-1:] + part1[:3], - part1[-2:] + part1[:2], part1[-3:] + part1[:1]]) - self.assertTrue(p.parts[1] in [part2, part2[-1:] + - part2[:2], part2[-2:] + part2[:1]]) + self.assertTrue( + p.parts[0] + in [ + part1, + part1[-1:] + part1[:3], + part1[-2:] + part1[:2], + part1[-3:] + part1[:1], + ] + ) + self.assertTrue( + p.parts[1] in [part2, part2[-1:] + part2[:2], part2[-2:] + part2[:1]] + ) elif len(p.parts[0]) == 3: - self.assertTrue(p.parts[0] in [part2, part2[-1:] + - part2[:2], part2[-2:] + part2[:1]]) - self.assertTrue(p.parts[1] in [part1, part1[-1:] + part1[:3], - part1[-2:] + part1[:2], part1[-3:] + part1[:1]]) + self.assertTrue( + p.parts[0] in [part2, part2[-1:] + part2[:2], part2[-2:] + part2[:1]] + ) + self.assertTrue( + p.parts[1] + in [ + part1, + part1[-1:] + part1[:3], + part1[-2:] + part1[:2], + part1[-3:] + part1[:1], + ] + ) else: self.fail() def test_perimeter1(self): - """ - Test with multiple parts. + """Test with multiple parts. Test tag: #tests#Polygon.perimeter + """ p = Polygon( - [[Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))]]) + [ + [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], + [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))], + ] + ) self.assertEqual(p.perimeter, 80) def test_perimeter2(self): - """ - Test with holes. + """Test with holes. Test tag: #tests#Polygon.perimeter + """ p = Polygon( - [[Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - [Point( - ( - 30, 30)), Point( - (40, 30)), Point((40, 40)), Point((30, 40))]], - holes=[[Point( - (2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], - [Point((6, 6)), Point((6, 8)), Point((8, 8)), Point((8, 6))]]) + [ + [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], + [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))], + ], + holes=[ + [Point((2, 2)), Point((4, 2)), Point((4, 4)), Point((2, 4))], + [Point((6, 6)), Point((6, 8)), Point((8, 8)), Point((8, 6))], + ], + ) self.assertEqual(p.perimeter, 80 + 16) def test_vertices1(self): - """ - Test for correct values/order of vertices. + """Test for correct values/order of vertices. Test tag: #tests#Polygon.vertices + """ - p = Polygon([Point( - (0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))]) + p = Polygon([Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))]) self.assertEqual(len(p.vertices), 4) - e_verts = [Point( - (0, 0)), Point((0, 10)), Point((10, 10)), Point((10, 0))] - self.assertTrue(p.vertices in [e_verts, e_verts[-1:] + e_verts[:3], - e_verts[-2:] + e_verts[:2], e_verts[-3:] + e_verts[:1]]) + e_verts = [Point((0, 0)), Point((0, 10)), Point((10, 10)), Point((10, 0))] + self.assertTrue( + p.vertices + in [ + e_verts, + e_verts[-1:] + e_verts[:3], + e_verts[-2:] + e_verts[:2], + e_verts[-3:] + e_verts[:1], + ] + ) def test_vertices2(self): - """ - Test for multiple parts. + """Test for multiple parts. Test tag: #tests#Polygon.vertices + """ p = Polygon( - [[Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], - [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))]]) + [ + [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], + [Point((30, 30)), Point((40, 30)), Point((40, 40)), Point((30, 40))], + ] + ) self.assertEqual(len(p.vertices), 8) def test_contains_point(self): - p = Polygon([Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], [Point((1, 2)), Point((2, 2)), Point((2, 1)), Point((1, 1))]) + p = Polygon( + [Point((0, 0)), Point((10, 0)), Point((10, 10)), Point((0, 10))], + [Point((1, 2)), Point((2, 2)), Point((2, 1)), Point((1, 1))], + ) self.assertEqual(p.contains_point((0, 0)), 0) self.assertEqual(p.contains_point((1, 1)), 0) self.assertEqual(p.contains_point((5, 5)), 1) @@ -605,12 +693,11 @@ def test_contains_point(self): class test_Rectangle(unittest.TestCase): - def test___init__1(self): - """ - Test exceptions are thrown correctly. + """Test exceptions are thrown correctly. Test tag: #tests#Rectangle.__init__ + """ try: r = Rectangle(1, 1, -1, 5) # right < left @@ -627,10 +714,10 @@ def test___init__1(self): self.fail() def test_set_centroid1(self): - """ - Test with rectangles of zero width or height. + """Test with rectangles of zero width or height. Test tag: #tests#Rectangle.set_centroid + """ r = Rectangle(5, 5, 5, 10) # Zero width r.set_centroid(Point((0, 0))) @@ -654,10 +741,10 @@ def test_set_centroid1(self): self.assertEqual(r.upper, -4) def test_set_scale1(self): - """ - Test repeated scaling. + """Test repeated scaling. Test tag: #tests#Rectangle.set_scale + """ r = Rectangle(2, 2, 4, 4) @@ -674,10 +761,10 @@ def test_set_scale1(self): self.assertEqual(r.upper, 4) def test_set_scale2(self): - """ - Test scaling of rectangles with zero width/height.. + """Test scaling of rectangles with zero width/height.. Test tag: #tests#Rectangle.set_scale + """ r = Rectangle(5, 5, 5, 10) # Zero width r.set_scale(2) @@ -708,10 +795,10 @@ def test_set_scale2(self): self.assertEqual(r.upper, 0) def test_area1(self): - """ - Test rectangles with zero width/height + """Test rectangles with zero width/height Test tag: #tests#Rectangle.area + """ r = Rectangle(5, 5, 5, 10) # Zero width self.assertEqual(r.area, 0) @@ -723,35 +810,35 @@ def test_area1(self): self.assertEqual(r.area, 0) def test_height1(self): - """ - Test rectangles with zero height. + """Test rectangles with zero height. Test tag: #tests#Rectangle.height + """ r = Rectangle(10, 5, 20, 5) # Zero height self.assertEqual(r.height, 0) def test_width1(self): - """ - Test rectangles with zero width. + """Test rectangles with zero width. Test tag: #tests#Rectangle.width + """ r = Rectangle(5, 5, 5, 10) # Zero width self.assertEqual(r.width, 0) -#suite = unittest.TestSuite() -#suite.addTest(doctest.DocTestSuite('pysal.cg.shapes')) -#A = unittest.TestLoader().loadTestsFromTestCase(_TestPoint) -#B = unittest.TestLoader().loadTestsFromTestCase(_TestLineSegment) -#C = unittest.TestLoader().loadTestsFromTestCase(_TestLine) -#D = unittest.TestLoader().loadTestsFromTestCase(_TestRay) -#E = unittest.TestLoader().loadTestsFromTestCase(_TestChain) -#F = unittest.TestLoader().loadTestsFromTestCase(_TestPolygon) -#G = unittest.TestLoader().loadTestsFromTestCase(_TestRectangle) -#suite.addTests([A,B,C,D,E,D,G]) -if __name__ == '__main__': +# suite = unittest.TestSuite() +# suite.addTest(doctest.DocTestSuite('pysal.cg.shapes')) +# A = unittest.TestLoader().loadTestsFromTestCase(_TestPoint) +# B = unittest.TestLoader().loadTestsFromTestCase(_TestLineSegment) +# C = unittest.TestLoader().loadTestsFromTestCase(_TestLine) +# D = unittest.TestLoader().loadTestsFromTestCase(_TestRay) +# E = unittest.TestLoader().loadTestsFromTestCase(_TestChain) +# F = unittest.TestLoader().loadTestsFromTestCase(_TestPolygon) +# G = unittest.TestLoader().loadTestsFromTestCase(_TestRectangle) +# suite.addTests([A,B,C,D,E,D,G]) +if __name__ == "__main__": unittest.main() - #runner = unittest.TextTestRunner() - #runner.run(suite) + # runner = unittest.TextTestRunner() + # runner.run(suite) diff --git a/libpysal/cg/tests/test_sphere.py b/libpysal/cg/tests/test_sphere.py index 2a2260ca4..9e253222d 100644 --- a/libpysal/cg/tests/test_sphere.py +++ b/libpysal/cg/tests/test_sphere.py @@ -7,11 +7,10 @@ class Sphere(unittest.TestCase): - def setUp(self): self.pt0 = (0, 0) self.pt1 = (180, 0) - f = psopen(pysal_examples.get_path('stl_hom.shp'), 'r') + f = psopen(pysal_examples.get_path("stl_hom.shp"), "r") self.shapes = f.read() self.p0 = (-87.893517, 41.981417) self.p1 = (-87.519295, 41.657498) @@ -30,7 +29,7 @@ def test_arcdist2linear(self): def test_radangle(self): p0 = (-87.893517, 41.981417) p1 = (-87.519295, 41.657498) - self.assertAlmostEqual(sphere.radangle(p0,p1), 0.007460167953189258) + self.assertAlmostEqual(sphere.radangle(p0, p1), 0.007460167953189258) def test_linear2arcdist(self): d = sphere.arcdist(self.pt0, self.pt1, sphere.RADIUS_EARTH_MILES) @@ -38,11 +37,9 @@ def test_linear2arcdist(self): self.assertEqual(d, ad) def test_harcdist(self): - d1 = sphere.harcdist(self.p0, self.p1, - radius=sphere.RADIUS_EARTH_MILES) + d1 = sphere.harcdist(self.p0, self.p1, radius=sphere.RADIUS_EARTH_MILES) self.assertAlmostEqual(d1, 29.532983644123796) - d1 = sphere.harcdist(self.p3, self.p4, - radius=sphere.RADIUS_EARTH_MILES) + d1 = sphere.harcdist(self.p3, self.p4, radius=sphere.RADIUS_EARTH_MILES) self.assertAlmostEqual(d1, 25.871647470233675) def test_geointerpolate(self): @@ -52,61 +49,116 @@ def test_geointerpolate(self): self.assertAlmostEqual(pn2, (41.949079912574796, -87.85592403438788)) def test_geogrid(self): - grid = [(42.023768, -87.946389), (42.02393997819538, - -87.80562679358316), - (42.02393997819538, -87.66486420641684), (42.023768, - -87.524102), - (41.897317, -87.94638900000001), (41.8974888973743, - -87.80562679296166), - (41.8974888973743, -87.66486420703835), (41.897317, - -87.524102), - (41.770866000000005, -87.94638900000001), (41.77103781320412, - -87.80562679234043), - (41.77103781320412, -87.66486420765956), (41.770866000000005, - -87.524102), - (41.644415, -87.946389), (41.64458672568646, - -87.80562679171955), - (41.64458672568646, -87.66486420828045), (41.644415, - -87.524102)] + grid = [ + (42.023768, -87.946389), + (42.02393997819538, -87.80562679358316), + (42.02393997819538, -87.66486420641684), + (42.023768, -87.524102), + (41.897317, -87.94638900000001), + (41.8974888973743, -87.80562679296166), + (41.8974888973743, -87.66486420703835), + (41.897317, -87.524102), + (41.770866000000005, -87.94638900000001), + (41.77103781320412, -87.80562679234043), + (41.77103781320412, -87.66486420765956), + (41.770866000000005, -87.524102), + (41.644415, -87.946389), + (41.64458672568646, -87.80562679171955), + (41.64458672568646, -87.66486420828045), + (41.644415, -87.524102), + ] - pup = (42.023768, -87.946389) # Arlington Heights IL + pup = (42.023768, -87.946389) # Arlington Heights IL pdown = (41.644415, -87.524102) # Hammond, IN grid1 = sphere.geogrid(pup, pdown, 3, lonx=False) np.testing.assert_array_almost_equal(grid, grid1) - def test_toXYZ(self): - w2 = {0: [2, 5, 6, 10], 1: [4, 7, 9, 14], 2: [6, 0, 3, 8], - 3: [8, 2, 12, 4], 4: [1, 9, 12, 3], 5: [11, 10, 0, 15], - 6: [2, 10, 8, 0], 7: [14, 1, 16, 9], 8: [12, 3, 19, 6], - 9: [12, 16, 4, 1], 10: [17, 6, 15, 5], 11: [15, 13, 5, 21], - 12: [8, 19, 9, 3], 13: [21, 11, 15, 28], 14: - [7, 16, 22, 9], 15: [11, 27, 10, 26], 16: [14, 25, 9, 20], 17: - [31, 18, 10, 26], 18: [17, 19, 23, 32], 19: [23, 20, 12, 18], 20: - [23, 25, 19, 34], 21: [13, 28, 27, 15], 22: [30, 14, 29, 24], 23: - [20, 19, 18, 34], 24: [30, 22, 41, 43], 25: [20, 16, 33, 34], 26: - [31, 27, 38, 17], 27: [35, 28, 26, 21], 28: [21, 37, 27, 35], 29: - [33, 30, 22, 25], 30: [24, 29, 43, 22], 31: [40, 26, 17, 32], 32: - [39, 45, 31, 18], 33: [29, 25, 44, 34], 34: [36, 25, 39, 33], 35: - [27, 37, 46, 38], 36: [39, 34, 50, 48], 37: [47, 28, 35, 46], 38: - [51, 35, 26, 40], 39: [36, 45, 32, 34], 40: [49, 31, 38, 45], 41: - [52, 43, 30, 53], 42: [43, 44, 33, 53], 43: [42, 53, 41, 30], 44: - [42, 33, 50, 58], 45: [48, 39, 32, 40], 46: [47, 35, 55, 37], 47: - [46, 37, 54, 35], 48: [45, 50, 56, 39], 49: [40, 57, 51, 45], 50: - [48, 36, 59, 44], 51: [61, 38, 55, 49], 52: [41, 53, 64, 43], 53: - [60, 43, 52, 64], 54: [55, 47, 46, 61], 55: [54, 61, 46, 51], 56: - [62, 66, 48, 57], 57: [49, 65, 61, 56], 58: [59, 60, 68, 44], 59: - [58, 63, 50, 69], 60: [53, 64, 68, 58], 61: [67, 51, 55, 57], 62: - [63, 56, 66, 48], 63: [62, 70, 69, 59], 64: [60, 53, 52, 71], 65: - [57, 72, 66, 67], 66: [62, 56, 75, 65], 67: [61, 65, 72, 55], 68: - [60, 58, 76, 71], 69: [73, 70, 63, 59], 70: [74, 63, 69, 77], 71: - [68, 76, 64, 60], 72: [65, 75, 67, 66], 73: [69, 76, 77, 68], 74: - [75, 70, 77, 66], 75: [74, 66, 72, 65], 76: [73, 68, 71, 69], 77: - [70, 74, 73, 69]} + w2 = { + 0: [2, 5, 6, 10], + 1: [4, 7, 9, 14], + 2: [6, 0, 3, 8], + 3: [8, 2, 12, 4], + 4: [1, 9, 12, 3], + 5: [11, 10, 0, 15], + 6: [2, 10, 8, 0], + 7: [14, 1, 16, 9], + 8: [12, 3, 19, 6], + 9: [12, 16, 4, 1], + 10: [17, 6, 15, 5], + 11: [15, 13, 5, 21], + 12: [8, 19, 9, 3], + 13: [21, 11, 15, 28], + 14: [7, 16, 22, 9], + 15: [11, 27, 10, 26], + 16: [14, 25, 9, 20], + 17: [31, 18, 10, 26], + 18: [17, 19, 23, 32], + 19: [23, 20, 12, 18], + 20: [23, 25, 19, 34], + 21: [13, 28, 27, 15], + 22: [30, 14, 29, 24], + 23: [20, 19, 18, 34], + 24: [30, 22, 41, 43], + 25: [20, 16, 33, 34], + 26: [31, 27, 38, 17], + 27: [35, 28, 26, 21], + 28: [21, 37, 27, 35], + 29: [33, 30, 22, 25], + 30: [24, 29, 43, 22], + 31: [40, 26, 17, 32], + 32: [39, 45, 31, 18], + 33: [29, 25, 44, 34], + 34: [36, 25, 39, 33], + 35: [27, 37, 46, 38], + 36: [39, 34, 50, 48], + 37: [47, 28, 35, 46], + 38: [51, 35, 26, 40], + 39: [36, 45, 32, 34], + 40: [49, 31, 38, 45], + 41: [52, 43, 30, 53], + 42: [43, 44, 33, 53], + 43: [42, 53, 41, 30], + 44: [42, 33, 50, 58], + 45: [48, 39, 32, 40], + 46: [47, 35, 55, 37], + 47: [46, 37, 54, 35], + 48: [45, 50, 56, 39], + 49: [40, 57, 51, 45], + 50: [48, 36, 59, 44], + 51: [61, 38, 55, 49], + 52: [41, 53, 64, 43], + 53: [60, 43, 52, 64], + 54: [55, 47, 46, 61], + 55: [54, 61, 46, 51], + 56: [62, 66, 48, 57], + 57: [49, 65, 61, 56], + 58: [59, 60, 68, 44], + 59: [58, 63, 50, 69], + 60: [53, 64, 68, 58], + 61: [67, 51, 55, 57], + 62: [63, 56, 66, 48], + 63: [62, 70, 69, 59], + 64: [60, 53, 52, 71], + 65: [57, 72, 66, 67], + 66: [62, 56, 75, 65], + 67: [61, 65, 72, 55], + 68: [60, 58, 76, 71], + 69: [73, 70, 63, 59], + 70: [74, 63, 69, 77], + 71: [68, 76, 64, 60], + 72: [65, 75, 67, 66], + 73: [69, 76, 77, 68], + 74: [75, 70, 77, 66], + 75: [74, 66, 72, 65], + 76: [73, 68, 71, 69], + 77: [70, 74, 73, 69], + } pts = [shape.centroid for shape in self.shapes] pts = list(map(sphere.toXYZ, pts)) - self.assertAlmostEqual(sphere.brute_knn(pts, 4, 'xyz'), w2) + self.assertAlmostEqual(sphere.brute_knn(pts, 4, "xyz"), w2) + -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/libpysal/cg/tests/test_standalone.py b/libpysal/cg/tests/test_standalone.py index ed2d48a5b..f620869bd 100644 --- a/libpysal/cg/tests/test_standalone.py +++ b/libpysal/cg/tests/test_standalone.py @@ -69,24 +69,31 @@ def test_get_angle_between_expect90(self): class TestIsCollinear(unittest.TestCase): def test_is_collinear(self): - self.assertEqual(True, is_collinear(Point((0, 0)), Point(( - 1, 1)), Point((5, 5)))) + self.assertEqual( + True, is_collinear(Point((0, 0)), Point((1, 1)), Point((5, 5))) + ) def test_is_collinear_expectFalse(self): - self.assertEqual(False, is_collinear(Point((0, 0)), Point(( - 1, 1)), Point((5, 0)))) + self.assertEqual( + False, is_collinear(Point((0, 0)), Point((1, 1)), Point((5, 0))) + ) def test_is_collinear_AlongX(self): - self.assertEqual(True, is_collinear(Point((0, 0)), Point(( - 1, 0)), Point((5, 0)))) + self.assertEqual( + True, is_collinear(Point((0, 0)), Point((1, 0)), Point((5, 0))) + ) def test_is_collinear_AlongY(self): - self.assertEqual(True, is_collinear( - Point((0, 0)), Point((0, 1)), Point((0, -1)))) + self.assertEqual( + True, is_collinear(Point((0, 0)), Point((0, 1)), Point((0, -1))) + ) def test_is_collinear_smallFloat(self): """ - Given: p1 = (0.1, 0.2), p2 = (0.2, 0.3), p3 = (0.3, 0.4) + Given: + + ``` + p1 = (0.1, 0.2), p2 = (0.2, 0.3), p3 = (0.3, 0.4) Line(p1,p2): y = mx + b m = (0.3-0.2) / (0.2-0.1) = .1/.1 = 1 @@ -105,33 +112,48 @@ def test_is_collinear_smallFloat(self): y = 1*x + 0.1 Line(p1,p2) == Line(p2,p3) - Therefore p1,p2,p3 are collinear. - + ``` + + Therefore ``(p1, p2, p3)`` are collinear. Due to floating point rounding areas the standard test, - ((p2[0]-p1[0])*(p3[1]-p1[1]) - (p2[1]-p1[1])*(p3[0]-p1[0])) == 0 - will fail. To get around this we use an epsilon. numpy.finfo function + + ``` + ((p2[0]-p1[0])*(p3[1]-p1[1]) - (p2[1]-p1[1])*(p3[0]-p1[0])) == 0 + ``` + + will fail. To get around this we use an epsilon. numpy.finfo function return an smallest epsilon for the given data types such that, - (numpy.finfo(float).eps + 1.0) != 1.0 - + + ``` + (numpy.finfo(float).eps + 1.0) != 1.0 + ``` + Therefore if - abs((p2[0]-p1[0])*(p3[1]-p1[1]) - (p2[1]-p1[1])*( - p3[0]-p1[0])) < numpy.finfo(p1[0]).eps + + ``` + abs((p2[0]-p1[0])*(p3[1]-p1[1]) - (p2[1]-p1[1])*(p3[0]-p1[0])) < numpy.finfo(p1[0]).eps + ``` + The points are collinear. + """ - self.assertEqual(True, is_collinear( - Point((0.1, 0.2)), Point((0.2, 0.3)), Point((0.3, 0.4)))) + self.assertEqual( + True, is_collinear(Point((0.1, 0.2)), Point((0.2, 0.3)), Point((0.3, 0.4))) + ) def test_is_collinear_random(self): for i in range(10): a, b, c = np.random.random(3) * 10 ** (i) - self.assertEqual(True, is_collinear( - Point((a, a)), Point((b, b)), Point((c, c)))) + self.assertEqual( + True, is_collinear(Point((a, a)), Point((b, b)), Point((c, c))) + ) def test_is_collinear_random2(self): for i in range(1000): a, b, c = np.random.random(3) - self.assertEqual(True, is_collinear( - Point((a, a)), Point((b, b)), Point((c, c)))) + self.assertEqual( + True, is_collinear(Point((a, a)), Point((b, b)), Point((c, c))) + ) class TestGetSegmentsIntersect(unittest.TestCase): @@ -146,9 +168,9 @@ def test_get_segments_intersect_shared_vert(self): self.assertEqual((0.0, 10.0), get_segments_intersect(seg1, seg2)[:]) def test_get_segments_intersect_floats(self): - seg1 = LineSegment(Point((0, 0)), Point((0, .10))) - seg2 = LineSegment(Point((-.5, .05)), Point((.5, .05))) - self.assertEqual((0.0, .05), get_segments_intersect(seg1, seg2)[:]) + seg1 = LineSegment(Point((0, 0)), Point((0, 0.10))) + seg2 = LineSegment(Point((-0.5, 0.05)), Point((0.5, 0.05))) + self.assertEqual((0.0, 0.05), get_segments_intersect(seg1, seg2)[:]) def test_get_segments_intersect_angles(self): seg1 = LineSegment(Point((0, 0)), Point((1, 1))) @@ -194,7 +216,7 @@ def test_get_segment_point_intersect_right_end(self): def test_get_segment_point_intersect_angle(self): seg = LineSegment(Point((0, 0)), Point((1, 1))) - pt = Point((.1, .1)) + pt = Point((0.1, 0.1)) self.assertEqual(pt, get_segment_point_intersect(seg, pt)) def test_get_segment_point_intersect_no_intersect(self): @@ -208,44 +230,41 @@ def test_get_segment_point_intersect_no_intersect_collinear(self): self.assertEqual(None, get_segment_point_intersect(seg, pt)) def test_get_segment_point_intersect_floats(self): - seg = LineSegment(Point((0.3, 0.3)), Point((.9, .9))) - pt = Point((.5, .5)) + seg = LineSegment(Point((0.3, 0.3)), Point((0.9, 0.9))) + pt = Point((0.5, 0.5)) self.assertEqual(pt, get_segment_point_intersect(seg, pt)) def test_get_segment_point_intersect_floats(self): - seg = LineSegment(Point((0.0, 0.0)), Point(( - 2.7071067811865475, 2.7071067811865475))) + seg = LineSegment( + Point((0.0, 0.0)), Point((2.7071067811865475, 2.7071067811865475)) + ) pt = Point((1.0, 1.0)) self.assertEqual(pt, get_segment_point_intersect(seg, pt)) def test_get_segment_point_intersect_floats_no_intersect(self): - seg = LineSegment(Point((0.3, 0.3)), Point((.9, .9))) - pt = Point((.1, .1)) + seg = LineSegment(Point((0.3, 0.3)), Point((0.9, 0.9))) + pt = Point((0.1, 0.1)) self.assertEqual(None, get_segment_point_intersect(seg, pt)) class TestGetPolygonPointIntersect(unittest.TestCase): def test_get_polygon_point_intersect(self): - poly = Polygon([Point( - (0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) + poly = Polygon([Point((0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) pt = Point((0.5, 0.5)) self.assertEqual(pt, get_polygon_point_intersect(poly, pt)) def test_get_polygon_point_intersect_on_edge(self): - poly = Polygon([Point( - (0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) + poly = Polygon([Point((0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) pt = Point((1.0, 0.5)) self.assertEqual(pt, get_polygon_point_intersect(poly, pt)) def test_get_polygon_point_intersect_on_vertex(self): - poly = Polygon([Point( - (0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) + poly = Polygon([Point((0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) pt = Point((1.0, 1.0)) self.assertEqual(pt, get_polygon_point_intersect(poly, pt)) def test_get_polygon_point_intersect_outside(self): - poly = Polygon([Point( - (0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) + poly = Polygon([Point((0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) pt = Point((2.0, 2.0)) self.assertEqual(None, get_polygon_point_intersect(poly, pt)) @@ -276,7 +295,7 @@ class TestGetRaySegmentIntersect(unittest.TestCase): def test_get_ray_segment_intersect(self): ray = Ray(Point((0, 0)), Point((0, 1))) seg = LineSegment(Point((-1, 10)), Point((1, 10))) - self.assertEqual((0.0, 10.), get_ray_segment_intersect(ray, seg)[:]) + self.assertEqual((0.0, 10.0), get_ray_segment_intersect(ray, seg)[:]) def test_get_ray_segment_intersect_orgin(self): ray = Ray(Point((0, 0)), Point((0, 1))) @@ -304,15 +323,13 @@ def test_get_rectangle_rectangle_intersection_leftright(self): r0 = Rectangle(0, 4, 6, 9) r1 = Rectangle(4, 0, 9, 7) expected = [4.0, 4.0, 6.0, 7.0] - self.assertEqual( - expected, get_rectangle_rectangle_intersection(r0, r1)[:]) + self.assertEqual(expected, get_rectangle_rectangle_intersection(r0, r1)[:]) def test_get_rectangle_rectangle_intersection_topbottom(self): r0 = Rectangle(0, 0, 4, 4) r1 = Rectangle(2, 1, 6, 3) expected = [2.0, 1.0, 4.0, 3.0] - self.assertEqual( - expected, get_rectangle_rectangle_intersection(r0, r1)[:]) + self.assertEqual(expected, get_rectangle_rectangle_intersection(r0, r1)[:]) def test_get_rectangle_rectangle_intersection_nested(self): r0 = Rectangle(0, 0, 4, 4) @@ -322,20 +339,23 @@ def test_get_rectangle_rectangle_intersection_nested(self): def test_get_rectangle_rectangle_intersection_shared_corner(self): r0 = Rectangle(0, 0, 4, 4) r1 = Rectangle(4, 4, 8, 8) - self.assertEqual(Point( - (4, 4)), get_rectangle_rectangle_intersection(r0, r1)) + self.assertEqual(Point((4, 4)), get_rectangle_rectangle_intersection(r0, r1)) def test_get_rectangle_rectangle_intersection_shared_edge(self): r0 = Rectangle(0, 0, 4, 4) r1 = Rectangle(0, 4, 4, 8) - self.assertEqual(LineSegment(Point((0, 4)), Point( - (4, 4))), get_rectangle_rectangle_intersection(r0, r1)) + self.assertEqual( + LineSegment(Point((0, 4)), Point((4, 4))), + get_rectangle_rectangle_intersection(r0, r1), + ) def test_get_rectangle_rectangle_intersection_shifted_edge(self): r0 = Rectangle(0, 0, 4, 4) r1 = Rectangle(2, 4, 6, 8) - self.assertEqual(LineSegment(Point((2, 4)), Point( - (4, 4))), get_rectangle_rectangle_intersection(r0, r1)) + self.assertEqual( + LineSegment(Point((2, 4)), Point((4, 4))), + get_rectangle_rectangle_intersection(r0, r1), + ) def test_get_rectangle_rectangle_intersection_no_intersect(self): r0 = Rectangle(0, 0, 4, 4) @@ -345,29 +365,25 @@ def test_get_rectangle_rectangle_intersection_no_intersect(self): class TestGetPolygonPointDist(unittest.TestCase): def test_get_polygon_point_dist(self): - poly = Polygon([Point( - (0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) + poly = Polygon([Point((0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) pt = Point((2, 0.5)) expected = 1.0 self.assertEqual(expected, get_polygon_point_dist(poly, pt)) def test_get_polygon_point_dist_inside(self): - poly = Polygon([Point( - (0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) + poly = Polygon([Point((0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) pt = Point((0.5, 0.5)) expected = 0.0 self.assertEqual(expected, get_polygon_point_dist(poly, pt)) def test_get_polygon_point_dist_on_vertex(self): - poly = Polygon([Point( - (0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) + poly = Polygon([Point((0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) pt = Point((1.0, 1.0)) expected = 0.0 self.assertEqual(expected, get_polygon_point_dist(poly, pt)) def test_get_polygon_point_dist_on_edge(self): - poly = Polygon([Point( - (0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) + poly = Polygon([Point((0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 1))]) pt = Point((0.5, 1.0)) expected = 0.0 self.assertEqual(expected, get_polygon_point_dist(poly, pt)) @@ -453,8 +469,10 @@ def test_get_point_at_angle_and_dist_diag_45(self): class TestConvexHull(unittest.TestCase): def test_convex_hull(self): points = [Point((0, 0)), Point((4, 4)), Point((4, 0)), Point((3, 1))] - self.assertEqual([Point((0.0, 0.0)), Point( - (4.0, 0.0)), Point((4.0, 4.0))], convex_hull(points)) + self.assertEqual( + [Point((0.0, 0.0)), Point((4.0, 0.0)), Point((4.0, 4.0))], + convex_hull(points), + ) class TestIsClockwise(unittest.TestCase): @@ -467,51 +485,66 @@ def test_is_clockwise_expect_false(self): self.assertEqual(False, is_clockwise(vertices)) def test_is_clockwise_big(self): - vertices = [( - -106.57798, 35.174143999999998), (-106.583412, 35.174141999999996), - (-106.58417999999999, 35.174143000000001), (-106.58377999999999, 35.175542999999998), - (-106.58287999999999, 35.180543), ( - -106.58263099999999, 35.181455), - (-106.58257999999999, 35.181643000000001), (-106.58198299999999, 35.184615000000001), - (-106.58148, 35.187242999999995), ( - -106.58127999999999, 35.188243), - (-106.58138, 35.188243), (-106.58108, 35.189442999999997), - (-106.58104, 35.189644000000001), ( - -106.58028, 35.193442999999995), - (-106.580029, 35.194541000000001), (-106.57974399999999, - 35.195785999999998), - (-106.579475, 35.196961999999999), (-106.57922699999999, - 35.198042999999998), - (-106.578397, 35.201665999999996), (-106.57827999999999, - 35.201642999999997), - (-106.57737999999999, 35.201642999999997), (-106.57697999999999, 35.201543000000001), - (-106.56436599999999, 35.200311999999997), ( - -106.56058, 35.199942999999998), - (-106.56048, 35.197342999999996), ( - -106.56048, 35.195842999999996), - (-106.56048, 35.194342999999996), ( - -106.56048, 35.193142999999999), - (-106.56048, 35.191873999999999), ( - -106.56048, 35.191742999999995), - (-106.56048, 35.190242999999995), (-106.56037999999999, - 35.188642999999999), - (-106.56037999999999, 35.187242999999995), (-106.56037999999999, 35.186842999999996), - (-106.56037999999999, 35.186552999999996), (-106.56037999999999, 35.185842999999998), - (-106.56037999999999, 35.184443000000002), (-106.56037999999999, 35.182943000000002), - (-106.56037999999999, 35.181342999999998), (-106.56037999999999, 35.180433000000001), - (-106.56037999999999, 35.179943000000002), (-106.56037999999999, 35.178542999999998), - (-106.56037999999999, 35.177790999999999), (-106.56037999999999, 35.177143999999998), - (-106.56037999999999, 35.175643999999998), (-106.56037999999999, 35.174444000000001), - (-106.56037999999999, 35.174043999999995), ( - -106.560526, 35.174043999999995), - (-106.56478, 35.174043999999995), (-106.56627999999999, - 35.174143999999998), - (-106.566541, 35.174144999999996), ( - -106.569023, 35.174157000000001), - (-106.56917199999999, 35.174157999999998), ( - -106.56938, 35.174143999999998), - (-106.57061499999999, 35.174143999999998), (-106.57097999999999, 35.174143999999998), - (-106.57679999999999, 35.174143999999998), (-106.57798, 35.174143999999998)] + vertices = [ + (-106.57798, 35.174143999999998), + (-106.583412, 35.174141999999996), + (-106.58417999999999, 35.174143000000001), + (-106.58377999999999, 35.175542999999998), + (-106.58287999999999, 35.180543), + (-106.58263099999999, 35.181455), + (-106.58257999999999, 35.181643000000001), + (-106.58198299999999, 35.184615000000001), + (-106.58148, 35.187242999999995), + (-106.58127999999999, 35.188243), + (-106.58138, 35.188243), + (-106.58108, 35.189442999999997), + (-106.58104, 35.189644000000001), + (-106.58028, 35.193442999999995), + (-106.580029, 35.194541000000001), + (-106.57974399999999, 35.195785999999998), + (-106.579475, 35.196961999999999), + (-106.57922699999999, 35.198042999999998), + (-106.578397, 35.201665999999996), + (-106.57827999999999, 35.201642999999997), + (-106.57737999999999, 35.201642999999997), + (-106.57697999999999, 35.201543000000001), + (-106.56436599999999, 35.200311999999997), + (-106.56058, 35.199942999999998), + (-106.56048, 35.197342999999996), + (-106.56048, 35.195842999999996), + (-106.56048, 35.194342999999996), + (-106.56048, 35.193142999999999), + (-106.56048, 35.191873999999999), + (-106.56048, 35.191742999999995), + (-106.56048, 35.190242999999995), + (-106.56037999999999, 35.188642999999999), + (-106.56037999999999, 35.187242999999995), + (-106.56037999999999, 35.186842999999996), + (-106.56037999999999, 35.186552999999996), + (-106.56037999999999, 35.185842999999998), + (-106.56037999999999, 35.184443000000002), + (-106.56037999999999, 35.182943000000002), + (-106.56037999999999, 35.181342999999998), + (-106.56037999999999, 35.180433000000001), + (-106.56037999999999, 35.179943000000002), + (-106.56037999999999, 35.178542999999998), + (-106.56037999999999, 35.177790999999999), + (-106.56037999999999, 35.177143999999998), + (-106.56037999999999, 35.175643999999998), + (-106.56037999999999, 35.174444000000001), + (-106.56037999999999, 35.174043999999995), + (-106.560526, 35.174043999999995), + (-106.56478, 35.174043999999995), + (-106.56627999999999, 35.174143999999998), + (-106.566541, 35.174144999999996), + (-106.569023, 35.174157000000001), + (-106.56917199999999, 35.174157999999998), + (-106.56938, 35.174143999999998), + (-106.57061499999999, 35.174143999999998), + (-106.57097999999999, 35.174143999999998), + (-106.57679999999999, 35.174143999999998), + (-106.57798, 35.174143999999998), + ] self.assertEqual(True, is_clockwise(vertices)) @@ -539,51 +572,45 @@ def test_point_touches_rectangle_outside(self): class TestGetSharedSegments(unittest.TestCase): def test_get_shared_segments(self): - poly1 = Polygon([Point( - (0, 0)), Point((0, 1)), Point((1, 1)), Point((1, 0))]) - poly2 = Polygon([Point( - (1, 0)), Point((1, 1)), Point((2, 1)), Point((2, 0))]) - poly3 = Polygon([Point( - (0, 1)), Point((0, 2)), Point((1, 2)), Point((1, 1))]) - poly4 = Polygon([Point( - (1, 1)), Point((1, 2)), Point((2, 2)), Point((2, 1))]) - self.assertEqual( - True, get_shared_segments(poly1, poly2, bool_ret=True)) - self.assertEqual( - True, get_shared_segments(poly1, poly3, bool_ret=True)) + poly1 = Polygon([Point((0, 0)), Point((0, 1)), Point((1, 1)), Point((1, 0))]) + poly2 = Polygon([Point((1, 0)), Point((1, 1)), Point((2, 1)), Point((2, 0))]) + poly3 = Polygon([Point((0, 1)), Point((0, 2)), Point((1, 2)), Point((1, 1))]) + poly4 = Polygon([Point((1, 1)), Point((1, 2)), Point((2, 2)), Point((2, 1))]) + self.assertEqual(True, get_shared_segments(poly1, poly2, bool_ret=True)) + self.assertEqual(True, get_shared_segments(poly1, poly3, bool_ret=True)) + self.assertEqual(True, get_shared_segments(poly3, poly4, bool_ret=True)) + self.assertEqual(True, get_shared_segments(poly4, poly2, bool_ret=True)) + + self.assertEqual(False, get_shared_segments(poly1, poly4, bool_ret=True)) + self.assertEqual(False, get_shared_segments(poly3, poly2, bool_ret=True)) + + def test_get_shared_segments_non_bool(self): + poly1 = Polygon([Point((0, 0)), Point((0, 1)), Point((1, 1)), Point((1, 0))]) + poly2 = Polygon([Point((1, 0)), Point((1, 1)), Point((2, 1)), Point((2, 0))]) + poly3 = Polygon([Point((0, 1)), Point((0, 2)), Point((1, 2)), Point((1, 1))]) + poly4 = Polygon([Point((1, 1)), Point((1, 2)), Point((2, 2)), Point((2, 1))]) self.assertEqual( - True, get_shared_segments(poly3, poly4, bool_ret=True)) + LineSegment(Point((1, 0)), Point((1, 1))), + get_shared_segments(poly1, poly2)[0], + ) self.assertEqual( - True, get_shared_segments(poly4, poly2, bool_ret=True)) - + LineSegment(Point((0, 1)), Point((1, 1))), + get_shared_segments(poly1, poly3)[0], + ) self.assertEqual( - False, get_shared_segments(poly1, poly4, bool_ret=True)) + LineSegment(Point((1, 2)), Point((1, 1))), + get_shared_segments(poly3, poly4)[0], + ) self.assertEqual( - False, get_shared_segments(poly3, poly2, bool_ret=True)) - - def test_get_shared_segments_non_bool(self): - poly1 = Polygon([Point( - (0, 0)), Point((0, 1)), Point((1, 1)), Point((1, 0))]) - poly2 = Polygon([Point( - (1, 0)), Point((1, 1)), Point((2, 1)), Point((2, 0))]) - poly3 = Polygon([Point( - (0, 1)), Point((0, 2)), Point((1, 2)), Point((1, 1))]) - poly4 = Polygon([Point( - (1, 1)), Point((1, 2)), Point((2, 2)), Point((2, 1))]) - self.assertEqual(LineSegment(Point((1, 0)), Point((1, 1))), - get_shared_segments(poly1, poly2)[0]) - self.assertEqual(LineSegment(Point((0, 1)), Point((1, 1))), - get_shared_segments(poly1, poly3)[0]) - self.assertEqual(LineSegment(Point((1, 2)), Point((1, 1))), - get_shared_segments(poly3, poly4)[0]) - self.assertEqual(LineSegment(Point((2, 1)), Point((1, 1))), - get_shared_segments(poly4, poly2)[0]) - #expected = [LineSegment(Point((1, 1)), Point((1, 0)))] - #assert expected == get_shared_segments(poly1, poly3) - #expected = [LineSegment(Point((1, 1)), Point((1, 0)))] - #assert expected == get_shared_segments(poly3, poly4) - #expected = [LineSegment(Point((1, 1)), Point((1, 0)))] - #assert expected == get_shared_segments(poly4, poly2) + LineSegment(Point((2, 1)), Point((1, 1))), + get_shared_segments(poly4, poly2)[0], + ) + # expected = [LineSegment(Point((1, 1)), Point((1, 0)))] + # assert expected == get_shared_segments(poly1, poly3) + # expected = [LineSegment(Point((1, 1)), Point((1, 0)))] + # assert expected == get_shared_segments(poly3, poly4) + # expected = [LineSegment(Point((1, 1)), Point((1, 0)))] + # assert expected == get_shared_segments(poly4, poly2) class TestDistanceMatrix(unittest.TestCase): @@ -597,5 +624,6 @@ def test_distance_matrix(self): d = ((x - X) ** 2 + (y - Y) ** 2) ** (0.5) self.assertEqual(dist[i, j], d) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/libpysal/cg/tests/test_voronoi.py b/libpysal/cg/tests/test_voronoi.py index a3f2c921b..6eeece3a0 100644 --- a/libpysal/cg/tests/test_voronoi.py +++ b/libpysal/cg/tests/test_voronoi.py @@ -4,31 +4,38 @@ class Voronoi(unittest.TestCase): - def setUp(self): - self.points = [(10.2, 5.1), (4.7, 2.2), (5.3, 5.7), (2.7, 5.3)] - - self.vertices = [[4.21783295711061, 4.084085778781038], [7.519560251284979, 3.518075385494004], [9.464219298524961, 19.399457604620512], [14.982106844470032, -10.63503022227075], [-9.226913414477298, -4.58994413837245], [14.982106844470032, -10.63503022227075], [1.7849180090475505, 19.898032941190912], [9.464219298524961, 19.399457604620512], [1.7849180090475505, 19.898032941190912], [-9.226913414477298, -4.58994413837245]] + self.points = [(10.2, 5.1), (4.7, 2.2), (5.3, 5.7), (2.7, 5.3)] + + self.vertices = [ + [4.21783295711061, 4.084085778781038], + [7.519560251284979, 3.518075385494004], + [9.464219298524961, 19.399457604620512], + [14.982106844470032, -10.63503022227075], + [-9.226913414477298, -4.58994413837245], + [14.982106844470032, -10.63503022227075], + [1.7849180090475505, 19.898032941190912], + [9.464219298524961, 19.399457604620512], + [1.7849180090475505, 19.898032941190912], + [-9.226913414477298, -4.58994413837245], + ] def test_voronoi(self): regions, vertices = voronoi(self.points) - self.assertEqual(regions, [[1, 3, 2], - [4, 5, 1, 0], - [0, 1, 7, 6], - [9, 0, 8]]) + self.assertEqual(regions, [[1, 3, 2], [4, 5, 1, 0], [0, 1, 7, 6], [9, 0, 8]]) self.assertTrue(vertices.tolist() == self.vertices) def test_voronoi_frames(self): r_df, p_df = voronoi_frames(self.points) - region = r_df.iloc[0]['geometry'] + region = r_df.iloc[0]["geometry"] try: import geopandas as df + self.assertTrue(isinstance(asShape(region), Polygon)) except ImportError: self.assertTrue(isinstance(region, Polygon)) - -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() From c6007d5db71cc07512d9256012603ed48d176374 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Wed, 26 Aug 2020 09:20:46 -0400 Subject: [PATCH 26/33] blackening cg/ops/tests/test_* --- libpysal/cg/ops/tests/test_accessors.py | 587 +++++++++++++++--------- libpysal/cg/ops/tests/test_shapely.py | 111 ++--- libpysal/cg/ops/tests/test_tabular.py | 37 +- 3 files changed, 440 insertions(+), 295 deletions(-) diff --git a/libpysal/cg/ops/tests/test_accessors.py b/libpysal/cg/ops/tests/test_accessors.py index a5f750df2..e1216a403 100644 --- a/libpysal/cg/ops/tests/test_accessors.py +++ b/libpysal/cg/ops/tests/test_accessors.py @@ -7,12 +7,14 @@ import unittest as ut PANDAS_EXTINCT = pandas is None -@ut.skipIf(PANDAS_EXTINCT, 'missing pandas') + + +@ut.skipIf(PANDAS_EXTINCT, "Missing pandas") class Test_Accessors(ut.TestCase): def setUp(self): - self.polygons = rf(get_path('Polygon.shp')) - self.points = rf(get_path('Point.shp')) - self.lines = rf(get_path('Line.shp')) + self.polygons = rf(get_path("Polygon.shp")) + self.points = rf(get_path("Point.shp")) + self.lines = rf(get_path("Line.shp")) def test_area(self): @@ -22,8 +24,8 @@ def test_area(self): to_test.area(self.lines) areas = to_test.area(self.polygons).values - answer = [.000284, .000263, .001536] - np.testing.assert_allclose(answer, areas, rtol=RTOL, atol=ATOL*10) + answer = [0.000284, 0.000263, 0.001536] + np.testing.assert_allclose(answer, areas, rtol=RTOL, atol=ATOL * 10) def test_bbox(self): @@ -32,18 +34,26 @@ def test_bbox(self): with self.assertRaises(AttributeError): to_test.bbox(self.lines) - answer = [[-0.010809397704086565, - -0.26282711761789435, - 0.12787295484449185, - -0.250785835510383], - [ 0.0469057130870883, - -0.35957259110238166, - 0.06309916143856897, - -0.3126531125455273], - [-0.04527237752903268, - -0.4646223970748078, - 0.1432359699471787, - -0.40150947016647276]] + answer = [ + [ + -0.010809397704086565, + -0.26282711761789435, + 0.12787295484449185, + -0.250785835510383, + ], + [ + 0.0469057130870883, + -0.35957259110238166, + 0.06309916143856897, + -0.3126531125455273, + ], + [ + -0.04527237752903268, + -0.4646223970748078, + 0.1432359699471787, + -0.40150947016647276, + ], + ] bboxes = to_test.bbox(self.polygons).tolist() for ans, bbox in zip(answer, bboxes): @@ -54,30 +64,48 @@ def test_bounding_box(self): to_test.bounding_box(self.points) line_rects = to_test.bounding_box(self.lines).tolist() - line_bboxes = [[(a.left, a.lower),(a.right, a.upper)] for a in line_rects] + line_bboxes = [[(a.left, a.lower), (a.right, a.upper)] for a in line_rects] pgon_rects = to_test.bounding_box(self.polygons).tolist() - pgon_bboxes = [[(a.left, a.lower),(a.right, a.upper)] for a in pgon_rects] - - line_answers = [[(-0.009053924887015952, -0.2589587703323735), - ( 0.007481157395930582, -0.25832280562918325)], - [( 0.10923550990637088, -0.2564149115196125), - ( 0.12895041570526866, -0.2564149115196125)], - [( 0.050726757212867735, -0.356261369920482), - ( 0.06153815716710198, -0.3130157701035449)], - [(-0.0414881247497188, -0.46055958124368335), - ( 0.1391258509563127, -0.4058666167693217)]] - pgon_answers = [[(-0.010809397704086565, -0.26282711761789435), - ( 0.12787295484449185, -0.250785835510383)], - [( 0.0469057130870883, -0.35957259110238166), - ( 0.06309916143856897, -0.3126531125455273)], - [(-0.04527237752903268, -0.4646223970748078), - ( 0.1432359699471787, -0.40150947016647276)]] + pgon_bboxes = [[(a.left, a.lower), (a.right, a.upper)] for a in pgon_rects] + + line_answers = [ + [ + (-0.009053924887015952, -0.2589587703323735), + (0.007481157395930582, -0.25832280562918325), + ], + [ + (0.10923550990637088, -0.2564149115196125), + (0.12895041570526866, -0.2564149115196125), + ], + [ + (0.050726757212867735, -0.356261369920482), + (0.06153815716710198, -0.3130157701035449), + ], + [ + (-0.0414881247497188, -0.46055958124368335), + (0.1391258509563127, -0.4058666167693217), + ], + ] + pgon_answers = [ + [ + (-0.010809397704086565, -0.26282711761789435), + (0.12787295484449185, -0.250785835510383), + ], + [ + (0.0469057130870883, -0.35957259110238166), + (0.06309916143856897, -0.3126531125455273), + ], + [ + (-0.04527237752903268, -0.4646223970748078), + (0.1432359699471787, -0.40150947016647276), + ], + ] for bbox, answer in zip(line_bboxes, line_answers): np.testing.assert_allclose(bbox, answer, atol=ATOL, rtol=RTOL) for bbox, answer in zip(pgon_bboxes, pgon_answers): np.testing.assert_allclose(bbox, answer, atol=ATOL, rtol=RTOL) - for rectangle in (line_rects + pgon_rects): + for rectangle in line_rects + pgon_rects: self.assertIsInstance(rectangle, Rectangle) def test_centroid(self): @@ -88,15 +116,17 @@ def test_centroid(self): centroids = to_test.centroid(self.polygons).tolist() - centroid_answers = [(0.06466214975239247, -0.257330080795802), - (0.05151163524856857, -0.33495102150875505), - (0.04759584610455384, -0.44147205133285744)] + centroid_answers = [ + (0.06466214975239247, -0.257330080795802), + (0.05151163524856857, -0.33495102150875505), + (0.04759584610455384, -0.44147205133285744), + ] for ct, answer in zip(centroids, centroid_answers): np.testing.assert_allclose(ct, answer, rtol=RTOL, atol=ATOL) def test_holes(self): - holed_polygons = rf(get_path('Polygon_Holes.shp')) + holed_polygons = rf(get_path("Polygon_Holes.shp")) with self.assertRaises(AttributeError): to_test.centroid(self.points) with self.assertRaises(AttributeError): @@ -108,42 +138,61 @@ def test_holes(self): for elist in no_holes: self.assertEqual(elist, [[]]) - answers = [[[(-0.002557818613137461, -0.25599115990199145), - ( 0.0012028146993492903, -0.25561239107915107), - ( 0.004909338180001697, -0.2596435735508095), - (-0.0019896653788768724, -0.2616726922445973), - (-0.007021879739470651, -0.25834493758678534), - (-0.002557818613137461, -0.25599115990199145)], - [( 0.11456291239229519, -0.2534750527216944), - ( 0.11878347927537383, -0.2540973157877893), - ( 0.11878347927537383, -0.2540973157877893), - ( 0.12335576006537571, -0.25596410498607414), - ( 0.11605093276773958, -0.258155553175365), - ( 0.11020707092963067, -0.2579391138480276), - ( 0.11456291239229519, -0.2534750527216944)]], - [[( 0.04818367618951632, -0.31403748200228154), - ( 0.052755956979518195, -0.31384809759086135), - ( 0.04975286131271223, -0.3566219196559085), - ( 0.04818367618951632, -0.31403748200228154)]], - [[(-0.039609525961703126, -0.4112999047245106), - (-0.013745026344887779, -0.43770550265966934), - (-0.015260101636249357, -0.4393287976146996), - (-0.04242323721708889, -0.4140053963162277), - (-0.039609525961703126, -0.4112999047245106)], - [( 0.027838379419803827, -0.4597823140480808), - ( 0.07350707748798824, -0.45859189774772524), - ( 0.07469749378834376, -0.46064807135743024), - ( 0.028487697401815927, -0.46270424496713525), - ( 0.027838379419803827, -0.4597823140480808)], - [( 0.11192505809037084, -0.43467535207694624), - ( 0.13962929198955382, -0.4037245282677028), - ( 0.14092792795357803, -0.405023164231727), - ( 0.11463054968208794, -0.4370561846776573), - ( 0.11192505809037084, -0.43467535207694624)]]] + answers = [ + [ + [ + (-0.002557818613137461, -0.25599115990199145), + (0.0012028146993492903, -0.25561239107915107), + (0.004909338180001697, -0.2596435735508095), + (-0.0019896653788768724, -0.2616726922445973), + (-0.007021879739470651, -0.25834493758678534), + (-0.002557818613137461, -0.25599115990199145), + ], + [ + (0.11456291239229519, -0.2534750527216944), + (0.11878347927537383, -0.2540973157877893), + (0.11878347927537383, -0.2540973157877893), + (0.12335576006537571, -0.25596410498607414), + (0.11605093276773958, -0.258155553175365), + (0.11020707092963067, -0.2579391138480276), + (0.11456291239229519, -0.2534750527216944), + ], + ], + [ + [ + (0.04818367618951632, -0.31403748200228154), + (0.052755956979518195, -0.31384809759086135), + (0.04975286131271223, -0.3566219196559085), + (0.04818367618951632, -0.31403748200228154), + ] + ], + [ + [ + (-0.039609525961703126, -0.4112999047245106), + (-0.013745026344887779, -0.43770550265966934), + (-0.015260101636249357, -0.4393287976146996), + (-0.04242323721708889, -0.4140053963162277), + (-0.039609525961703126, -0.4112999047245106), + ], + [ + (0.027838379419803827, -0.4597823140480808), + (0.07350707748798824, -0.45859189774772524), + (0.07469749378834376, -0.46064807135743024), + (0.028487697401815927, -0.46270424496713525), + (0.027838379419803827, -0.4597823140480808), + ], + [ + (0.11192505809037084, -0.43467535207694624), + (0.13962929198955382, -0.4037245282677028), + (0.14092792795357803, -0.405023164231727), + (0.11463054968208794, -0.4370561846776573), + (0.11192505809037084, -0.43467535207694624), + ], + ], + ] for hole, answer in zip(holes, answers): for sub_hole, sub_answer in zip(hole, answer): - np.testing.assert_allclose(sub_hole, sub_answer, - rtol=RTOL, atol=ATOL) + np.testing.assert_allclose(sub_hole, sub_answer, rtol=RTOL, atol=ATOL) def test_len(self): with self.assertRaises(AttributeError): @@ -153,10 +202,12 @@ def test_len(self): pgon_len = to_test.len(self.polygons) pgon_answers = [24, 7, 14] - line_answers = [0.016547307853772356, - 0.019714905798897786, - 0.058991346117778738, - 0.21634275419393173] + line_answers = [ + 0.016547307853772356, + 0.019714905798897786, + 0.058991346117778738, + 0.21634275419393173, + ] np.testing.assert_allclose(line_len, line_answers, rtol=RTOL, atol=ATOL) np.testing.assert_allclose(pgon_len, pgon_answers, rtol=RTOL, atol=ATOL) @@ -167,71 +218,104 @@ def test_parts(self): line_parts = to_test.parts(self.lines) pgon_parts = to_test.parts(self.polygons) - pgon_answers =[[[(-0.010809397704086565, -0.25825973474952796), - (-0.007487664708911018, -0.25493800175435244), - (-0.0016746319673538457, -0.2532771352567647), - ( 0.003307967525409461, -0.2545227851299555), - ( 0.006214483896188033, -0.25701408487633715), - ( 0.007044917144981927, -0.26033581787151266), - ( 0.003307967525409461, -0.26241190099349737), - (-0.0029202818405446584, -0.26282711761789435), - (-0.008318097957704912, -0.26199668436910045), - (-0.009978964455292672, -0.26075103449590964), - (-0.010809397704086565, -0.25825973474952796)], - [( 0.10711212362464478, -0.25618365162754325), - ( 0.1112642898686142, -0.25203148538357384), - ( 0.11583167273698053, -0.250785835510383), - ( 0.12164470547853773, -0.25203148538357384), - ( 0.12538165509811022, -0.25410756850555855), - ( 0.12787295484449185, -0.2574293015007341), - ( 0.12579687172250714, -0.26033581787151266), - ( 0.1191534057321561, -0.26116625112030656), - ( 0.1141708062393928, -0.26158146774470353), - ( 0.11084907324421728, -0.25992060124711575), - ( 0.10794255687343868, -0.25909016799832185), - ( 0.10794255687343868, -0.25909016799832185), - ( 0.10711212362464478, -0.25618365162754325)]], - [[( 0.05396439570183631, -0.3126531125455273), - ( 0.05147309595545463, -0.35251390848763364), - ( 0.059777428443393454, -0.34254870950210703), - ( 0.06309916143856897, -0.34462479262409174), - ( 0.048981796209073, -0.35957259110238166), - ( 0.0469057130870883, -0.3126531125455273), - ( 0.05396439570183631, -0.3126531125455273)]], - [[(-0.04527237752903268, -0.413550752273984), - (-0.039874561411872456, -0.4077377195324269), - (-0.039874561411872456, -0.4077377195324269), - (-0.010809397704086565, -0.43680288324021277), - ( 0.02656009849163815, -0.45756371446005983), - ( 0.07181871055090477, -0.45673328121126594), - ( 0.1104338566198203, -0.4338963668694341), - ( 0.1394990203276062, -0.40150947016647276), - ( 0.1432359699471787, -0.4052464197860452), - ( 0.1162468893613775, -0.4380485331134035), - ( 0.07763174329246192, -0.4625463139528231), - ( 0.02780574836482899, -0.4646223970748078), - (-0.013715914074865138, -0.4442767824793577), - (-0.04527237752903268, -0.413550752273984)]]] - line_answers = [[[(-0.009053924887015952, -0.25832280562918325), - (0.007481157395930582, -0.2589587703323735), - (0.007481157395930582, -0.2589587703323735)]], - [[(0.10923550990637088, -0.2564149115196125), - (0.12895041570526866, -0.2564149115196125)]], - [[(0.050726757212867735, -0.3130157701035449), - (0.050726757212867735, -0.356261369920482), - (0.06153815716710198, -0.3448140052630575), - (0.06153815716710198, -0.3448140052630575)]], - [[(-0.0414881247497188, -0.41286222850441445), - (-0.012233748402967204, -0.4402087107415953), - (0.027196063194828424, -0.46055958124368335), - (0.07489341593409732, -0.4586516871341126), - (0.11241533342232213, -0.43639292252245376), - (0.1391258509563127, -0.4058666167693217)]]] + pgon_answers = [ + [ + [ + (-0.010809397704086565, -0.25825973474952796), + (-0.007487664708911018, -0.25493800175435244), + (-0.0016746319673538457, -0.2532771352567647), + (0.003307967525409461, -0.2545227851299555), + (0.006214483896188033, -0.25701408487633715), + (0.007044917144981927, -0.26033581787151266), + (0.003307967525409461, -0.26241190099349737), + (-0.0029202818405446584, -0.26282711761789435), + (-0.008318097957704912, -0.26199668436910045), + (-0.009978964455292672, -0.26075103449590964), + (-0.010809397704086565, -0.25825973474952796), + ], + [ + (0.10711212362464478, -0.25618365162754325), + (0.1112642898686142, -0.25203148538357384), + (0.11583167273698053, -0.250785835510383), + (0.12164470547853773, -0.25203148538357384), + (0.12538165509811022, -0.25410756850555855), + (0.12787295484449185, -0.2574293015007341), + (0.12579687172250714, -0.26033581787151266), + (0.1191534057321561, -0.26116625112030656), + (0.1141708062393928, -0.26158146774470353), + (0.11084907324421728, -0.25992060124711575), + (0.10794255687343868, -0.25909016799832185), + (0.10794255687343868, -0.25909016799832185), + (0.10711212362464478, -0.25618365162754325), + ], + ], + [ + [ + (0.05396439570183631, -0.3126531125455273), + (0.05147309595545463, -0.35251390848763364), + (0.059777428443393454, -0.34254870950210703), + (0.06309916143856897, -0.34462479262409174), + (0.048981796209073, -0.35957259110238166), + (0.0469057130870883, -0.3126531125455273), + (0.05396439570183631, -0.3126531125455273), + ] + ], + [ + [ + (-0.04527237752903268, -0.413550752273984), + (-0.039874561411872456, -0.4077377195324269), + (-0.039874561411872456, -0.4077377195324269), + (-0.010809397704086565, -0.43680288324021277), + (0.02656009849163815, -0.45756371446005983), + (0.07181871055090477, -0.45673328121126594), + (0.1104338566198203, -0.4338963668694341), + (0.1394990203276062, -0.40150947016647276), + (0.1432359699471787, -0.4052464197860452), + (0.1162468893613775, -0.4380485331134035), + (0.07763174329246192, -0.4625463139528231), + (0.02780574836482899, -0.4646223970748078), + (-0.013715914074865138, -0.4442767824793577), + (-0.04527237752903268, -0.413550752273984), + ] + ], + ] + line_answers = [ + [ + [ + (-0.009053924887015952, -0.25832280562918325), + (0.007481157395930582, -0.2589587703323735), + (0.007481157395930582, -0.2589587703323735), + ] + ], + [ + [ + (0.10923550990637088, -0.2564149115196125), + (0.12895041570526866, -0.2564149115196125), + ] + ], + [ + [ + (0.050726757212867735, -0.3130157701035449), + (0.050726757212867735, -0.356261369920482), + (0.06153815716710198, -0.3448140052630575), + (0.06153815716710198, -0.3448140052630575), + ] + ], + [ + [ + (-0.0414881247497188, -0.41286222850441445), + (-0.012233748402967204, -0.4402087107415953), + (0.027196063194828424, -0.46055958124368335), + (0.07489341593409732, -0.4586516871341126), + (0.11241533342232213, -0.43639292252245376), + (0.1391258509563127, -0.4058666167693217), + ] + ], + ] for part, answer in zip(pgon_parts, pgon_answers): for piece, sub_answer in zip(part, answer): - np.testing.assert_allclose(piece, sub_answer, - rtol=RTOL,atol=ATOL) + np.testing.assert_allclose(piece, sub_answer, rtol=RTOL, atol=ATOL) def test_perimeter(self): with self.assertRaises(AttributeError): @@ -240,10 +324,11 @@ def test_perimeter(self): to_test.perimeter(self.lines) pgon_perim = to_test.perimeter(self.polygons) - pgon_answers = np.array([ 0.09381641, 0.13141213, 0.45907697]) + pgon_answers = np.array([0.09381641, 0.13141213, 0.45907697]) - np.testing.assert_allclose(pgon_perim.values, pgon_answers, - rtol=RTOL, atol=ATOL) + np.testing.assert_allclose( + pgon_perim.values, pgon_answers, rtol=RTOL, atol=ATOL + ) def test_segments(self): with self.assertRaises(AttributeError): @@ -254,33 +339,65 @@ def test_segments(self): line_segments = to_test.segments(self.lines) flattened = [l[0] for l in line_segments] - answers = [[((-0.009053924887015952, -0.25832280562918325), - (0.007481157395930582, -0.2589587703323735)), - ((0.007481157395930582, -0.2589587703323735), - (0.007481157395930582, -0.2589587703323735))], - [((0.10923550990637088, -0.2564149115196125), - (0.12895041570526866, -0.2564149115196125))], - [((0.050726757212867735, -0.3130157701035449), - (0.050726757212867735, -0.356261369920482)), - ((0.050726757212867735, -0.356261369920482), - (0.06153815716710198, -0.3448140052630575)), - ((0.06153815716710198, -0.3448140052630575), - (0.06153815716710198, -0.3448140052630575))], - [((-0.0414881247497188, -0.41286222850441445), - (-0.012233748402967204, -0.4402087107415953)), - ((-0.012233748402967204, -0.4402087107415953), - (0.027196063194828424, -0.46055958124368335)), - ((0.027196063194828424, -0.46055958124368335), - (0.07489341593409732, -0.4586516871341126)), - ((0.07489341593409732, -0.4586516871341126), - (0.11241533342232213, -0.43639292252245376)), - ((0.11241533342232213, -0.43639292252245376), - (0.1391258509563127, -0.4058666167693217))]] + answers = [ + [ + ( + (-0.009053924887015952, -0.25832280562918325), + (0.007481157395930582, -0.2589587703323735), + ), + ( + (0.007481157395930582, -0.2589587703323735), + (0.007481157395930582, -0.2589587703323735), + ), + ], + [ + ( + (0.10923550990637088, -0.2564149115196125), + (0.12895041570526866, -0.2564149115196125), + ) + ], + [ + ( + (0.050726757212867735, -0.3130157701035449), + (0.050726757212867735, -0.356261369920482), + ), + ( + (0.050726757212867735, -0.356261369920482), + (0.06153815716710198, -0.3448140052630575), + ), + ( + (0.06153815716710198, -0.3448140052630575), + (0.06153815716710198, -0.3448140052630575), + ), + ], + [ + ( + (-0.0414881247497188, -0.41286222850441445), + (-0.012233748402967204, -0.4402087107415953), + ), + ( + (-0.012233748402967204, -0.4402087107415953), + (0.027196063194828424, -0.46055958124368335), + ), + ( + (0.027196063194828424, -0.46055958124368335), + (0.07489341593409732, -0.4586516871341126), + ), + ( + (0.07489341593409732, -0.4586516871341126), + (0.11241533342232213, -0.43639292252245376), + ), + ( + (0.11241533342232213, -0.43639292252245376), + (0.1391258509563127, -0.4058666167693217), + ), + ], + ] for parts, points in zip(flattened, answers): for piece, answer in zip(parts, points): self.assertIsInstance(piece, LineSegment) - p1,p2 = piece.p1, piece.p2 + p1, p2 = piece.p1, piece.p2 np.testing.assert_allclose([p1, p2], answer) def test_vertices(self): @@ -290,66 +407,84 @@ def test_vertices(self): line_verts = to_test.vertices(self.lines).tolist() pgon_verts = to_test.vertices(self.polygons).tolist() - line_answers = [[(-0.009053924887015952, -0.25832280562918325), - (0.007481157395930582, -0.2589587703323735), - (0.007481157395930582, -0.2589587703323735)], - [(0.10923550990637088, -0.2564149115196125), - (0.12895041570526866, -0.2564149115196125)], - [(0.050726757212867735, -0.3130157701035449), - (0.050726757212867735, -0.356261369920482), - (0.06153815716710198, -0.3448140052630575), - (0.06153815716710198, -0.3448140052630575)], - [(-0.0414881247497188, -0.41286222850441445), - (-0.012233748402967204, -0.4402087107415953), - (0.027196063194828424, -0.46055958124368335), - (0.07489341593409732, -0.4586516871341126), - (0.11241533342232213, -0.43639292252245376), - (0.1391258509563127, -0.4058666167693217)]] - pgon_answers = [[(-0.010809397704086565, -0.25825973474952796), - (-0.007487664708911018, -0.25493800175435244), - (-0.0016746319673538457, -0.2532771352567647), - (0.003307967525409461, -0.2545227851299555), - (0.006214483896188033, -0.25701408487633715), - (0.007044917144981927, -0.26033581787151266), - (0.003307967525409461, -0.26241190099349737), - (-0.0029202818405446584, -0.26282711761789435), - (-0.008318097957704912, -0.26199668436910045), - (-0.009978964455292672, -0.26075103449590964), - (-0.010809397704086565, -0.25825973474952796), - (0.10711212362464478, -0.25618365162754325), - (0.1112642898686142, -0.25203148538357384), - (0.11583167273698053, -0.250785835510383), - (0.12164470547853773, -0.25203148538357384), - (0.12538165509811022, -0.25410756850555855), - (0.12787295484449185, -0.2574293015007341), - (0.12579687172250714, -0.26033581787151266), - (0.1191534057321561, -0.26116625112030656), - (0.1141708062393928, -0.26158146774470353), - (0.11084907324421728, -0.25992060124711575), - (0.10794255687343868, -0.25909016799832185), - (0.10794255687343868, -0.25909016799832185), - (0.10711212362464478, -0.25618365162754325)], - [(0.05396439570183631, -0.3126531125455273), - (0.05147309595545463, -0.35251390848763364), - (0.059777428443393454, -0.34254870950210703), - (0.06309916143856897, -0.34462479262409174), - (0.048981796209073, -0.35957259110238166), - (0.0469057130870883, -0.3126531125455273), - (0.05396439570183631, -0.3126531125455273)], - [(-0.04527237752903268, -0.413550752273984), - (-0.039874561411872456, -0.4077377195324269), - (-0.039874561411872456, -0.4077377195324269), - (-0.010809397704086565, -0.43680288324021277), - (0.02656009849163815, -0.45756371446005983), - (0.07181871055090477, -0.45673328121126594), - (0.1104338566198203, -0.4338963668694341), - (0.1394990203276062, -0.40150947016647276), - (0.1432359699471787, -0.4052464197860452), - (0.1162468893613775, -0.4380485331134035), - (0.07763174329246192, -0.4625463139528231), - (0.02780574836482899, -0.4646223970748078), - (-0.013715914074865138, -0.4442767824793577), - (-0.04527237752903268, -0.413550752273984)]] + line_answers = [ + [ + (-0.009053924887015952, -0.25832280562918325), + (0.007481157395930582, -0.2589587703323735), + (0.007481157395930582, -0.2589587703323735), + ], + [ + (0.10923550990637088, -0.2564149115196125), + (0.12895041570526866, -0.2564149115196125), + ], + [ + (0.050726757212867735, -0.3130157701035449), + (0.050726757212867735, -0.356261369920482), + (0.06153815716710198, -0.3448140052630575), + (0.06153815716710198, -0.3448140052630575), + ], + [ + (-0.0414881247497188, -0.41286222850441445), + (-0.012233748402967204, -0.4402087107415953), + (0.027196063194828424, -0.46055958124368335), + (0.07489341593409732, -0.4586516871341126), + (0.11241533342232213, -0.43639292252245376), + (0.1391258509563127, -0.4058666167693217), + ], + ] + pgon_answers = [ + [ + (-0.010809397704086565, -0.25825973474952796), + (-0.007487664708911018, -0.25493800175435244), + (-0.0016746319673538457, -0.2532771352567647), + (0.003307967525409461, -0.2545227851299555), + (0.006214483896188033, -0.25701408487633715), + (0.007044917144981927, -0.26033581787151266), + (0.003307967525409461, -0.26241190099349737), + (-0.0029202818405446584, -0.26282711761789435), + (-0.008318097957704912, -0.26199668436910045), + (-0.009978964455292672, -0.26075103449590964), + (-0.010809397704086565, -0.25825973474952796), + (0.10711212362464478, -0.25618365162754325), + (0.1112642898686142, -0.25203148538357384), + (0.11583167273698053, -0.250785835510383), + (0.12164470547853773, -0.25203148538357384), + (0.12538165509811022, -0.25410756850555855), + (0.12787295484449185, -0.2574293015007341), + (0.12579687172250714, -0.26033581787151266), + (0.1191534057321561, -0.26116625112030656), + (0.1141708062393928, -0.26158146774470353), + (0.11084907324421728, -0.25992060124711575), + (0.10794255687343868, -0.25909016799832185), + (0.10794255687343868, -0.25909016799832185), + (0.10711212362464478, -0.25618365162754325), + ], + [ + (0.05396439570183631, -0.3126531125455273), + (0.05147309595545463, -0.35251390848763364), + (0.059777428443393454, -0.34254870950210703), + (0.06309916143856897, -0.34462479262409174), + (0.048981796209073, -0.35957259110238166), + (0.0469057130870883, -0.3126531125455273), + (0.05396439570183631, -0.3126531125455273), + ], + [ + (-0.04527237752903268, -0.413550752273984), + (-0.039874561411872456, -0.4077377195324269), + (-0.039874561411872456, -0.4077377195324269), + (-0.010809397704086565, -0.43680288324021277), + (0.02656009849163815, -0.45756371446005983), + (0.07181871055090477, -0.45673328121126594), + (0.1104338566198203, -0.4338963668694341), + (0.1394990203276062, -0.40150947016647276), + (0.1432359699471787, -0.4052464197860452), + (0.1162468893613775, -0.4380485331134035), + (0.07763174329246192, -0.4625463139528231), + (0.02780574836482899, -0.4646223970748078), + (-0.013715914074865138, -0.4442767824793577), + (-0.04527237752903268, -0.413550752273984), + ], + ] for part, answer in zip(line_verts, line_answers): np.testing.assert_allclose(part, answer, atol=ATOL, rtol=RTOL) for part, answer in zip(pgon_verts, pgon_answers): diff --git a/libpysal/cg/ops/tests/test_shapely.py b/libpysal/cg/ops/tests/test_shapely.py index 4f3c0051b..77e35350a 100644 --- a/libpysal/cg/ops/tests/test_shapely.py +++ b/libpysal/cg/ops/tests/test_shapely.py @@ -1,19 +1,21 @@ import unittest as ut from .. import _shapely as sht from ...shapes import Point, Chain, Polygon -#from ... import comparators as comp -#from ... import shapely as she + +# from ... import comparators as comp +# from ... import shapely as she from ....io.geotable import read_files as rf from ....examples import get_path import numpy as np from warnings import warn -@ut.skip('skipping shapely during reorg') + +@ut.skip("Skipping shapely during reorg.") class Test_Shapely(ut.TestCase): def setUp(self): - self.polygons = rf(get_path('Polygon.shp')) - self.points = rf(get_path('Point.shp')) - self.lines = rf(get_path('Line.shp')) + self.polygons = rf(get_path("Polygon.shp")) + self.points = rf(get_path("Point.shp")) + self.lines = rf(get_path("Line.shp")) self.target_poly = self.polygons.geometry[2] self.target_point = self.points.geometry[1] self.target_line = self.lines.geometry[0] @@ -29,189 +31,192 @@ def compare(self, func_name, df, **kwargs): try: she_vals = (shefunc(geom, **kwargs) for geom in geom_list) sht_vals = shtfunc(df, inplace=False, **kwargs) - sht_list = sht_vals['shape_{}'.format(func_name)].tolist() + sht_list = sht_vals["shape_{}".format(func_name)].tolist() for tabular, shapely in zip(sht_list, she_vals): - if (comp.is_shape(tabular) and - comp.is_shape(shapely)): + if comp.is_shape(tabular) and comp.is_shape(shapely): comp.equal(tabular, shapely) else: self.assertEqual(tabular, shapely) except NotImplementedError as e: - warn('The shapely/pysal bridge is not implemented: {}'.format(e)) + warn("The shapely/pysal bridge is not implemented: {}".format(e)) return True def test_to_wkb(self): for df in self.dframes: - self.compare('to_wkb', df) + self.compare("to_wkb", df) def test_to_wkt(self): for df in self.dframes: - self.compare('to_wkt', df) + self.compare("to_wkt", df) def test_area(self): for df in self.dframes: - self.compare('area', df) + self.compare("area", df) def test_distance(self): for df in self.dframes: for other in self.targets: - self.compare('distance', df, other=other) + self.compare("distance", df, other=other) def test_length(self): for df in self.dframes: - self.compare('length', df) + self.compare("length", df) def test_boundary(self): for df in self.dframes: - self.compare('boundary', df) + self.compare("boundary", df) def test_bounds(self): for df in self.dframes: - self.compare('bounds', df) + self.compare("bounds", df) def test_centroid(self): for df in self.dframes: - self.compare('centroid', df) + self.compare("centroid", df) def test_representative_point(self): for df in self.dframes: - self.compare('representative_point', df) + self.compare("representative_point", df) def test_convex_hull(self): for df in self.dframes: - self.compare('convex_hull', df) + self.compare("convex_hull", df) def test_envelope(self): for df in self.dframes: - self.compare('envelope', df) + self.compare("envelope", df) def test_buffer(self): np.random.seed(555) for df in self.dframes: - self.compare('buffer', df, radius=np.random.randint(10)) + self.compare("buffer", df, radius=np.random.randint(10)) def test_simplify(self): - tol = .001 + tol = 0.001 for df in self.dframes: - self.compare('simplify', df, tolerance=tol) + self.compare("simplify", df, tolerance=tol) def test_difference(self): for df in self.dframes: for target in self.targets: - self.compare('difference', df, other=target) + self.compare("difference", df, other=target) def test_intersection(self): for df in self.dframes: for target in self.targets: - self.compare('intersection', df, other=target) + self.compare("intersection", df, other=target) def test_symmetric_difference(self): for df in self.dframes: for target in self.targets: - self.compare('symmetric_difference', df, other=target) + self.compare("symmetric_difference", df, other=target) def test_union(self): for df in self.dframes: for target in self.targets: - self.compare('union', df, other=target) + self.compare("union", df, other=target) def test_has_z(self): for df in self.dframes: - self.compare('has_z', df) + self.compare("has_z", df) def test_is_empty(self): - """ - PySAL doesn't really support empty shapes. Like, the following errors - out: - + """PySAL doesn't really support empty shapes. + Like, the following errors out: + + ``` ps.cg.Polygon([[]]) + ``` and you can make it work by: + + ``` ps.cg.Polygon([[()]]) - + ``` + but that won't convert over to shapely. - So, we're only testing the negative here. + """ for df in self.dframes: - self.compare('is_empty', df) + self.compare("is_empty", df) def test_is_ring(self): for df in self.dframes: - self.compare('is_ring', df) + self.compare("is_ring", df) def test_is_simple(self): for df in self.dframes: - self.compare('is_simple', df) + self.compare("is_simple", df) def test_is_valid(self): for df in self.dframes: - self.compare('is_valid', df) + self.compare("is_valid", df) def test_relate(self): for df in self.dframes: for target in self.targets: - self.compare('relate', df, other=target) + self.compare("relate", df, other=target) def test_contains(self): for df in self.dframes: for target in self.targets: - self.compare('contains', df, other=target) + self.compare("contains", df, other=target) def test_crosses(self): for df in self.dframes: for target in self.targets: - self.compare('crosses', df, other=target) + self.compare("crosses", df, other=target) def test_disjoint(self): for df in self.dframes: for target in self.targets: - self.compare('disjoint', df, other=target) + self.compare("disjoint", df, other=target) def test_equals(self): for df in self.dframes: for target in self.targets: - self.compare('equals', df, other=target) + self.compare("equals", df, other=target) def test_intersects(self): for df in self.dframes: for target in self.targets: - self.compare('intersects', df, other=target) + self.compare("intersects", df, other=target) def test_overlaps(self): for df in self.dframes: for target in self.targets: - self.compare('overlaps', df, other=target) + self.compare("overlaps", df, other=target) def test_touches(self): for df in self.dframes: for target in self.targets: - self.compare('touches', df, other=target) + self.compare("touches", df, other=target) def test_within(self): for df in self.dframes: for target in self.targets: - self.compare('within', df, other=target) + self.compare("within", df, other=target) def test_equals_exact(self): for df in self.dframes: for target in self.targets: - self.compare('equals_exact', df, other=target, tolerance=.1) + self.compare("equals_exact", df, other=target, tolerance=0.1) def test_almost_equals(self): for df in self.dframes: for target in self.targets: - self.compare('almost_equals', df, other=target) + self.compare("almost_equals", df, other=target) def test_project(self): np.random.seed(555) - self.compare('project', self.lines, other=self.targets[2]) + self.compare("project", self.lines, other=self.targets[2]) def test_interpolate(self): np.random.seed(555) for df in self.dframes: if isinstance(df.geometry[0], Chain): - self.compare('interpolate', df, distance=np.random.randint(10)) + self.compare("interpolate", df, distance=np.random.randint(10)) else: with self.assertRaises(TypeError): - self.compare('interpolate', df, distance=np.random.randint(10)) + self.compare("interpolate", df, distance=np.random.randint(10)) diff --git a/libpysal/cg/ops/tests/test_tabular.py b/libpysal/cg/ops/tests/test_tabular.py index c0cd0bfa4..52921cedf 100644 --- a/libpysal/cg/ops/tests/test_tabular.py +++ b/libpysal/cg/ops/tests/test_tabular.py @@ -15,25 +15,30 @@ PANDAS_EXTINCT = pandas is None SHAPELY_EXTINCT = shp is None -@ut.skipIf(PANDAS_EXTINCT or SHAPELY_EXTINCT, 'missing pandas or shapely') + +@ut.skipIf(PANDAS_EXTINCT or SHAPELY_EXTINCT, "Missing pandas or shapely.") class Test_Tabular(ut.TestCase): def setUp(self): import pandas as pd - self.columbus = pdio.read_files(get_path('columbus.shp')) - grid = [Polygon([(0,0),(0,1),(1,1),(1,0)]), - Polygon([(0,1),(0,2),(1,2),(1,1)]), - Polygon([(1,2),(2,2),(2,1),(1,1)]), - Polygon([(1,1),(2,1),(2,0),(1,0)])] - regime = [0,0,1,1] + + self.columbus = pdio.read_files(get_path("columbus.shp")) + grid = [ + Polygon([(0, 0), (0, 1), (1, 1), (1, 0)]), + Polygon([(0, 1), (0, 2), (1, 2), (1, 1)]), + Polygon([(1, 2), (2, 2), (2, 1), (1, 1)]), + Polygon([(1, 1), (2, 1), (2, 0), (1, 0)]), + ] + regime = [0, 0, 1, 1] ids = list(range(4)) data = np.array((regime, ids)).T - self.exdf = pd.DataFrame(data, columns=['regime', 'ids']) - self.exdf['geometry'] = grid + self.exdf = pd.DataFrame(data, columns=["regime", "ids"]) + self.exdf["geometry"] = grid - @_requires('geopandas') + @_requires("geopandas") def test_round_trip(self): import geopandas as gpd import pandas as pd + geodf = GIS.tabular.to_gdf(self.columbus) self.assertIsInstance(geodf, gpd.GeoDataFrame) new_df = GIS.tabular.to_df(geodf) @@ -46,14 +51,14 @@ def test_spatial_join(self): def test_spatial_overlay(self): pass - + def test_dissolve(self): - out = GIS.tabular.dissolve(self.exdf, by='regime') + out = GIS.tabular.dissolve(self.exdf, by="regime") self.assertEqual(out[0].area, 2.0) self.assertEqual(out[1].area, 2.0) - answer_vertices0 = [(0,0), (0,1), (0,2), (1,2), (1,1), (1,0), (0,0)] - answer_vertices1 = [(2,1), (2,0), (1,0), (1,1), (1,2), (2,2), (2,1)] + answer_vertices0 = [(0, 0), (0, 1), (0, 2), (1, 2), (1, 1), (1, 0), (0, 0)] + answer_vertices1 = [(2, 1), (2, 0), (1, 0), (1, 1), (1, 2), (2, 2), (2, 1)] np.testing.assert_allclose(out[0].vertices, answer_vertices0) np.testing.assert_allclose(out[1].vertices, answer_vertices1) @@ -65,8 +70,8 @@ def test_erase(self): pass def test_union(self): - new_geom = GIS.tabular.union(self.exdf) - self.assertEqual(new_geom.area, 4) + new_geom = GIS.tabular.union(self.exdf) + self.assertEqual(new_geom.area, 4) def test_intersection(self): pass From 0caa78db847d8eb20552fff088714e856c09ee90 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Wed, 26 Aug 2020 09:22:31 -0400 Subject: [PATCH 27/33] minor updates to examples/*.py stuff --- libpysal/common.py | 5 +++- libpysal/examples/__init__.py | 4 ++-- libpysal/examples/base.py | 45 +++++++++++++++++++++++++---------- libpysal/examples/builtin.py | 11 +++++++++ libpysal/examples/remotes.py | 5 +++- 5 files changed, 54 insertions(+), 16 deletions(-) diff --git a/libpysal/common.py b/libpysal/common.py index fe12e3fa4..f77d8d7d0 100644 --- a/libpysal/common.py +++ b/libpysal/common.py @@ -1,3 +1,6 @@ +"""Function used throughout the PySAL ecosystem. +""" + import copy import sys import time @@ -73,7 +76,7 @@ def simport(modname): Returns ------- _simport : tuple - Either (True, Module) or (False, None) depending + Either (``True``, ````) or (``False``, None) depending on whether the import succeeded. Notes diff --git a/libpysal/examples/__init__.py b/libpysal/examples/__init__.py index 0b0828ffc..352f79bc5 100644 --- a/libpysal/examples/__init__.py +++ b/libpysal/examples/__init__.py @@ -1,5 +1,5 @@ -""" The :mod:`libpysal.examples` module includes a number of small built-in - example datasets as well as functions to fetch larger datasets. +"""The ``libpysal.examples`` module includes a number of small built-in +example datasets as well as functions to fetch larger datasets. """ from .base import example_manager diff --git a/libpysal/examples/base.py b/libpysal/examples/base.py index f9698b7f9..3371b4c6b 100644 --- a/libpysal/examples/base.py +++ b/libpysal/examples/base.py @@ -22,10 +22,11 @@ def get_data_home(): - """Return the path of the ``libpysal`` data directory. This folder (``~/pysal_data``) - is used by some large dataset loaders to avoid downloading the data multiple times. - Alternatively, it can be set by the 'PYSALDATA' environment variable or programmatically - by giving an explicit folder path. The ``'~'`` symbol is expanded to the user home + """Return the path of the ``libpysal`` data directory. This folder + (``~/pysal_data``) is used by some large dataset loaders to avoid + downloading the data multiple times. Alternatively, it can be set + by the 'PYSALDATA' environment variable or programmatically by giving + an explicit folder path. The ``'~'`` symbol is expanded to the user home folder If the folder does not already exist, it is automatically created. Returns @@ -39,6 +40,7 @@ def get_data_home(): data_home = expanduser(data_home) if not exists(data_home): makedirs(data_home) + return data_home @@ -55,12 +57,8 @@ def get_list_of_files(dir_name): all_files : list All file and directory paths. - Raises - ------ - FileNotFoundError - If the file or directory is not found. - """ + # names in the given directory all_files = list() try: @@ -89,7 +87,7 @@ def type_of_script() -> str: return "jupyter" if "terminal" in ipy_str: return "ipython" - except: + except NameError: return "terminal" @@ -114,7 +112,7 @@ class Example: Attributes ---------- root : str - The ``name`` parameter with filled spaces (_). + The ``name`` parameter with filled underscores (``'_`'`). installed : bool ``True`` if the example is installed, otherwise ``False``. zipfile : zipfile.ZipFile @@ -141,12 +139,15 @@ def get_path(self, file_name, verbose=True) -> Union[str, None]: """Get the path for local file.""" file_list = self.get_file_list() + for file_path in file_list: base_name = os.path.basename(file_path) if file_name == base_name: return file_path + if verbose: print("{} is not a file in this example".format(file_name)) + return None def downloaded(self) -> bool: @@ -156,20 +157,24 @@ def downloaded(self) -> bool: if os.path.isdir(path): self.installed = True return True + return False def explain(self) -> None: """Provide a description of the example.""" file_name = self.explain_url.split("/")[-1] + if file_name == "README.md": explain_page = requests.get(self.explain_url) crawled = BeautifulSoup(explain_page.text, "html.parser") print(crawled.text) return None + if type_of_script() == "terminal": webbrowser.open(self.explain_url) return None + from IPython.display import IFrame return IFrame(self.explain_url, width=700, height=350) @@ -190,24 +195,31 @@ def download(self, path=get_data_home()): def get_file_list(self) -> Union[list, None]: """Get the list of local files for the example.""" + path = self.get_local_path() + if os.path.isdir(path): return get_list_of_files(path) + return None def json_dict(self) -> dict: """Container for example meta data.""" + meta = {} meta["name"] = self.name meta["description"] = self.description meta["download_url"] = self.download_url meta["explain_url"] = self.explain_url meta["root"] = self.root + return meta def load(self, file_name) -> io.FileIO: """Dispatch to libpysal.io to open file.""" + pth = self.get_path(file_name) + if pth: return ps_open(pth) @@ -230,24 +242,30 @@ def explain(self, example_name) -> str: def available(self): """Report available datasets.""" + datasets = self.datasets names = list(datasets.keys()) names.sort() rows = [] + for name in names: description = datasets[name].description installed = datasets[name].installed rows.append([name, description, installed]) + datasets = pandas.DataFrame( data=rows, columns=["Name", "Description", "Installed"] ) + datasets.style.set_properties(subset=["text"], **{"width": "300px"}) print(datasets.to_string()) - def load(self, example_name: str) -> Example: + def load(self, example_name: str) -> Union[Example, None]: """Load example dataset, download if not locally available.""" + if example_name in self.datasets: example = self.datasets[example_name] + if example.installed: return example else: @@ -260,6 +278,7 @@ def load(self, example_name: str) -> Example: def download_remotes(self): """Download all remotes.""" + names = list(self.remotes.keys()) names.sort() @@ -273,7 +292,9 @@ def download_remotes(self): def get_installed_names(self) -> list: """Return names of all currently installed datasets.""" + ds = self.datasets + return [name for name in ds if ds[name].installed] diff --git a/libpysal/examples/builtin.py b/libpysal/examples/builtin.py index 0dd478eb8..3507bbcd7 100644 --- a/libpysal/examples/builtin.py +++ b/libpysal/examples/builtin.py @@ -41,6 +41,7 @@ class LocalExample: """Builtin pysal example dataset.""" def __init__(self, name: str, dirname: str): + self.name = name self.dirname = dirname self.installed = True @@ -48,30 +49,40 @@ def __init__(self, name: str, dirname: str): def get_file_list(self) -> list: """Return a list of file names.""" + return get_list_of_files(self.dirname) def get_path(self, file_name: str, verbose=True) -> str: """Get path for local file.""" + file_list = self.get_file_list() + for file_path in file_list: base_name = os.path.basename(file_path) if file_name == base_name: return file_path + if verbose: print("{} is not a file in this example".format(file_name)) + return None def explain(self): """Provide a printed description of the example.""" + description = [f for f in self.get_file_list() if "README.md" in f][0] + with open(description, "r", encoding="utf8") as f: print(f.read()) def get_description(self) -> str: """Dataset description.""" + description = [f for f in self.get_file_list() if "README.md" in f][0] + with open(description, "r", encoding="utf8") as f: lines = f.readlines() + return lines[3].strip() diff --git a/libpysal/examples/remotes.py b/libpysal/examples/remotes.py index 9d14a06bb..7318d6599 100644 --- a/libpysal/examples/remotes.py +++ b/libpysal/examples/remotes.py @@ -25,10 +25,12 @@ def poll_remotes(): except: warnings.warn("Remote data sets not available. Check connection.") return {} + soup = BeautifulSoup(page.text, "html.parser") samples = soup.find(class_="samples") rows = samples.find_all("tr") datasets = {} + for row in rows[1:]: data = row.find_all("td") name = data[0].text.strip() @@ -42,7 +44,6 @@ def poll_remotes(): # Other Remotes - # rio name = "Rio Grande do Sul" description = "Cities of the Brazilian State of Rio Grande do Sul" @@ -104,9 +105,11 @@ def download(datasets=datasets): names = list(datasets.keys()) names.sort() + for name in names: print(name) example = datasets[name] + try: example.download() except: From b90b118c58d3155073bf43ac593c654a91d575c0 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Wed, 26 Aug 2020 09:23:03 -0400 Subject: [PATCH 28/33] latex update to weights/spintW.py --- libpysal/weights/spintW.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libpysal/weights/spintW.py b/libpysal/weights/spintW.py index ba182fec2..45410cb15 100644 --- a/libpysal/weights/spintW.py +++ b/libpysal/weights/spintW.py @@ -12,7 +12,7 @@ def ODW(Wo, Wd, transform="r", silence_warnings=True): - """Construct an :math:`(o \cdot d)\cdot(o \cdot d)` + """Construct an :math:`(o \cdot d)\\times(o \cdot d)` origin-destination style spatial weight for :math:`o \cdot d` flows using standard spatial weights on :math:`o` origins and :math:`d` destinations. Input spatial weights must be @@ -38,7 +38,7 @@ def ODW(Wo, Wd, transform="r", silence_warnings=True): Ww : libpysal.weights.WSP A sparse spatial contiguity `W` object for assocations between flows between :math:`o` origins and :math:`d` destinations, - :math:`(o \cdot d)\cdot(o \cdot d)`. + :math:`(o \cdot d)\\times(o \cdot d)`. Raises ------ From 57721a953867f0ab274745c48371c02308d00c70 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Tue, 27 Oct 2020 14:17:25 -0400 Subject: [PATCH 29/33] merge in upstream/master --- codecov.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 codecov.yml diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..3f7277717 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,22 @@ +codecov: + notify: + after_n_builds: 9 +coverage: + range: 50..95 + round: nearest + precision: 1 + status: + project: + default: + threshold: 5% + patch: + default: + threshold: 20% + target: 60% + ignore: + - "tests/*" +comment: + layout: "reach, diff, files" + behavior: once + after_n_builds: 9 + require_changes: true From b8b207dfa4f2945778c26ae6ef67d2458f5a973b Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Tue, 27 Oct 2020 14:19:11 -0400 Subject: [PATCH 30/33] Update .pre-commit-config.yaml --- .pre-commit-config.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cc3fcbf92..efa01fd74 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,4 +4,5 @@ repos: hooks: - id: black language_version: python3.8 - \ No newline at end of file + + From 9d73b08256ef552e9cdd112fb257a8e7996bb260 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Thu, 10 Dec 2020 15:58:29 -0500 Subject: [PATCH 31/33] add missing kwarg to fuzzy_contiguity --- libpysal/weights/util.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libpysal/weights/util.py b/libpysal/weights/util.py index f1522f667..ada1b863e 100644 --- a/libpysal/weights/util.py +++ b/libpysal/weights/util.py @@ -1689,7 +1689,13 @@ def nonplanar_neighbors(w, geodataframe, tolerance=0.001, **kwargs): @requires("geopandas") def fuzzy_contiguity( - gdf, tolerance=0.005, buffering=False, drop=True, buffer=None, **kwargs + gdf, + tolerance=0.005, + buffering=False, + drop=True, + buffer=None, + predicate="intersects", + **kwargs ): """Fuzzy contiguity spatial weights. From ded03e32f2b97dac3a0355039009c244efd15632 Mon Sep 17 00:00:00 2001 From: Sergio Rey Date: Sun, 26 Feb 2023 12:23:35 -0800 Subject: [PATCH 32/33] TEST: 3.11 dev coverage (#516) * TEST: add 3.11 and shapely_dev coverage * TEST: fix environment-file list * TEST: remove option numba * Update ci/311_shapely_dev.yaml Co-authored-by: Martin Fleischmann --------- Co-authored-by: James Gaboardi Co-authored-by: Martin Fleischmann --- .github/workflows/unittests.yml | 3 ++- ci/311_shapely_dev.yaml | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 ci/311_shapely_dev.yaml diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml index 8284b6a5e..614316e17 100644 --- a/.github/workflows/unittests.yml +++ b/.github/workflows/unittests.yml @@ -23,7 +23,8 @@ ci/38.yaml, ci/39.yaml, ci/310.yaml, - ci/310_shapely_dev.yaml + ci/310_shapely_dev.yaml, + ci/311_shapely_dev.yaml ] include: - environment-file: ci/310.yaml diff --git a/ci/311_shapely_dev.yaml b/ci/311_shapely_dev.yaml new file mode 100644 index 000000000..8b68a0d6d --- /dev/null +++ b/ci/311_shapely_dev.yaml @@ -0,0 +1,31 @@ +name: test +channels: + - conda-forge +dependencies: + - python=3.11 + - platformdirs + - beautifulsoup4 + - jinja2 + - pandas>=1.0 + - scipy>=1.0 + - xarray + # testing + - codecov + - matplotlib + - pytest + - pytest-cov + - pytest-xdist + # optional + - geopandas>=0.12.0 + - joblib + - networkx + - packaging + - shapely>=2.0b1 + - xarray + - zstd + # for docs build action (this env only) + - nbsphinx + - numpydoc + - sphinx + - sphinxcontrib-bibtex + - sphinx_bootstrap_theme From c8c27b185b5c1af68805c77ea53e099c7171ad8b Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Mon, 27 Feb 2023 00:20:46 -0500 Subject: [PATCH 33/33] remove 311_shapely_dev.yaml --- ci/311_shapely_dev.yaml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 ci/311_shapely_dev.yaml diff --git a/ci/311_shapely_dev.yaml b/ci/311_shapely_dev.yaml deleted file mode 100644 index 8b68a0d6d..000000000 --- a/ci/311_shapely_dev.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: test -channels: - - conda-forge -dependencies: - - python=3.11 - - platformdirs - - beautifulsoup4 - - jinja2 - - pandas>=1.0 - - scipy>=1.0 - - xarray - # testing - - codecov - - matplotlib - - pytest - - pytest-cov - - pytest-xdist - # optional - - geopandas>=0.12.0 - - joblib - - networkx - - packaging - - shapely>=2.0b1 - - xarray - - zstd - # for docs build action (this env only) - - nbsphinx - - numpydoc - - sphinx - - sphinxcontrib-bibtex - - sphinx_bootstrap_theme