From 0f82cedf5540373ca39a87f436d47c846ab0bcb5 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Thu, 26 May 2016 23:28:07 +0200 Subject: [PATCH] BUG: preserve metadata on merge/concat (#247, #320) (#322) --- geopandas/geodataframe.py | 22 +++++++++---- geopandas/tests/test_merge.py | 62 +++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 geopandas/tests/test_merge.py diff --git a/geopandas/geodataframe.py b/geopandas/geodataframe.py index ef6e071..12e24b9 100644 --- a/geopandas/geodataframe.py +++ b/geopandas/geodataframe.py @@ -409,10 +409,18 @@ class GeoDataFrame(GeoPandasBase, DataFrame): return GeoDataFrame def __finalize__(self, other, method=None, **kwargs): - """ propagate metadata from other to self """ - # NOTE: backported from pandas master (upcoming v0.13) - for name in self._metadata: - object.__setattr__(self, name, getattr(other, name, None)) + """propagate metadata from other to self """ + # merge operation: using metadata of the left object + if method == 'merge': + for name in self._metadata: + object.__setattr__(self, name, getattr(other.left, name, None)) + # concat operation: using metadata of the first object + elif method == 'concat': + for name in self._metadata: + object.__setattr__(self, name, getattr(other.objs[0], name, None)) + else: + for name in self._metadata: + object.__setattr__(self, name, getattr(other, name, None)) return self def copy(self, deep=True): @@ -443,7 +451,7 @@ class GeoDataFrame(GeoPandasBase, DataFrame): def dissolve(self, by=None, aggfunc='first'): """ - Dissolve geometries within `groupby` into single observation. + Dissolve geometries within `groupby` into single observation. Parameters ---------- @@ -473,14 +481,14 @@ class GeoDataFrame(GeoPandasBase, DataFrame): merged_geom = block.unary_union new_index = block.drop(self.geometry.name, axis=1).iloc[0][by] - merged_w_index = GeoSeries(merged_geom, index=Index(Series(new_index),name=by), + merged_w_index = GeoSeries(merged_geom, index=Index(Series(new_index),name=by), name=self.geometry.name) return merged_w_index g = geometry.groupby(by=by, group_keys=False).apply(merge_geometries) - aggregated_geometry = GeoDataFrame(g, + aggregated_geometry = GeoDataFrame(g, index=g.index, geometry=self.geometry.name) # Recombine diff --git a/geopandas/tests/test_merge.py b/geopandas/tests/test_merge.py new file mode 100644 index 0000000..17cd238 --- /dev/null +++ b/geopandas/tests/test_merge.py @@ -0,0 +1,62 @@ +from __future__ import absolute_import + +import pandas as pd +from shapely.geometry import Point + +from geopandas import GeoDataFrame, GeoSeries +from geopandas.tests.util import unittest + + +class TestMerging(unittest.TestCase): + + def setUp(self): + + self.gseries = GeoSeries([Point(i, i) for i in range(3)]) + self.series = pd.Series([1, 2, 3]) + self.gdf = GeoDataFrame({'geometry': self.gseries, 'values': range(3)}) + self.df = pd.DataFrame({'col1': [1, 2, 3], 'col2': [0.1, 0.2, 0.3]}) + + def _check_metadata(self, gdf, geometry_column_name='geometry', crs=None): + + self.assertEqual(gdf._geometry_column_name, geometry_column_name) + self.assertEqual(gdf.crs, crs) + + def test_merge(self): + + res = self.gdf.merge(self.df, left_on='values', right_on='col1') + + # check result is a GeoDataFrame + self.assert_(isinstance(res, GeoDataFrame)) + + # check geometry property gives GeoSeries + self.assert_(isinstance(res.geometry, GeoSeries)) + + # check metadata + self._check_metadata(res) + + ## test that crs and other geometry name are preserved + self.gdf.crs = {'init' :'epsg:4326'} + self.gdf = (self.gdf.rename(columns={'geometry': 'points'}) + .set_geometry('points')) + res = self.gdf.merge(self.df, left_on='values', right_on='col1') + self.assert_(isinstance(res, GeoDataFrame)) + self.assert_(isinstance(res.geometry, GeoSeries)) + self._check_metadata(res, 'points', self.gdf.crs) + + def test_concat_axis0(self): + + res = pd.concat([self.gdf, self.gdf]) + + self.assertEqual(res.shape, (6, 2)) + self.assert_(isinstance(res, GeoDataFrame)) + self.assert_(isinstance(res.geometry, GeoSeries)) + self._check_metadata(res) + + def test_concat_axis1(self): + + res = pd.concat([self.gdf, self.df], axis=1) + + self.assertEqual(res.shape, (3, 4)) + self.assert_(isinstance(res, GeoDataFrame)) + self.assert_(isinstance(res.geometry, GeoSeries)) + self._check_metadata(res)