Format Floats with Standard JSON Module

Format floats with standard json module

Note: This does not work in any recent version of Python.

Unfortunately, I believe you have to do this by monkey-patching (which, to my opinion, indicates a design defect in the standard library json package). E.g., this code:

import json
from json import encoder
encoder.FLOAT_REPR = lambda o: format(o, '.2f')

print(json.dumps(23.67))
print(json.dumps([23.67, 23.97, 23.87]))

emits:

23.67
[23.67, 23.97, 23.87]

as you desire. Obviously, there should be an architected way to override FLOAT_REPR so that EVERY representation of a float is under your control if you wish it to be; but unfortunately that's not how the json package was designed.

formatting json.dump floats from dictionary in Python

Well,
Don't use var name dict as it's a safe word in python. use dct instead.

You can loop over your keys, vals and round them them to 2nd floating point. Full working example:

dct = {"a": 3.55555, "b": 6.66666}

for k, v in dct.items():
if type(v) is float:
dct[k] = round(v, 2)

with open('dict.txt', 'w') as json_file:
json.dump(dict, json_file)

How to limit the number of float digits JSONEncoder produces?

Option 1: Use regular expression matching to round.

You can dump your object to a string using json.dumps and then use the technique shown on this post to find and round your floating point numbers.

To test it out, I added some more complicated nested structures on top of the example you provided::

d = dict()
d['val'] = 5.78686876876089075543
d['name'] = 'kjbkjbkj'
d["mylist"] = [1.23456789, 12, 1.23, {"foo": "a", "bar": 9.87654321}]
d["mydict"] = {"bar": "b", "foo": 1.92837465}

# dump the object to a string
d_string = json.dumps(d, indent=4)

# find numbers with 8 or more digits after the decimal point
pat = re.compile(r"\d+\.\d{8,}")
def mround(match):
return "{:.7f}".format(float(match.group()))

# write the modified string to a file
with open('test.json', 'w') as f:
f.write(re.sub(pat, mround, d_string))

The output test.json looks like:

{
"val": 5.7868688,
"name": "kjbkjbkj",
"mylist": [
1.2345679,
12,
1.23,
{
"foo": "a",
"bar": 9.8765432
}
],
"mydict": {
"bar": "b",
"foo": 1.9283747
}
}

One limitation of this method is that it will also match numbers that are within double quotes (floats represented as strings). You could come up with a more restrictive regex to handle this, depending on your needs.

Option 2: subclass json.JSONEncoder

Here is something that will work on your example and handle most of the edge cases you will encounter:

import json

class MyCustomEncoder(json.JSONEncoder):
def iterencode(self, obj):
if isinstance(obj, float):
yield format(obj, '.7f')
elif isinstance(obj, dict):
last_index = len(obj) - 1
yield '{'
i = 0
for key, value in obj.items():
yield '"' + key + '": '
for chunk in MyCustomEncoder.iterencode(self, value):
yield chunk
if i != last_index:
yield ", "
i+=1
yield '}'
elif isinstance(obj, list):
last_index = len(obj) - 1
yield "["
for i, o in enumerate(obj):
for chunk in MyCustomEncoder.iterencode(self, o):
yield chunk
if i != last_index:
yield ", "
yield "]"
else:
for chunk in json.JSONEncoder.iterencode(self, obj):
yield chunk

Now write the file using the custom encoder.

with open('test.json', 'w') as f:
json.dump(d, f, cls = MyCustomEncoder)

The output file test.json:

{"val": 5.7868688, "name": "kjbkjbkj", "mylist": [1.2345679, 12, 1.2300000, {"foo": "a", "bar": 9.8765432}], "mydict": {"bar": "b", "foo": 1.9283747}}

In order to get other keyword arguments like indent to work, the easiest way would be to read in the file that was just written and write it back out using the default encoder:

# write d using custom encoder
with open('test.json', 'w') as f:
json.dump(d, f, cls = MyCustomEncoder)

# load output into new_d
with open('test.json', 'r') as f:
new_d = json.load(f)

# write new_d out using default encoder
with open('test.json', 'w') as f:
json.dump(new_d, f, indent=4)

Now the output file is the same as shown in option 1.

Python: forcing precision on a floating point number in json?

Builtin function round can help

In [16]: v=2.030000002

In [17]: json.dumps({'x': round(v, 3)})
Out[17]: '{"x": 2.03}'

Format numbers as float or int using json in python

Don't write each row as a separate call to json.dumps(). Collect all the rows into a list, and dump that all at once.

To convert the string fields to integers, call int() on those entries in the dict.

import json
import csv

with csvfile_ind = open("test.csv",'r'):
reader_ind = csv.DictReader(csvfile_ind)
rows = []
for row in reader_ind:
row["RRC_attempts"] = int(row["RRC_attempts"])
row["rrc_succ_rate"] = int(row["rrc_succ_rate"])
rows.append(row)

with json_file_ind = open("test_json.json", 'w'):
json.dump(rows, json_file_ind, sort_keys=False, indent=4, separators=(',', ': '))

write json float in scientific notation

Eventually I created my own json library that can format floats, fjson. Install with

pip install fjson

and use as

import math
import fjson

data = {"a": 1, "b": math.pi}
string = fjson.dumps(data, float_format=".6e", indent=2, separators=(", ", ": "))
print(string)
{
"a": 1,
"b": 3.141593e+00
}

Fixing scientific notation in json to float

Those values are already stored as floats! You can see this with the following code (modified from yours):

#!/usr/bin/python3

import urllib.request, json
from pprint import pprint
from json import encoder

encoder.FLOAT_REPR = lambda o: format(o, '.8f')
with urllib.request.urlopen("https://bittrex.com/api/v1.1/public/getmarketsummaries") as url:
data = json.loads(url.read().decode())
# pprint (data)
thing = list(filter(lambda x:x['MarketName']=='BTC-1ST', data['result']))[0]
pprint(thing['Ask'])
print('{:.10f}'.format(thing['Ask']))
print(type(thing['Ask']))

Notice how the first value from pprint shows scientific notation (ie. the default string representation of that float value), while the second shows it formatted similar to how you are wanting it by forcing it to display that way.

So, if you're wanting to simply print out one of those values, you'll have to use string formatting as in my example, or if you want to pass those values to another program or function, you don't have to change anything since they are already floats!

Python JSON serialize a Decimal object

How about subclassing json.JSONEncoder?

class DecimalEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, decimal.Decimal):
# wanted a simple yield str(o) in the next line,
# but that would mean a yield on the line with super(...),
# which wouldn't work (see my comment below), so...
return (str(o) for o in [o])
return super(DecimalEncoder, self).default(o)

Then use it like so:

json.dumps({'x': decimal.Decimal('5.5')}, cls=DecimalEncoder)


Related Topics



Leave a reply



Submit