2015-04-14 13 views
10

Tôi đang tìm kiếm một cách thanh lịch để in số lượng vật lý với tiền tố thích hợp nhất (như trong 12300 grams12.3 kilograms). Một phương pháp đơn giản như sau:Số lượng vật lý in ấn với tỷ lệ tự động của tiền tố SI

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 

Nhìn qua unitsPint, tôi không thể tìm thấy chức năng đó. Có bất kỳ thư viện nào khác sắp xếp đơn vị SI toàn diện hơn không (để xử lý các trường hợp đặc biệt như góc độ, nhiệt độ, v.v ...)?

+0

gì nên được dự kiến ​​đại diện cho sau lượng: 'a = 4.0' 'a = a/3.0'' in (pprint_units (a,' g ', "{: 7.2f}")) '? Tôi không thể đưa ra câu trả lời mà không biết điều đó. –

+0

@Serge: '' pprint_units (4./3, 'g', "{: 7.2f}") '' sẽ dẫn đến '' '1,33 g'''. – Dietrich

Trả lời

9

Tôi đã giải quyết cùng một vấn đề một lần. Và IMHO với sự sang trọng hơn. Không có độ hoặc nhiệt độ mặc dù.

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

Degrees như thế:

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

chỉnh sửa:

gia tăng kích thước của các đơn vị

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

Sản lượng thứ hai không phải là rất đẹp, tôi biết. Bạn có thể muốn chỉnh sửa chuỗi định dạng.

chỉnh sửa:

Parse đầu vào như 0.000009 m². Hoạt động trên các kích thước nhỏ hơn 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 
+0

Bí quyết khai thác các chỉ mục tiêu cực cho chuỗi tiền tố. Tôi mất một chút thời gian để tìm ra rằng liên kết tới * CommaCalc * là tham chiếu đến mã ban đầu của bạn. Trừ khi ai đó chỉ ra một giải pháp toàn diện hơn (như đối phó với các trường hợp phức tạp hơn như mm²), tiền thưởng là của bạn. – Dietrich

+1

Tôi không biết về '' unicodedata'' - rất hay. – Dietrich

2

Mô-đun decimal có thể trợ giúp. Morover nó ngăn chặn làm tròn phao xấu.

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' 

nó có thể được điều chỉnh để quản lý định dạng.

+0

Cảm ơn, sử dụng mô-đun '' thập phân'' là một lựa chọn tốt. – Dietrich

1

Nếu bạn quan tâm đến việc sử dụng Pint, hãy xem phương thức to_compact. Điều này đã không làm cho nó vào tài liệu, nhưng tôi nghĩ rằng nó làm những gì bạn đang tìm kiếm!

Đây là việc thực hiện các ví dụ từ 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 
Các vấn đề liên quan