J'ai des chaînes représentant des nombres avec un format de devise spécifique, par exemple:
money="$6,150,593.22"
Je veux convertir cette chaîne en nombre
6150593.22
Quel est le meilleur moyen d'y parvenir?
Essaye ça:
from re import sub
from decimal import Decimal
money = '$6,150,593.22'
value = Decimal(sub(r'[^\d.]', '', money))
Cela présente certains avantages, car il utilise Decimal
au lieu de float
(ce qui est mieux pour représenter la devise) et évite également les problèmes de paramètres régionaux en ne codant pas en dur un symbole monétaire spécifique.
Si vos paramètres régionaux sont définis correctement, vous pouvez utiliser locale.atof
, mais vous devrez tout de même supprimer le '$' manuellement:
>>> import locale
>>> locale.setlocale(locale.LC_ALL, 'en_US.UTF8')
'en_US.UTF8'
>>> money = "$6,150,593.22"
>>> locale.atof(money.strip("$"))
6150593.2199999997
Développer pour inclure des nombres négatifs entre parenthèses:
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
Notez que les virgules doivent être supprimées de float ()/Decimal (). Replace () ou translate () avec une table de traduction peut être utilisé pour convertir l’ouverture (to -, la traduction est légèrement plus rapide. Float () est le plus rapide de 10 à 15 fois, mais manque de précision et pourrait poser des problèmes de localisation. ) a une précision et est 50% plus rapide que locale.atof (), mais présente également des problèmes de localisation. locale.atof () est la plus lente, mais la plus générale.
Modifier: nouvelle API str.translate
(les caractères mappés sur None
sont passés de la fonction str.translate
à la table de traduction)
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)
Pour une solution sans codage en dur de la position monétaire ou du symbole:
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'].decode('utf-8'))
amount = locale.atof(raw_numbers)
J'ai trouvé le paquet babel
très utile pour contourner le problème
Il est facile d'analyser un nombre dans un rendu localisé:
>>> babel.numbers.parse_decimal('1,024.64', locale='en')
Decimal('1024.64')
>>> babel.numbers.parse_decimal('1.024,64', locale='de')
Decimal('1024.64')
>>>
Vous pouvez utiliser babel.numbers.get_currency_symbol('USD')
pour supprimer les préfixes/suffixes sans les coder en dur.
Hth, Dtk
J'ai créé cette fonction il y a quelques années pour résoudre le même problème.
def money(number):
number = number.strip('$')
try:
[num,dec]=number.rsplit('.')
dec = int(dec)
aside = str(dec)
x = int('1'+'0'*len(aside))
price = float(dec)/x
num = num.replace(',','')
num = int(num)
price = num + price
except:
price = int(number)
return price