Python 在 numpy 中四舍五入到有效数字
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18915378/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rounding to significant figures in numpy
提问by dmon
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:
我想获取一个数字列表/数组并将它们全部四舍五入为 n 个有效数字。我已经写了一个函数来做到这一点,但我想知道是否有一个标准的方法?我已经搜索过,但找不到。例子:
In: [ 0.0, -1.2366e22, 1.2544444e-15, 0.001222 ], n=2
Out: [ 0.00, -1.24e22, 1.25e-15, 1.22e-3 ]
Thanks
谢谢
回答by Andrew Walker
Is numpy.set_printoptionswhat you're looking for?
是numpy.set_printoptions你在找什么?
import numpy as np
np.set_printoptions(precision=2)
print np.array([ 0.0, -1.2366e22, 1.2544444e-15, 0.001222 ])
Gives:
给出:
[ 0.00e+00 -1.24e+22 1.25e-15 1.22e-03]
Edit:
编辑:
numpy.aroundappears to solve aspects of this problem if you're trying to transform the data. However, it doesn't do what you want in cases where the exponent is negative.
如果您尝试转换数据,numpy.around似乎可以解决此问题的各个方面。但是,在指数为负的情况下,它不会执行您想要的操作。
回答by Greg
From the example numbers you have I think you mean significant figures rather than decimal places (-1.2366e22
to 0 decimal places is still -1.2366e22
).
从您拥有的示例数字中,我认为您的意思是有效数字而不是小数位(-1.2366e22
仍然是 0 位小数-1.2366e22
)。
This piece of code works for me, I've always thought there should be an inbuilt function though:
这段代码对我有用,但我一直认为应该有一个内置函数:
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
回答by dmon
Okay, so reasonably safe to say this is not allowed for in standard functionality. To close this off then, this is my attempt at a robust solution. It's rather ugly/non-pythonic and prob illustrates better then anything why I asked this question, so please feel free to correct or beat :)
好的,可以说这在标准功能中是不允许的。为了关闭它,这是我对强大解决方案的尝试。这是相当丑陋/非pythonic和prob更好地说明了我问这个问题的任何原因,所以请随时纠正或击败:)
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
Nearest I get to neat does not account for 0.0, nan, inf or complex:
最近我得到的整洁不占 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))
giving:
给予:
>>> m = [0.0, -1.2366e22, 1.2544444e-15, 0.001222]
>>> signifFig(m,2)
array([ nan, -1.24e+22, 1.25e-15, 1.22e-03])
回答by Sean Lake
First a criticism: you're counting the number of significant figures wrong. In your example you want n=3, not 2.
首先批评:你计算的有效数字是错误的。在您的示例中,您需要 n=3,而不是 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.
如果您使用使该算法的二进制版本变得简单的函数:frexp,则可以通过让 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.
"""
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.isreal( x ):
raise TypeError( "RoundToSigFigs_fp: x must be real." )
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=sigfigs - 1 ) * 10.0**omag
And it handles all of your cases correctly, including infinite, nan, 0.0, and a subnormal number:
它可以正确处理所有情况,包括无穷大、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])
Edit: 2016/10/12 I found an edge case that the original code handled wrong. I have placed a fuller version of the codein a GitHub repository.
编辑:2016/10/12 我发现原始代码处理错误的边缘情况。我已经在 GitHub 存储库中放置了更完整的代码版本。
Edit: 2019/03/01 Replace with recoded version.
编辑:2019/03/01 替换为重新编码的版本。
回答by denizb
There is a simple solution that uses the logic built into pythons string formatting system:
有一个简单的解决方案,它使用 python 字符串格式化系统中内置的逻辑:
def round_sig(f, p):
return float(('%.' + str(p) + 'e') % f)
Test with the following example:
使用以下示例进行测试:
for f in [0.01, 0.1, 1, 10, 100, 1000, 1000]:
f *= 1.23456789
print('%e --> %f' % (f, round_sig(f,3)))
which yields:
产生:
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
Best of luck!
祝你好运!
(If you like lambdas use:
(如果你喜欢 lambdas 使用:
round_sig = lambda f,p: float(('%.' + str(p) + 'e') % f)
)
)
回答by user11336338
I like Greg's very short effective routine above. However, it suffers from two drawbacks. One is that it doesn't work for x<0
, not for me anyway. (That np.sign(x)
should be removed.) Another is that it does not work if x
is an array. I've fixed both of those problems with the routine below. Notice that I've changed the definition of n
.
我喜欢上面 Greg 非常简短的有效例程。然而,它有两个缺点。一是它对 不起作用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
回答by A. West
For Scalars
对于标量
sround = lambda x,p: float(f'%.{p-1}e'%x)
Example
例子
>>> print( sround(123.45, 2) )
120.0
For Arrays
对于数组
Use Scott Gigante's signif(x, p)
fig1fig2
使用Scott Gigante的signif(x, p)
fig1 fig2
回答by Autumn
Most of the solutions given here either (a) don't give correct significant figures, or (b) are unnecessarily complex.
这里给出的大多数解决方案要么 (a) 没有给出正确的有效数字,要么 (b) 不必要地复杂。
If your goal is displayformatting, then numpy.format_float_positionalsupports the desired behaviour directly. The following fragment returns the float x
formatted to 4 significant figures, with scientific notation suppressed.
如果您的目标是显示格式,则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.
回答by Scott Gigante
Testing all of the already proposed solutions, I find they either
测试所有已经提出的解决方案,我发现它们要么
- convert to and from strings, which is inefficient
- can't handle negative numbers
- can't handle arrays
- 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.)
这是我尝试解决所有这些问题的解决方案。(编辑 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
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)
回答by Aditya Sinha
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!
只需插入您的 np 数组 X 和所需的有效数字数。干杯!