ENH: integrate pygeos.get_parts for faster explode() (#1693)

This commit is contained in:
Brendan Ward authored and GitHub committed 2021-01-28 09:44:56 +01:00
1 parent 6bdb001ed6
commit 5759f0c4ec
3 files changed
+50 -4

No files matched your search

+10 -2
View File
@@ -2,7 +2,7 @@ import random
import numpy as np
from geopandas import GeoSeries
from shapely.geometry import Point, LineString, Polygon
from shapely.geometry import Point, Polygon, MultiPolygon
def with_attributes(**attrs):
@@ -25,10 +25,15 @@ class Bench:
triangles3 = GeoSeries([Polygon([(random.random(), random.random())
for _ in range(3)])
for _ in range(10000)])
triangles4 = GeoSeries([
MultiPolygon([
Polygon([(random.random(), random.random()) for _ in range(3)])
]) for _ in range(10000)])
triangle = Polygon([(random.random(), random.random())
for _ in range(3)])
self.triangles, self.triangles2 = triangles, triangles2
self.triangles_big = triangles3
self.multi_triangles = triangles4
self.triangle = triangle
@with_attributes(param_names=['op'],
@@ -98,7 +103,10 @@ class Bench:
def time_buffer(self, *args):
self.points.buffer(2)
def time_explode(self, *args):
self.multi_triangles.explode()
# TODO
# project, interpolate, affine_transform, translate, rotate, scale, skew, explode
# project, interpolate, affine_transform, translate, rotate, scale, skew
# cx indexer
+5
View File
@@ -26,14 +26,19 @@ SHAPELY_GE_17 = str(shapely.__version__) >= LooseVersion("1.7.0")
SHAPELY_GE_18 = str(shapely.__version__) >= LooseVersion("1.8")
SHAPELY_GE_20 = str(shapely.__version__) >= LooseVersion("2.0")
HAS_PYGEOS = None
USE_PYGEOS = None
PYGEOS_SHAPELY_COMPAT = None
PYGEOS_GE_09 = None
try:
import pygeos # noqa
HAS_PYGEOS = True
PYGEOS_GE_09 = str(pygeos.__version__) >= LooseVersion("0.9")
except ImportError:
HAS_PYGEOS = False
+35 -2
View File
@@ -15,7 +15,7 @@ from geopandas.plotting import plot_series
from .array import GeometryArray, GeometryDtype, from_shapely
from .base import is_geometry_type
from . import _vectorized as vectorized
from ._compat import ignore_shapely2_warnings
from . import _compat as compat
_SERIES_WARNING_MSG = """\
@@ -197,7 +197,7 @@ class GeoSeries(GeoPandasBase, Series):
# https://github.com/pandas-dev/pandas/issues/26469
kwargs.pop("dtype", None)
# Use Series constructor to handle input data
with ignore_shapely2_warnings():
with compat.ignore_shapely2_warnings():
s = pd.Series(data, index=index, name=name, **kwargs)
# prevent trying to convert non-geometry objects
if s.dtype != object:
@@ -669,6 +669,39 @@ class GeoSeries(GeoPandasBase, Series):
GeoDataFrame.explode
"""
if compat.USE_PYGEOS and compat.PYGEOS_GE_09:
import pygeos # noqa
geometries, outer_idx = pygeos.get_parts(
self.values.data, return_index=True
)
if len(outer_idx):
# Generate inner index as a range per value of outer_idx
# 1. identify the start of each run of values in outer_idx
# 2. count number of values per run
# 3. use cumulative sums to create an incremental range
# starting at 0 in each run
run_start = np.r_[True, outer_idx[:-1] != outer_idx[1:]]
counts = np.diff(np.r_[np.nonzero(run_start)[0], len(outer_idx)])
inner_index = (~run_start).cumsum()
inner_index -= np.repeat(inner_index[run_start], counts)
else:
inner_index = []
# extract original index values based on integer index
outer_index = self.index.take(outer_idx)
index = MultiIndex.from_arrays(
[outer_index, inner_index], names=self.index.names + [None]
)
return GeoSeries(geometries, index=index, crs=self.crs).__finalize__(self)
# else PyGEOS is not available or version <= 0.8
index = []
geometries = []
for idx, s in self.geometry.iteritems():