Comprobando si una cadena se puede convertir en flotante en Python

182

Tengo un código de Python que se ejecuta a través de una lista de cadenas y, si es posible, las convierte a números enteros o números de coma flotante. Hacer esto para enteros es bastante fácil

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

Los números de coma flotante son más difíciles. En este momento estoy usando partition('.')para dividir la cadena y comprobando que uno o ambos lados sean dígitos.

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)

Esto funciona, pero obviamente la declaración if para eso es un poco difícil. La otra solución que consideré es simplemente envolver la conversión en un bloque try / catch y ver si tiene éxito, como se describe en esta pregunta .

¿Alguien tiene alguna otra idea? ¿Opiniones sobre los méritos relativos de la partición y los enfoques try / catch?

Chris Upchurch
fuente

Respuestas:

305

Yo solo usaría ...

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

... es simple y funciona

Otra opción sería una expresión regular:

import re
if re.match(r'^-?\d+(?:\.\d+)?$', element) is None:
    print "Not float"
dbr
fuente
3
@ S.Lott: La mayoría de las cadenas a las que se aplica esto resultarán ser ints o flotantes.
Chris Upchurch
10
Su expresión regular no es óptima. "^ \ d + \. \ d + $" fallará una partida a la misma velocidad que antes, pero tendrá éxito más rápido. Además, una forma más correcta sería: "^ [+ -]? \ D (>? \. \ D +)? $" Sin embargo, eso todavía no coincide con números como: + 1.0e-10
John Gietzen
86
Excepto que olvidó nombrar su función "will_it_float".
desmontado el
3
La segunda opción no capturará nan y expresión exponencial, como 2e3.
Patrick B.
44
Creo que la expresión regular no está analizando números negativos.
Carlos
191

Método de Python para verificar el flotador:

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

¡No te muerdas por los duendes que se esconden en el bote flotante! HAGA LAS PRUEBAS DE LA UNIDAD!

Lo que es y no es un flotador puede sorprenderte:

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
Eric Leschinski
fuente
66
Gran respuesta. Simplemente agregue 2 más donde float = True: isfloat(" 1.23 ")y isfloat(" \n \t 1.23 \n\t\n"). Útil en solicitudes web; No es necesario recortar espacios en blanco primero.
BareNakedCoder
22
'1.43'.replace('.','',1).isdigit()

que volverá truesolo si hay uno o no '.' en la cadena de dígitos.

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

volverá false

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

volverá false

TulasiReddy
fuente
3
No es óptimo, pero en realidad es bastante inteligente. No manejará +/- y exponentes.
Físico loco
Años de retraso, pero este es un buen método. Trabajó para mí usando lo siguiente en un marco de datos de pandas:[i for i in df[i].apply(lambda x: str(x).replace('.','').isdigit()).any()]
Mark Moretto
1
@MarkMoretto Te sorprenderás cuando te enteres de la existencia de números negativos
David Heffernan
La mejor frase para mi escenario, donde solo necesito verificar flotadores o números positivos. Me gusta.
MJohnyJ
8

TL; DR :

  • Si su entrada es principalmente cadenas que se pueden convertir en flotantes, el try: except:método es el mejor método nativo de Python.
  • Si su entrada es principalmente cadenas que no se pueden convertir en flotantes, las expresiones regulares o el método de partición serán mejores.
  • Si está 1) inseguro de su entrada o necesita más velocidad y 2) no le importa y puede instalar una extensión C de terceros, fastnumbers funciona muy bien.

Hay otro método disponible a través de un módulo de terceros llamado números rápidos (divulgación, yo soy el autor); Proporciona una función llamada isfloat . Tomé el ejemplo de prueba de unidad descrito por Jacob Gabrielson en esta respuesta , pero agregué el fastnumbers.isfloatmétodo. También debo tener en cuenta que el ejemplo de Jacob no hizo justicia a la opción regex porque la mayor parte del tiempo en ese ejemplo se gastó en búsquedas globales debido al operador de punto ... He modificado esa función para hacer una comparación más justa 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 __name__ == '__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()

En mi máquina, el resultado es:

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

Como puede ver, la expresión regular no es tan mala como parecía originalmente, y si tiene una verdadera necesidad de velocidad, el fastnumbersmétodo es bastante bueno.

SethMMorton
fuente
la comprobación rápida de números funciona muy bien si tiene una mayoría de cadenas que no se pueden convertir en flotadores, realmente acelera las cosas, gracias
ragardner
5

Si le importaba el rendimiento (y no estoy sugiriendo que debería hacerlo), el enfoque basado en intentos es el claro ganador (en comparación con su enfoque basado en particiones o el enfoque regexp), siempre que no espere mucho cadenas no válidas, en cuyo caso es potencialmente más lento (presumiblemente debido al costo del manejo de excepciones).

Nuevamente, no estoy sugiriendo que se preocupe por el rendimiento, solo le doy los datos en caso de que esté haciendo esto 10 mil millones de veces por segundo, o algo así. Además, el código basado en particiones no maneja al menos una cadena válida.

