格式化数字,使它们在小数点对齐
Formatting Numbers So They Align On Decimal Point
在 Python 中,我需要格式化数字以便它们在小数点上对齐,如下所示:
4.8
49.723
456.781
-72.18
5
13
有没有直接的方法来做到这一点?
我认为没有直接的方法,因为在开始打印之前您需要知道所有数字中小数点的位置. (我刚刚查看了 Caramiriel 的 link,以及该页面中的一些 link,但我找不到任何特别适用于这种情况的东西)。
看来您必须对列表中的数字进行一些基于字符串的检查和操作。例如,
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
如果你想处理一个 float
的列表,其中混入了一些普通的 int
,那么这种方法会有点混乱。
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 在评论中指出的那样,我们可以通过使用 .split
:
来简化对 int
的处理
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
如果您知道所需的前导空格和小数位数,就像在其他回复中一样,那么简单的方法就是
# 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
作为 的替代方案,要动态计算点列的正确位置,您可以使用以下解决方案之一:
# 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
修正小数位
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')
您可以使用 Python [decimal][1]
类型。
有一个 recipe 用于格式化货币值。
为什么 decimal
输入:避免舍入问题,正确处理有效数字...
使用 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
我来晚了,但你也可以使用数学,特别是对数的性质,计算出将所有数字填充到正确的小数位所需的空格量。
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
是的,有许多直接的方法可以使浮点数与小数点对齐。下面给出的两行代码是一个例子
`在 [20] 中:浮动=([.022,12.645,544.5645,.54646,-554.56,-.2215,-546.5446])
In [21]: for xxx in floating: print "{0: 10.4f}".format(xxx)
`
这里{0:10.4f}中的0是每个浮点数项的维度。在冒号之后 space 是可选的减号。 10 是小数点前的位数,4 是小数点后的位数。
这是我输出的 JPG result
如果您知道所需的精度(小数点后的数字),并且不介意在使用整数时有一些尾随零,则可以在 Python 3.6 (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
我是这样做的!
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'
我喜欢其他解决方案,但需要一些具体的东西,我想为什么不分享!
如果您不介意尾随零,最简单的方法是:
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
但是,这会给您不同宽度的列。唯一的区别是空格,所以你看不到它,但如果你把它作为 table 的一部分来做,它看起来像这样:
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 |
希望对您有所帮助!
大多数答案首先生成一个字符串,然后将其拆分为小数点以进行进一步的操作。也可以使用 div
和 mod
将数字拆分为整数和小数部分。解决方案可能如下所示:
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 部分来进一步简化。
在 Python 中,我需要格式化数字以便它们在小数点上对齐,如下所示:
4.8
49.723
456.781
-72.18
5
13
有没有直接的方法来做到这一点?
我认为没有直接的方法,因为在开始打印之前您需要知道所有数字中小数点的位置. (我刚刚查看了 Caramiriel 的 link,以及该页面中的一些 link,但我找不到任何特别适用于这种情况的东西)。
看来您必须对列表中的数字进行一些基于字符串的检查和操作。例如,
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
如果你想处理一个 float
的列表,其中混入了一些普通的 int
,那么这种方法会有点混乱。
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 在评论中指出的那样,我们可以通过使用 .split
:
int
的处理
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
如果您知道所需的前导空格和小数位数,就像在其他回复中一样,那么简单的方法就是
# 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
作为
# 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
修正小数位
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')
您可以使用 Python [decimal][1]
类型。
有一个 recipe 用于格式化货币值。
为什么 decimal
输入:避免舍入问题,正确处理有效数字...
使用 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
我来晚了,但你也可以使用数学,特别是对数的性质,计算出将所有数字填充到正确的小数位所需的空格量。
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
是的,有许多直接的方法可以使浮点数与小数点对齐。下面给出的两行代码是一个例子
`在 [20] 中:浮动=([.022,12.645,544.5645,.54646,-554.56,-.2215,-546.5446])
In [21]: for xxx in floating: print "{0: 10.4f}".format(xxx) `
这里{0:10.4f}中的0是每个浮点数项的维度。在冒号之后 space 是可选的减号。 10 是小数点前的位数,4 是小数点后的位数。 这是我输出的 JPG result
如果您知道所需的精度(小数点后的数字),并且不介意在使用整数时有一些尾随零,则可以在 Python 3.6 (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
我是这样做的!
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'
我喜欢其他解决方案,但需要一些具体的东西,我想为什么不分享!
如果您不介意尾随零,最简单的方法是:
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
但是,这会给您不同宽度的列。唯一的区别是空格,所以你看不到它,但如果你把它作为 table 的一部分来做,它看起来像这样:
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 |
希望对您有所帮助!
大多数答案首先生成一个字符串,然后将其拆分为小数点以进行进一步的操作。也可以使用 div
和 mod
将数字拆分为整数和小数部分。解决方案可能如下所示:
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 部分来进一步简化。