27

我试过搜索这个,但找不到满意的答案。

我想获取一个数字列表/数组并将它们全部四舍五入到 n 个有效数字。我已经写了一个函数来做到这一点,但我想知道是否有一个标准的方法呢?我已经搜索但找不到它。例子:

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

谢谢

4

13 回答 13

13

首先是批评:您计算错误的有效数字的数量。在您的示例中,您想要 n = 3,而不是 2。

如果您使用使该算法的二进制版本变得简单的函数:freexp,则可以通过让 numpy 库函数处理它们来绕过大多数边缘情况。作为奖励,该算法也将运行得更快,因为它从不调用 log 函数。

#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

它可以正确处理您的所有情况,包括无限、nan、0.0 和次正规数:

>>> 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])

编辑:2016/10/12 我发现原始代码处理错误的边缘情况。我已将更完整的代码版本放在GitHub 存储库中。

编辑:2019/03/01 替换为重新编码的版本。

编辑:2020/11/19 替换为来自 Github 的处理数组的矢量化版本。请注意,尽可能保留输入数据类型也是此代码的目标。

于 2015-04-29T22:17:34.757 回答
11

测试所有已经提出的解决方案,我发现它们要么

  1. 与字符串相互转换,效率低下
  2. 不能处理负数
  3. 无法处理数组
  4. 有一些数值错误。

这是我对应该处理所有这些事情的解决方案的尝试。(编辑 2020-03-18:np.asarray按照 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

测试:

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()

结果:

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, 

定时:

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)

结果:

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)
于 2020-01-24T00:28:00.657 回答
8

这里给出的大多数解决方案要么 (a) 没有给出正确的有效数字,要么 (b) 过于复杂。

如果您的目标是显示格式,那么numpy.format_float_positional直接支持所需的行为。以下片段返回x格式化为 4 位有效数字的浮点数,并抑制科学计数法。

import numpy as np
x=12345.6
np.format_float_positional(x, precision=4, unique=False, fractional=False, trim='k')
> 12340.
于 2019-10-21T17:04:41.500 回答
6

numpy.set_printoptions你在找什么

import numpy as np
np.set_printoptions(precision=2)
print np.array([  0.0, -1.2366e22, 1.2544444e-15, 0.001222 ])

给出:

[  0.00e+00  -1.24e+22   1.25e-15   1.22e-03]

编辑:

如果您尝试转换数据,numpy.around似乎可以解决此问题的各个方面。但是,在指数为负的情况下,它不会做你想做的事。

于 2013-09-20T11:16:48.377 回答
3

从您拥有的示例数字中,我认为您的意思是有效数字而不是小数位(-1.2366e22仍然是 0 小数位-1.2366e22)。

这段代码对我有用,我一直认为应该有一个内置函数:

def Round_To_n(x, n):
    return round(x, -int(np.floor(np.sign(x) * np.log10(abs(x)))) + n)

>>> Round_To_n(1.2544444e-15,2)
1.25e-15

>>> Round_To_n(2.128282321e3, 6)
2130.0
于 2013-09-20T14:02:58.947 回答
2

好的,可以相当安全地说这在标准功能中是不允许的。为了关闭它,这是我尝试一个强大的解决方案。它相当丑陋/非pythonic,并且概率比我问这个问题的任何原因都更好,所以请随时纠正或击败:)

import numpy as np

def round2SignifFigs(vals,n):
    """
    (list, int) -> numpy array
    (numpy array, int) -> numpy array

    In: a list/array of values
    Out: array of values rounded to n significant figures

    Does not accept: inf, nan, complex

    >>> m = [0.0, -1.2366e22, 1.2544444e-15, 0.001222]
    >>> round2SignifFigs(m,2)
    array([  0.00e+00,  -1.24e+22,   1.25e-15,   1.22e-03])
    """

    if np.all(np.isfinite(vals)) and np.all(np.isreal((vals))):
        eset = np.seterr(all='ignore')
        mags = 10.0**np.floor(np.log10(np.abs(vals)))  # omag's
        vals = np.around(vals/mags,n)*mags             # round(val/omag)*omag
        np.seterr(**eset)
        vals[np.where(np.isnan(vals))] = 0.0           # 0.0 -> nan -> 0.0
    else:
        raise IOError('Input must be real and finite')
    return vals

最接近整洁的不考虑 0.0、nan、inf 或 complex:

