【问题标题】:Controlling the number of decimals in a mixed list printout (pprint.pformat, json.dumps)控制混合列表打印输出中的小数位数(pprint.pformat、json.dumps)
【发布时间】:2019-10-16 07:36:25
【问题描述】:

我有一个“混合列表”(这里的意思是一个可能包括列表、字典、字符串、整数或浮点数的列表),我想打印它——也就是说,获取它的字符串表示,希望是“漂亮”的一个 - 但是,以这种方式,此数据结构中的浮点数的小数位数受到限制。然后,原则上,我可能想将此字符串保存到文件中,然后再次加载。

根据经验,我希望所有绝对值 > 0.01 的值都只用两位小数格式化,其余的用科学记数法格式化。

查看一些 SO 帖子,我设法提出了以下示例(适用于 MSYS2、Windows 10 上的 Python 2.7.16 和 Python 3.7.4):

#!/usr/bin/env python

import math
import pprint

# https://stackoverflow.com/questions/1447287/format-floats-with-standard-json-module
import json
from json import encoder
encoder.FLOAT_REPR = lambda o: format(o, '.2f')

# https://stackoverflow.com/questions/1447287/format-floats-with-standard-json-module
def round_floats(o):
  if isinstance(o, float): return "{:.2f}".format(o) if abs(o)>0.01 else "{:.2e}".format(o)
  if isinstance(o, dict): return {k: round_floats(v) for k, v in o.items()}
  if isinstance(o, (list, tuple)): return [round_floats(x) for x in o]
  return o

import collections
try: # https://stackoverflow.com/questions/53978542/how-to-use-collections-abc
  import collections.abc
  collectionsAbc = collections.abc
except (ImportError, AttributeError) as e:
  collectionsAbc = collections
import numbers

# https://stackoverflow.com/questions/7076254/rounding-decimals-in-nested-data-structures-in-python
def fpformat(thing, formatfunc):
  if isinstance(thing, dict):
    try: # Python 2
      thingiter = thing.iteritems()
    except: # Python 3
      thingiter = thing.items()
    return type(thing)((key, fpformat(value, formatfunc)) for key, value in thingiter)
  if isinstance(thing, collectionsAbc.Container):
    return type(thing)(fpformat(value, formatfunc) for value in thing)
  if isinstance(thing, numbers.Number):
    return formatfunc(thing)
  return thing
def formatfloat(thing):
  return "%.3g" % float(thing)

#############

# make a source array, mixed data

tarr = [
  ["aa",         "bb",        "cc",        "dd",        "ee"          ],
  [ {'v': 1.1},  {'w': 2.2},  {'x': 3.3},  {'y': 4.4},  {'z': 5.5555} ],
  [ 10,          20,          30,          40,          50            ],
  [ 11.1,        22.22,       33.333,      44.4444,     55.55555      ]
]

# create some more decimals:
appendrow = []

for ind, tnum in enumerate(tarr[2]):
  tpnum = ((ind+1.0)/(ind+2.0))*math.pi*tnum
  appendrow.append(tpnum)

tarr.append(appendrow)

appendrow = []

for ind, tnum in enumerate(tarr[2]):
  tpnum = ((ind+1.0)/(ind+2.0))*math.pi*tnum/100000.0
  appendrow.append(tpnum)

tarr.append(appendrow)

tarr_ppf_string = pprint.pformat(tarr)

print("printout 1:\n{}\n".format(tarr_ppf_string))

tarr_ppf_string2 = pprint.pformat(round_floats(tarr))

print("printout 2:\n{}\n".format(tarr_ppf_string2))

tarr_json_string = json.dumps(tarr)

print("printout 3:\n{}\n".format(tarr_json_string))

tarr_json_string2 = json.dumps(round_floats(tarr))

print("printout 4:\n{}\n".format(tarr_json_string2))

tarr_fp_string = fpformat(tarr, formatfloat)

print("printout 5:\n{}\n".format(tarr_fp_string))

这个脚本在 Python 3 中的输出是这样的:

