How do I convert a currency string to a floating point number in Python?

Solution 1:

Try this:

from re import sub
from decimal import Decimal

money = '$6,150,593.22'
value = Decimal(sub(r'[^\d.]', '', money))

This has some advantages since it uses Decimal instead of float (which is better for representing currency) and it also avoids any locale issues by not hard-coding a specific currency symbol.

Solution 2:

If your locale is set properly you can use locale.atof, but you will still need to strip off the '$' manually:

>>> import locale
>>> locale.setlocale(locale.LC_ALL, 'en_US.UTF8')
'en_US.UTF8'
>>> money = "$6,150,593.22"
>>> locale.atof(money.strip("$"))
6150593.2199999997

Solution 3:

For a solution without hardcoding the currency position or symbol:

raw_price = "17,30 €"
import locale
locale.setlocale(locale.LC_ALL, 'fr_FR.UTF8')
conv = locale.localeconv()
raw_numbers = raw_price.strip(conv['currency_symbol'])
amount = locale.atof(raw_numbers)

Solution 4:

I found the babel package very helpful to work around

  • localized parsing
  • and the need to change the locale globally

It makes it easy to parse a number in a localized rendition:

>>> babel.numbers.parse_decimal('1,024.64', locale='en')                                                                                                                           
Decimal('1024.64')
>>> babel.numbers.parse_decimal('1.024,64', locale='de')
Decimal('1024.64')
>>>

You can use babel.numbers.get_currency_symbol('USD') to strip pre/suffixes without hardcoding them.

Hth, dtk

Solution 5:

Expanding to include negative numbers in parentheses:

In [1]: import locale, string

In [2]: from decimal import Decimal

In [3]: n = ['$1,234.56','-$1,234.56','($1,234.56)', '$ -1,234.56']

In [4]: tbl = string.maketrans('(','-')

In [5]: %timeit -n10000 [locale.atof( x.translate(tbl, '$)')) for x in n]
10000 loops, best of 3: 31.9 æs per loop

In [6]: %timeit -n10000 [Decimal( x.translate(tbl, '$,)')) for x in n]
10000 loops, best of 3: 21 æs per loop

In [7]: %timeit -n10000 [float( x.replace('(','-').translate(None, '$,)')) for x in n]
10000 loops, best of 3: 3.49 æs per loop

In [8]: %timeit -n10000 [float( x.translate(tbl, '$,)')) for x in n]
10000 loops, best of 3: 2.19 æs per loop

Note that commas must be stripped from float()/Decimal(). Either replace() or translate() w/ a translation table can be used to convert the opening ( to -, translate is slightly faster. float() is fastest by 10-15x, but lacks precision and could present locale issues. Decimal() has precision and is 50% faster than locale.atof(), but also has locale issues. locale.atof() is the slowest, but most general.

Edit: new str.translate API (characters mapped to None moved from str.translate function to the translation table)

In [1]: import locale, string
        from decimal import Decimal

        locale.setlocale(locale.LC_ALL, '')

        n = ['$1,234.56','-$1,234.56','($1,234.56)', '$ -1,234.56']

In [2]: tbl = str.maketrans('(', '-', '$)')
        %timeit -n10000 [locale.atof( x.translate(tbl)) for x in n]
18 µs ± 296 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [3]: tbl2 = str.maketrans('(', '-', '$,)')
        %timeit -n10000 [Decimal( x.translate(tbl2)) for x in n]
3.77 µs ± 50.8 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [4]: %timeit -n10000 [float( x.translate(tbl2)) for x in n]
3.13 µs ± 66.3 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [5]: tbl3 = str.maketrans('', '', '$,)')
        %timeit -n10000 [float( x.replace('(','-').translate(tbl3)) for x in n]
3.51 µs ± 84.8 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)