Skip to content

Commit

Permalink
ENH: add neighbors
Browse files Browse the repository at this point in the history
  • Loading branch information
martinfleis committed Mar 21, 2024
1 parent 9f98387 commit bf67bca
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 1 deletion.
44 changes: 44 additions & 0 deletions momepy/functional/_distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"neighbor_distance",
"mean_interbuilding_distance",
"building_adjacency",
"neighbors",
]

GPD_GE_013 = Version(gpd.__version__) >= Version("0.13.0")
Expand Down Expand Up @@ -274,3 +275,46 @@ def building_adjacency(
result.name = "building_adjacency"
result.index.name = None
return result


def neighbors(
geometry: GeoDataFrame | GeoSeries, graph: Graph, weighted=False
) -> pd.Series:
"""Calculate the number of neighbours captured by ``graph``.
If ``weighted=True``, the number of neighbours will be divided by the perimeter of
the object to return a relative value (neighbors per meter).
Adapted from :cite:`hermosilla2012`.
Notes
-----
The index of ``geometry`` must match the index along which the ``graph`` is
built.
Parameters
----------
gdf : gpd.GeoDataFrame
GeoDataFrame containing geometries to analyse.
graph : libpysal.graph.Graph
Graph representing spatial relationships between elements.
weighted : bool
If True, the number of neighbours will be divided by the perimeter of the object
to return a relative value (neighbors per meter).
Returns
-------
pd.Series
"""
if weighted:
r = graph.cardinalities / geometry.length
else:
r = graph.cardinalities

r.name = "neighbors"
return r

# CPU times: user 49.5 ms, sys: 121 ms, total: 170 ms
# Wall time: 179 ms
# 25.7 ms ± 190 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
# 549 ms ± 2.07 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
46 changes: 46 additions & 0 deletions momepy/functional/tests/test_distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ def setup_method(self):
test_file_path = mm.datasets.get_path("bubenec")
self.df_buildings = gpd.read_file(test_file_path, layer="buildings")
self.df_streets = gpd.read_file(test_file_path, layer="streets")
self.df_tessellation = gpd.read_file(test_file_path, layer="tessellation")
self.graph = Graph.build_knn(self.df_buildings.centroid, k=5)
self.contiguity = Graph.build_contiguity(self.df_buildings)
self.neighborhood_graph = self.graph.higher_order(3, lower_order=True)
self.tess_contiguity = Graph.build_contiguity(self.df_tessellation)

def test_orientation(self):
expected = {
Expand Down Expand Up @@ -88,6 +90,25 @@ def test_building_adjacency(self):
r = mm.building_adjacency(self.contiguity, self.graph)
assert_result(r, expected, self.df_buildings, exact=False)

def test_neighbors(self):
expected = {
"mean": 5.180555555555555,
"sum": 746,
"min": 2,
"max": 12,
}
r = mm.neighbors(self.df_tessellation, self.tess_contiguity, weighted=False)
assert_result(r, expected, self.df_buildings, exact=False, check_names=False)

expected = {
"mean": 0.029066398893536072,
"sum": 4.185561440669194,
"min": 0.008659386154613532,
"max": 0.08447065801729325,
}
r = mm.neighbors(self.df_tessellation, self.tess_contiguity, weighted=True)
assert_result(r, expected, self.df_buildings, exact=False, check_names=False)


class TestEquality:
def setup_method(self):
Expand Down Expand Up @@ -142,3 +163,28 @@ def test_building_adjacency(self):
self.df_buildings.reset_index(), self.graph.to_W(), "uID", verbose=False
).series
assert_series_equal(new, old, check_names=False, check_index=False)

def test_neighbors(self):
new = mm.neighbors(
self.df_tessellation, self.tessellation_contiguity, weighted=False
)
old = mm.Neighbors(
self.df_tessellation.reset_index(),
self.tessellation_contiguity.to_W(),
"uID",
weighted=False,
verbose=False,
).series
assert_series_equal(new, old, check_names=False, check_index=False)

new = mm.neighbors(
self.df_tessellation, self.tessellation_contiguity, weighted=True
)
old = mm.Neighbors(
self.df_tessellation.reset_index(),
self.tessellation_contiguity.to_W(),
"uID",
weighted=True,
verbose=False,
).series
assert_series_equal(new, old, check_names=False, check_index=False)
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ momepy = ["datasets/bubenec.gpkg", "datasets/tests.gpkg"]
[tool.ruff]
line-length = 88
select = ["E", "F", "W", "I", "UP", "N", "B", "A", "C4", "SIM", "ARG"]
ignore = ["B006", "F403"]
ignore = ["B006", "F403", "SIM108"]
exclude = ["momepy/tests/*", "docs/*", "benchmarks/*"]

[tool.coverage.run]
Expand Down

0 comments on commit bf67bca

Please sign in to comment.