diff --git a/geopandas/geodataframe.py b/geopandas/geodataframe.py index 78ec62b..ef6e071 100644 --- a/geopandas/geodataframe.py +++ b/geopandas/geodataframe.py @@ -8,7 +8,7 @@ import os import sys import numpy as np -from pandas import DataFrame, Series +from pandas import DataFrame, Series, Index from shapely.geometry import mapping, shape from shapely.geometry.base import BaseGeometry from six import string_types @@ -441,6 +441,53 @@ class GeoDataFrame(GeoPandasBase, DataFrame): plot.__doc__ = plot_dataframe.__doc__ + def dissolve(self, by=None, aggfunc='first'): + """ + Dissolve geometries within `groupby` into single observation. + + Parameters + ---------- + by : string, default None + Column whose values define groups to be dissolved + aggfunc : function or string, default "first" + Aggregation function for manipulation of data associated + with each group. Passed to pandas `groupby.agg` method. + + Returns + ------- + GeoDataFrame + """ + + # Process non-spatial component + data = self.drop(labels=self.geometry.name, axis=1).copy() + aggregated_data = data.groupby(by=by).agg(aggfunc) + + + # Process spatial component + groupby_plus_geometry_cols = [self.geometry.name] + groupby_plus_geometry_cols.append(by) + geometry = self[groupby_plus_geometry_cols].copy() + + def merge_geometries(block): + + 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), + name=self.geometry.name) + return merged_w_index + + + g = geometry.groupby(by=by, group_keys=False).apply(merge_geometries) + + aggregated_geometry = GeoDataFrame(g, + index=g.index, + geometry=self.geometry.name) + # Recombine + aggregated = aggregated_geometry.join(aggregated_data) + aggregated = aggregated.set_geometry(self.geometry.name) + return aggregated + def _dataframe_set_geometry(self, col, drop=False, inplace=False, crs=None): if inplace: raise ValueError("Can't do inplace setting when converting from" diff --git a/tests/test_dissolve.py b/tests/test_dissolve.py new file mode 100644 index 0000000..58c91ac --- /dev/null +++ b/tests/test_dissolve.py @@ -0,0 +1,64 @@ +from __future__ import absolute_import +import tempfile +import shutil +import numpy as np +from shapely.geometry import Point +from geopandas import GeoDataFrame, read_file +from geopandas.tools import overlay +from .util import unittest, download_nybb +from pandas.util.testing import assert_frame_equal +from pandas import Index + +class TestDataFrame(unittest.TestCase): + + def setUp(self): + + nybb_filename, nybb_zip_path = download_nybb() + self.polydf = read_file(nybb_zip_path, vfs='zip://' + nybb_filename) + self.polydf = self.polydf[['geometry', 'BoroName', 'BoroCode']] + + self.polydf = self.polydf.rename(columns={'geometry':'myshapes'}) + self.polydf = self.polydf.set_geometry('myshapes') + + self.polydf['manhattan_bronx'] = 5 + self.polydf.loc[3:4,'manhattan_bronx']=6 + + # Merged geometry + manhattan_bronx = self.polydf.loc[3:4,] + others = self.polydf.loc[0:2,] + + collapsed = [others.geometry.unary_union, manhattan_bronx.geometry.unary_union] + merged_shapes = GeoDataFrame({'myshapes': collapsed}, geometry='myshapes', + index=Index([5,6], name='manhattan_bronx')) + + # Different expected results + self.first = merged_shapes.copy() + self.first['BoroName'] = ['Staten Island', 'Manhattan'] + self.first['BoroCode'] = [5, 1] + + self.mean = merged_shapes.copy() + self.mean['BoroCode'] = [4,1.5] + + + def test_geom_dissolve(self): + test = self.polydf.dissolve('manhattan_bronx') + self.assertTrue(test.geometry.name == 'myshapes') + self.assertTrue(test.geom_almost_equals(self.first).all()) + + def test_first_dissolve(self): + test = self.polydf.dissolve('manhattan_bronx') + test = test.drop('myshapes', axis=1) + first = self.first.drop('myshapes', axis=1) + assert_frame_equal(first, test) + + def test_mean_dissolve(self): + test = self.polydf.dissolve('manhattan_bronx', aggfunc='mean') + test = test.drop('myshapes', axis=1) + mean = self.mean.drop('myshapes', axis=1) + assert_frame_equal(mean, test) + + test = self.polydf.dissolve('manhattan_bronx', aggfunc=np.mean) + test = test.drop('myshapes', axis=1) + assert_frame_equal(mean, test) + +