Rounding to significant figures in numpy

后端 未结 13 1468
无人及你
无人及你 2020-12-09 16:00

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

13条回答
  •  眼角桃花
    2020-12-09 16:38

    Testing all of the already proposed solutions, I find they either

    1. convert to and from strings, which is inefficient
    2. can't handle negative numbers
    3. can't handle arrays
    4. have some numerical errors.

    Here's my attempt at a solution which should handle all of these things. (Edit 2020-03-18: added np.asarray as suggested by A. West.)

    def signif(x, p):
        x = np.asarray(x)
        x_positive = np.where(np.isfinite(x) & (x != 0), np.abs(x), 10**(p-1))
        mags = 10 ** (p - 1 - np.floor(np.log10(x_positive)))
        return np.round(x * mags) / mags
    

    Testing:

    def scottgigante(x, p):
        x_positive = np.where(np.isfinite(x) & (x != 0), np.abs(x), 10**(p-1))
        mags = 10 ** (p - 1 - np.floor(np.log10(x_positive)))
        return np.round(x * mags) / mags
    
    def awest(x,p):
        return float(f'%.{p-1}e'%x)
    
    def denizb(x,p):
        return float(('%.' + str(p-1) + 'e') % x)
    
    def autumn(x, p):
        return np.format_float_positional(x, precision=p, unique=False, fractional=False, trim='k')
    
    def greg(x, p):
        return round(x, -int(np.floor(np.sign(x) * np.log10(abs(x)))) + p-1)
    
    def user11336338(x, p):         
        xr = (np.floor(np.log10(np.abs(x)))).astype(int)
        xr=10.**xr*np.around(x/10.**xr,p-1)   
        return xr
    
    def dmon(x, p):
        if np.all(np.isfinite(x)):
            eset = np.seterr(all='ignore')
            mags = 10.0**np.floor(np.log10(np.abs(x)))  # omag's
            x = np.around(x/mags,p-1)*mags             # round(val/omag)*omag
            np.seterr(**eset)
            x = np.where(np.isnan(x), 0.0, x)           # 0.0 -> nan -> 0.0
        return x
    
    def seanlake(x, p):
        __logBase10of2 = 3.010299956639811952137388947244930267681898814621085413104274611e-1
        xsgn = np.sign(x)
        absx = xsgn * x
        mantissa, binaryExponent = np.frexp( absx )
    
        decimalExponent = __logBase10of2 * binaryExponent
        omag = np.floor(decimalExponent)
    
        mantissa *= 10.0**(decimalExponent - omag)
    
        if mantissa < 1.0:
            mantissa *= 10.0
            omag -= 1.0
    
        return xsgn * np.around( mantissa, decimals=p - 1 ) * 10.0**omag
    
    solns = [scottgigante, awest, denizb, autumn, greg, user11336338, dmon, seanlake]
    
    xs = [
        1.114, # positive, round down
        1.115, # positive, round up
        -1.114, # negative
        1.114e-30, # extremely small
        1.114e30, # extremely large
        0, # zero
        float('inf'), # infinite
        [1.114, 1.115e-30], # array input
    ]
    p = 3
    
    print('input:', xs)
    for soln in solns:
        print(f'{soln.__name__}', end=': ')
        for x in xs:
            try:
                print(soln(x, p), end=', ')
            except Exception as e:
                print(type(e).__name__, end=', ')
        print()
    

    Results:

    input: [1.114, 1.115, -1.114, 1.114e-30, 1.114e+30, 0, inf, [1.114, 1.115e-30]]
    scottgigante: 1.11, 1.12, -1.11, 1.11e-30, 1.11e+30, 0.0, inf, [1.11e+00 1.12e-30], 
    awest: 1.11, 1.11, -1.11, 1.11e-30, 1.11e+30, 0.0, inf, TypeError, 
    denizb: 1.11, 1.11, -1.11, 1.11e-30, 1.11e+30, 0.0, inf, TypeError, 
    autumn: 1.11, 1.11, -1.11, 0.00000000000000000000000000000111, 1110000000000000000000000000000., 0.00, inf, TypeError, 
    greg: 1.11, 1.11, -1.114, 1.11e-30, 1.11e+30, ValueError, OverflowError, TypeError, 
    user11336338: 1.11, 1.12, -1.11, 1.1100000000000002e-30, 1.1100000000000001e+30, nan, nan, [1.11e+00 1.12e-30], 
    dmon: 1.11, 1.12, -1.11, 1.1100000000000002e-30, 1.1100000000000001e+30, 0.0, inf, [1.11e+00 1.12e-30], 
    seanlake: 1.11, 1.12, -1.11, 1.1100000000000002e-30, 1.1100000000000001e+30, 0.0, inf, ValueError, 
    

    Timing:

    def test_soln(soln):
        try:
            soln(np.linspace(1, 100, 1000), 3)
        except Exception:
            [soln(x, 3) for x in np.linspace(1, 100, 1000)]
    
    for soln in solns:
        print(soln.__name__)
        %timeit test_soln(soln)
    

    Results:

    scottgigante
    135 µs ± 15.3 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    awest
    2.23 ms ± 430 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    denizb
    2.18 ms ± 352 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    autumn
    2.92 ms ± 206 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    greg
    14.1 ms ± 1.21 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
    user11336338
    157 µs ± 50.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    dmon
    142 µs ± 8.52 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    seanlake
    20.7 ms ± 994 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
    

提交回复
热议问题