color - Comprobando si una cadena se puede convertir para flotar en Python
plotly layout (11)
Método Python para verificar float:
def isfloat(value):
try:
float(value)
return True
except ValueError:
return False
¡No te muerdan los duendes que se esconden en el bote! ¡HAGA LAS PRUEBAS UNITARIAS!
Lo que es, y no es una carroza puede sorprenderlo:
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
Tengo un código de Python que se ejecuta a través de una lista de cadenas y las convierte en números enteros o en coma flotante si es posible. 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 la partition(''.'')
Para dividir la cadena y verificar 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 oso. La otra solución que consideré es simplemente ajustar 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?
Esta expresión regular comprobará los números científicos de punto 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.
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 si es así convertirlo a un flotador.
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: ")
Si le preocupa el rendimiento (y no estoy sugiriendo que deba hacerlo), el enfoque basado en la prueba es el claro ganador (en comparación con su enfoque basado en particiones o el enfoque de expresiones regulares), siempre y cuando no espere mucho cadenas inválidas, en cuyo caso es potencialmente más lento (presumiblemente debido al costo del manejo de excepciones).
De nuevo, no estoy sugiriendo que se preocupe por el rendimiento, solo dándole 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.. partition sad: 3.1102449894 partition happy: 2.09208488464 .. re sad: 7.76906108856 re happy: 7.09421992302 .. try sad: 12.1525540352 try happy: 1.44165301323 . ====================================================================== FAIL: test_partition (__main__.ConvertTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "./floatstr.py", line 48, in test_partition self.failUnless(is_float_partition("20e2")) AssertionError ---------------------------------------------------------------------- Ran 8 tests in 33.670s FAILED (failures=1)
Aquí está el código (Python 2.6, expresión regular tomada de la answer 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()
Si no necesita preocuparse por las expresiones científicas o de otro tipo de números, solo trabaja con cadenas que pueden ser números con o sin período:
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 forma, no está convirtiendo accidentalmente lo que debería ser un int, en un float.
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 se mantendrá hasta todos los casos de flotación, especialmente cuando hay un exponente. Para resolver eso se ve así. Esto devolverá True only val is a float y False for int pero es probablemente menos eficiente 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
Usé la función ya mencionada, pero pronto noté que las cadenas como "Nan", "Inf" y su variación se consideran como números. Así que propongo una versión mejorada de la función, que devolverá falso en ese tipo de entrada y no fallará en las variantes "1e3":
def is_float(text):
try:
float(text)
# check for nan/infinity etc.
if text.isalpha():
return False
return True
except ValueError:
return False
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("^/d+?/./d+?$", element) is None:
print "Not float"
str (strval) .isdigit () parece ser simple. Maneja los valores almacenados como una cadena o int o float
TL; DR :
- Si su entrada es principalmente cadenas que se pueden convertir en flotantes, el método
try: except:
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 tiene 1) dudas sobre su información o si necesita más velocidad y 2) no tiene inconveniente 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 denominado fastnumbers (divulgación, soy el autor); proporciona una función llamada isfloat . Tomé el ejemplo de unittest descrito por Jacob Gabrielson en esta respuesta , pero agregué el método fastnumbers.isfloat
. También debo señalar que el ejemplo de Jacob no hizo justicia a la opción de expresión regular porque la mayoría de las veces en ese ejemplo se gastó en búsquedas globales debido al operador punto ... He modificado esa función para ofrecer una comparación más justa para 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, la salida 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, Regex en realidad no es tan malo como originalmente parecía, y si tiene una necesidad real de velocidad, el método de fastnumbers
es bastante bueno.
''1.43''.replace(''.'','''',1).isdigit()
que volverá true
solo si hay uno o no ''.'' en la cadena de dígitos.
''1.4.3''.replace(''.'','''',1).isdigit()
devolverá false
''1.ww''.replace(''.'','''',1).isdigit()
devolverá false