python - 美观地打印误差条

8 投票
2 回答
991 浏览
提问于 2025-04-16 21:23

我正在使用Python,配合numpy、scipy和matplotlib来进行数据评估。最终我得到了平均值和带误差条的拟合参数。

我希望Python能够根据指定的精度自动美化打印这些数据。比如说:

假设我得到了结果 x = 0.012345 +/- 0.000123。有没有办法在指定精度为2的情况下,自动把它格式化成 1.235(12) x 10^-2?也就是说,误差条的精度要被计算在内,而不是数值本身。

有没有人知道有什么包可以提供这样的功能,还是我得自己实现这个?

有没有办法把这个功能融入到Python的字符串格式化机制中?也就是说,能够写出类似 "%.2N" % (0.012345, 0.0000123) 的代码。

我已经查看了numpy和scipy的文档,还在网上搜索过,但没有找到任何相关的信息。我觉得这个功能对所有处理统计数据的人来说都很有用。

谢谢你的帮助!

编辑:根据Nathan Whitehead的要求,我会给出一些例子。

123 +- 1         ----precision 1----->  123(1)
123 +- 1.1       ----precision 2----->  123.0(11)
0.0123 +- 0.001  ----precision 1----->  0.012(1)
123.111 +- 0.123 ----precision 2----->  123.11(12)

为了清晰起见,省略了十的幂。括号里的数字是标准误差的简写表示。括号前面的数字的最后一位和括号里数字的最后一位必须在同一个小数位上。不知道为什么我在网上找不到这个概念的好解释。唯一找到的是这篇德文维基百科文章 这里。不过,这种表示法相当常见且非常实用。

编辑2:我自己实现了这个简写表示法:

#!/usr/bin/env python
# *-* coding: utf-8 *-*

from math import floor, log10

# uncertainty to string
def un2str(x, xe, precision=2):
    """pretty print nominal value and uncertainty

    x  - nominal value
    xe - uncertainty
    precision - number of significant digits in uncertainty

    returns shortest string representation of `x +- xe` either as
        x.xx(ee)e+xx
    or as
        xxx.xx(ee)"""
    # base 10 exponents
    x_exp = int(floor(log10(x)))
    xe_exp = int(floor(log10(xe)))

    # uncertainty
    un_exp = xe_exp-precision+1
    un_int = round(xe*10**(-un_exp))

    # nominal value
    no_exp = un_exp
    no_int = round(x*10**(-no_exp))

    # format - nom(unc)exp
    fieldw = x_exp - no_exp
    fmt = '%%.%df' % fieldw
    result1 = (fmt + '(%.0f)e%d') % (no_int*10**(-fieldw), un_int, x_exp)

    # format - nom(unc)
    fieldw = max(0, -no_exp)
    fmt = '%%.%df' % fieldw
    result2 = (fmt + '(%.0f)') % (no_int*10**no_exp, un_int*10**max(0, un_exp))

    # return shortest representation
    if len(result2) <= len(result1):
        return result2
    else:
        return result1


if __name__ == "__main__":
    xs    = [123456, 12.34567, 0.123456, 0.001234560000, 0.0000123456]
    xes   = [   123,  0.00123, 0.000123, 0.000000012345, 0.0000001234]
    precs = [     1,        2,        3,              4,            1]

    for (x, xe, prec) in zip(xs, xes, precs):
        print '%.6e +- %.6e @%d --> %s' % (x, xe, prec, un2str(x, xe, prec))

输出:

1.234560e+05 +- 1.230000e+02 @1 --> 1.235(1)e5
1.234567e+01 +- 1.230000e-03 @2 --> 12.3457(12)
1.234560e-01 +- 1.230000e-04 @3 --> 0.123456(123)
1.234560e-03 +- 1.234500e-08 @4 --> 0.00123456000(1235)
1.234560e-05 +- 1.234000e-07 @1 --> 1.23(1)e-5

2 个回答

2

因为 x +- y 不是一个标准的数据类型(可以把它看作是一个复数,其中实部是 x,虚部是 y,不过这样并没有简化问题...),但你可以通过创建一个新的类型并重写字符串显示的功能,来完全控制它的显示方式,也就是说可以像这样做:

class Res(object):
   def __init__(self, res, delta):
     self.res = res
     self.delta = delta

   def __str__(self):
     return "%f +- %f"%(self.res,self.delta)

if __name__ == '__main__':
   x = Res(0.2710,0.001)
   print(x)
   print(" a result: %s" % x)

__str__ 函数里面,你当然可以做一些更花哨的事情...

2

对于那些仍然对这个问题感兴趣的人,可以看看gvar这个库,以及这里有一个示例,展示了提问者想要的功能(在最后部分)。

撰写回答