numpy中的重要数字四舍五入

时间:2013-09-20 11:13:49

标签: python numpy

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

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

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

由于

13 个答案:

答案 0 :(得分:9)

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

  1. 在字符串之间来回转换,效率低下
  2. 无法处理负数
  3. 无法处理数组
  4. 有一些数字错误。

这是我尝试解决所有这些问题的解决方案。 (编辑2020-03-18:按照A. West的建议添加了np.asarray。)

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)

答案 1 :(得分:5)

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似乎可以解决此问题的各个方面。但是,在指数为负数的情况下,它不会执行您想要的操作。

答案 2 :(得分:2)

从示例数字中我认为你的意思是重要的数字而不是小数位(-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

答案 3 :(得分:2)

好的,如此合理安全地说这在标准功能中是不允许的。为了关闭它,这是我尝试一个强大的解决方案。这是相当丑陋/非pythonic和prob说明为什么我问这个问题更好,所以请随意纠正或击败:)

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])
    """
    import numpy as np
    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或复杂:

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

答案 4 :(得分:2)

人们对&#34;重要数字&#34;的理解非常薄弱,这也是为什么很少有编程语言有内置的原因之一。

0.00是两个有效数字,因为小数点左边的零点不重要,但右边两个是。

1.234568e-01 - &gt; 0.123500 右手数字有7个有效数字,因为如果显示,则分数中的尾随零是显着的。因此映射是不正确的,因为正确的版本0.1234568到4 sig fig是0.1235。将其写为0.1234500意味着最后两个零是真实的并且是准确的,在这种情况下它们不是。

在某些情况下,甚至可以在给定数值的情况下计算sig figs的数量,而必须知道值的历史或来源。例如,如果溶液的pH值为7.00,它只有两个sig figs,它们是&#34; .00&#34;。由于pH是对数函数,因此7并不显着,而7只是基础数中10的幂。例如,1.0e-7,-log10(1.00e-7)= 7.00。原始数字有2个sig fig(1.0),-log10有2个sig fig&#39; s,=(#34; .00&#34;)而7不是sig fig。

在实践中,有时候图形的数量是模糊的。如果一个人写了1234000,则规则说000是无关紧要的。但如果他们真的很重要怎么办?如果作者想要正确地进行,并且无论如何,他们会写1.234000e6(1.234000x10 ^ 6)。该版本有7 sig fig。

12300     3 sig fig (but may be ambiguous to some)
1.23e4    3 sig fig (unambiguous)
1.2300e4  5 sig fig
12300.0   6 sig fig
12300.100 8 sig fig

答案 5 :(得分:1)

此处给出的大多数解决方案要么(a)没有给出正确的有效数字,要么(b)不必要地复杂。

如果您的目标是 display 格式,则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.

答案 6 :(得分:1)

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

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

答案 7 :(得分: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

祝你好运!

(如果你喜欢使用lambdas:

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

答案 8 :(得分:0)

我喜欢Greg上面非常简短的有效程序。但是,它具有两个缺点。一是它不适用于x<0,无论如何都不适合我。 (应该删除np.sign(x)。)另外,如果x是一个数组,则它不起作用。我已经用下面的例程修复了这两个问题。请注意,我已经更改了n的定义。

def Round_n_sig_dig(x, n):

    import numpy as np               
    xr = (np.floor(np.log10(np.abs(x)))).astype(int)
    xr=10.**xr*np.around(x/10.**xr,n-1)   
    return xr    

答案 9 :(得分:0)

没有内置方法。

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

示例

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

答案 10 :(得分:0)

在搜寻互联网并没有找到答案之后,我感到非常沮丧,因此我编写了自己的代码。希望这就是您要寻找的

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

只需插入np数组X和所需数量的有效数字。干杯!

答案 11 :(得分:0)

对于以指数符号表示的(display-)格式,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类型...这显然将只保留字符串中存在的数字。尽管它将转换为位置浮点格式,但需要做更多的工作-因此,我认为对于大量数字而言,重新转换可能是个坏主意。

此外,这不适用于可迭代对象...请在docs上查找更多信息。

答案 12 :(得分:0)

另一种效果很好的解决方案。从@ScottGigante进行测试,以1.75ms的时间倒数第二。

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)

信用:受this post启发的答案