BUG: pd.concat(axis=0) can override crs information (#2067)

This commit is contained in:
Giacomo Caria
2022-10-22 15:48:35 +02:00
committed by GitHub
parent be7d998001
commit 6efd9e24ff
3 changed files with 68 additions and 7 deletions
+2 -1
View File
@@ -28,7 +28,8 @@ Deprecations and compatibility notes:
Bug fixes:
- Accessing `gdf.geometry` where the active geometry column is missing, and a column named `"geometry"` is present
will now raise an `AttributeError`, rather than returning `gdf["geometry"]` (#2575)
- Combining GeoSeries/GeoDataFrames with ``pandas.concat`` will no longer silently
override CRS information if not all inputs have the same CRS (#2056).
Version 0.11.1 (July 24, 2022)
------------------------------
+24 -1
View File
@@ -1317,7 +1317,7 @@ class GeometryArray(ExtensionArray):
ExtensionArray
"""
data = np.concatenate([ga.data for ga in to_concat])
return GeometryArray(data, crs=to_concat[0].crs)
return GeometryArray(data, crs=_get_common_crs(to_concat))
def _reduce(self, name, skipna=True, **kwargs):
# including the base class version here (that raises by default)
@@ -1388,3 +1388,26 @@ class GeometryArray(ExtensionArray):
else:
return False
return (self == item).any()
def _get_common_crs(arr_seq):
crs_set = {arr.crs for arr in arr_seq}
crs_not_none = [crs for crs in crs_set if crs is not None]
names = [crs.name for crs in crs_not_none]
if len(crs_not_none) == 0:
return None
if len(crs_not_none) == 1:
if len(crs_set) != 1:
warnings.warn(
"CRS not set for some of the concatenation inputs. "
f"Setting output's CRS as {names[0]} "
"(the single non-null crs provided)."
)
return crs_not_none[0]
raise ValueError(
f"Cannot determine common CRS for concatenation inputs, got {names}. "
"Use `to_crs()` to transform geometries to the same CRS before merging."
)
+42 -5
View File
@@ -54,11 +54,6 @@ class TestMerging:
self._check_metadata(res)
exp = GeoDataFrame(pd.concat([pd.DataFrame(self.gdf), pd.DataFrame(self.gdf)]))
assert_geodataframe_equal(exp, res)
# check metadata comes from first gdf
res4 = pd.concat([self.gdf.set_crs("epsg:4326"), self.gdf], axis=0)
# Note: this behaviour potentially does not make sense. If geom cols are
# concatenated but have different CRS, then the CRS will be overridden.
self._check_metadata(res4, crs="epsg:4326")
# series
res = pd.concat([self.gdf.geometry, self.gdf.geometry])
@@ -66,6 +61,48 @@ class TestMerging:
assert isinstance(res, GeoSeries)
assert isinstance(res.geometry, GeoSeries)
def test_concat_axis0_crs(self):
# CRS not set for both GeoDataFrame
res = pd.concat([self.gdf, self.gdf])
self._check_metadata(res)
# CRS set for both GeoDataFrame, same CRS
res1 = pd.concat([self.gdf.set_crs("epsg:4326"), self.gdf.set_crs("epsg:4326")])
self._check_metadata(res1, crs="epsg:4326")
# CRS not set for one GeoDataFrame, but set for the other GeoDataFrame
with pytest.warns(
UserWarning, match=r"CRS not set for some of the concatenation inputs.*"
):
res2 = pd.concat([self.gdf, self.gdf.set_crs("epsg:4326")])
self._check_metadata(res2, crs="epsg:4326")
# CRS set for both GeoDataFrame, different CRS
with pytest.raises(
ValueError, match=r"Cannot determine common CRS for concatenation inputs.*"
):
pd.concat([self.gdf.set_crs("epsg:4326"), self.gdf.set_crs("epsg:4327")])
# CRS not set for one GeoDataFrame, but set for the other GeoDataFrames,
# same CRS
with pytest.warns(
UserWarning, match=r"CRS not set for some of the concatenation inputs.*"
):
res3 = pd.concat(
[self.gdf, self.gdf.set_crs("epsg:4326"), self.gdf.set_crs("epsg:4326")]
)
self._check_metadata(res3, crs="epsg:4326")
# CRS not set for one GeoDataFrame, but set for the other GeoDataFrames,
# different CRS
with pytest.raises(
ValueError, match=r"Cannot determine common CRS for concatenation inputs.*"
):
pd.concat(
[self.gdf, self.gdf.set_crs("epsg:4326"), self.gdf.set_crs("epsg:4327")]
)
def test_concat_axis1(self):
res = pd.concat([self.gdf, self.df], axis=1)