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:
Jacob Wasserman
2013-11-01 12:58:47 -04:00
parent 72153f4c3b
commit 0e671667b0
2 changed files with 53 additions and 12 deletions
+31 -9
View File
@@ -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',
+22 -3
View File
@@ -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)