7

Is there a way to limit the representation of floating point numbers so that they correspond only to the prefixes used in physical units.

An example will make it clearer:

0.01   ->   currently       1e-02  
       ->   what I'd like   10e-03

0.0001   ->   currently       1e-04  
         ->   what I'd like   100e-06

The motivation behind it is that I'm doing some time measurements and having the representations as representations of 1000 is much more user friendly. I know that 10e-03 -> 10mili or 100e-06 -> 100u

Update

For numpy 1.7< there is a formatter attribute in the set_printoptions(). The solution for which I'm heading for is writing an inline function which would do the formating. Once set, it the formatting should be available globally.

Reference about the formatter

TheMeaningfulEngineer
  • 15,679
  • 27
  • 85
  • 143
  • 1
    This is known as "engineering notation" and searching for "python engineering notation" will yield some recipes and code for you to use. – Robert Kern Jul 29 '13 at 18:02

2 Answers2

4

If helps, a 'solution' can be:

Use ticker.EngFormatter() from matplotlib library together with numpy's set_printoptions. As pointed out by @TheMeaningfulEngineer's update link, it's formatter attribute requires a dict of keys each with a callable value (a function basically). The possible keys are basically the types to apply the format to. Those can be found in numpy's documentation.

Matplolib ticker don't use exponent but SI units (n, μ, k, k, M, etc)

Example:

import numpy as np
from matplotlib import ticker
formatter = ticker.EngFormatter()
np.set_printoptions(formatter={'float': formatter})
np.array([0.0000001, 0.000001, 10, 1000., 10000, 1000000])

which results in:

array([100 n, 1 μ, 10, 1 k, 10 k, 1 M])

For more on how to redefine SI units, I recommend this blog.


Update:

For those interested, matplolib's EngFormatter can accept argument such as units=' Hz', so that after the prefix values are printed with the unit. Like so:

array([100 n Hz, 1 μ Hz, 10  Hz, 1 k Hz, 10 k Hz, 1 M Hz])

Also, same idea can be applied to pandas by overwriting corresponding formatter.

pd.options.display.float_format = formatter
Traxidus Wolf
  • 808
  • 1
  • 9
  • 18
  • This is great but at first I could only get it to work when printing an array to screen. However, I want to capture the printed string in a variable to combine with other strings and print to screen (or a matplotlib figure) later. To do that, try this: Instead of `np.set_printoptions(formatter={"float": formatter}); print(array)` do something like `my_string = formatter.format_data(array[0])` and re-use `my_string` as required :) – Biggsy Oct 08 '21 at 09:10
1

I think that this topic might be helpful for you. On the other hand, I would try to create a few "buckets" for values in specific ranges and check a simple condition > (lowest value in range), then multiply that value by a range, e.g. 2e-5 by 1e6 to get 20u. Not quite efficient, but easy.

Community
  • 1
  • 1
akson128
  • 59
  • 1
  • 5