1

I've seen many discussions here about rounding floating values in python or similar topics, but I have a related problem that I want a good solution for.

Context:

I use the netCDF4 python library to extract data from NetCDF files. My organization keeps a precision attribute on variables within these files.

Example: TS:data_precision = 0.01f ;

I collect these precision attributes using the library like this:

d = netCDF4.Dataset(path) # assume path is the file or url link
precisions = {}
for v in d.variables:
    try:
        precisions[v] = d.variables[v].__getattribute__('data_precision')
    except AttributeError:
        pass
return precisions

When I retrieve these precision values from a dataset, in python they end up showing up like:

{u'lat': 9.9999997e-05, u'lon': 9.9999997e-05, u'TS': 0.0099999998, u'SSPS': 0.0099999998}

But, what I really want is:

{u'lat': 0.0001, u'lon': 0.0001, u'TS': 0.01, u'SSPS': 0.01}

Essentially I need a way in python to intelligently round these values to their most appropriate decimal place. I am sure I can come up with a really ugly method to do this, but I want to know if there is already a 'nice' solution to this problem.

For my use case, I suppose I can take advantage of the fact that since these values are all 'data_precision' values, I can just count the zero's from the decimal place, and then round to the last 0. (I'm making the assumption that 0 < n < 1). With these assumptions, this would be my solution:

#/usr/bin/python

def intelli_round(n):
    def get_decimal_place(n):
        count = 0
        while n < 1:
            n *= 10
            count += 1
        return count
    return round(n, get_decimal_place(n))

examples = [0.0099999, 0.00000999, 0.99999]
for e in examples:
    print e, intelli_round(e)

.

0.0099999 0.01
9.99e-06 1e-05
0.99999 1.0

Does this seem appropriate? It seems to work under the constraints, but I'm curious to see alternatives.

spanishgum
  • 1,030
  • 1
  • 14
  • 29
  • 3
    Isn't this what the [`decimal` module](https://docs.python.org/3/library/decimal.html) is for? – jwodder Aug 23 '16 at 16:51
  • 1
    Please can you provide the sample input and required output. It will help us in helping you. – Moinuddin Quadri Aug 23 '16 at 16:53
  • 1
    Python already does this for double-precision floats. I don't know of a way to make this happen if your data is single-precision. – Dietrich Epp Aug 23 '16 at 16:54
  • 2
    There is a good solution here: http://stackoverflow.com/questions/3410976/how-to-round-a-number-to-significant-figures-in-python – Benjamin Aug 23 '16 at 16:55
  • @jwodder, I haven't used the module too much, but at a brief glance it seems most of the utilities have the programmer specify the precision to round to. I'm trying to avoid that here. @Moinuddin the examples i put at the bottom would pretty much cover my use cases since the raw netcdf input is always `1.0e^p, p < 0`. @DietrichEpp I did not know that! My data is natively single precision. But ty that is interesting to know. – spanishgum Aug 23 '16 at 17:00
  • @spanishgum: Added an answer, but still I am not sure whether this is actually what you need. – Moinuddin Quadri Aug 23 '16 at 17:22
  • Benjamin, thank you for that link! The solution provided by @Ingar on that page does what I need and is much more elegant. Moinuddin, your solution specifies a precision to use on all input. I wanted something that determines precision upon each input. After seeing the other post, I realized I needed something that rounds to 1 significant figure. I'll put the solution in an answer below. – spanishgum Aug 23 '16 at 19:59

2 Answers2

1

Thanks to Benjamin for linking another post in the comments above to the solution I was looking for. A better way to word my question is that I want to operate on float values such that only 1 significant digit is retained.

Simple examples:

0.00999  -> 0.01
0.09998  -> 0.1
0.00099  -> 0.001

This solution was perfect for my needs:

>>> from math import log10, floor
>>> def round_to_1(x):
...   return round(x, -int(floor(log10(abs(x)))))

It handles inputs within my specific context (1.0e^p, p < 0) just fine so thank you so much for the help guys!

Community
  • 1
  • 1
spanishgum
  • 1,030
  • 1
  • 14
  • 29
0

For rounding the values to float with 2 decimal precision, you may use below code:

>>> x = [0.0099999, 0.00000999, 0.99999]
>>> ['%.2f' % i for i in x]
['0.01', '0.00', '1.00']

OR, you may use format as:

>>> ["{0:.2f}".format(i) for i in x]
['0.01', '0.00', '1.00']

In case you do not want to use these pythonic approach, and interested to implement it via mathematical logic, you may do:

>>> [int((i * 100) + 0.5) / 100.0 for i in x]
[0.01, 0.0, 1.0]
Moinuddin Quadri
  • 46,825
  • 13
  • 96
  • 126
  • 1
    I don't think this is what the question is actually asking for. The OP is actually changing the value to the less precise value using the round() function, and he or she wants a way for this to happen intelligently. Your response merely changes the presentation of the number as it's output. Underneath, it's still a float with higher precision. – Matthew Cole Aug 23 '16 at 17:29
  • I already added the comment regarding clarification. The actual requirement of OP is not clear to me. – Moinuddin Quadri Aug 23 '16 at 17:32