printout 1:
[['aa', 'bb', 'cc', 'dd', 'ee'],
 [{'v': 1.1}, {'w': 2.2}, {'x': 3.3}, {'y': 4.4}, {'z': 5.5555}],
 [10, 20, 30, 40, 50],
 [11.1, 22.22, 33.333, 44.4444, 55.55555],
 [15.707963267948966,
  41.8879020478639,
  70.68583470577035,
  100.53096491487338,
  130.89969389957471],
 [0.00015707963267948965,
  0.00041887902047863906,
  0.0007068583470577034,
  0.0010053096491487337,
  0.0013089969389957472]]

printout 2:
[['aa', 'bb', 'cc', 'dd', 'ee'],
 [{'v': '1.10'}, {'w': '2.20'}, {'x': '3.30'}, {'y': '4.40'}, {'z': '5.56'}],
 [10, 20, 30, 40, 50],
 ['11.10', '22.22', '33.33', '44.44', '55.56'],
 ['15.71', '41.89', '70.69', '100.53', '130.90'],
 ['1.57e-04', '4.19e-04', '7.07e-04', '1.01e-03', '1.31e-03']]

printout 3:
[["aa", "bb", "cc", "dd", "ee"], [{"v": 1.1}, {"w": 2.2}, {"x": 3.3}, {"y": 4.4}, {"z": 5.5555}], [10, 20, 30, 40, 50], [11.1, 22.22, 33.333, 44.4444, 55.55555], [15.707963267948966, 41.8879020478639, 70.68583470577035, 100.53096491487338, 130.89969389957471], [0.00015707963267948965, 0.00041887902047863906, 0.0007068583470577034, 0.0010053096491487337, 0.0013089969389957472]]

printout 4:
[["aa", "bb", "cc", "dd", "ee"], [{"v": "1.10"}, {"w": "2.20"}, {"x": "3.30"}, {"y": "4.40"}, {"z": "5.56"}], [10, 20, 30, 40, 50], ["11.10", "22.22", "33.33", "44.44", "55.56"], ["15.71", "41.89", "70.69", "100.53", "130.90"], ["1.57e-04", "4.19e-04", "7.07e-04", "1.01e-03", "1.31e-03"]]

printout 5:
[['<generator object fpformat.<locals>.<genexpr> at 0x6ffffcc57d0>', '<generator object fpformat.<locals>.<genexpr> at 0x6ffffcc57d0>', '<generator object fpformat.<locals>.<genexpr> at 0x6ffffcc57d0>', '<generator object fpformat.<locals>.<genexpr> at 0x6ffffcc57d0>', '<generator object fpformat.<locals>.<genexpr> at 0x6ffffcc57d0>'], [{'v': '1.1'}, {'w': '2.2'}, {'x': '3.3'}, {'y': '4.4'}, {'z': '5.56'}], ['10', '20', '30', '40', '50'], ['11.1', '22.2', '33.3', '44.4', '55.6'], ['15.7', '41.9', '70.7', '101', '131'], ['0.000157', '0.000419', '0.000707', '0.00101', '0.00131']]

基本上,我想要的是“打印输出 2”——除了数字是剩余的数字,而不是作为字符串打印;也就是说,我希望打印输出是这样的:

