Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Formatting Numbers So They Align On Decimal Point

In Python, I need to format numbers so they align on the decimal point, like so:

  4.8
 49.723
456.781
-72.18
  5
 13

Is there a straighforward way to do this?

like image 656
mcu Avatar asked Sep 27 '15 13:09

mcu


People also ask

What is decimal alignment?

Follow. In tables in Classic documents, when using right justification with indents (to center numbers but still align by decimal), numbers with and without brackets would align with the numbers together, and the right bracket would "float" to the right of the numbers.

Why do we line up decimal points?

In order to keep the numbers in the proper place-value column when adding decimals, align the decimal points. This will keep the numbers aligned; ones to ones, tenths to tenths, hundredths to hundredths, and so on.


2 Answers

If you know the precision (digits after the decimal point) that you need, and you don't mind having some trailing zeros when whole numbers are used, you could use the new f-string in Python 3.6 (PEP498):

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

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

Prints:

  4.8000
 49.7230
456.7810
-72.1800
  5.0000
 13.0000
like image 56
artomason Avatar answered Sep 18 '22 14:09

artomason


I don't think there's a straight-forward way to do it, since you need to know the position of the decimal point in all the numbers before you start printing them. (I just had a look at Caramiriel's link, and some of the links from that page, but I couldn't find anything particularly applicable to this case).

So it looks like you have to do some string-based inspection & manipulation of the numbers in the list. Eg,

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)

output

  4.8
 49.723
456.781
-72.18

If you want to handle a list of floats with some plain ints mixed in, then this approach gets a bit messier.

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)
    

output

  4.8
 49.723
456.781
-72.18
  5
 13

As Mark Ransom notes in the comments, we can simplify handling ints by using .split:

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 mentions in a comment that it can be useful to add padding on the right so that the numbers can be printed in aligned columns. However, we don't need to compute the size of that padding for each string, we can use the str.ljust method.

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

output

  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
like image 43
PM 2Ring Avatar answered Sep 17 '22 14:09

PM 2Ring