Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
324 views
in Technique[技术] by (71.8m points)

python - Rounding to significant figures in numpy

I've tried searching this and can't find a satisfactory answer.

I want to take a list/array of numbers and round them all to n significant figures. I have written a function to do this, but I was wondering if there is a standard method for this? I've searched but can't find it. Example:

In:  [  0.0, -1.2366e22, 1.2544444e-15, 0.001222 ], n=2
Out: [ 0.00, -1.24e22,        1.25e-15,  1.22e-3 ]

Thanks

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

First a criticism: you're counting the number of significant figures wrong. In your example you want n=3, not 2.

It is possible to get around most of the edge cases by letting numpy library functions handle them if you use the function that makes the binary version of this algorithm simple: frexp. As a bonus, this algorithm will also run much faster because it never calls the log function.

#The following constant was computed in maxima 5.35.1 using 64 bigfloat digits of precision
__logBase10of2 = 3.010299956639811952137388947244930267681898814621085413104274611e-1

import numpy as np

def RoundToSigFigs_fp( x, sigfigs ):
"""
Rounds the value(s) in x to the number of significant figures in sigfigs.
Return value has the same type as x.

Restrictions:
sigfigs must be an integer type and store a positive value.
x must be a real value or an array like object containing only real values.
"""
if not ( type(sigfigs) is int or type(sigfigs) is long or
         isinstance(sigfigs, np.integer) ):
    raise TypeError( "RoundToSigFigs_fp: sigfigs must be an integer." )

if sigfigs <= 0:
    raise ValueError( "RoundToSigFigs_fp: sigfigs must be positive." )

if not np.all(np.isreal( x )):
    raise TypeError( "RoundToSigFigs_fp: all x must be real." )

#temporarily suppres floating point errors
errhanddict = np.geterr()
np.seterr(all="ignore")

matrixflag = False
if isinstance(x, np.matrix): #Convert matrices to arrays
    matrixflag = True
    x = np.asarray(x)

xsgn = np.sign(x)
absx = xsgn * x
mantissas, binaryExponents = np.frexp( absx )

decimalExponents = __logBase10of2 * binaryExponents
omags = np.floor(decimalExponents)

mantissas *= 10.0**(decimalExponents - omags)

if type(mantissas) is float or isinstance(mantissas, np.floating):
    if mantissas < 1.0:
        mantissas *= 10.0
        omags -= 1.0
        
else: #elif np.all(np.isreal( mantissas )):
    fixmsk = mantissas < 1.0, 
    mantissas[fixmsk] *= 10.0
    omags[fixmsk] -= 1.0

result = xsgn * np.around( mantissas, decimals=sigfigs - 1 ) * 10.0**omags
if matrixflag:
    result = np.matrix(result, copy=False)

np.seterr(**errhanddict)
return result

And it handles all of your cases correctly, including infinite, nan, 0.0, and a subnormal number:

>>> eglist = [  0.0, -1.2366e22, 1.2544444e-15, 0.001222, 0.0, 
...        float("nan"), float("inf"), float.fromhex("0x4.23p-1028"), 
...        0.5555, 1.5444, 1.72340, 1.256e-15, 10.555555  ]
>>> eglist
[0.0, -1.2366e+22, 1.2544444e-15, 0.001222, 0.0, 
nan, inf, 1.438203867284623e-309, 
0.5555, 1.5444, 1.7234, 1.256e-15, 10.555555]
>>> RoundToSigFigs(eglist, 3)
array([  0.00000000e+000,  -1.24000000e+022,   1.25000000e-015,
         1.22000000e-003,   0.00000000e+000,               nan,
                     inf,   1.44000000e-309,   5.56000000e-001,
         1.54000000e+000,   1.72000000e+000,   1.26000000e-015,
         1.06000000e+001])
>>> RoundToSigFigs(eglist, 1)
array([  0.00000000e+000,  -1.00000000e+022,   1.00000000e-015,
         1.00000000e-003,   0.00000000e+000,               nan,
                     inf,   1.00000000e-309,   6.00000000e-001,
         2.00000000e+000,   2.00000000e+000,   1.00000000e-015,
         1.00000000e+001])

Edit: 2016/10/12 I found an edge case that the original code handled wrong. I have placed a fuller version of the code in a GitHub repository.

Edit: 2019/03/01 Replace with recoded version.

Edit: 2020/11/19 Replace with vectorized version from Github that handles arrays. Note that preserving input data types, where possible, was also a goal of this code.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...