mirror of
https://github.com/wassname/geopandas.git
synced 2026-09-12 12:20:25 +08:00
ENH: Introduce method to handle NaNs in to_json()
This adds an 'na' option specifying how NaN values should be handled in GeoDataFrame's to_json(). The default is 'null' which outputs JSON null. The other options are 'drop' which removes any missing property, and 'keep' which will output them as NaN.
This commit is contained in:
@@ -107,22 +107,44 @@ class GeoDataFrame(DataFrame):
|
||||
coerce_float, params)
|
||||
|
||||
|
||||
def to_json(self, omitna=False, **kwargs):
|
||||
def to_json(self, na='null', **kwargs):
|
||||
"""Returns a GeoJSON representation of the GeoDataFrame.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
omitna : boolean, default False
|
||||
Indicates whether null properties should be included in the
|
||||
output. This applies to each feature individually so that
|
||||
some features may have different numbers of features if some
|
||||
have null elements
|
||||
na : {'null', 'drop', 'keep'}, default 'null'
|
||||
Indicates how to output missing (NaN) values in the GeoDataFrame
|
||||
* null: ouput the missing entries as JSON null
|
||||
* drop: remove the property from the feature. This applies to
|
||||
each feature individually so that features may have
|
||||
different properties
|
||||
* keep: output the missing entries as NaN
|
||||
|
||||
The *kwargs* are passed to json.dumps().
|
||||
The remaining *kwargs* are passed to json.dumps().
|
||||
"""
|
||||
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-like
|
||||
na_methods = {'null': fill_none,
|
||||
'drop': lambda row: row.dropna(),
|
||||
'keep': lambda row: row}
|
||||
|
||||
if na not in na_methods:
|
||||
raise ValueError('Unknown na method {}'.format(na))
|
||||
f = na_methods[na]
|
||||
|
||||
def feature(i, row):
|
||||
if omitna:
|
||||
row = row.dropna()
|
||||
row = f(row)
|
||||
return {
|
||||
'id': str(i),
|
||||
'type': 'Feature',
|
||||
|
||||
@@ -89,13 +89,13 @@ class TestDataFrame(unittest.TestCase):
|
||||
props = f['properties']
|
||||
self.assertEqual(len(props), 4)
|
||||
if props['BoroName'] == 'Queens':
|
||||
self.assertTrue(np.isnan(props['Shape_Area']))
|
||||
self.assertTrue(props['Shape_Area'] is None)
|
||||
|
||||
def test_to_json_omitna(self):
|
||||
def test_to_json_dropna(self):
|
||||
self.df['Shape_Area'][self.df['BoroName']=='Queens'] = np.nan
|
||||
self.df['Shape_Leng'][self.df['BoroName']=='Bronx'] = np.nan
|
||||
|
||||
text = self.df.to_json(omitna=True)
|
||||
text = self.df.to_json(na='drop')
|
||||
data = json.loads(text)
|
||||
self.assertEqual(len(data['features']), 5)
|
||||
for f in data['features']:
|
||||
@@ -113,6 +113,25 @@ class TestDataFrame(unittest.TestCase):
|
||||
else:
|
||||
self.assertEqual(len(props), 4)
|
||||
|
||||
def test_to_json_keepna(self):
|
||||
self.df['Shape_Area'][self.df['BoroName']=='Queens'] = np.nan
|
||||
self.df['Shape_Leng'][self.df['BoroName']=='Bronx'] = np.nan
|
||||
|
||||
text = self.df.to_json(na='keep')
|
||||
data = json.loads(text)
|
||||
self.assertEqual(len(data['features']), 5)
|
||||
for f in data['features']:
|
||||
props = f['properties']
|
||||
self.assertEqual(len(props), 4)
|
||||
if props['BoroName'] == 'Queens':
|
||||
self.assertTrue(np.isnan(props['Shape_Area']))
|
||||
# Just make sure setting it to nan in a different row
|
||||
# doesn't affect this one
|
||||
self.assertTrue('Shape_Leng' in props)
|
||||
elif props['BoroName'] == 'Bronx':
|
||||
self.assertTrue(np.isnan(props['Shape_Leng']))
|
||||
self.assertTrue('Shape_Area' in props)
|
||||
|
||||
def test_copy(self):
|
||||
df2 = self.df.copy()
|
||||
self.assertTrue(type(df2) is GeoDataFrame)
|
||||
|
||||
Reference in New Issue
Block a user