【问题标题】:Pretty-printing physical quantities with automatic scaling of SI prefixes具有 SI 前缀自动缩放的漂亮打印物理量
【发布时间】:2015-06-20 02:09:27
【问题描述】:

我正在寻找一种优雅的方式来漂亮地打印具有最合适前缀的物理量(如12300 grams12.3 kilograms)。一个简单的方法如下所示:

def pprint_units(v, unit_str, num_fmt="{:.3f}"):
    """ Pretty printer for physical quantities """
    # prefixes and power:
    u_pres = [(-9, u'n'), (-6, u'µ'), (-3, u'm'), (0, ''),
              (+3, u'k'), (+6, u'M'), (+9, u'G')]

    if v == 0:
        return num_fmt.format(v) + " " + unit_str
    p = np.log10(1.0*abs(v))
    p_diffs = np.array([(p - u_p[0]) for u_p in u_pres])
    idx = np.argmin(p_diffs * (1+np.sign(p_diffs))) - 1
    u_p = u_pres[idx if idx >= 0 else 0]

    return num_fmt.format(v / 10.**u_p[0]) + " " + u_p[1]  + unit_str

for v in [12e-6, 3.4, .123, 3452]:
    print(pprint_units(v, 'g', "{: 7.2f}"))
# Prints:
#  12.00 µg
#   3.40 g
# 123.00 mg
#   3.45 kg

查看unitsPint,我找不到那个功能。是否还有其他库可以更全面地排版 SI 单位(以处理角度、温度等特殊情况)?

【问题讨论】:

  • 以下数量的预期表示形式:a=4.0a = a / 3.0print(pprint_units(a, 'g', "{: 7.2f}"))?我无法在不知情的情况下给出答案。
  • @Serge: pprint_units(4./3, 'g', "{: 7.2f}") 应该导致' 1.33 g'

标签: python units-of-measurement


【解决方案1】:

我曾经解决过同样的问题。恕我直言,更加优雅。不过没有度数或温度。

def sign(x, value=1):
    """Mathematical signum function.

    :param x: Object of investigation
    :param value: The size of the signum (defaults to 1)
    :returns: Plus or minus value
    """
    return -value if x < 0 else value

def prefix(x, dimension=1):
    """Give the number an appropriate SI prefix.

    :param x: Too big or too small number.
    :returns: String containing a number between 1 and 1000 and SI prefix.
    """
    if x == 0:
        return "0  "

    l = math.floor(math.log10(abs(x)))
    if abs(l) > 24:
        l = sign(l, value=24)

    div, mod = divmod(l, 3*dimension)
    return "%.3g %s" % (x * 10**(-l + mod), " kMGTPEZYyzafpnµm"[div])

CommaCalc

这样的度数:

def intfloatsplit(x):
    i = int(x)
    f = x - i
    return i, f

def prettydegrees(d):
    degrees, rest = intfloatsplit(d)
    minutes, rest = intfloatsplit(60*rest)
    seconds = round(60*rest)
    return "{degrees}° {minutes}' {seconds}''".format(**locals())

编辑:

增加了单位的维度

>>> print(prefix(0.000009, 2))
9 m
>>> print(prefix(0.9, 2))
9e+05 m

第二个输出不是很漂亮,我知道。您可能需要编辑格式化字符串。

编辑:

解析像0.000009 m² 这样的输入。适用于小于 10 的尺寸。

import unicodedata

def unitprefix(val):
    """Give the unit an appropriate SI prefix.

    :param val: Number and a unit, e.g. "0.000009 m²"
    """
    xstr, unit = val.split(None, 2)
    x = float(xstr)

    try:
        dimension = unicodedata.digit(unit[-1])
    except ValueError:
        dimension = 1

    return prefix(x, dimension) + unit

【讨论】:

  • 利用前缀字符串的负索引的好技巧。我花了一点时间才发现 CommaCalc 的链接是对您原始代码的引用。除非有人指出更全面的解决方案(如处理更复杂的情况,如 mm²),否则赏金是你的。
【解决方案2】:

decimal 模块可以提供帮助。此外,它可以防止错误的浮点舍入。

import decimal
prefix="yzafpnµm kMGTPEZY"
shift=decimal.Decimal('1E24')
def prettyprint(x,baseunit):
    d=(decimal.Decimal(str(x))*shift).normalize()
    m,e=d.to_eng_string().split('E')    
    return m + " " + prefix[int(e)//3] + baseunit

print(prettyprint (12300,'g'))
>>>> '12.3 kg'

它可以调整为管理格式。

【讨论】:

    【解决方案3】:

    如果您对使用Pint 感兴趣,请查看to_compact 方法。这还没有进入文档,但我认为它可以满足您的需求!

    这是来自 OP 的示例的实现:

    import pint
    ureg = pint.UnitRegistry()
    
    for v in [12e-6, 3.4, .123, 3452]:
        print('{:~7.2f}'.format((v * ureg('g')).to_compact()))   
    
    >>> 12.00 ug
    >>> 3.40 g
    >>> 123.00 mg
    >>> 3.45 kg
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-30
      • 2011-10-22
      • 1970-01-01
      • 2021-06-22
      • 1970-01-01
      相关资源
      最近更新 更多