【问题标题】:Formatting Numbers So They Align On Decimal Point格式化数字,使它们在小数点上对齐
【发布时间】:2015-12-24 19:59:21
【问题描述】:

在 Python 中,我需要对数字进行格式化,使其与小数点对齐,如下所示:

  4.8
 49.723
456.781
-72.18
  5
 13

有没有直接的方法来做到这一点?

【问题讨论】:

标签: python formatting output


【解决方案1】:

如果您知道所需的精度(小数点后的数字),并且不介意在使用整数时有一些尾随零,则可以在 Python 3.6 中使用新的 f-string (PEP498 ):

numbers = [4.8, 49.723, 456.781, -72.18, 5, 13]

for number in numbers:
    print(f'{number:9.4f}')

打印:

  4.8000
 49.7230
456.7810
-72.1800
  5.0000
 13.0000

【讨论】:

  • 请注意,此方法还需要在格式表达式中指定(给定可能数字范围的最大值)前缀长度。
【解决方案2】:

我不认为有一种直接的方法可以做到这一点,因为在开始打印之前您需要知道所有数字中小数点的位置。 (我刚刚查看了 Caramiriel 的链接,以及该页面中的一些链接,但我找不到特别适用于这种情况的任何内容)。

所以看起来您必须对列表中的数字进行一些基于字符串的检查和操作。例如,

def dot_aligned(seq):
    snums = [str(n) for n in seq]
    dots = [s.find('.') for s in snums]
    m = max(dots)
    return [' '*(m - d) + s for s, d in zip(snums, dots)]

nums = [4.8, 49.723, 456.781, -72.18]

for s in dot_aligned(nums):
    print(s)

输出

  4.8
 49.723
456.781
-72.18

如果你想处理一个floats 列表和一些简单的ints 混合,那么这种方法会有点混乱。

def dot_aligned(seq):
    snums = [str(n) for n in seq]
    dots = []
    for s in snums:
        p = s.find('.')
        if p == -1:
            p = len(s)
        dots.append(p)
    m = max(dots)
    return [' '*(m - d) + s for s, d in zip(snums, dots)]

nums = [4.8, 49.723, 456.781, -72.18, 5, 13]

for s in dot_aligned(nums):
    print(s)
    

输出

  4.8
 49.723
456.781
-72.18
  5
 13

正如 Mark Ransom 在 cmets 中指出的那样,我们可以通过使用 .split 来简化处理 ints:

def dot_aligned(seq):
    snums = [str(n) for n in seq]
    dots = [len(s.split('.', 1)[0]) for s in snums]
    m = max(dots)
    return [' '*(m - d) + s for s, d in zip(snums, dots)]

Masher 在评论中提到,在右侧添加填充非常有用,这样数字可以打印在对齐的列中。但是,我们不需要计算每个字符串的填充大小,我们可以使用str.ljust 方法。

def dot_aligned(seq):
    snums = [str(n) for n in seq]
    dots = [len(s.split('.', 1)[0]) for s in snums]
    m = max(dots)
    left_pad = [' '*(m - d) + s for s, d in zip(snums, dots)]
    ms = max(map(len, left_pad))
    return [s.ljust(ms) for s in left_pad]

nums = [4.8, 49.723, 456.781, -72.18, 5, 13, 1.2345] * 3
cols = 4
# Get number of cells in the output grid, using ceiling division
size = len(nums) // -cols * -cols

padded = dot_aligned(nums)
for i in range(0, size, cols):
    print(*padded[i:i+cols])

输出

  4.8     49.723  456.781  -72.18  
  5       13        1.2345   4.8   
 49.723  456.781  -72.18     5     
 13        1.2345   4.8     49.723 
456.781  -72.18     5       13     
  1.2345

