我试过搜索这个,但找不到满意的答案。
我想获取一个数字列表/数组并将它们全部四舍五入到 n 个有效数字。我已经写了一个函数来做到这一点,但我想知道是否有一个标准的方法呢?我已经搜索但找不到它。例子:
In: [ 0.0, -1.2366e22, 1.2544444e-15, 0.001222 ], n=2
Out: [ 0.00, -1.24e22, 1.25e-15, 1.22e-3 ]
谢谢
首先是批评:您计算错误的有效数字的数量。在您的示例中,您想要 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 的处理数组的矢量化版本。请注意,尽可能保留输入数据类型也是此代码的目标。
测试所有已经提出的解决方案,我发现它们要么
这是我对应该处理所有这些事情的解决方案的尝试。(编辑 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)
这里给出的大多数解决方案要么 (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.
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似乎可以解决此问题的各个方面。但是,在指数为负的情况下,它不会做你想做的事。
从您拥有的示例数字中,我认为您的意思是有效数字而不是小数位(-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
好的,可以相当安全地说这在标准功能中是不允许的。为了关闭它,这是我尝试一个强大的解决方案。它相当丑陋/非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])
这是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])
有一个简单的解决方案,使用 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)
)
另一种效果很好的解决方案。从@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)
信用:受这篇文章启发的答案
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!
我喜欢上面格雷格非常简短的有效例程。然而,它有两个缺点。一是它对 , 对我不起作用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
sround = lambda x,p: float(f'%.{p-1}e'%x)
>>> print( sround(123.45, 2) )
120.0
使用Scott Gigante的signif(x, p)
fig1
fig2
对于指数符号的(显示)格式,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
类型......这显然只会保留字符串中存在的数字。它将被转换为位置浮点格式......需要更多的工作 - 所以我想重新转换对于大量数字来说可能是一个非常糟糕的主意。
此外,这不适用于可迭代...查看文档以获取更多信息。