76

What is an easy way in Python to format integers into strings representing thousands with K, and millions with M, and leaving just couple digits after comma?

I'd like to show 7436313 as 7.44M, and 2345 as 2,34K.

Is there some % string formatting operator available for that? Or that could be done only by actually dividing by 1000 in a loop and constructing result string step by step?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
user63503
  • 6,243
  • 14
  • 41
  • 44

14 Answers14

108

This version does not suffer from the bug in the previous answers where 999,999 gives you 1000.0K. It also only allows 3 significant figures and eliminates trailing 0's.

def human_format(num):
    num = float('{:.3g}'.format(num))
    magnitude = 0
    while abs(num) >= 1000:
        magnitude += 1
        num /= 1000.0
    return '{}{}'.format('{:f}'.format(num).rstrip('0').rstrip('.'), ['', 'K', 'M', 'B', 'T'][magnitude])

The output looks like:

>>> human_format(999999)
'1M'
>>> human_format(999499)
'999K'
>>> human_format(9994)
'9.99K'
>>> human_format(9900)
'9.9K'
>>> human_format(6543165413)
'6.54B'
rtaft
  • 2,139
  • 1
  • 16
  • 32
97

I don't think there's a built-in function that does that. You'll have to roll your own, e.g.:

def human_format(num):
    magnitude = 0
    while abs(num) >= 1000:
        magnitude += 1
        num /= 1000.0
    # add more suffixes if you need them
    return '%.2f%s' % (num, ['', 'K', 'M', 'G', 'T', 'P'][magnitude])

print('the answer is %s' % human_format(7436313))  # prints 'the answer is 7.44M'
Community
  • 1
  • 1
Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
18

A more "math-y" solution is to use math.log:

from math import log, floor


def human_format(number):
    units = ['', 'K', 'M', 'G', 'T', 'P']
    k = 1000.0
    magnitude = int(floor(log(number, k)))
    return '%.2f%s' % (number / k**magnitude, units[magnitude])

Tests:

>>> human_format(123456)
'123.46K'
>>> human_format(123456789)
'123.46M'
>>> human_format(1234567890)
'1.23G'
manugrandio
  • 181
  • 1
  • 2
  • 2
    999999 displays 1000.00K when it should say 1M. – rtaft Aug 21 '17 at 19:51
  • Best approach in my opinion because it doesn't require a loop – Addison Klinke Jan 29 '20 at 20:43
  • If you'd like to support negative values as well add abs to the magnitude calculation: `magnitude = int(floor(log(abs(number), k)))` – benlev Aug 02 '21 at 11:29
  • In addition to negative value support, handling numbers between 0 and 1 need to be handled: `magnitude = int(math.floor(math.log(max(abs(number), 1), k)))` – benlev Aug 19 '21 at 18:59
  • @AddisonKlinke is it really make a performance diferent? How is the log function isc alculated? If it does I may use it to improve my answer below – m-schwob Apr 11 '22 at 20:21
10

Variable precision and no 999999 bug:

def human_format(num, round_to=2):
    magnitude = 0
    while abs(num) >= 1000:
        magnitude += 1
        num = round(num / 1000.0, round_to)
    return '{:.{}f}{}'.format(num, round_to, ['', 'K', 'M', 'G', 'T', 'P'][magnitude])
michauwilliam
  • 814
  • 1
  • 11
  • 22
  • 1
    In the format string on the last line, I think you can replace `round(num, round_to)` with just `num` since it's already been rounded by that point. – tdy Apr 16 '21 at 18:25
6

I needed this function today, refreshed the accepted answer a bit for people with Python >= 3.6:

def human_format(num, precision=2, suffixes=['', 'K', 'M', 'G', 'T', 'P']):
    m = sum([abs(num/1000.0**x) >= 1 for x in range(1, len(suffixes))])
    return f'{num/1000.0**m:.{precision}f}{suffixes[m]}'

print('the answer is %s' % human_format(7454538))  # prints 'the answer is 7.45M'

Edit: given the comments, you might want to change to round(num/1000.0)

Roelant
  • 4,508
  • 1
  • 32
  • 62
5

Numerize library is good.

from numerize import numerize
a = numerize.numerize(1000)
print(a)
1k

Thanks @tdy for pointing this,

a = numerize.numerize(999999) 
print(a)  # 1000K 
1000K
John Prawyn
  • 1,423
  • 3
  • 19
  • 28
2

I was kind of confused by some of the stuff that other people showed, so I made the below code. It rounds to the second decimal point, ex. '23.56 Billion', but you can change what decimal place it rounds to by replacing the two '100.0's in the last line with a larger or smaller number, ex. '10.0' rounds to one decimal point and '1000.0' rounds to three decimal points. Also, using this code, it always rounds down from what it actually is. You can change this if you like, by replacing 'floor' with 'ceil' or 'round'.

#make the dictionary to store what to put after the result (ex. 'Billion'). You can go further with this then I did, or to wherever you wish. 
#import the desired rounding mechanism. You will not need to do this for round. 
from math import floor
magnitudeDict={0:'', 1:'Thousand', 2:'Million', 3:'Billion', 4:'Trillion', 5:'Quadrillion', 6:'Quintillion', 7:'Sextillion', 8:'Septillion', 9:'Octillion', 10:'Nonillion', 11:'Decillion'}
def simplify(num):
    num=floor(num)
    magnitude=0
    while num>=1000.0:
        magnitude+=1
        num=num/1000.0
    return(f'{floor(num*100.0)/100.0} {magnitudeDict[magnitude]}')

The 'f' before the string in the last line is to let python know you are formatting it. The result from running print(simplify(34867123012.13)) is this:

