From c49ffbe08cf7cbe77812638bfe56e21bfa905427 Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Mon, 8 Dec 2025 09:50:36 +0100 Subject: [PATCH 1/4] COMPAT: pandas 3.0 compatibillity in Graph --- libpysal/graph/_utils.py | 2 +- libpysal/graph/base.py | 9 +++++++-- libpysal/graph/tests/test_base.py | 7 ++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/libpysal/graph/_utils.py b/libpysal/graph/_utils.py index 411779479..bed5ed368 100644 --- a/libpysal/graph/_utils.py +++ b/libpysal/graph/_utils.py @@ -143,7 +143,7 @@ def _neighbor_dict_to_edges(neighbors, weights=None): FutureWarning, ) idxs = idxs.fillna(pd.Series(idxs.index, index=idxs.index)) # self-loops - heads, tails = idxs.index.values, idxs.values + heads, tails = idxs.index.to_numpy(), idxs.to_numpy() tails = tails.astype(heads.dtype) if weights is not None: with warnings.catch_warnings(): diff --git a/libpysal/graph/base.py b/libpysal/graph/base.py index dce9ef605..d9e7bb588 100644 --- a/libpysal/graph/base.py +++ b/libpysal/graph/base.py @@ -1853,8 +1853,13 @@ def asymmetry(self, intrinsic=True): zip(np.arange(self.unique_ids.shape[0]), self.unique_ids, strict=True) ) focal, neighbor = np.nonzero(wd) - focal = focal.astype(self._adjacency.index.dtypes["focal"]) - neighbor = neighbor.astype(self._adjacency.index.dtypes["focal"]) + dtype = ( + self._adjacency.index.dtypes["focal"] + if self._adjacency.index.dtypes["focal"] != "str" + else "object" + ) + focal = focal.astype(dtype) + neighbor = neighbor.astype(dtype) for i in i2id: focal[focal == i] = i2id[i] neighbor[neighbor == i] = i2id[i] diff --git a/libpysal/graph/tests/test_base.py b/libpysal/graph/tests/test_base.py index 3b3ff3392..92cd36026 100644 --- a/libpysal/graph/tests/test_base.py +++ b/libpysal/graph/tests/test_base.py @@ -56,7 +56,8 @@ def setup_method(self): self.weight_dict_str_binary.values(), name="weight", index=pd.MultiIndex.from_arrays( - [self.index_str, self.neighbor_dict_str.values()], + # list() to allow pandas to coerce the data to its StringDtype + [list(self.index_str), self.neighbor_dict_str.values()], names=["focal", "neighbor"], ), ) @@ -457,7 +458,8 @@ def test_from_dicts(self): pd.testing.assert_series_equal( g._adjacency, self.adjacency_str_binary, - check_dtype=False, + check_dtype=True, + check_index_type=False, ) @pytest.mark.parametrize("y", [3, 5]) @@ -617,7 +619,6 @@ def test_cardinalities(self): [3, 3, 2, 3, 4, 3, 2, 3, 2, 0], index=pd.Index( ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"], - dtype="object", name="focal", ), name="cardinalities", From 6ca90514ab80854682e1879c70402e81300448b8 Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Mon, 8 Dec 2025 09:54:33 +0100 Subject: [PATCH 2/4] skip tests depending on EEA large rivers --- .github/workflows/unittests.yml | 1 - libpysal/graph/tests/test_contiguity.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml index 5ac24eb20..c56bb5055 100644 --- a/.github/workflows/unittests.yml +++ b/.github/workflows/unittests.yml @@ -69,7 +69,6 @@ jobs: geodatasets.fetch("nybb") geodatasets.fetch("geoda liquor_stores") - geodatasets.fetch("eea large_rivers") geodatasets.fetch("geoda groceries") geodatasets.fetch("geoda guerry") libpysal.examples.fetch_all() diff --git a/libpysal/graph/tests/test_contiguity.py b/libpysal/graph/tests/test_contiguity.py index 997a97f10..1c907e291 100644 --- a/libpysal/graph/tests/test_contiguity.py +++ b/libpysal/graph/tests/test_contiguity.py @@ -27,6 +27,7 @@ @pytest.fixture(scope="session") def rivers(): + pytest.skip("eea large rivers not accessible") numpy.random.seed(111211) rivers = geopandas.read_file(geodatasets.get_path("eea large_rivers")).sample( frac=1, replace=False From 33d1588688cfa00c0eee0a768e03641aebb29b92 Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Mon, 8 Dec 2025 09:59:39 +0100 Subject: [PATCH 3/4] properly skip --- libpysal/graph/tests/test_contiguity.py | 14 +++++++------- libpysal/graph/tests/test_utils.py | 9 +++++---- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/libpysal/graph/tests/test_contiguity.py b/libpysal/graph/tests/test_contiguity.py index 1c907e291..6d263e8a2 100644 --- a/libpysal/graph/tests/test_contiguity.py +++ b/libpysal/graph/tests/test_contiguity.py @@ -28,13 +28,13 @@ @pytest.fixture(scope="session") def rivers(): pytest.skip("eea large rivers not accessible") - numpy.random.seed(111211) - rivers = geopandas.read_file(geodatasets.get_path("eea large_rivers")).sample( - frac=1, replace=False - ) - rivers["strID"] = rivers.NAME - rivers["intID"] = rivers.index.values + 2 - return rivers + # numpy.random.seed(111211) + # rivers = geopandas.read_file(geodatasets.get_path("eea large_rivers")).sample( + # frac=1, replace=False + # ) + # rivers["strID"] = rivers.NAME + # rivers["intID"] = rivers.index.values + 2 + # return rivers @pytest.fixture(scope="session") diff --git a/libpysal/graph/tests/test_utils.py b/libpysal/graph/tests/test_utils.py index a873b3896..94abe9e63 100644 --- a/libpysal/graph/tests/test_utils.py +++ b/libpysal/graph/tests/test_utils.py @@ -22,10 +22,11 @@ def guerry(): @pytest.fixture(scope="session") def rivers(): - rivers = geopandas.read_file(geodatasets.get_path("eea large_rivers")) - rivers["strID"] = rivers.NAME - rivers["intID"] = rivers.index.values + 1 - return rivers + pytest.skip("eea large rivers not accessible") + # rivers = geopandas.read_file(geodatasets.get_path("eea large_rivers")) + # rivers["strID"] = rivers.NAME + # rivers["intID"] = rivers.index.values + 1 + # return rivers @pytest.fixture(params=["guerry", "guerry centroids", "rivers"]) From f12f3bbf974cd04505436c5980384f136c4574f3 Mon Sep 17 00:00:00 2001 From: Martin Fleischmann Date: Mon, 8 Dec 2025 16:10:44 +0100 Subject: [PATCH 4/4] enable eea tests again --- libpysal/graph/tests/test_contiguity.py | 15 +++++++-------- libpysal/graph/tests/test_utils.py | 9 ++++----- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/libpysal/graph/tests/test_contiguity.py b/libpysal/graph/tests/test_contiguity.py index 6d263e8a2..997a97f10 100644 --- a/libpysal/graph/tests/test_contiguity.py +++ b/libpysal/graph/tests/test_contiguity.py @@ -27,14 +27,13 @@ @pytest.fixture(scope="session") def rivers(): - pytest.skip("eea large rivers not accessible") - # numpy.random.seed(111211) - # rivers = geopandas.read_file(geodatasets.get_path("eea large_rivers")).sample( - # frac=1, replace=False - # ) - # rivers["strID"] = rivers.NAME - # rivers["intID"] = rivers.index.values + 2 - # return rivers + numpy.random.seed(111211) + rivers = geopandas.read_file(geodatasets.get_path("eea large_rivers")).sample( + frac=1, replace=False + ) + rivers["strID"] = rivers.NAME + rivers["intID"] = rivers.index.values + 2 + return rivers @pytest.fixture(scope="session") diff --git a/libpysal/graph/tests/test_utils.py b/libpysal/graph/tests/test_utils.py index 94abe9e63..a873b3896 100644 --- a/libpysal/graph/tests/test_utils.py +++ b/libpysal/graph/tests/test_utils.py @@ -22,11 +22,10 @@ def guerry(): @pytest.fixture(scope="session") def rivers(): - pytest.skip("eea large rivers not accessible") - # rivers = geopandas.read_file(geodatasets.get_path("eea large_rivers")) - # rivers["strID"] = rivers.NAME - # rivers["intID"] = rivers.index.values + 1 - # return rivers + rivers = geopandas.read_file(geodatasets.get_path("eea large_rivers")) + rivers["strID"] = rivers.NAME + rivers["intID"] = rivers.index.values + 1 + return rivers @pytest.fixture(params=["guerry", "guerry centroids", "rivers"])