mirror of
https://github.com/wassname/geopandas.git
synced 2026-09-21 13:00:14 +08:00
PERF: Improve iterfeatures performance (#864)
Co-authored-by: Joris Van den Bossche <jorisvandenbossche@gmail.com>
This commit is contained in:
committed by
Joris Van den Bossche
co-authored by
Joris Van den Bossche
parent
6e9b82b855
commit
4f933d38f1
+38
-33
@@ -327,45 +327,50 @@ class GeoDataFrame(GeoPandasBase, DataFrame):
|
||||
* keep: output the missing entries as NaN
|
||||
|
||||
show_bbox : include bbox (bounds) in the geojson. default False
|
||||
|
||||
"""
|
||||
def fill_none(row):
|
||||
"""
|
||||
Takes in a Series, converts to a dictionary with null values
|
||||
set to None
|
||||
|
||||
"""
|
||||
na_keys = row.index[row.isnull()]
|
||||
d = row.to_dict()
|
||||
for k in na_keys:
|
||||
d[k] = None
|
||||
return d
|
||||
|
||||
# na_methods must take in a Series and return dict
|
||||
na_methods = {'null': fill_none,
|
||||
'drop': lambda row: row.dropna().to_dict(),
|
||||
'keep': lambda row: row.to_dict()}
|
||||
|
||||
if na not in na_methods:
|
||||
if na not in ['null', 'drop', 'keep']:
|
||||
raise ValueError('Unknown na method {0}'.format(na))
|
||||
f = na_methods[na]
|
||||
|
||||
for name, row in self.iterrows():
|
||||
properties = f(row)
|
||||
del properties[self._geometry_column_name]
|
||||
ids = np.array(self.index, copy=False)
|
||||
geometries = np.array(self[self._geometry_column_name], copy=False)
|
||||
|
||||
feature = {
|
||||
'id': str(name),
|
||||
'type': 'Feature',
|
||||
'properties': properties,
|
||||
'geometry': mapping(row[self._geometry_column_name])
|
||||
if row[self._geometry_column_name] else None
|
||||
}
|
||||
properties_cols = self.columns.difference([self._geometry_column_name])
|
||||
|
||||
if show_bbox:
|
||||
feature['bbox'] = row.geometry.bounds
|
||||
if len(properties_cols) > 0:
|
||||
# convert to object to get python scalars.
|
||||
properties = self[properties_cols].astype(object).values
|
||||
if na == 'null':
|
||||
properties[pd.isnull(self[properties_cols]).values] = None
|
||||
|
||||
yield feature
|
||||
for i, row in enumerate(properties):
|
||||
geom = geometries[i]
|
||||
|
||||
if na == 'drop':
|
||||
properties_items = dict((k, v) for k, v
|
||||
in zip(properties_cols, row)
|
||||
if not pd.isnull(v))
|
||||
else:
|
||||
properties_items = dict((k, v) for k, v
|
||||
in zip(properties_cols, row))
|
||||
|
||||
feature = {'id': str(ids[i]),
|
||||
'type': 'Feature',
|
||||
'properties': properties_items,
|
||||
'geometry': mapping(geom) if geom else None}
|
||||
|
||||
if show_bbox:
|
||||
feature['bbox'] = geom.bounds if geom else None
|
||||
yield feature
|
||||
|
||||
else:
|
||||
for fid, geom in zip(ids, geometries):
|
||||
feature = {'id': str(fid),
|
||||
'type': 'Feature',
|
||||
'properties': {},
|
||||
'geometry': mapping(geom) if geom else None}
|
||||
if show_bbox:
|
||||
feature['bbox'] = geom.bounds if geom else None
|
||||
yield feature
|
||||
|
||||
def _to_geo(self, **kwargs):
|
||||
"""
|
||||
|
||||
@@ -619,6 +619,46 @@ class TestDataFrame:
|
||||
assert self.df.__geo_interface__['type'] == 'FeatureCollection'
|
||||
assert len(self.df.__geo_interface__['features']) == self.df.shape[0]
|
||||
|
||||
def test_geodataframe_iterfeatures(self):
|
||||
df = self.df.iloc[:1].copy()
|
||||
df.loc[0, 'BoroName'] = np.nan
|
||||
# when containing missing values
|
||||
# null: ouput the missing entries as JSON null
|
||||
result = list(df.iterfeatures(na='null'))[0]['properties']
|
||||
assert result['BoroName'] is None
|
||||
# drop: remove the property from the feature.
|
||||
result = list(df.iterfeatures(na='drop'))[0]['properties']
|
||||
assert 'BoroName' not in result.keys()
|
||||
# keep: output the missing entries as NaN
|
||||
result = list(df.iterfeatures(na='keep'))[0]['properties']
|
||||
assert np.isnan(result['BoroName'])
|
||||
|
||||
# test for checking that the (non-null) features are python scalars and
|
||||
# not numpy scalars
|
||||
assert type(df.loc[0, 'Shape_Leng']) is np.float64
|
||||
# null
|
||||
result = list(df.iterfeatures(na='null'))[0]
|
||||
assert type(result['properties']['Shape_Leng']) is float
|
||||
# drop
|
||||
result = list(df.iterfeatures(na='drop'))[0]
|
||||
assert type(result['properties']['Shape_Leng']) is float
|
||||
# keep
|
||||
result = list(df.iterfeatures(na='keep'))[0]
|
||||
assert type(result['properties']['Shape_Leng']) is float
|
||||
|
||||
# when only having numerical columns
|
||||
df_only_numerical_cols = df[['Shape_Leng', 'Shape_Area', 'geometry']]
|
||||
assert type(df_only_numerical_cols.loc[0, 'Shape_Leng']) is np.float64
|
||||
# null
|
||||
result = list(df_only_numerical_cols.iterfeatures(na='null'))[0]
|
||||
assert type(result['properties']['Shape_Leng']) is float
|
||||
# drop
|
||||
result = list(df_only_numerical_cols.iterfeatures(na='drop'))[0]
|
||||
assert type(result['properties']['Shape_Leng']) is float
|
||||
# keep
|
||||
result = list(df_only_numerical_cols.iterfeatures(na='keep'))[0]
|
||||
assert type(result['properties']['Shape_Leng']) is float
|
||||
|
||||
def test_geodataframe_geojson_no_bbox(self):
|
||||
geo = self.df._to_geo(na="null", show_bbox=False)
|
||||
assert 'bbox' not in geo.keys()
|
||||
|
||||
Reference in New Issue
Block a user