34.86 Billion

Please let me know if you have questions! Thanks, Angus

  • As is, 999999 displays `999.99 Thousand` when it should say `1 Million`. Also this can only be used on Python 3.6+ – rtaft Jun 02 '21 at 10:54
  • 1
    I know. It was made that way. For the kind of games I have used this for, I wanted it this way so that you always actually had what it said, not one less, as to avoid confusion when you want to buy something that says the same as what your funds say but you can't because the actual number is one lower. You can fix that by replacing floor() with round(). – Angus The Car Jun 09 '21 at 02:29
1

I had the same need. And if anyone comes on this topic, I found a lib to do so: https://github.com/azaitsev/millify

Hope it helps :)

Jordi Riera
  • 101
  • 1
  • 5
0

No String Formatting Operator, according to the docs. I've never heard of such a thing, so you may have to roll your own, as you suggest.

Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
0

I don't think there are format operators for that, but you can simply divide by 1000 until the result is between 1 and 999 and then use a format string for 2 digits after comma. Unit is a single character (or perhaps a small string) in most cases, which you can store in a string or array and iterate through it after each divide.

schnaader
  • 49,103
  • 10
  • 104
  • 136
0

I don't know of any built-in capability like this, but here are a couple of list threads that may help:

http://coding.derkeiler.com/Archive/Python/comp.lang.python/2005-09/msg03327.html http://mail.python.org/pipermail/python-list/2008-August/503417.html

zweiterlinde
  • 14,557
  • 2
  • 27
  • 32
0

Based on the comments here, I made an improved code for that. It is a little bit longer but gives solutions for more cases including small numbers (m,u,n,p).

Hope it will be helpful for someone

# print number in a readable format.
# default is up to 3 decimal digits and can be changed
# works on numbers in the range of 1e-15 to 1e 1e15 include negatives numbers
# can force the number to a specific magnitude unit
def human_format(num:float, force=None, ndigits=3):
    perfixes = ('p', 'n', 'u', 'm', '', 'K', 'M', 'G', 'T')
    one_index = perfixes.index('')
    if force:
        if force in perfixes:
            index = perfixes.index(force)
            magnitude = 3*(index - one_index)
            num = num/(10**magnitude)
        else:
            raise ValueError('force value not supported.')
    else:
        div_sum = 0
        if(abs(num) >= 1000):
            while abs(num) >= 1000:
                div_sum += 1
                num /= 1000
        else:
            while abs(num) <= 1:
                div_sum -= 1
                num *= 1000
        temp = round(num, ndigits) if ndigits else num
        if temp < 1000:
            num = temp 
        else:
            num = 1
            div_sum += 1
        index = one_index + div_sum
    return str(num).rstrip('0').rstrip('.') + perfixes[index]

tests from here and some more

# some tests
print(human_format(999)              ,' = '         , '999') 
print(human_format(999.999)          ,' = '         , '999.999') 
print(human_format(999.9999)         ,' = '         , '1K')  
print(human_format(999999)           ,' = '         , '999.999K')   
print(human_format(999499)           ,' = '         , '999.499K')   
print(human_format(9994)             ,' = '         , '9.994K')   
print(human_format(9900)             ,' = '         , '9.9K')   
print(human_format(6543165413)       ,' = '         , '6.543G')  
print(human_format(46780.9)          ,' = '         , '46.781K')  
print(human_format(0.001)            ,' = '         , '1m')   
print(human_format(0.000000999999)   ,' = '         , '999.999n')  
print(human_format(1.00394200)       ,' = '         , '1.004')   
print(human_format(0.0999)           ,' = '         , '99.9m')  
print(human_format(0.00000000999999) ,' = '         , '10n') 
print(human_format(0.0000000099995)  ,' = '         , '9.999n')  
print(human_format(0.000000009999)   ,' = '         , '9.999n') 
print(human_format(999999            ,ndigits=2)    ,' = '           , '1M')   
print(human_format(9994              ,force='')     ,' = '           , '9994K')   
print(human_format(6543165413        ,ndigits=5)    ,' = '           , '6.54317G')  
print(human_format(6543165413        ,ndigits=None) ,' = '           , '6.543165413G')  
print(human_format(7436313           ,ndigits=2)    ,' = '           , '7.44M')   
print(human_format(2344              ,ndigits=2)    ,' = '           , '2.34K')
print(human_format(34867123012.13    ,ndigits=2)    ,' = '           , '34.87G')   
m-schwob
  • 81
  • 1
  • 6
  • How is 9994 == 9994K? Do you think 999.999K is cleaner looking than 999999? Also, ndigits should be called ndecimals, since that is how you are using it. You also have `if force:` which will evaluate as false when using `''`. – rtaft May 04 '22 at 14:34
0
def human_format(value):
   num = value
   magnitude = 0
   while abs(num) >= 1000:
      magnitude += 1
      num /= 1000.0
   result = round(value / (1000**magnitude),3)
   return '{}{}'.format(result, ['', 'K', 'M', 'B', 'T'][magnitude])
  • 1
    You should add some explanation about how the code you have posted is different from (and/or better than) that in several other answers. To me, it looks *very* similar. – Adrian Mole Aug 25 '22 at 12:51
0

I know this is an old question but I'd like to add this one-liner:

>>> human_format = lambda num: [f'{num/10**(3*i):.0f}{k}' for i, k in enumerate(' kMGTPEZY') if num >= 10**(3*i) * 0.9995][-1]
>>> human_format(10_000)
'10k'
>>> human_format(12_345_678)
'12M'
>>> human_format(999_999)
'1M'
M.G.Poirot
  • 1,066
  • 2
  • 11
  • 22