【讨论】:

  • 你可以使用len(s.split('.')[0]),而不是s.find('.'),它也适用于int
  • @MarkRansom:现在为什么没有想到呢? :) 谢谢!
  • 如果您需要对齐多列:def dot_aligned(seq): snums = [str(n) for n in seq] nums_before_dot = [len(s.split('.', 1)[0]) for s in snums] m = max(nums_before_dot) left_pad = [' '*(m - d) + s for s, d in zip(snums, nums_before_dot)] ms = len(max(left_pad, key=len)) return [s + " "*(ms - len(s)) for s in left_pad]
  • @masher 好主意,但我们可以使用str.ljust 方法。看我的更新。顺便说一句,不建议在 cmets 中发布多行代码,因为它很难阅读。这尤其适用于 Python 代码,因为缩进通常会搞砸(但我必须承认你在缩进方面做得很好)。
  • 不用担心。我正在玩一堆游戏,我在点号之前和之后都有,这很混乱。
【解决方案3】:

如果您不介意尾随零,那么简单的方法是:

numbers = [4.8,49.723,456.781,-72.18,5,13]
for f in numbers:
    print('{:>7.3f}'.format(f))

哪些打印:

   4.800
  49.723
 456.781
 -72.180
   5.000
  13.000

如果您想去除尾随零,您可以做的一件事是使用正则表达式模块中的 re.sub 方法:

import re

numbers = [4.8,49.723,456.781,-72.18,5,13]
for f in numbers:
    print(re.sub(r'\.?0+$','','{:>7.3f}'.format(f)))

哪些打印:

  4.8
 49.723
456.781
-72.18
  5
 13

但是,这将为您提供不同宽度的列。唯一的区别是空格,所以你看不到它,但如果你把它作为表格的一部分,它看起来像这样:

import re

numbers = [4.8,49.723,456.781,-72.18,5,13]
for f in numbers:
    print(re.sub(r'\.?0+$','','{:>7.3f}'.format(f)),'|')

打印:

  4.8 |
 49.723 |
456.781 |
-72.18 |
  5 |
 13 |

为了避免这种情况,如果你想真的喜欢你可以这样做:

import re

numbers = [4.8,49.723,456.781,-72.18,5,13]
for f in numbers:
    print(re.sub(r'\.?0+$',lambda match: ' '*(match.end()-match.start()),'{:>7.3f}'.format(f)),'|')

哪些打印:

  4.8   |
 49.723 |
456.781 |
-72.18  |
  5     |
 13     |

希望这会有所帮助!

【讨论】:

  • 你必须用数字表示 n(除非你的意思是 fumbers ;-)
【解决方案4】:

这就是我的做法!

def alignDots(number):
    try:
        whole, dec = str(number).split('.')
        numWholeSpaces = 5 - len(whole) # Where 5 is number of spaces you want to theleft
        numDecSpaces   = 3 - len(dec)   # 3 is number of spaces to the right of the dot
        thousands = ' '* Math.abs(numWholeSpaces) + whole
        decimals  = dec + '0'*Math.abs(numDecSpaces)
        print thousands + '.' + decimals  
        return thousands + '.' + decimals  
    except:
        print "Failed to align dots of ",number
        return ' '*5+'ERROR'

我喜欢其他解决方案,但需要一些具体的东西,并想为什么不分享!

【讨论】:

  • 这种方法的好处是不必提前知道所有的数字,只要知道会遇到的数字范围即可。
【解决方案5】:

使用 Python 文档中的配方:https://docs.python.org/2/library/decimal.html#recipes

from decimal import Decimal


def moneyfmt(value, places=3, curr='', sep=',', dp='.',
             pos='', neg='-', trailneg=''):
    [...]

numbers = [4.8, 49.723, 456.781, -72.18]
for x in numbers:
    value = moneyfmt(Decimal(x), places=2, pos=" ")
    print("{0:>10s}".format(value))

您将获得:

  4.800
 49.723
456.781
-72.180

【讨论】:

【解决方案6】:

修正小数位

import decimal

numbers = [4.8, 49.723, 456.781, 50, -72.18, 12345.12345, 5000000000000]

dp = abs(min([decimal.Decimal(str(number)).as_tuple().exponent for number in numbers]))
width = max([len(str(int(number))) for number in numbers]) + dp + 1 #including .

