diff --git a/geopandas/geodataframe.py b/geopandas/geodataframe.py index 1bcc6ce..fd110f6 100644 --- a/geopandas/geodataframe.py +++ b/geopandas/geodataframe.py @@ -1652,6 +1652,34 @@ box': (2.0, 1.0, 2.0, 1.0)}], 'bbox': (1.0, 1.0, 2.0, 2.0)} # do not return a GeoDataFrame return pd.DataFrame(df) + def convert_dtypes(self, *args, **kwargs): + """ + Convert columns to best possible dtypes using dtypes supporting ``pd.NA``. + + Always returns a GeoDataFrame as no conversions are applied to the + geometry column. + + See the pandas.DataFrame.convert_dtypes docstring for more details. + + Returns + ------- + GeoDataFrame + + """ + # Overridden to fix GH1870, that return type is not preserved always + # (and where it was, geometry col was not) + + if not compat.PANDAS_GE_10: + raise NotImplementedError( + "GeoDataFrame.convert_dtypes requires pandas >= 1.0" + ) + + return GeoDataFrame( + super().convert_dtypes(*args, **kwargs), + geometry=self.geometry.name, + crs=self.crs, + ) + def to_postgis( self, name, diff --git a/geopandas/tests/test_pandas_methods.py b/geopandas/tests/test_pandas_methods.py index 3267313..2db561d 100644 --- a/geopandas/tests/test_pandas_methods.py +++ b/geopandas/tests/test_pandas_methods.py @@ -262,6 +262,42 @@ def test_astype_invalid_geodataframe(): assert res["a"].dtype == object +@pytest.mark.xfail( + not compat.PANDAS_GE_10, + reason="Convert dtypes new in pandas 1.0", + raises=NotImplementedError, +) +def test_convert_dtypes(df): + # https://github.com/geopandas/geopandas/issues/1870 + + # Test geometry col is first col, first, geom_col_name=geometry + # (order is important in concat, used internally) + res1 = df.convert_dtypes() # note res1 done first for pandas < 1 xfail check + + expected1 = GeoDataFrame( + pd.DataFrame(df).convert_dtypes(), crs=df.crs, geometry=df.geometry.name + ) + + # Checking type and metadata are right + assert_geodataframe_equal(expected1, res1) + + # Test geom last, geom_col_name=geometry + res2 = df[["value1", "value2", "geometry"]].convert_dtypes() + assert_geodataframe_equal(expected1[["value1", "value2", "geometry"]], res2) + + # Test again with crs set and custom geom col name + df2 = df.set_crs(epsg=4326).rename_geometry("points") + expected2 = GeoDataFrame( + pd.DataFrame(df2).convert_dtypes(), crs=df2.crs, geometry=df2.geometry.name + ) + res3 = df2.convert_dtypes() + assert_geodataframe_equal(expected2, res3) + + # Test geom last, geom_col=geometry + res4 = df2[["value1", "value2", "points"]].convert_dtypes() + assert_geodataframe_equal(expected2[["value1", "value2", "points"]], res4) + + def test_to_csv(df): exp = (