Python中的小数对齐格式化

9 投票
2 回答
9523 浏览
提问于 2025-04-15 12:25

应该很简单。

这是我的数组(其实是生成测试数组的一种方法):

>>> ri = numpy.random.randint
>>> ri2 = lambda x: ''.join(ri(0,9,x).astype('S'))
>>> a = array([float(ri2(x)+ '.' + ri2(y)) for x,y in ri(1,10,(10,2))])
>>> a
array([  7.99914000e+01,   2.08000000e+01,   3.94000000e+02,
         4.66100000e+03,   5.00000000e+00,   1.72575100e+03,
         3.91500000e+02,   1.90610000e+04,   1.16247000e+04,
         3.53920000e+02])

我想要一个字符串列表,使用 '\n'.join(list_o_strings) 打印出来的结果是:

   79.9914
   20.8
  394.0
 4661.0
    5.0
 1725.751
  391.5
19061.0
11624.7
  353.92

我想要在左边和右边都加空格而且不能多加。

如果小数点后面只有零,我希望显示一个零。

我不想要科学计数法。

..而且我不想丢失任何有效数字。(比如在353.98000000000002中,最后的2并不算有效数字)

嗯,想要这些东西是不错的..

Python 2.5中的%g, %fx.x等让我感到困惑,或者根本做不到。 我还没尝试过import decimal。我也看不出NumPy能做到这一点(虽然array.__str__array.__repr__是对齐小数的,但有时会返回科学计数法)。

哦,还有速度很重要。我正在处理大数组。

我现在的解决方案有:

  1. 把数组转成字符串,然后去掉NumPy的括号
  2. 把数组中的每个元素转成字符串,分割小数点,然后加空格再重组
  3. a.astype('S'+str(i)),其中i是最大字符串长度,然后加空格

感觉应该有现成的解决方案...(不过不是必须的)

最好的建议在dtype是float64时失败:

>>> a
array([  5.50056103e+02,   6.77383566e+03,   6.01001513e+05,
         3.55425142e+08,   7.07254875e+05,   8.83174744e+02,
         8.22320510e+01,   4.25076609e+08,   6.28662635e+07,
         1.56503068e+02])
>>> ut0 = re.compile(r'(\d)0+$')
>>> thelist = [ut0.sub(r'\1', "%12f" % x) for x in a]
>>> print '\n'.join(thelist)
  550.056103
 6773.835663
601001.513
355425141.8471
707254.875038
  883.174744
   82.232051
425076608.7676
62866263.55
  156.503068

2 个回答

3

在Python中,字符串格式化可以用两种方式来显示数字:一种是只显示必要的小数位(用%g),另一种是固定显示小数位(用%f)。但是,如果你想只显示必要的小数位,除非这个数字是整数,那你希望它显示一个小数,这就变得有点复杂了。

这意味着你最终得到的结果可能是这样的:

def printarr(arr):
    for x in array:
        if math.floor(x) == x:
            res = '%.1f' % x
        else:
            res = '%.10g' % x
        print "%*s" % (15-res.find('.')+len(res), res)

这个代码首先会检查数字,如果是整数,就会生成一个带有1位小数的字符串;如果不是整数,它会自动决定小数位数(最多显示10位)。最后,它会把这个结果打印出来,并确保小数点对齐。

不过,实际上,numpy可能更符合你的需求,因为如果数字太长,你通常希望它以科学计数法的形式显示。

11

抱歉,经过仔细调查,我发现没有办法在不进行一些后处理的情况下完成你需要的任务(比如去掉你不想看到的尾随零)。像这样:

import re
ut0 = re.compile(r'(\d)0+$')

thelist = [ut0.sub(r'\1', "%12f" % x) for x in a]

print '\n'.join(thelist)

这个方法快速且简洁,但不符合你说的“现成”的要求——它实际上是将一般格式化和一个正则表达式组合在一起,前者几乎能满足你的需求,但会留下你想隐藏的尾随零。实际上,我认为它正好能做到你需要的,但你所列出的条件似乎限制得太多了。

编辑:原问题被修改,增加了对有效数字的要求,不需要比最大数字多的前导空格,并提供了一个新的例子(我之前的建议不符合期望的输出)。去掉一堆字符串中共同的前导空白,最好的方法是使用 textwrap.dedent ——但这个方法只适用于单个字符串(带换行符),而你需要的输出是一个字符串列表。没问题,我们只需把这些行放在一起,去掉前导空白,然后再分开:

import re
import textwrap

a = [  5.50056103e+02,   6.77383566e+03,   6.01001513e+05,
         3.55425142e+08,   7.07254875e+05,   8.83174744e+02,
         8.22320510e+01,   4.25076609e+08,   6.28662635e+07,
         1.56503068e+02]

thelist = textwrap.dedent(
        '\n'.join(ut0.sub(r'\1', "%20f" % x) for x in a)).splitlines()

print '\n'.join(thelist)

输出为:

      550.056103
     6773.83566
   601001.513
355425142.0
   707254.875
      883.174744
       82.232051
425076609.0
 62866263.5
      156.503068

撰写回答