46

is there a python library that would make numbers such as this more human readable

$187,280,840,422,780

edited: for example iw ant the output of this to be 187 Trillion not just comma separated. So I want output to be trillions, millions, billions etc

kevin
  • 3,967
  • 10
  • 31
  • 33
  • 1
    install clisp and write this: `(format t "~r" (parse-integer (read-line *standard-input*)))` then use subprocess to call `clisp prettynum.cl 187,000,000,000,000`... Though I just asked about an alternative http://stackoverflow.com/questions/3158132/is-there-a-python-version-of-lisps-format-r – Wayne Werner Jul 01 '10 at 13:20
  • [`python-ballpark`](https://github.com/debrouwere/python-ballpark) seems to be designed exactly for what the OP asks for. – 0 _ May 26 '17 at 21:03
  • Check out [millify](https://github.com/azaitsev/millify) – Alex Zaitsev Jan 16 '18 at 11:42
  • 1
    I obtain my desired effect (e.g., 11,232,222 to 11.23 M) using the millify package with one line of code `millify(the_number, precision=2)`. check https://github.com/azaitsev/millify. – YinchaoOnline Apr 05 '21 at 12:56

5 Answers5

88

As I understand it, you only want the 'most significant' part. To do so, use floor(log10(abs(n))) to get number of digits and then go from there. Something like this, maybe:

import math

millnames = ['',' Thousand',' Million',' Billion',' Trillion']

def millify(n):
    n = float(n)
    millidx = max(0,min(len(millnames)-1,
                        int(math.floor(0 if n == 0 else math.log10(abs(n))/3))))

    return '{:.0f}{}'.format(n / 10**(3 * millidx), millnames[millidx])

Running the above function for a bunch of different numbers:

for n in (1.23456789 * 10**r for r in range(-2, 19, 1)):
    print('%20.1f: %20s' % (n,millify(n)))
                 0.0:                    0
                 0.1:                    0
                 1.2:                    1
                12.3:                   12
               123.5:                  123
              1234.6:           1 Thousand
             12345.7:          12 Thousand
            123456.8:         123 Thousand
           1234567.9:            1 Million
          12345678.9:           12 Million
         123456789.0:          123 Million
        1234567890.0:            1 Billion
       12345678900.0:           12 Billion
      123456789000.0:          123 Billion
     1234567890000.0:           1 Trillion
    12345678900000.0:          12 Trillion
   123456789000000.0:         123 Trillion
  1234567890000000.0:        1235 Trillion
 12345678899999998.0:       12346 Trillion
123456788999999984.0:      123457 Trillion
1234567890000000000.0:     1234568 Trillion
Janus
  • 5,199
  • 25
  • 35
  • 4
    I should probably point out that using Billion, Trillion doesn't mean the same in continental Europe as it does in the US. Even the UK didn't adopt the US convention until recently. See http://en.wikipedia.org/wiki/Long_and_short_scales – Janus Jul 06 '10 at 05:26
  • 9
    The result should be expressed using SI unit prefixes, i.e. kilodollars, megadollars, gigadollars etc :) – tzot Jul 30 '10 at 12:35
  • @tzot LOL but this is something I could get used to. – Autodidact Jan 09 '12 at 09:16
  • 1
    nice! fyi the above does not work for 0, but easy to throw in a check for that – gnr Mar 01 '13 at 18:00
  • 3
    You should use "humanize". See https://pypi.org/project/humanize/ – Saeed Zahedian Abroodi Sep 06 '18 at 04:58
51

With Python2.7+ or v3 you just use the "," option in your string formatting - see PEP 378: Format Specifier for Thousands Separator for more info:

>>> "{:,}".format(100000000)
'100,000,000'

With Python3.6+ you can also use the "_" format - see PEP 515 for details:

>>> "{:_}".format(100000000)
'100_000_000'

or you can use a similar syntax with f-strings:

>>> number = 100000000
>>>f"{number:,}"
'100,000,000'

So I don't have to squint while reading big numbers, I have the 2.7 version of the code wrapped up in a shell function:

human_readable_numbers () {
    python2.7 -c "print('{:,}').format($1)"
}

Lastly, if for some reason you must work with code from Python versions 2.6 or earlier, you can solve the problem using the locale standard library module:

import locale
locale.setlocale(locale.LC_ALL, 'en_US')
locale.format('%d', 2**32, grouping=True)   # returns '4,294,967,296'
Lester Cheung
  • 1,710
  • 15
  • 17
  • 2
    What kind of syntax is `'{:,}'.format(x)`? I can try it out and see that it works, but I haven't seen that syntax before, so, how does it work? – HelloGoodbye Jun 16 '18 at 15:59
  • 5
    It's in PEP-0378: "The ',' option indicates that commas should be included in the output as a thousands separator. As with locales which do not use a period as the decimal point, locales which use a different convention for digit separation will need to use the locale module to obtain appropriate formatting." – Lester Cheung Jun 19 '18 at 03:04
  • 1
    Thank you Lester! – HelloGoodbye Jun 20 '18 at 08:58
  • 1
    The use of `,` and `_` for this purpose in format specifiers is also described [in the documentation for the format mini-language](https://docs.python.org/3/library/string.html#format-specification-mini-language). – Karl Knechtel Dec 10 '21 at 01:34
3

That number seems pretty human-readable to me. An unfriendly number would be 187289840422780.00. To add commas, you could create your own function or search for one (I found this):

import re

def comma_me(amount):
    orig = amount
    new = re.sub("^(-?\d+)(\d{3})", '\g<1>,\g<2>', amount)
    if orig == new:
        return new
    else:
        return comma_me(new)

f = 12345678
print comma_me(`f`)
Output: 12,345,678

If you want to round a number to make it more readable, there is a python function for that: round().

You could move even further away from the actual data and say "A very high amount" or "Above 100 trillion" using a function that would return a different value based on your programmed benchmarks.

alecwh
  • 966
  • 2
  • 10
  • 17
1

From here:

def commify(n):
    if n is None: return None
    if type(n) is StringType:
        sepdec = localenv['mon_decimal_point']
    else:
        #if n is python float number we use everytime the dot
        sepdec = '.'
    n = str(n)
    if sepdec in n:
        dollars, cents = n.split(sepdec)
    else:
        dollars, cents = n, None

    r = []
    for i, c in enumerate(reversed(str(dollars))):
        if i and (not (i % 3)):
            r.insert(0, localenv['mon_thousands_sep'])
        r.insert(0, c)
    out = ''.join(r)
    if cents:
        out += localenv['mon_decimal_point'] + cents
    return out
Arkady
  • 13,115
  • 7
  • 38
  • 46
0

If by 'readable' you mean 'words'; here's a good solution that you can adapt.

http://www.andrew-hoyer.com/experiments/numbers

dagoof
  • 1,127
  • 11
  • 14