From d80af4d65095925e0bb3bac861fdcebcbe540578 Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Tue, 23 Apr 2024 23:08:42 +0200 Subject: [PATCH 01/11] ENH: describe as a replacement of AverageCharacter --- momepy/functional/_distribution.py | 84 +++++++++++++++++++++++++++++- momepy/utils.py | 6 +-- 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/momepy/functional/_distribution.py b/momepy/functional/_distribution.py index e6ded9d0..25dbbc35 100644 --- a/momepy/functional/_distribution.py +++ b/momepy/functional/_distribution.py @@ -3,9 +3,12 @@ import shapely from geopandas import GeoDataFrame, GeoSeries from libpysal.graph import Graph +from numpy.typing import NDArray from packaging.version import Version -from pandas import Series -from scipy import sparse +from pandas import DataFrame, Series +from scipy import sparse, stats + +from ..utils import limit_range __all__ = [ "orientation", @@ -16,6 +19,7 @@ "building_adjacency", "neighbors", "street_alignment", + "describe", ] GPD_GE_013 = Version(gpd.__version__) >= Version("0.13.0") @@ -339,3 +343,79 @@ def street_alignment( Series """ return (building_orientation - street_orientation.loc[street_index].values).abs() + + +def describe( + values: NDArray[np.float_] | Series, + graph: Graph, + q: tuple[float, float] | None = None, + include_mode: bool = False, +) -> DataFrame: + """Describe the distribution of values within a set neighbourhood. + + Given the graph, computes the descriptive statisitcs of values within the + neighbourhood of each node. Optionally, the values can be limited to a certain + quantile range before computing the statistics. + + Notes + ----- + The index of ``values`` must match the index along which the ``graph`` is + built. + + Parameters + ---------- + values : NDArray[np.float_] | Series + array of values + graph : libpysal.graph.Graph + Graph representing spatial relationships between elements. + q : tuple[float, float] | None, optional + tuple of percentages for the percentiles to compute. Values must be between 0 + and 100 inclusive. When set, values below and above the percentiles will be + discarded before computation of the average. The percentiles are computed for + each neighborhood. By default None + include_mode : False + Compute mode along with other statistics. Default is False. Mode is + computationally expensive and not useful for continous variables. + + Returns + ------- + DataFrame + A DataFrame with descriptive statistics + """ + + def _describe(values, q, include_mode=False): + """Helper function to calculate average.""" + values = limit_range(values, q) + + results = [ + np.mean(values), + np.median(values), + np.std(values), + np.min(values), + np.max(values), + np.sum(values), + ] + if include_mode: + results.append(stats.mode(values, keepdims=False)[0]) + return results + + if not isinstance(values, Series): + values = Series(values) + + grouper = values.take(graph._adjacency.index.codes[1]).groupby( + graph._adjacency.index.codes[0] + ) + + if q is None: + stat_ = grouper.agg(["mean", "median", "std", "min", "max", "sum"]) + if include_mode: + stat_["mode"] = grouper.agg(lambda x: stats.mode(x, keepdims=False)[0]) + else: + agg = graph.apply(values, _describe, q=q, include_mode=include_mode) + stat_ = DataFrame(zip(*agg, strict=True)).T + cols = ["mean", "median", "std", "min", "max", "sum"] + if include_mode: + cols.append("mode") + stat_.columns = cols + + return stat_ diff --git a/momepy/utils.py b/momepy/utils.py index 89d8e3c6..f7134b4b 100644 --- a/momepy/utils.py +++ b/momepy/utils.py @@ -479,11 +479,9 @@ def limit_range(vals, rng): method = {"interpolation": "nearest"} rng = sorted(rng) if nan_tracker.any(): - lower = np.nanpercentile(vals, rng[0], **method) - higher = np.nanpercentile(vals, rng[1], **method) + lower, higher = np.nanpercentile(vals, rng, **method) else: - lower = np.percentile(vals, rng[0], **method) - higher = np.percentile(vals, rng[1], **method) + lower, higher = np.percentile(vals, rng, **method) vals = vals[(lower <= vals) & (vals <= higher)] return vals From 595a5c4794d3e017b2cbc2f3796ce7b32071128c Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Tue, 23 Apr 2024 23:26:07 +0200 Subject: [PATCH 02/11] perf + fixup --- momepy/functional/_distribution.py | 4 ++-- momepy/utils.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/momepy/functional/_distribution.py b/momepy/functional/_distribution.py index 25dbbc35..4783045d 100644 --- a/momepy/functional/_distribution.py +++ b/momepy/functional/_distribution.py @@ -385,7 +385,7 @@ def describe( def _describe(values, q, include_mode=False): """Helper function to calculate average.""" - values = limit_range(values, q) + values = limit_range(values.values, q) results = [ np.mean(values), @@ -411,7 +411,7 @@ def _describe(values, q, include_mode=False): if include_mode: stat_["mode"] = grouper.agg(lambda x: stats.mode(x, keepdims=False)[0]) else: - agg = graph.apply(values, _describe, q=q, include_mode=include_mode) + agg = grouper.apply(_describe, q=q, include_mode=include_mode) stat_ = DataFrame(zip(*agg, strict=True)).T cols = ["mean", "median", "std", "min", "max", "sum"] if include_mode: diff --git a/momepy/utils.py b/momepy/utils.py index f7134b4b..bb437718 100644 --- a/momepy/utils.py +++ b/momepy/utils.py @@ -469,7 +469,6 @@ def limit_range(vals, rng): The limited array. """ - vals = np.asarray(vals) nan_tracker = np.isnan(vals) if (len(vals) > 2) and (not nan_tracker.all()): From 24abb99bc24f90faafbcaf19bc14ae1fff171357 Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Tue, 23 Apr 2024 23:30:29 +0200 Subject: [PATCH 03/11] limit_range array input --- momepy/dimension.py | 2 +- momepy/diversity.py | 2 +- momepy/tests/test_utils.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/momepy/dimension.py b/momepy/dimension.py index eac95128..61956fbc 100644 --- a/momepy/dimension.py +++ b/momepy/dimension.py @@ -438,7 +438,7 @@ def __init__( values_list = data.loc[neighbours] if rng: - values_list = limit_range(values_list, rng=rng) + values_list = limit_range(values_list.values, rng=rng) if "mean" in mode: means.append(np.mean(values_list)) if "median" in mode: diff --git a/momepy/diversity.py b/momepy/diversity.py index a53725b9..f8399c6e 100644 --- a/momepy/diversity.py +++ b/momepy/diversity.py @@ -209,7 +209,7 @@ def __init__(self, gdf, values, spatial_weights, unique_id, rng=None, verbose=Tr values_list = data.loc[neighbours] if rng: - values_list = limit_range(values_list, rng=rng) + values_list = limit_range(values_list.values, rng=rng) results_list.append(Theil(values_list).T) else: results_list.append(np.nan) diff --git a/momepy/tests/test_utils.py b/momepy/tests/test_utils.py index dd289e4b..701d2dc5 100644 --- a/momepy/tests/test_utils.py +++ b/momepy/tests/test_utils.py @@ -196,9 +196,9 @@ def test_nx_to_gdf_osmnx(self): assert len(lines) == 16 def test_limit_range(self): - assert list(mm.limit_range(range(10), rng=(25, 75))) == [2, 3, 4, 5, 6, 7] - assert list(mm.limit_range(range(10), rng=(10, 90))) == [1, 2, 3, 4, 5, 6, 7, 8] - assert list(mm.limit_range([0, 1], rng=(25, 75))) == [0, 1] + assert list(mm.limit_range(np.arange(10), rng=(25, 75))) == [2, 3, 4, 5, 6, 7] + assert list(mm.limit_range(np.arange(10), rng=(10, 90))) == [1, 2, 3, 4, 5, 6, 7, 8] + assert list(mm.limit_range(np.array([0, 1]), rng=(25, 75))) == [0, 1] assert list( mm.limit_range(np.array([0, 1, 2, 3, 4, np.nan]), rng=(25, 75)) ) == [1, 2, 3] From 24bded69ce10386fcdf9c631762405686dc0ecd8 Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Wed, 24 Apr 2024 09:14:52 +0200 Subject: [PATCH 04/11] Apply suggestions from code review Co-authored-by: James Gaboardi --- momepy/functional/_distribution.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/momepy/functional/_distribution.py b/momepy/functional/_distribution.py index 4783045d..b71b8249 100644 --- a/momepy/functional/_distribution.py +++ b/momepy/functional/_distribution.py @@ -365,14 +365,14 @@ def describe( Parameters ---------- values : NDArray[np.float_] | Series - array of values + An array of values. graph : libpysal.graph.Graph Graph representing spatial relationships between elements. q : tuple[float, float] | None, optional - tuple of percentages for the percentiles to compute. Values must be between 0 + Tuple of percentages for the percentiles to compute. Values must be between 0 and 100 inclusive. When set, values below and above the percentiles will be discarded before computation of the average. The percentiles are computed for - each neighborhood. By default None + each neighborhood. By default None. include_mode : False Compute mode along with other statistics. Default is False. Mode is computationally expensive and not useful for continous variables. @@ -380,7 +380,7 @@ def describe( Returns ------- DataFrame - A DataFrame with descriptive statistics + A DataFrame with descriptive statistics. """ def _describe(values, q, include_mode=False): From 90005ba477ef6aa866f4b38f44dce082d8a4fa9a Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Wed, 24 Apr 2024 09:31:57 +0200 Subject: [PATCH 05/11] rename to y --- momepy/functional/_distribution.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/momepy/functional/_distribution.py b/momepy/functional/_distribution.py index b71b8249..9be1a1c5 100644 --- a/momepy/functional/_distribution.py +++ b/momepy/functional/_distribution.py @@ -346,7 +346,7 @@ def street_alignment( def describe( - values: NDArray[np.float_] | Series, + y: NDArray[np.float_] | Series, graph: Graph, q: tuple[float, float] | None = None, include_mode: bool = False, @@ -364,8 +364,8 @@ def describe( Parameters ---------- - values : NDArray[np.float_] | Series - An array of values. + y : NDArray[np.float_] | Series + An 1D array of numeric values to be described. graph : libpysal.graph.Graph Graph representing spatial relationships between elements. q : tuple[float, float] | None, optional @@ -399,10 +399,10 @@ def _describe(values, q, include_mode=False): results.append(stats.mode(values, keepdims=False)[0]) return results - if not isinstance(values, Series): - values = Series(values) + if not isinstance(y, Series): + y = Series(y) - grouper = values.take(graph._adjacency.index.codes[1]).groupby( + grouper = y.take(graph._adjacency.index.codes[1]).groupby( graph._adjacency.index.codes[0] ) From 88082f885c119a160604eb3daa9a7303fb8ab8ab Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Wed, 24 Apr 2024 09:33:43 +0200 Subject: [PATCH 06/11] move to diversity --- momepy/__init__.py | 1 + momepy/functional/_distribution.py | 84 +---------------------------- momepy/functional/_diversity.py | 85 ++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 82 deletions(-) create mode 100644 momepy/functional/_diversity.py diff --git a/momepy/__init__.py b/momepy/__init__.py index e18a4bd2..8ad653a4 100644 --- a/momepy/__init__.py +++ b/momepy/__init__.py @@ -9,6 +9,7 @@ from .elements import * from .functional._dimension import * from .functional._distribution import * +from .functional._diversity import * from .functional._elements import * from .functional._shape import * from .graph import * diff --git a/momepy/functional/_distribution.py b/momepy/functional/_distribution.py index 9be1a1c5..e6ded9d0 100644 --- a/momepy/functional/_distribution.py +++ b/momepy/functional/_distribution.py @@ -3,12 +3,9 @@ import shapely from geopandas import GeoDataFrame, GeoSeries from libpysal.graph import Graph -from numpy.typing import NDArray from packaging.version import Version -from pandas import DataFrame, Series -from scipy import sparse, stats - -from ..utils import limit_range +from pandas import Series +from scipy import sparse __all__ = [ "orientation", @@ -19,7 +16,6 @@ "building_adjacency", "neighbors", "street_alignment", - "describe", ] GPD_GE_013 = Version(gpd.__version__) >= Version("0.13.0") @@ -343,79 +339,3 @@ def street_alignment( Series """ return (building_orientation - street_orientation.loc[street_index].values).abs() - - -def describe( - y: NDArray[np.float_] | Series, - graph: Graph, - q: tuple[float, float] | None = None, - include_mode: bool = False, -) -> DataFrame: - """Describe the distribution of values within a set neighbourhood. - - Given the graph, computes the descriptive statisitcs of values within the - neighbourhood of each node. Optionally, the values can be limited to a certain - quantile range before computing the statistics. - - Notes - ----- - The index of ``values`` must match the index along which the ``graph`` is - built. - - Parameters - ---------- - y : NDArray[np.float_] | Series - An 1D array of numeric values to be described. - graph : libpysal.graph.Graph - Graph representing spatial relationships between elements. - q : tuple[float, float] | None, optional - Tuple of percentages for the percentiles to compute. Values must be between 0 - and 100 inclusive. When set, values below and above the percentiles will be - discarded before computation of the average. The percentiles are computed for - each neighborhood. By default None. - include_mode : False - Compute mode along with other statistics. Default is False. Mode is - computationally expensive and not useful for continous variables. - - Returns - ------- - DataFrame - A DataFrame with descriptive statistics. - """ - - def _describe(values, q, include_mode=False): - """Helper function to calculate average.""" - values = limit_range(values.values, q) - - results = [ - np.mean(values), - np.median(values), - np.std(values), - np.min(values), - np.max(values), - np.sum(values), - ] - if include_mode: - results.append(stats.mode(values, keepdims=False)[0]) - return results - - if not isinstance(y, Series): - y = Series(y) - - grouper = y.take(graph._adjacency.index.codes[1]).groupby( - graph._adjacency.index.codes[0] - ) - - if q is None: - stat_ = grouper.agg(["mean", "median", "std", "min", "max", "sum"]) - if include_mode: - stat_["mode"] = grouper.agg(lambda x: stats.mode(x, keepdims=False)[0]) - else: - agg = grouper.apply(_describe, q=q, include_mode=include_mode) - stat_ = DataFrame(zip(*agg, strict=True)).T - cols = ["mean", "median", "std", "min", "max", "sum"] - if include_mode: - cols.append("mode") - stat_.columns = cols - - return stat_ diff --git a/momepy/functional/_diversity.py b/momepy/functional/_diversity.py new file mode 100644 index 00000000..93836e74 --- /dev/null +++ b/momepy/functional/_diversity.py @@ -0,0 +1,85 @@ +import numpy as np +from libpysal.graph import Graph +from numpy.typing import NDArray +from pandas import DataFrame, Series +from scipy import stats + +from ..utils import limit_range + +__all__ = ["describe"] + + +def describe( + y: NDArray[np.float_] | Series, + graph: Graph, + q: tuple[float, float] | None = None, + include_mode: bool = False, +) -> DataFrame: + """Describe the distribution of values within a set neighbourhood. + + Given the graph, computes the descriptive statisitcs of values within the + neighbourhood of each node. Optionally, the values can be limited to a certain + quantile range before computing the statistics. + + Notes + ----- + The index of ``values`` must match the index along which the ``graph`` is + built. + + Parameters + ---------- + y : NDArray[np.float_] | Series + An 1D array of numeric values to be described. + graph : libpysal.graph.Graph + Graph representing spatial relationships between elements. + q : tuple[float, float] | None, optional + Tuple of percentages for the percentiles to compute. Values must be between 0 + and 100 inclusive. When set, values below and above the percentiles will be + discarded before computation of the average. The percentiles are computed for + each neighborhood. By default None. + include_mode : False + Compute mode along with other statistics. Default is False. Mode is + computationally expensive and not useful for continous variables. + + Returns + ------- + DataFrame + A DataFrame with descriptive statistics. + """ + + def _describe(values, q, include_mode=False): + """Helper function to calculate average.""" + values = limit_range(values.values, q) + + results = [ + np.mean(values), + np.median(values), + np.std(values), + np.min(values), + np.max(values), + np.sum(values), + ] + if include_mode: + results.append(stats.mode(values, keepdims=False)[0]) + return results + + if not isinstance(y, Series): + y = Series(y) + + grouper = y.take(graph._adjacency.index.codes[1]).groupby( + graph._adjacency.index.codes[0] + ) + + if q is None: + stat_ = grouper.agg(["mean", "median", "std", "min", "max", "sum"]) + if include_mode: + stat_["mode"] = grouper.agg(lambda x: stats.mode(x, keepdims=False)[0]) + else: + agg = grouper.apply(_describe, q=q, include_mode=include_mode) + stat_ = DataFrame(zip(*agg, strict=True)).T + cols = ["mean", "median", "std", "min", "max", "sum"] + if include_mode: + cols.append("mode") + stat_.columns = cols + + return stat_ From 8db53b51f34fd947b805ffc467b3ec71576dd570 Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Wed, 24 Apr 2024 09:35:59 +0200 Subject: [PATCH 07/11] move assert_result to its own file --- momepy/functional/tests/_testing.py | 11 +++++++++++ momepy/functional/tests/test_distribution.py | 2 +- momepy/functional/tests/test_shape.py | 12 +++--------- 3 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 momepy/functional/tests/_testing.py diff --git a/momepy/functional/tests/_testing.py b/momepy/functional/tests/_testing.py new file mode 100644 index 00000000..d8a7b62f --- /dev/null +++ b/momepy/functional/tests/_testing.py @@ -0,0 +1,11 @@ +import pandas as pd +import pytest +from pandas.testing import assert_index_equal + + +def assert_result(result, expected, geometry, **kwargs): + """Check the expected values and types of the result.""" + for key, value in expected.items(): + assert getattr(result, key)() == pytest.approx(value) + assert isinstance(result, pd.Series) + assert_index_equal(result.index, geometry.index, **kwargs) diff --git a/momepy/functional/tests/test_distribution.py b/momepy/functional/tests/test_distribution.py index 04ba73e2..76993ec0 100644 --- a/momepy/functional/tests/test_distribution.py +++ b/momepy/functional/tests/test_distribution.py @@ -4,7 +4,7 @@ import momepy as mm -from .test_shape import assert_result +from ._testing import assert_result class TestDistribution: diff --git a/momepy/functional/tests/test_shape.py b/momepy/functional/tests/test_shape.py index 78ca3003..b79d199a 100644 --- a/momepy/functional/tests/test_shape.py +++ b/momepy/functional/tests/test_shape.py @@ -3,19 +3,13 @@ import pandas as pd import pytest from packaging.version import Version -from pandas.testing import assert_frame_equal, assert_index_equal, assert_series_equal +from pandas.testing import assert_frame_equal, assert_series_equal import momepy as mm -GPD_013 = Version(gpd.__version__) >= Version("0.13") - +from ._testing import assert_result -def assert_result(result, expected, geometry, **kwargs): - """Check the expected values and types of the result.""" - for key, value in expected.items(): - assert getattr(result, key)() == pytest.approx(value) - assert isinstance(result, pd.Series) - assert_index_equal(result.index, geometry.index, **kwargs) +GPD_013 = Version(gpd.__version__) >= Version("0.13") class TestShape: From 0ce3c3bb6977915674ee700a57961714937c8372 Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Wed, 24 Apr 2024 09:57:17 +0200 Subject: [PATCH 08/11] add tests --- momepy/functional/tests/test_diversity.py | 111 ++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 momepy/functional/tests/test_diversity.py diff --git a/momepy/functional/tests/test_diversity.py b/momepy/functional/tests/test_diversity.py new file mode 100644 index 00000000..4bc63a5d --- /dev/null +++ b/momepy/functional/tests/test_diversity.py @@ -0,0 +1,111 @@ +import geopandas as gpd +from libpysal.graph import Graph +from pandas.testing import assert_frame_equal + +import momepy as mm + +from ._testing import assert_result + + +class TestDistribution: + def setup_method(self): + test_file_path = mm.datasets.get_path("bubenec") + self.df_buildings = gpd.read_file(test_file_path, layer="buildings") + self.graph = Graph.build_knn(self.df_buildings.centroid, k=3) + + def test_describe(self): + area = self.df_buildings.area + r = mm.describe(area, self.graph) + + expected_mean = { + "mean": 587.3761020554495, + "sum": 84582.15869598472, + "min": 50.44045729583316, + "max": 1187.2662413659234, + } + assert_result(r["mean"], expected_mean, self.df_buildings, exact=False) + + expected_median = { + "mean": 577.4640489818667, + "sum": 83154.8230533888, + "min": 50.43336175017242, + "max": 1225.8094201694726, + } + assert_result(r["median"], expected_median, self.df_buildings, exact=False) + + expected_std = { + "mean": 255.59307136480083, + "sum": 36805.40227653132, + "min": 0.05050450812944085, + "max": 1092.484902679786, + } + assert_result(r["std"], expected_std, self.df_buildings, exact=False) + + expected_min = { + "mean": 349.53354434499295, + "sum": 50332.830385678986, + "min": 50.39387578315866, + "max": 761.0313042971973, + } + assert_result(r["min"], expected_min, self.df_buildings, exact=False) + + expected_max = { + "mean": 835.1307128394886, + "sum": 120258.82264888636, + "min": 50.49413435416841, + "max": 2127.7522277389035, + } + assert_result(r["max"], expected_max, self.df_buildings, exact=False) + + expected_sum = { + "mean": 1762.128306166348, + "sum": 253746.47608795413, + "min": 151.32137188749948, + "max": 3561.79872409777, + } + assert_result(r["sum"], expected_sum, self.df_buildings, exact=False) + + def test_describe_quantile(self): + graph = Graph.build_knn(self.df_buildings.centroid, k=15) + area = self.df_buildings.area + r = mm.describe(area, graph, q=(25, 75)) + + expected_mean = { + "mean": 601.6960154385389, + "sum": 86644.2262231496, + "min": 250.25984637364323, + "max": 901.0028506943196, + } + assert_result(r["mean"], expected_mean, self.df_buildings, exact=False) + + def test_describe_mode(self): + corners = mm.corners(self.df_buildings) + r = mm.describe(corners, self.graph, include_mode=True) + + expected = { + "mean": 6.152777777777778, + "sum": 886, + "min": 4, + "max": 17, + } + assert_result(r["mode"], expected, self.df_buildings, exact=False) + + def test_describe_quantile_mode(self): + graph = Graph.build_knn(self.df_buildings.centroid, k=15) + corners = mm.corners(self.df_buildings) + r = mm.describe(corners, graph, q=(25, 75), include_mode=True) + + expected = { + "mean": 6.958333333333333, + "sum": 1002.0, + "min": 4.0, + "max": 12, + } + assert_result(r["mode"], expected, self.df_buildings, exact=False) + + def test_describe_array(self): + area = self.df_buildings.area + r = mm.describe(area, self.graph) + r2 = mm.describe(area.values, self.graph) + + assert_frame_equal(r, r2) From 7c04fa0aa00ff9369348d6000ea630591a1ce8dd Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Wed, 24 Apr 2024 10:02:17 +0200 Subject: [PATCH 09/11] skip mode tests on oldest --- momepy/functional/tests/test_diversity.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/momepy/functional/tests/test_diversity.py b/momepy/functional/tests/test_diversity.py index 4bc63a5d..4da03822 100644 --- a/momepy/functional/tests/test_diversity.py +++ b/momepy/functional/tests/test_diversity.py @@ -1,11 +1,15 @@ import geopandas as gpd +import pytest from libpysal.graph import Graph +from packaging.version import Version from pandas.testing import assert_frame_equal import momepy as mm from ._testing import assert_result +GPD_013 = Version(gpd.__version__) >= Version("0.13") + class TestDistribution: def setup_method(self): @@ -78,6 +82,7 @@ def test_describe_quantile(self): } assert_result(r["mean"], expected_mean, self.df_buildings, exact=False) + @pytest.mark.skipif(not GPD_013, reason="get_coordinates() not available") def test_describe_mode(self): corners = mm.corners(self.df_buildings) r = mm.describe(corners, self.graph, include_mode=True) @@ -90,6 +95,7 @@ def test_describe_mode(self): } assert_result(r["mode"], expected, self.df_buildings, exact=False) + @pytest.mark.skipif(not GPD_013, reason="get_coordinates() not available") def test_describe_quantile_mode(self): graph = Graph.build_knn(self.df_buildings.centroid, k=15) corners = mm.corners(self.df_buildings) From 46e6978e4e712d1730c9564cf34e1d0dc0e2ba77 Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Wed, 24 Apr 2024 19:06:55 +0200 Subject: [PATCH 10/11] use agg --- momepy/functional/_diversity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/momepy/functional/_diversity.py b/momepy/functional/_diversity.py index 93836e74..deac7176 100644 --- a/momepy/functional/_diversity.py +++ b/momepy/functional/_diversity.py @@ -75,7 +75,7 @@ def _describe(values, q, include_mode=False): if include_mode: stat_["mode"] = grouper.agg(lambda x: stats.mode(x, keepdims=False)[0]) else: - agg = grouper.apply(_describe, q=q, include_mode=include_mode) + agg = grouper.agg(_describe, q=q, include_mode=include_mode) stat_ = DataFrame(zip(*agg, strict=True)).T cols = ["mean", "median", "std", "min", "max", "sum"] if include_mode: From fdf600d27e3fa15cbc70d1d131bdc317f04b86dd Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Wed, 24 Apr 2024 19:07:04 +0200 Subject: [PATCH 11/11] conftest --- momepy/functional/tests/{_testing.py => conftest.py} | 0 momepy/functional/tests/test_distribution.py | 2 +- momepy/functional/tests/test_diversity.py | 2 +- momepy/functional/tests/test_shape.py | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename momepy/functional/tests/{_testing.py => conftest.py} (100%) diff --git a/momepy/functional/tests/_testing.py b/momepy/functional/tests/conftest.py similarity index 100% rename from momepy/functional/tests/_testing.py rename to momepy/functional/tests/conftest.py diff --git a/momepy/functional/tests/test_distribution.py b/momepy/functional/tests/test_distribution.py index 76993ec0..162c8f46 100644 --- a/momepy/functional/tests/test_distribution.py +++ b/momepy/functional/tests/test_distribution.py @@ -4,7 +4,7 @@ import momepy as mm -from ._testing import assert_result +from .conftest import assert_result class TestDistribution: diff --git a/momepy/functional/tests/test_diversity.py b/momepy/functional/tests/test_diversity.py index 4da03822..2fe06319 100644 --- a/momepy/functional/tests/test_diversity.py +++ b/momepy/functional/tests/test_diversity.py @@ -6,7 +6,7 @@ import momepy as mm -from ._testing import assert_result +from .conftest import assert_result GPD_013 = Version(gpd.__version__) >= Version("0.13") diff --git a/momepy/functional/tests/test_shape.py b/momepy/functional/tests/test_shape.py index b79d199a..85d1d97b 100644 --- a/momepy/functional/tests/test_shape.py +++ b/momepy/functional/tests/test_shape.py @@ -7,7 +7,7 @@ import momepy as mm -from ._testing import assert_result +from .conftest import assert_result GPD_013 = Version(gpd.__version__) >= Version("0.13")