web-dev-qa-db-fra.com

Vérifier si une chaîne peut être convertie en float en Python

J'ai du code Python qui parcourt une liste de chaînes et les convertit en nombres entiers ou en nombres à virgule flottante si possible. Faire cela pour les entiers est assez facile

if element.isdigit():
  newelement = int(element)

Les nombres en virgule flottante sont plus difficiles. En ce moment, j'utilise partition('.') pour scinder la chaîne et vérifie que les deux côtés sont des chiffres.

partition = element.partition('.')
if (partition[0].isdigit() and partition[1] == '.' and partition[2].isdigit()) 
    or (partition[0] == '' and partition[1] == '.' and partition[2].isdigit()) 
    or (partition[0].isdigit() and partition[1] == '.' and partition[2] == ''):
  newelement = float(element)

Cela fonctionne, mais évidemment, la déclaration if est un peu un ours. L'autre solution que j'ai envisagée consiste à envelopper la conversion dans un bloc try/catch et à voir si elle réussit, comme décrit dans cette question .

Quelqu'un a d'autres idées? Opinions sur les mérites relatifs de la partition et des approches try/catch?

128
Chris Upchurch

Je voudrais juste utiliser ..

try:
    float(element)
except ValueError:
    print "Not a float"

..c'est simple, et ça marche

Une autre option serait une expression régulière:

import re
if re.match("^\d+?\.\d+?$", element) is None:
    print "Not float"
220
dbr

Méthode Python pour vérifier si float:

def isfloat(value):
  try:
    float(value)
    return True
  except ValueError:
    return False

Ne soyez pas mordus par les gobelins cachés dans le bateau flottant! FAITES DES TESTS UNITAIRES!

Ce qui est et n’est pas un char peut vous surprendre:

Command to parse                        Is it a float?  Comment
--------------------------------------  --------------- ------------
print(isfloat(""))                      False
print(isfloat("1234567"))               True 
print(isfloat("NaN"))                   True            nan is also float
print(isfloat("NaNananana BATMAN"))     False
print(isfloat("123.456"))               True
print(isfloat("123.E4"))                True
print(isfloat(".1"))                    True
print(isfloat("1,234"))                 False
print(isfloat("NULL"))                  False           case insensitive
print(isfloat(",1"))                    False           
print(isfloat("123.EE4"))               False           
print(isfloat("6.523537535629999e-07")) True
print(isfloat("6e777777"))              True            This is same as Inf
print(isfloat("-iNF"))                  True
print(isfloat("1.797693e+308"))         True
print(isfloat("infinity"))              True
print(isfloat("infinity and BEYOND"))   False
print(isfloat("12.34.56"))              False           Two dots not allowed.
print(isfloat("#56"))                   False
print(isfloat("56%"))                   False
print(isfloat("0E0"))                   True
print(isfloat("x86E0"))                 False
print(isfloat("86-5"))                  False
print(isfloat("True"))                  False           Boolean is not a float.   
print(isfloat(True))                    True            Boolean is a float
print(isfloat("+1e1^5"))                False
print(isfloat("+1e1"))                  True
print(isfloat("+1e1.3"))                False
print(isfloat("+1.3P1"))                False
print(isfloat("-+1"))                   False
print(isfloat("(1)"))                   False           brackets not interpreted
140
Eric Leschinski
'1.43'.replace('.','',1).isdigit()

qui retournera true seulement s'il y en a un ou pas '.' dans la chaîne de chiffres .

'1.4.3'.replace('.','',1).isdigit()

retournera false

'1.ww'.replace('.','',1).isdigit()

retournera false

11
TulasiReddy

Si vous vous souciez de la performance (et je ne vous le suggère pas), l’approche basée sur l’essai est clairement gagnante (comparée à votre approche basée sur les partitions ou à l’approche regexp), tant que vous ne vous attendez pas à beaucoup de choses. chaînes non valides, auquel cas il est potentiellement plus lent (probablement en raison du coût de la gestion des exceptions).

Encore une fois, je ne dis pas que vous vous souciez de la performance, mais simplement de vous fournir les données au cas où vous le feriez 10 milliards de fois par seconde, ou quelque chose du genre. En outre, le code basé sur la partition ne gère pas au moins une chaîne valide.

 $, ...
 try sad: 12.1525540352 
 try happy: 1.44165301323 
.
 ========================= ==========================================. FAIL: test_partition (__main __. ConvertTests) 
---------------------------------------------- ----------------------------
 Traceback (dernier appel passé): 
 Fichier "./floatstr.py", ligne 48, dans test_partition 
 self.failUnless (is_float_partition ("20e2")) 
 AssertionError 