$ ./floatstr.py
F..
partición triste: 3.1102449894
partición feliz: 2.09208488464
..
re triste: 7.76906108856
re feliz: 7.09421992302
..
prueba triste: 12.1525540352
intenta feliz: 1.44165301323
.
================================================== ====================
FAIL: test_partition (__main __. ConvertTests)
-------------------------------------------------- --------------------
Rastreo (llamadas recientes más última):
  Archivo "./floatstr.py", línea 48, en test_partition
    self.failUnless (is_float_partition ("20e2"))
AserciónError

-------------------------------------------------- --------------------
Ran 8 pruebas en 33.670s

FALLIDO (fallas = 1)

Aquí está el código (Python 2.6, regexp tomado de la respuesta de John Gietzen ):

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 __name__ == '__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()
Jacob Gabrielson
fuente
4

Solo por variedad, aquí hay otro método para hacerlo.

>>> 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

Editar: estoy seguro de que no resistirá todos los casos de flotación, especialmente cuando hay un exponente. Para resolver eso se ve así. Esto devolverá Verdadero solo val es flotante y Falso para int pero probablemente sea menos eficaz 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
Peter Moore
fuente
La función numérica parece una mala elección, ya que devuelve verdadero en varios caracteres Unicode como fracciones. Los documentos dicen: "Los caracteres numéricos incluyen caracteres de dígitos y todos los caracteres que tienen la propiedad de valor numérico Unicode, por ejemplo, U + 2155, FRACCIÓN VULGAR UNA QUINTA"
gwideman
3

Esta expresión regular verificará los números científicos de coma flotante:

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

Sin embargo, creo que su mejor opción es usar el analizador en un intento.

John Gietzen
fuente
2

Si no necesita preocuparse por las expresiones científicas u otras expresiones de números y solo está trabajando con cadenas que podrían ser números con o sin punto:

Función

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

Versión lambda

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

Ejemplo

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 esta manera, no está convirtiendo accidentalmente lo que debería ser un int, en un flotante.

kodetojoy
fuente
2

Versión simplificada de la función is_digit(str) , que es suficiente en la mayoría de los casos (no considera la notación exponencial y el valor "NaN" ):

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

Utilicé la función ya mencionada, pero pronto noté que las cadenas como "Nan", "Inf" y su variación se consideran números. Por lo tanto, le propongo una versión mejorada de la función, que devolverá falso en ese tipo de entrada y no fallará las 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
mathfac
fuente
1
¿No podríamos comenzar con el if text.isalpha():cheque de inmediato?
Csaba Toth
Por cierto, necesito lo mismo: no quiero aceptar NaN, Inf y esas cosas
Csaba Toth
1

Intenta convertir a flotador. Si hay un error, imprima la excepción ValueError.

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

Salida:

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

Al pasar el diccionario como argumento, convertirá cadenas que se pueden convertir en flotantes y dejará otras

def covertDict_float(data):
        for i in data:
            if data[i].split(".")[0].isdigit():
                try:
                    data[i] = float(data[i])
                except:
                    continue
        return data
Rahul Jain
fuente
0

Estaba buscando un código similar, pero parece que usar try / excepts es la mejor manera. Aquí está el código que estoy usando. Incluye una función de reintento si la entrada no es válida. Necesitaba verificar si la entrada era mayor que 0 y, de ser así, convertirla en flotante.

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: ")
Lockey
fuente
0
def try_parse_float(item):
  result = None
  try:
    float(item)
  except:
    pass
  else:
    result = float(item)
  return result
Tawanda Matereke
fuente
2
Si bien este código puede resolver la pregunta, incluir una explicación de cómo y por qué esto resuelve el problema realmente ayudaría a mejorar la calidad de su publicación, y probablemente resultaría en más votos positivos. Recuerde que está respondiendo la pregunta para los lectores en el futuro, no solo la persona que pregunta ahora. Por favor, editar su respuesta para agregar explicaciones y dar una indicación de lo que se aplican limitaciones y supuestos.
doble pitido
0

Probé algunas de las opciones simples anteriores, usando una prueba de prueba para convertir a flotante, y descubrí que hay un problema en la mayoría de las respuestas.

Prueba simple (en la línea de las respuestas anteriores):

entry = ttk.Entry(self, validate='key')
entry['validatecommand'] = (entry.register(_test_num), '%P')

def _test_num(P):
    try: 
        float(P)
        return True
    except ValueError:
        return False

El problema viene cuando:

  • Ingrese '-' para comenzar un número negativo:

Entonces estás intentando lo float('-')que falla

  • Ingresas un número, pero luego intentas eliminar todos los dígitos

Entonces estás intentando lo float('')que también falla

La solución rápida que tuve es:

def _test_num(P):
    if P == '' or P == '-': return True
    try: 
        float(P)
        return True
    except ValueError:
        return False
Ricardo
fuente
-2
str(strval).isdigit()

Parece ser simple.

Maneja valores almacenados como una cadena o int o flotante

muks
fuente
En [2]: '123,123'.isdigit () Fuera [2]: Falso
Daniil Mashkin
1
No funciona para números negativos literales, por favor arregle su respuesta
RandomEli
'39 .1'.isdigit ()
Ohad the Lad
all ([x.isdigit () para x en str (VAR) .strip ('-'). replace (',', '.'). split ('.')]) Si está buscando una más completa implementación.
lotrus28