[['aa', 'bb', 'cc', 'dd', 'ee'],
 [{'v': 1.1'}, {'w': 2.20}, {'x': 3.30}, {'y': 4.40}, {'z': 5.56}],
 [10, 20, 30, 40, 50],
 [11.10, 22.22, 33.33, 44.44, 55.56],
 [15.71, 41.89, 70.69, 100.53, 130.90],
 [1.57e-04, 4.19e-04, 7.07e-04, 1.01e-03, 1.31e-03]]

如何在 Python 中实现这种打印输出? (Python 3 需要这个,但 Python 2 的解决方案也很棒)

【问题讨论】:

    标签: python formatting


    【解决方案1】:

    旧答案

    问题是您将浮点数作为字符串而不是作为浮点数插入。您正在打印包含字符串的字典,因此它们被打印为字符串。您想将数字插入为浮点数。

    您可以将浮点数四舍五入到一定数量的小数位,而无需将它们转换为字符串。

    def round_floats(o):
      if isinstance(o, float): return round(o, 2) #Line 13, using round instead of
                                                      #string formatting
      if isinstance(o, dict): return {k: round_floats(v) for k, v in o.items()}
      if isinstance(o, (list, tuple)): return [round_floats(x) for x in o]
      return o
    
    

    round(float, decimals) 函数替换字符串格式的使用为 printout2 提供以下输出:

    printout 2:
    [['aa', 'bb', 'cc', 'dd', 'ee'],
     [{'v': 1.1}, {'w': 2.2}, {'x': 3.3}, {'y': 4.4}, {'z': 5.56}],
     [10, 20, 30, 40, 50],
     [11.1, 22.22, 33.33, 44.44, 55.56],
     [15.71, 41.89, 70.69, 100.53, 130.9],
     [0.0, 0.0, 0.0, 0.0, 0.0]]
    
    

    新答案

    编辑 - 经过多次调试,我们偶然发现了一个问题。强制漂亮打印始终使用某种指数格式是不可能的。

    我尝试使用this 代码来覆盖漂亮打印机的浮点运算符,但它不适用于列表。如果该类型嵌套在列表/字典/结构中,则此解决方案不会覆盖该类型的格式化程序。不幸的是,如果不重写一半漂亮的打印机代码,这个解决方案似乎不可行。

    好消息是可能不需要。您可以对所有浮点数使用两位小数的精度。这并不能保证该数字将以科学计数法表示,但在大多数情况下,这将适合您。

    def round_floats(o):
      if isinstance(o, float): return float("{:.2f}".format(o) if abs(o)>0.01 else "{:.2e}".format(o))
      #Edited line 13, just casting back to float
      if isinstance(o, dict): return {k: round_floats(v) for k, v in o.items()}
      if isinstance(o, (list, tuple)): return [round_floats(x) for x in o]
    
    

    改用小数类来调整数字的精度可能会更好。

    import decimal
    decimal.getcontext().prec = 3
    
    def round_floats(o):
      if isinstance(o, float): return float(+decimal.Decimal(o))
      if isinstance(o, dict): return {k: round_floats(v) for k, v in o.items()}
      if isinstance(o, (list, tuple)): return [round_floats(x) for x in o]
    
    

    在任何一种情况下,坏消息是 0 附近的数字不会按照您想要的方式运行。像 0.0001 这样的数字将保持相同的表示形式(而不是 1.0e-4)。然而,它确实会执行计算并检查哪种表示法(科学的或正常的)占用的空间更少,因此采用这种方法可以保证每个表示都是尽可能短的。

    输出:

    [['aa', 'bb', 'cc', 'dd', 'ee'],
     [{'v': 1.1}, {'w': 2.2}, {'x': 3.3}, {'y': 4.4}, {'z': 5.56}],
     [10, 20, 30, 40, 50],
     [11.1, 22.2, 33.3, 44.4, 55.6],
     [15.7, 41.9, 70.7, 101.0, 131.0],
     [0.000157, 0.000419, 0.000707, 0.00101, 0.00131]]
     #Note that the bottom row is badly represented, but this representation is
     #not longer than writing out the same number in scientific notation. If
     #These numbers were smaller, they would be represented scientifically.
    
    

    【讨论】:

    • 谢谢@anerisgreat - 看起来几乎很棒,除了现在最后一行全为零,它不应该是(这就是为什么我提到科学记数法,如果值小于 0.01)。有什么方法可以做到这一点?
    • 没问题!更新了答案。
    • 非常感谢@anerisgreat - 更新后的答案详细介绍了问题所在,非常感谢!
    猜你喜欢
    • 2011-01-18
    • 2018-04-30
    • 1970-01-01
    • 1970-01-01
    • 2015-08-29
    • 2012-12-25
    • 1970-01-01
    相关资源
    最近更新 更多