>>> omag      = lambda x: 10**np.floor(np.log10(np.abs(x)))
>>> signifFig = lambda x, n: (np.around(x/omag(x),n)*omag(x))

给予:

>>> m = [0.0, -1.2366e22, 1.2544444e-15, 0.001222]
>>> signifFig(m,2)
array([ nan, -1.24e+22,   1.25e-15,   1.22e-03])
于 2013-09-21T12:37:34.430 回答
1

这是Autumns答案的一个版本,它是矢量化的,因此它可以应用于浮点数组,而不仅仅是单个浮点数

x = np.array([12345.6, 12.5673])
def sf4(x):
    x = float(np.format_float_positional(x, precision=4, unique=False, fractional=False,trim='k'))
    return x
vec_sf4 = np.vectorize(sf4)

vec_sf4(x)

>>>np.array([12350., 12.57])
于 2020-08-05T20:19:49.370 回答
0

有一个简单的解决方案,使用 pythons 字符串格式化系统中内置的逻辑:

def round_sig(f, p):
    return float(('%.' + str(p) + 'e') % f)

使用以下示例进行测试:

for f in [0.01, 0.1, 1, 10, 100, 1000, 1000]:
    f *= 1.23456789
    print('%e --> %f' % (f, round_sig(f,3)))

产生:

1.234568e-02 --> 0.012350
1.234568e-01 --> 0.123500
1.234568e+00 --> 1.235000
1.234568e+01 --> 12.350000
1.234568e+02 --> 123.500000
1.234568e+03 --> 1235.000000
1.234568e+03 --> 1235.000000

祝你好运!

(如果您喜欢 lambda,请使用:

round_sig = lambda f,p: float(('%.' + str(p) + 'e') % f)

)

于 2017-08-06T22:39:23.543 回答
0

另一种效果很好的解决方案。从@ScottGigante 进行测试,时间为 1.75 毫秒,这将是第二好的。

import math

def sig_dig(x, n_sig_dig = 5):
  num_of_digits = len(str(x).replace(".", ""))
  if n_sig_dig >= num_of_digits:
      return x
  n = math.floor(math.log10(abs(x)) + 1 - n_sig_dig)
  result = round(x * 10**(-n)) * 10**n
  return result

如果它也应该应用于列表/数组,您可以将其矢量化为

sig_dig_vec = np.vectorize(sig_dig)

信用:受这篇文章启发的答案

于 2020-11-06T13:50:38.313 回答
0

I got quite frustrated after scouring the internet and not finding an answer for this, so I wrote my own piece of code. Hope this is what you're looking for

import numpy as np
from numpy import ma

exp = np.floor(ma.log10(abs(X)).filled(0))
ans = np.round(X*10**-exp, sigfigs-1) * 10**exp

Just plug in your np array X and the required number of significant figures. Cheers!

于 2020-02-02T03:55:30.957 回答
0

我喜欢上面格雷格非常简短的有效例程。然而,它有两个缺点。一是它对 , 对我不起作用x<0。(应该删除。)另一个是如果是数组np.sign(x),它就不起作用。x我已经用下面的例程解决了这两个问题。请注意,我已经更改了n.

import numpy as np

def Round_n_sig_dig(x, n):

    xr = (np.floor(np.log10(np.abs(x)))).astype(int)
    xr=10.**xr*np.around(x/10.**xr,n-1)   
    return xr    
于 2019-04-09T18:26:23.953 回答
0

对于标量

sround = lambda x,p: float(f'%.{p-1}e'%x)

例子

>>> print( sround(123.45, 2) )
120.0

对于数组

使用Scott Gigantesignif(x, p) fig1 fig2

于 2019-05-09T15:38:36.553 回答
0

对于指数符号的(显示)格式,numpy.format_float_scientific(x, precision = n)(其中x是要格式化的数字)似乎效果很好。该方法返回一个string. (这类似于@Autumn的回答)

这是一个例子:

>>> x = 7.92398e+05
>>> print(numpy.format_float_scientific(x, precision = 3))
7.924e+05

在这里,参数precision = n固定尾数中的小数位数(通过四舍五入)。可以将其重新转换回float类型......这显然只会保留字符串中存在的数字。它将被转换为位置浮点格式......需要更多的工作 - 所以我想重新转换对于大量数字来说可能是一个非常糟糕的主意。

此外,这不适用于可迭代...查看文档以获取更多信息。

于 2020-05-12T13:57:45.477 回答