----------------------------- -----------------------------------------
 A couru 8 tests en 33.670s 

 FAILED (échecs = 1) 

Voici le code (Python 2.6, expression rationnelle extraite de/Giunzen's answer ):

def is_float_try(str):
    try:
        float(str)
        return True
    except ValueError:
        return False

import re
_float_regexp = re.compile(r"^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+\b)(?:[eE][-+]?[0-9]+\b)?$")
def is_float_re(str):
    return re.match(_float_regexp, str)


def is_float_partition(element):
    partition=element.partition('.')
    if (partition[0].isdigit() and partition[1]=='.' and partition[2].isdigit()) or (partition[0]=='' and partition[1]=='.' and pa\
rtition[2].isdigit()) or (partition[0].isdigit() and partition[1]=='.' and partition[2]==''):
        return True

if __== '__main__':
    import unittest
    import timeit

    class ConvertTests(unittest.TestCase):
        def test_re(self):
            self.failUnless(is_float_re("20e2"))

        def test_try(self):
            self.failUnless(is_float_try("20e2"))

        def test_re_perf(self):
            print
            print 're sad:', timeit.Timer('floatstr.is_float_re("12.2x")', "import floatstr").timeit()
            print 're happy:', timeit.Timer('floatstr.is_float_re("12.2")', "import floatstr").timeit()

        def test_try_perf(self):
            print
            print 'try sad:', timeit.Timer('floatstr.is_float_try("12.2x")', "import floatstr").timeit()
            print 'try happy:', timeit.Timer('floatstr.is_float_try("12.2")', "import floatstr").timeit()

        def test_partition_perf(self):
            print
            print 'partition sad:', timeit.Timer('floatstr.is_float_partition("12.2x")', "import floatstr").timeit()
            print 'partition happy:', timeit.Timer('floatstr.is_float_partition("12.2")', "import floatstr").timeit()

        def test_partition(self):
            self.failUnless(is_float_partition("20e2"))

        def test_partition2(self):
            self.failUnless(is_float_partition(".2"))

        def test_partition3(self):
            self.failIf(is_float_partition("1234x.2"))

    unittest.main()
5
Jacob Gabrielson

TL; DR:

  • Si votre entrée est principalement composée de chaînes que peut être converties en floats, la méthode try: except: est la meilleure méthode Python native.
  • Si votre entrée est principalement composée de chaînes que ne peut pas être converties en floats, les expressions régulières ou la méthode de partition seront meilleures.
  • Si vous êtes 1) incertain de votre saisie ou si vous avez besoin de plus de vitesse et 2) que cela ne vous gêne pas et que vous pouvez installer une extension C tierce, fastnumbers fonctionne très bien.

Une autre méthode est disponible via un module tiers appelé fastnumbers (divulgation, je suis l’auteur); il fournit une fonction appelée isfloat . J'ai pris l'exemple le plus incertain décrit par Jacob Gabrielson dans cette réponse , mais en ajoutant la méthode fastnumbers.isfloat. Je devrais également noter que l'exemple de Jacob ne rend pas justice à l'option regex, car la plupart du temps, cet exemple est consacré aux recherches globales à cause de l'opérateur point ... J'ai modifié cette fonction pour permettre une comparaison plus juste avec try: except:


def is_float_try(str):
    try:
        float(str)
        return True
    except ValueError:
        return False

import re
_float_regexp = re.compile(r"^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+\b)(?:[eE][-+]?[0-9]+\b)?$").match
def is_float_re(str):
    return True if _float_regexp(str) else False

def is_float_partition(element):
    partition=element.partition('.')
    if (partition[0].isdigit() and partition[1]=='.' and partition[2].isdigit()) or (partition[0]=='' and partition[1]=='.' and partition[2].isdigit()) or (partition[0].isdigit() and partition[1]=='.' and partition[2]==''):
        return True
    else:
        return False

from fastnumbers import isfloat


if __== '__main__':
    import unittest
    import timeit

    class ConvertTests(unittest.TestCase):

        def test_re_perf(self):
            print
            print 're sad:', timeit.Timer('ttest.is_float_re("12.2x")', "import ttest").timeit()
            print 're happy:', timeit.Timer('ttest.is_float_re("12.2")', "import ttest").timeit()

        def test_try_perf(self):
            print
            print 'try sad:', timeit.Timer('ttest.is_float_try("12.2x")', "import ttest").timeit()
            print 'try happy:', timeit.Timer('ttest.is_float_try("12.2")', "import ttest").timeit()

        def test_fn_perf(self):
            print
            print 'fn sad:', timeit.Timer('ttest.isfloat("12.2x")', "import ttest").timeit()
            print 'fn happy:', timeit.Timer('ttest.isfloat("12.2")', "import ttest").timeit()


        def test_part_perf(self):
            print
            print 'part sad:', timeit.Timer('ttest.is_float_partition("12.2x")', "import ttest").timeit()
            print 'part happy:', timeit.Timer('ttest.is_float_partition("12.2")', "import ttest").timeit()

    unittest.main()