for number in numbers:
    number = ("{:"+str(width)+"."+str(dp)+"f}").format(number)
    print number.rstrip('0').rstrip('.') if '.' in number else number

根据要求更正以考虑宽度:

numbers = [4.8, 49.723, 456.781, 50, -72.18]
width = max([len(str(number)) for number in numbers]) + 1
for number in numbers:
    number = ("{:"+str(width)+".4f}").format(number)
    print number.rstrip('0').rstrip('.') if '.' in number else number

编辑:如果你想包含整数

numbers = [4.8, 49.723, 456.781, 50, -72.18]

for number in numbers:
    number = "{:10.4f}".format(number)
    print number.rstrip('0').rstrip('.') if '.' in number else number

numbers = [4.8, 49.723, 456.781, -72.18]

for number in numbers:
    print "{:10.4f}".format(number).rstrip('0')

【讨论】:

  • 当您不关心左侧偏移时,这是一个出色的轻量级解决方案。
  • @coding4fun 我已经编辑了代码以考虑左偏移。
  • 您的最新版本中存在错误。试试numbers = [4.8, 49.723, 456.781, 50, -72.18, 12345.12345]。谢谢。
  • @coding4fun 好的,我想我已经修好了。
【解决方案7】:

如果您之前知道所需的前导空格和小数位数的数量,就像在其他响应中一样,那么简单的方法就是

    # python 2 version
    numbers = [4.8, 49.723, 456.781, -72.18, 5, 13, 0.1, .6666, 50000, -40000]

    for number in numbers:
        print '{:16.4f}'.format(number).rstrip('0').rstrip('.')
    # python 3 version
    numbers = [4.8, 49.723, 456.781, -72.18, 5, 13, 0.1, .6666, 50000, -40000]

    for number in numbers:
        print f'{number:16.4f}'.rstrip('0').rstrip('.')

输出:

          4.8
         49.723
        456.781
        -72.18
          5
         13
          0.1
          0.6666
      50000
     -40000

作为PM 2Ring's answer 的替代方案,要动态计算点列的正确位置,您可以使用以下解决方案之一:

# python 3, f-string and .format() mixed version

numbers = [4.8, 49.723, 456.781, -72.18, 5, 13, 0.1, .6666, 50000, -40000]
numbers2string = [str(X) for X in numbers]
numbers_splitted = [X.split(".") for X in numbers2string]
len_max_before = max([len(X[0]) for X in numbers_splitted])
len_max_after = max([len(X[1]) for X in numbers_splitted if len(X) > 1])
len_max_total = len_max_before + len_max_after + 1

for n in numbers:
    numstring = f'{"{0: >#0"}{len_max_total}.{len_max_after}f{"}"}'
    print(numstring.format(n).rstrip('0').rstrip('.'))
# python 3, .format() version

numbers = [4.8, 49.723, 456.781, -72.18, 5, 13, 0.1, .6666, 50000, -40000]
numbers2string = [str(X) for X in numbers]
numbers_splitted = [X.split(".") for X in numbers2string]
len_max_before = max([len(X[0]) for X in numbers_splitted])
len_max_after = max([len(X[1]) for X in numbers_splitted if len(X) > 1])

for number in numbers2string:
    if '.' in number:
        number = number.split('.')
        print("{number[0]:>{len_max_before}}.{number[1]:<{len_max_before}}".format(
            number=number,
            len_max_before=len_max_before,
            len_max_after=len_max_after
        ))
    else:
        print("{number:>{len_max_before}}".format(
            number=number,
            len_max_before=len_max_before
        ))
# python 2 version

numbers = [4.8, 49.723, 456.781, -72.18, 5, 13, 0.1, .6666, 50000, -40000]
numbers2string = [str(X) for X in numbers]
numbers_splitted = [X.split(".") for X in numbers2string]
len_max_before = max([len(X[0]) for X in numbers_splitted])
len_max_after = max([len(X[1]) for X in numbers_splitted if len(X) > 1])