Sur ma machine, la sortie est:

fn sad: 0.220988988876
fn happy: 0.212214946747
.
part sad: 1.2219619751
part happy: 0.754667043686
.
re sad: 1.50515985489
re happy: 1.01107215881
.
try sad: 2.40243887901
try happy: 0.425730228424
.
----------------------------------------------------------------------
Ran 4 tests in 7.761s

OK

Comme vous pouvez le constater, l’expression rationnelle n’est en réalité pas aussi mauvaise qu’elle le semblait à l’origine, et si vous avez un réel besoin de rapidité, la méthode fastnumbers est assez bonne.

5
SethMMorton

Juste pour la variété, voici une autre méthode pour le faire.

>>> all([i.isnumeric() for i in '1.2'.split('.',1)])
True
>>> all([i.isnumeric() for i in '2'.split('.',1)])
True
>>> all([i.isnumeric() for i in '2.f'.split('.',1)])
False

Edit: Je suis sûr que cela ne tiendra pas à tous les cas de float, surtout s'il y a un exposant. Pour résoudre cela, il ressemble à ceci. Cela retournera True uniquement val est un float et False pour int mais est probablement moins performant que regex. 

>>> def isfloat(val):
...     return all([ [any([i.isnumeric(), i in ['.','e']]) for i in val],  len(val.split('.')) == 2] )
...
>>> isfloat('1')
False
>>> isfloat('1.2')
True
>>> isfloat('1.2e3')
True
>>> isfloat('12e3')
False
4
Peter Moore

Si vous n'avez pas à vous soucier d'expressions de chiffres scientifiques ou autres et que vous travaillez uniquement avec des chaînes pouvant être des nombres avec ou sans point:

Une fonction

def is_float(s):
    result = False
    if s.count(".") == 1:
        if s.replace(".", "").isdigit():
            result = True
    return result

Version Lambda

is_float = lambda x: x.replace('.','',1).isdigit() and "." in x

Exemple

if is_float(some_string):
    some_string = float(some_string)
Elif some_string.isdigit():
    some_string = int(some_string)
else:
    print "Does not convert to int or float."

De cette façon, vous ne convertissez pas accidentellement ce qui devrait être un int, un float.

2
kodetojoy

Cette expression régulière recherchera des nombres à virgule flottante scientifiques:

^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+\b)(?:[eE][-+]?[0-9]+\b)?$

Cependant, je crois que votre meilleur pari est d’utiliser l’analyseur lors d’un essai.

2
John Gietzen

Essayez de convertir pour flotter. En cas d'erreur, imprimez l'exception ValueError.

try:
    x = float('1.23')
    print('val=',x)
    y = float('abc')
    print('val=',y)
except ValueError as err:
    print('floatErr;',err)

Sortie:

val= 1.23
floatErr: could not convert string to float: 'abc'
1
edW

J'ai utilisé la fonction déjà mentionnée, mais j'ai vite remarqué que les chaînes telles que "Nan", "Inf" et leurs variations sont considérées comme des nombres. Je vous propose donc une version améliorée de la fonction, qui renverra false sur ce type d'entrée et n'échouera pas aux variantes "1e3":

def is_float(text):
    # check for nan/infinity etc.
    if text.isalpha():
        return False
    try:
        float(text)
        return True
    except ValueError:
        return False
1
mathfac

Je cherchais du code similaire, mais il semble que l'utilisation de try/excepts soit la meilleure solution… .. Voici le code que j'utilise. Il inclut une fonction de nouvelle tentative si l'entrée n'est pas valide. Je devais vérifier si l’entrée était supérieure à 0 et, le cas échéant, la convertir en float.

def cleanInput(question,retry=False): 
    inputValue = input("\n\nOnly positive numbers can be entered, please re-enter the value.\n\n{}".format(question)) if retry else input(question)
    try:
        if float(inputValue) <= 0 : raise ValueError()
        else : return(float(inputValue))
    except ValueError : return(cleanInput(question,retry=True))


willbefloat = cleanInput("Give me the number: ")
0
Lockey
str(strval).isdigit()

semble être simple. 

Traite les valeurs stockées sous forme de chaîne, int ou float

0
muks

Version simplifiée de la fonction is_digit(str), qui suffit dans la plupart des cas (ne considère pas la notation exponentielle et la valeur "NaN"):

def is_digit(str):
    return str.lstrip('-').replace('.', '').isdigit()
0
simhumileco