for number in numbers2string:
    if '.' in number:
        number = number.split('.')
        print "{number[0]:>{len_max_before}}.{number[1]:<{len_max_before}}".format(
            number=number,
            len_max_before=len_max_before,
            len_max_after=len_max_after
        )
    else:
        print "{number:>{len_max_before}}".format(
            number=number,
            len_max_before=len_max_before
        )
# python 3 f-string version

numbers = [4.8, 49.723, 456.781, -72.18, 5, 13, 0.1, .6666, 50000, -40000]
numbers2string = [str(X) for X in numbers]
numbers_splitted = [X.split(".") for X in numbers2string]
len_max_before = max([len(X[0]) for X in numbers_splitted])
len_max_after = max([len(X[1]) for X in numbers_splitted if len(X) > 1])

for number in numbers2string:
    if '.' in number:
        number = number.split('.')
        numstring = f"{number[0]:>{len_max_before}}.{number[1]:<{len_max_after}}"
    else:
        numstring = f"{number:>{len_max_before}}"
    print(numstring)

输出:

     4.8   
    49.723 
   456.781 
   -72.18  
     5
    13
     0.1   
     0.6666
 50000
-40000

【讨论】:

    【解决方案8】:

    我已经很晚了,但是您也可以使用数学,特别是对数的属性,来计算将所有数字填充到正确的小数位所需的空格量。

    from math import log10
    
    nums = [4.8, 49.723, 456.781, -72.18, 5, 13]
    
    def pre_spaces(nums):
        absmax = max([abs(max(nums)), abs(min(nums))])
        max_predot = int(log10(absmax))
        spaces = [' '*(max_predot-int(log10(abs(num))) - (1 if num<0 else 0)) for num in nums]
        return spaces
    
    for s,n in zip(pre_spaces(nums), nums):
        print('{}{}'.format(s,n))
    

    结果是:

      4.8
     49.723
    456.781
    -72.18
      5
     13
    

    【讨论】:

      【解决方案9】:

      您可以使用 Python [decimal][1] 类型。

      有一个recipe 用于格式化货币值。

      为什么decimal 类型:避免舍入问题,正确处理有效数字...

      【讨论】:

      • 你能举一个与这个问题相关的例子吗?我不明白如何使用小数类型正确格式化与小数点对齐的数字列表。
      • 我知道有必要避免前导零。无论如何,我认为您应该避免多个答案,如here 所述。
      【解决方案10】:

      是的,有很多直接的方法可以将浮点数与小数点对齐。下面给出的 2 行代码是一个示例

      `在[20]中:浮动=([.022,12.645,544.5645,.54646,-554.56,-.2215,-546.5446])

      在 [21] 中:对于浮动中的 xxx:打印 "{0: 10.4f}".format(xxx) `

      这里的 {0: 10.4f} 0 是每个浮点条目的维度。冒号后的空格是可选的减号。 10 表示小数点前的位数,4 表示小数点后的位数。 这是我的 JPG result

      【讨论】:

        【解决方案11】:

        大多数答案首先创建一个字符串,然后将其拆分为十进制以进行进一步的操作。也可以使用divmod 将数字拆分为整数部分和小数部分。解决方案可能如下所示:

        for x in [4.8, 49.723, 456.781, -72.18,  5, 13]:
        
            print('[{:s}{}{:4s}]'.format('{:6.0f}'.format(x//1),
                                         '.' if x%1 else ' ',
                                         '{:0.4f}'.format(x%1).strip('.0')))
        
        

        产生:

        [     4.8   ]
        [    49.723 ]
        [   456.781 ]
        [   -73.82  ]
        [     5     ]
        [    13     ]
        

        如果您可以容忍尾随小数,则可以通过将 if/else 部分放在中间来进一步简化。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-07-03
          • 2018-07-06
          • 1970-01-01
          • 1970-01-01
          • 2017-05-21
          • 2012-08-29
          • 1970-01-01
          相关资源
          最近更新 更多