¿Cómo verifico si una cadena es un número (flotante)?

1610

¿Cuál es la mejor manera posible de verificar si una cadena se puede representar como un número en Python?

La función que tengo actualmente en este momento es:

def is_number(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

Lo cual, no solo es feo y lento, parece torpe. Sin embargo, no he encontrado un método mejor porque llamar floata la función principal es aún peor.

Daniel Goldberg
fuente
61
¿Qué tiene de malo su solución actual? Es corto, rápido y legible.
Coronel Panic
55
Y no solo tiene que devolver Verdadero o Falso. En su lugar, puede devolver el valor adecuadamente modificado; por ejemplo, podría usar esto para poner no números entre comillas.
Thruston
77
¿No sería mejor devolver el resultado de flotante (s) en el caso de una conversión exitosa? Todavía tiene la verificación del éxito (el resultado es Falso) y, de hecho, TIENE la conversión, que es probable que desee de todos modos.
Jiminion
8
Aunque esta pregunta es más antigua, solo quería decir que esta es una forma elegante que está documentada como EAFP . Probablemente sea la mejor solución para este tipo de problema.
thiruvenkadam
77
No devuelva el resultado de flotante (s) o Ninguno en caso de error. si lo usa, ya x = float('0.00'); if x: use_float(x);que ahora tiene un error en su código. Los valores de verdad son la razón por la cual estas funciones generan una excepción en lugar de regresar Noneen primer lugar. Una mejor solución es simplemente evitar la función de utilidad y rodear la llamada para flotar en un try catchmomento en que desee usarla.
ovangle

Respuestas:

699

Lo cual, no solo es feo y lento

Yo disputaría ambos.

Un regex u otro método de análisis de cadenas sería más feo y lento.

No estoy seguro de que algo más pueda ser más rápido que lo anterior. Llama a la función y vuelve. Try / Catch no introduce demasiados gastos generales porque la excepción más común se detecta sin una búsqueda exhaustiva de marcos de pila.

El problema es que cualquier función de conversión numérica tiene dos tipos de resultados.

  • Un número, si el número es válido.
  • Un código de estado (por ejemplo, a través de errno) o una excepción para mostrar que no se puede analizar un número válido.

C (como ejemplo) piratea esto de varias maneras. Python lo presenta clara y explícitamente.

Creo que tu código para hacer esto es perfecto.

S.Lott
fuente
21
No creo que el código es perfecto (pero creo que está muy cerca): es más habitual para poner solamente la parte que se "prueba" de la trycláusula, por lo que pondría el return Truede una elsecláusula de la try. Una de las razones es que con el código en la pregunta, si tuviera que revisarlo, tendría que verificar que la segunda declaración en la trycláusula no pueda generar un ValueError: concedido, esto no requiere demasiado tiempo o poder mental, pero ¿por qué usar alguno cuando no se necesita ninguno?
Eric O Lebigot
44
La respuesta parece convincente, pero me hace preguntarme por qué no se proporciona de fábrica ... Copiaré esto y lo usaré en cualquier caso.
sabio
99
Que horrible. ¿Y si no me importa lo que el número es sólo que es un número (que es lo que me trajo aquí)? En lugar de una línea IsNumeric(), termino con un try / catch u otro que envuelve un try / catch. Ugh
Básico
66
No se proporciona "fuera de la caja" porque if is_number(s): x = float(x) else: // failes la misma cantidad de líneas de código que try: x = float(x) catch TypeError: # fail. Esta función de utilidad es una abstracción completamente innecesaria.
ovangle
12
Pero la abstracción es el objetivo de las bibliotecas. Tener una función 'isNumber' (en cualquier idioma) ayuda muchísimo porque puedes construirlo directamente en sentencias if y tener un código mucho más legible y mantenible que depende de bloques try-catch. Además, si necesita usar el código más de una vez en más de una clase / módulo, entonces ha usado más líneas de código de las que tendría una función incorporada.
JamEngulfer
1612

En caso de que esté buscando enteros de análisis (positivo, sin signo) en lugar de flotantes, puede usar la isdigit()función para objetos de cadena.

>>> a = "03523"
>>> a.isdigit()
True
>>> b = "963spam"
>>> b.isdigit()
False

Métodos de cadena isdigit(): Python2 , Python3

También hay algo en las cadenas Unicode, que no estoy muy familiarizado con Unicode: es decimal / decimal

Zoomulator
fuente
232
Eso también es negativo en los negativos
intrepion
22
También falla con exponenciales: '1e3'.isdigit () -> False
ssc
35
Mientras Number! = Digit, las personas que buscan formas de probar si una cadena contiene un número entero pueden tropezar con esta pregunta, y el enfoque isDigit puede ser perfectamente adecuado para su aplicación.
Adam Parkin
8
@AdamParkin: isdigit()y int()tiene diferentes opiniones sobre lo que es un número entero, por ejemplo, para el carácter Unicode u'\u00b9': u'¹'.isdigit()es Truepero int(u'¹')aumenta ValueError.
jfs
66
+1: isdigit () puede no ser lo que buscaba el OP, pero es exactamente lo que quería. Puede que no sea el caso que esta respuesta y este método no cubran todos los tipos de números, pero aún así es muy relevante, en contra de los argumentos sobre su precisión. Mientras que "Número! = Dígito", el dígito sigue siendo un subconjunto de números, particularmente números que son positivos, no negativos y usan la base 1-10. Además, este método es particularmente útil y breve para los casos en los que desea inspeccionar si una cadena es una ID numérica o no, que a menudo cae en el subconjunto de números que acabo de describir.
Justin Johnson
161

TL; DR La mejor solución ess.replace('.','',1).isdigit()

Hice algunos puntos de referencia comparando los diferentes enfoques

def is_number_tryexcept(s):
    """ Returns True is string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False

import re    
def is_number_regex(s):
    """ Returns True is string is a number. """
    if re.match("^\d+?\.\d+?$", s) is None:
        return s.isdigit()
    return True


def is_number_repl_isdigit(s):
    """ Returns True is string is a number. """
    return s.replace('.','',1).isdigit()

Si la cadena no es un número, el bloque except es bastante lento. Pero lo más importante, el método try-except es el único enfoque que maneja las anotaciones científicas correctamente.

funcs = [
          is_number_tryexcept, 
          is_number_regex,
          is_number_repl_isdigit
          ]

a_float = '.1234'

print('Float notation ".1234" is not supported by:')
for f in funcs:
    if not f(a_float):
        print('\t -', f.__name__)

La notación flotante ".1234" no es compatible con:
- is_number_regex

scientific1 = '1.000000e+50'
scientific2 = '1e50'


print('Scientific notation "1.000000e+50" is not supported by:')
for f in funcs:
    if not f(scientific1):
        print('\t -', f.__name__)




print('Scientific notation "1e50" is not supported by:')
for f in funcs:
    if not f(scientific2):
        print('\t -', f.__name__)

La notación científica "1.000000e + 50" no es compatible con:
- is_number_regex
- is_number_repl_isdigit
La notación científica "1e50" no es compatible con:
- is_number_regex
- is_number_repl_isdigit

EDITAR: los resultados de referencia

import timeit

test_cases = ['1.12345', '1.12.345', 'abc12345', '12345']
times_n = {f.__name__:[] for f in funcs}

for t in test_cases:
    for f in funcs:
        f = f.__name__
        times_n[f].append(min(timeit.Timer('%s(t)' %f, 
                      'from __main__ import %s, t' %f)
                              .repeat(repeat=3, number=1000000)))

donde se probaron las siguientes funciones

from re import match as re_match
from re import compile as re_compile

def is_number_tryexcept(s):
    """ Returns True is string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False

def is_number_regex(s):
    """ Returns True is string is a number. """
    if re_match("^\d+?\.\d+?$", s) is None:
        return s.isdigit()
    return True


comp = re_compile("^\d+?\.\d+?$")    

def compiled_regex(s):
    """ Returns True is string is a number. """
    if comp.match(s) is None:
        return s.isdigit()
    return True


def is_number_repl_isdigit(s):
    """ Returns True is string is a number. """
    return s.replace('.','',1).isdigit()

ingrese la descripción de la imagen aquí

Idok
fuente
15
para buenos gráficos +1. Vi benchmark y vi gráfico, todo el TL; DR se volvió claro e intuitivo.
jcchuks
Estoy de acuerdo con @JCChuks: el gráfico ayuda mucho a obtener todo el TL; DR rápidamente. Pero creo que un TL; DR (como: TL; DR : la mejor solución es s.replace('.','',1).isdigit()) debería aparecer al comienzo de esta respuesta. En cualquier caso, debe ser el aceptado. ¡Gracias!
Simon C.
10
Este método no maneja números negativos (guiones). Yo recomendaría usar el método de flotación, ya que es menos propenso a errores y funcionará siempre.
Erizo
3
Lo importante a tener en cuenta es que, incluso en el supuesto de que no pueda haber un guión, el método replace-isdigit es solo más rápido para los que no son números (resultado falso), mientras que el método try-except es más rápido para los números (resultado verdadero). Si la mayor parte de su entrada es válida, ¡está mejor con la solución try-except!
Markus von Broady
1
No funciona en notación exponencial como '1.5e-9'o en negativos.
EL_DON
68

Hay una excepción que es posible que desee tener en cuenta: la cadena 'NaN'

Si desea que is_number devuelva FALSE para 'NaN', este código no funcionará, ya que Python lo convierte en su representación de un número que no es un número (hablemos de problemas de identidad):

>>> float('NaN')
nan

De lo contrario, debería agradecerle el código que ahora uso ampliamente. :)

SOL.

gvrocha
fuente
2
En realidad, NaNpodría ser un buen valor para devolver (en lugar de False) si el texto pasado no es en realidad una representación de un número. Verificarlo es una molestia (el floattipo de Python realmente necesita un método para ello) pero puede usarlo en los cálculos sin producir un error, y solo necesita verificar el resultado.
poco
77
Otra excepción es la cadena 'inf'. O info NaNtambién se puede prefijar con un +o -y aún ser aceptado.
AGF
44
Si desea devolver False para un NaN e Inf, cambie la línea a x = flotante (s); return (x == x) y (x - 1! = x). Esto debería devolver True para todas las carrozas excepto Inf y NaN
RyanN
55
x-1 == xes cierto para flotadores grandes más pequeños que inf. Desde Python 3.2 puede usar math.isfinitepara probar números que no son NaN ni infinitos, o verificar ambos math.isnany math.isinfantes de eso.
Steve Jessop
56

Qué tal esto:

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

que devolverá verdadero solo si hay uno o no '.' en la cadena de dígitos.

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

devolverá falso

editar: acabo de ver otro comentario ... se puede agregar un .replace(badstuff,'',maxnum_badstuff)para otros casos. si está pasando sal y no condimentos arbitrarios (ref: xkcd # 974 ) esto funcionará bien: P

haxwithaxe
fuente
77
Sin embargo, esto no tiene en cuenta los números negativos.
Michael Barton
55
O números con exponentes como 1.234e56(que también podrían escribirse como +1.234E+56y varias variantes más).
Alfe
re.match(r'^[+-]*(0[xbo])?[0-9A-Fa-f]*\.?[0-9A-Fa-f]*(E[+-]*[0-9A-Fa-f]+)$', 'str')debería hacer un mejor trabajo para determinar un número (pero no todos, no estoy reclamando eso). No recomiendo usar esto, mucho mejor usar el código original del interlocutor.
Baldrickk
Si no le gusta esta solución, ¡lea esto antes de votar!
aloisdg se muda a codidact.com
¡hombre, esta es la solución más inteligente que he visto en este sitio web! ¡Hombre bien hecho!
Karam Qusai
41

Lo cual, no solo es feo y lento, parece torpe.

Puede tomar un tiempo acostumbrarse, pero esta es la forma pitónica de hacerlo. Como ya se señaló, las alternativas son peores. Pero hay otra ventaja de hacer las cosas de esta manera: el polimorfismo.

La idea central detrás del tipeo de patos es que "si camina y habla como un pato, entonces es un pato". ¿Qué sucede si decide que necesita subclasificar una cadena para poder cambiar la forma en que determina si algo se puede convertir en un flotador? ¿O qué pasa si decides probar algún otro objeto por completo? Puede hacer estas cosas sin tener que cambiar el código anterior.

Otros idiomas resuelven estos problemas mediante el uso de interfaces. Guardaré el análisis de qué solución es mejor para otro hilo. El punto, sin embargo, es que Python está decididamente del lado de la ecuación, y probablemente tendrá que acostumbrarse a una sintaxis como esta si planea hacer mucha programación en Python (pero eso no significa te tiene que gustar, por supuesto).

Otra cosa que quizás desee tener en cuenta: Python es bastante rápido en lanzar y capturar excepciones en comparación con muchos otros lenguajes (30 veces más rápido que .Net, por ejemplo). Diablos, el lenguaje en sí mismo incluso arroja excepciones para comunicar condiciones de programa normales y no excepcionales (cada vez que usa un bucle for). Por lo tanto, no me preocuparía demasiado por los aspectos de rendimiento de este código hasta que note un problema importante.

Jason Baker
fuente
1
Otro lugar común donde Python usa excepciones para funciones básicas es en el hasattr()que solo hay una getattr()llamada envuelta en un try/except. Aún así, el manejo de excepciones es más lento que el control de flujo normal, por lo que usarlo para algo que será cierto la mayoría de las veces puede resultar en una penalización de rendimiento.
poco
Parece que si quieres una frase, eres SOL
Básico
También es pitónica la idea de que "es mejor pedir perdón que permiso", en relación con el impacto de tener excepciones baratas.
heltonbiker
40

Actualizado después de que Alfe señaló que no es necesario verificar la flotación por separado, ya que el complejo maneja ambos:

def is_number(s):
    try:
        complex(s) # for int, long, float and complex
    except ValueError:
        return False

    return True

Dicho anteriormente: es posible que también necesite verificar números complejos (por ejemplo, 1 + 2i), que no pueden representarse mediante un flotante:

def is_number(s):
    try:
        float(s) # for int, long and float
    except ValueError:
        try:
            complex(s) # for complex
        except ValueError:
            return False

    return True
Matthew Wilcoxson
fuente
14
Estoy en desacuerdo. Eso es MUY improbable en el uso normal, y sería mejor construir una llamada is_complex_number () para cuando los esté usando, en lugar de cargar una llamada con una operación adicional para un 0,0001% de posibilidades de mal funcionamiento.
Jiminion
3
Puede quitar las float()cosas por completo y simplemente verificar si la complex()llamada tiene éxito. Todo lo analizado float()puede ser analizado por complex().
Alfe
Esta función devolverá los valores NaNs e Inf de Pandas como valores numéricos.
Fixxxer
complex('(01989)')devolverá (1989+0j). Pero float('(01989)')fallará. Así que creo que usar complexno es una buena idea.
plhn
26

Para intusar esto:

>>> "1221323".isdigit()
True

Pero para floateso necesitamos algunos trucos ;-). Cada número flotante tiene un punto ...

>>> "12.34".isdigit()
False
>>> "12.34".replace('.','',1).isdigit()
True
>>> "12.3.4".replace('.','',1).isdigit()
False

También para números negativos solo agregue lstrip():

>>> '-12'.lstrip('-')
'12'

Y ahora obtenemos una forma universal:

>>> '-12.34'.lstrip('-').replace('.','',1).isdigit()
True
>>> '.-234'.lstrip('-').replace('.','',1).isdigit()
False
Sdwdaw
fuente
2
No maneja cosas como 1.234e56y similares. Además, me interesaría cómo descubrirías que 99999999999999999999e99999999999999999999no es un número. Intentando analizarlo se entera rápidamente.
Alfe
Esto funciona ~ 30% más rápido que la solución aceptada en una lista de cadenas de 50m, y 150% más rápido en una lista de cadenas de 5k. 👏
Zev Averbach
15

Solo Mimic C #

En C # hay dos funciones diferentes que manejan el análisis de valores escalares:

  • Float.Parse ()
  • Float. TryParse ()

float.parse ():

def parse(string):
    try:
        return float(string)
    except Exception:
        throw TypeError

Nota: Si se pregunta por qué cambié la excepción a un TypeError, aquí está la documentación .

float.try_parse ():

def try_parse(string, fail=None):
    try:
        return float(string)
    except Exception:
        return fail;

Nota: No desea devolver el booleano 'False' porque todavía es un tipo de valor. Ninguno es mejor porque indica falla. Por supuesto, si desea algo diferente, puede cambiar el parámetro de falla a lo que desee.

Para extender float para incluir 'parse ()' y 'try_parse ()', necesitará hacer un parche en la clase 'float' para agregar estos métodos.

Si desea respetar las funciones preexistentes, el código debería ser algo como:

def monkey_patch():
    if(!hasattr(float, 'parse')):
        float.parse = parse
    if(!hasattr(float, 'try_parse')):
        float.try_parse = try_parse

Nota al margen: Personalmente prefiero llamarlo Monkey Punching porque siento que estoy abusando del lenguaje cuando hago esto, pero YMMV.

Uso:

float.parse('giggity') // throws TypeException
float.parse('54.3') // returns the scalar value 54.3
float.tryParse('twank') // returns None
float.tryParse('32.2') // returns the scalar value 32.2

Y el gran sabio Pythonas le dijo a la Santa Sede Sharpisus: "Todo lo que puedas hacer lo puedo hacer mejor; puedo hacer cualquier cosa mejor que tú".

Evan Plaice
fuente
Últimamente he estado codificando principalmente JS y en realidad no probé esto, por lo que puede haber algunos errores menores. Si ve alguno, no dude en corregir mis errores.
Evan Plaice
Para agregar soporte para números complejos, vea la respuesta de @Matthew Wilcoxson. stackoverflow.com/a/3335060/290340 .
Evan Plaice
1
El uso en !lugar de notpodría ser un error menor, pero definitivamente no puede asignar atributos al floatCPython incorporado .
BlackJack
15

Para cadenas de no números, en try: except:realidad es más lento que las expresiones regulares. Para cadenas de números válidos, la expresión regular es más lenta. Entonces, el método apropiado depende de su entrada.

Si encuentra que está en un enlace de rendimiento, puede usar un nuevo módulo de terceros llamado fastnumbers que proporciona una función llamada isfloat . Divulgación completa, soy el autor. He incluido sus resultados en los horarios a continuación.


from __future__ import print_function
import timeit

prep_base = '''\
x = 'invalid'
y = '5402'
z = '4.754e3'
'''

prep_try_method = '''\
def is_number_try(val):
    try:
        float(val)
        return True
    except ValueError:
        return False

'''

prep_re_method = '''\
import re
float_match = re.compile(r'[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$').match
def is_number_re(val):
    return bool(float_match(val))

'''

fn_method = '''\
from fastnumbers import isfloat

'''

print('Try with non-number strings', timeit.timeit('is_number_try(x)',
    prep_base + prep_try_method), 'seconds')
print('Try with integer strings', timeit.timeit('is_number_try(y)',
    prep_base + prep_try_method), 'seconds')
print('Try with float strings', timeit.timeit('is_number_try(z)',
    prep_base + prep_try_method), 'seconds')
print()
print('Regex with non-number strings', timeit.timeit('is_number_re(x)',
    prep_base + prep_re_method), 'seconds')
print('Regex with integer strings', timeit.timeit('is_number_re(y)',
    prep_base + prep_re_method), 'seconds')
print('Regex with float strings', timeit.timeit('is_number_re(z)',
    prep_base + prep_re_method), 'seconds')
print()
print('fastnumbers with non-number strings', timeit.timeit('isfloat(x)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print('fastnumbers with integer strings', timeit.timeit('isfloat(y)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print('fastnumbers with float strings', timeit.timeit('isfloat(z)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print()

Try with non-number strings 2.39108395576 seconds
Try with integer strings 0.375686168671 seconds
Try with float strings 0.369210958481 seconds

Regex with non-number strings 0.748660802841 seconds
Regex with integer strings 1.02021503448 seconds
Regex with float strings 1.08564686775 seconds

fastnumbers with non-number strings 0.174362897873 seconds
fastnumbers with integer strings 0.179651021957 seconds
fastnumbers with float strings 0.20222902298 seconds

Como puedes ver

  • try: except: fue rápido para la entrada numérica pero muy lento para una entrada no válida
  • regex es muy eficiente cuando la entrada no es válida
  • fastnumbers gana en ambos casos
SethMMorton
fuente
Estoy corregido: -} simplemente no parecía que estuviera haciendo esto. Tal vez usar nombres como prep_code_basisy prep_code_re_methodhubiera evitado mi error.
Alfe
¿Te importaría explicar cómo funciona tu módulo, al menos para la isfloatfunción?
Solomon Ucko
@SolomonUcko Aquí hay un enlace al código fuente de la parte de verificación de cadenas: github.com/SethMMorton/fastnumbers/blob/v1.0.0/src/… . Básicamente, recorre cada carácter de la cadena en orden y valida que siga un patrón para un flotante válido. Si la entrada ya es un número, solo usa el rápido PyFloat_Check .
SethMMorton
1
Probado contra las mejores alternativas en este hilo, confirmo que esta solución es, con mucho, la más rápida. ¡El segundo método más rápido str(s).strip('-').replace('.','',1).isdigit()es aproximadamente 10 veces más lento!
Alexander McFarlane
14

Sé que esto es particularmente antiguo, pero agregaría una respuesta que creo que cubre la información que falta en la respuesta más votada que podría ser muy valiosa para cualquiera que encuentre esto:

Para cada uno de los siguientes métodos, conéctelos con un recuento si necesita que se acepte alguna entrada. (Suponiendo que estamos usando definiciones vocales de enteros en lugar de 0-255, etc.)

x.isdigit() funciona bien para verificar si x es un número entero.

x.replace('-','').isdigit() funciona bien para verificar si x es negativo. (Verificar - en la primera posición)

x.replace('.','').isdigit() funciona bien para verificar si x es un decimal.

x.replace(':','').isdigit() funciona bien para verificar si x es una relación.

x.replace('/','',1).isdigit() funciona bien para verificar si x es una fracción.

Aruthawolf
fuente
1
Aunque para las fracciones, es probable que tenga que hacer x.replace('/','',1).isdigit()o fechas como el 4/7/2017 se interpretarían erróneamente como números.
Yuxuan Chen el
Para conocer las mejores formas de encadenar las condiciones: stackoverflow.com/q/3411771/5922329
Daniel Braun
13

Esta respuesta proporciona una guía paso a paso que tiene función con ejemplos para encontrar la cadena es:

  • Entero positivo
  • Positivo / negativo - entero / flotante
  • ¿Cómo descartar cadenas "NaN" (no un número) mientras se busca un número?

Compruebe si la cadena es un entero positivo

Puede usar str.isdigit()para verificar si la cadena dada es un entero positivo .

Resultados de muestra:

# For digit
>>> '1'.isdigit()
True
>>> '1'.isalpha()
False

Verifique que la cadena sea positiva / negativa - entero / flotante

str.isdigit()devuelve Falsesi la cadena es un número negativo o un número flotante. Por ejemplo:

# returns `False` for float
>>> '123.3'.isdigit()
False
# returns `False` for negative number
>>> '-123'.isdigit()
False

Si desea verificar también los enteros negativos yfloat , entonces puede escribir una función personalizada para verificarlo como:

def is_number(n):
    try:
        float(n)   # Type-casting the string to `float`.
                   # If string is not a valid `float`, 
                   # it'll raise `ValueError` exception
    except ValueError:
        return False
    return True

Ejecución de muestra:

>>> is_number('123')    # positive integer number
True

>>> is_number('123.4')  # positive float number
True

>>> is_number('-123')   # negative integer number
True

>>> is_number('-123.4') # negative `float` number
True

>>> is_number('abc')    # `False` for "some random" string
False

Descarte las cadenas "NaN" (no un número) mientras verifica el número

Las funciones anteriores volverán Truepara la cadena "NAN" (No es un número) porque para Python es un valor flotante válido que lo representa no es un número. Por ejemplo:

>>> is_number('NaN')
True

Para verificar si el número es "NaN", puede usarlo math.isnan()como:

>>> import math
>>> nan_num = float('nan')

>>> math.isnan(nan_num)
True

O si no desea importar una biblioteca adicional para verificar esto, simplemente puede verificarlo comparándolo con él mismo ==. Python regresa Falsecuando nanse compara el flotador consigo mismo. Por ejemplo:

# `nan_num` variable is taken from above example
>>> nan_num == nan_num
False

Por lo tanto, por encima de la función is_numberse puede actualizar para volver Falsea"NaN" como:

def is_number(n):
    is_number = True
    try:
        num = float(n)
        # check for "nan" floats
        is_number = num == num   # or use `math.isnan(num)`
    except ValueError:
        is_number = False
    return is_number

Ejecución de muestra:

>>> is_number('Nan')   # not a number "Nan" string
False

>>> is_number('nan')   # not a number string "nan" with all lower cased
False

>>> is_number('123')   # positive integer
True

>>> is_number('-123')  # negative integer
True

>>> is_number('-1.12') # negative `float`
True

>>> is_number('abc')   # "some random" string
False

PD: cada operación para cada verificación, dependiendo del tipo de número, viene con una sobrecarga adicional. Elija la versión de la is_numberfunción que se ajuste a sus necesidades.

Moinuddin Quadri
fuente
12

Lanzar para flotar y capturar ValueError es probablemente la forma más rápida, ya que float () está específicamente diseñado para eso. Cualquier otra cosa que requiera el análisis de cadenas (expresiones regulares, etc.) probablemente será más lenta debido al hecho de que no está ajustada para esta operación. Mis $ 0.02.

codelogic
fuente
11
Sus dólares "2e-2" también son flotantes (un argumento adicional para usar float :)
tzot
8
@tzot NUNCA use un flotador para representar un valor monetario.
Lucas
66
@Luke: Estoy totalmente de acuerdo contigo, aunque nunca sugerí usar flotadores para representar valores monetarios; Acabo de decir que los valores monetarios se pueden representar como flotantes :)
tzot
9

Quería ver qué método es el más rápido. En general, los resultados mejores y más consistentes fueron dados por la check_replacefunción. La check_exceptionfunción dio los resultados más rápidos , pero solo si no se activó ninguna excepción, lo que significa que su código es el más eficiente, pero la sobrecarga de lanzar una excepción es bastante grande.

Tenga en cuenta que la comprobación de una conversión exitosa es el único método que es exacto, por ejemplo, esto funciona check_exceptionpero las otras dos funciones de prueba devolverán False para un flotante válido:

huge_number = float('1e+100')

Aquí está el código de referencia:

import time, re, random, string

ITERATIONS = 10000000

class Timer:    
    def __enter__(self):
        self.start = time.clock()
        return self
    def __exit__(self, *args):
        self.end = time.clock()
        self.interval = self.end - self.start

def check_regexp(x):
    return re.compile("^\d*\.?\d*$").match(x) is not None

def check_replace(x):
    return x.replace('.','',1).isdigit()

def check_exception(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

to_check = [check_regexp, check_replace, check_exception]

print('preparing data...')
good_numbers = [
    str(random.random() / random.random()) 
    for x in range(ITERATIONS)]

bad_numbers = ['.' + x for x in good_numbers]

strings = [
    ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(random.randint(1,10)))
    for x in range(ITERATIONS)]

print('running test...')
for func in to_check:
    with Timer() as t:
        for x in good_numbers:
            res = func(x)
    print('%s with good floats: %s' % (func.__name__, t.interval))
    with Timer() as t:
        for x in bad_numbers:
            res = func(x)
    print('%s with bad floats: %s' % (func.__name__, t.interval))
    with Timer() as t:
        for x in strings:
            res = func(x)
    print('%s with strings: %s' % (func.__name__, t.interval))

Estos son los resultados con Python 2.7.10 en una MacBook Pro 13 2017:

check_regexp with good floats: 12.688639
check_regexp with bad floats: 11.624862
check_regexp with strings: 11.349414
check_replace with good floats: 4.419841
check_replace with bad floats: 4.294909
check_replace with strings: 4.086358
check_exception with good floats: 3.276668
check_exception with bad floats: 13.843092
check_exception with strings: 15.786169

Aquí están los resultados con Python 3.6.5 en una MacBook Pro 13 2017:

check_regexp with good floats: 13.472906000000009
check_regexp with bad floats: 12.977665000000016
check_regexp with strings: 12.417542999999995
check_replace with good floats: 6.011045999999993
check_replace with bad floats: 4.849356
check_replace with strings: 4.282754000000011
check_exception with good floats: 6.039081999999979
check_exception with bad floats: 9.322753000000006
check_exception with strings: 9.952595000000002

Estos son los resultados con PyPy 2.7.13 en una MacBook Pro 13 2017:

check_regexp with good floats: 2.693217
check_regexp with bad floats: 2.744819
check_regexp with strings: 2.532414
check_replace with good floats: 0.604367
check_replace with bad floats: 0.538169
check_replace with strings: 0.598664
check_exception with good floats: 1.944103
check_exception with bad floats: 2.449182
check_exception with strings: 2.200056
Ron Reiter
fuente
10
También debe probar el rendimiento para casos no válidos. No se plantea ninguna excepción con estos números, que es exactamente la parte "lenta".
Ugo Méda
1
@ UgoMéda tomé tu consejo de 2013 y lo hice :)
Ron Reiter
"Tenga en cuenta que la comprobación de un lanzamiento exitoso es el único método que es preciso" <- esto no es realmente cierto. Ejecuté su prueba usando la expresión regular en mi respuesta anterior, y en realidad se ejecuta más rápido que la expresión regular. Agregaré los resultados a mi respuesta anterior.
David Ljung Madison Stellar
Por cierto, como un punto divertido, su creador de números malos puede crear algunos números legales, aunque sería bastante raro. :)
David Ljung Madison Stellar
8

Entonces, para poner todo junto, verificar Nan, infinito y números complejos (parece que están especificados con j, no i, es decir, 1 + 2j) da como resultado:

def is_number(s):
    try:
        n=str(float(s))
        if n == "nan" or n=="inf" or n=="-inf" : return False
    except ValueError:
        try:
            complex(s) # for complex
        except ValueError:
            return False
    return True
a1an
fuente
Hasta ahora la mejor respuesta. Gracias
anish
6

La entrada puede ser la siguiente:

a="50" b=50 c=50.1 d="50.1"


1-Entrada general:

¡La entrada de esta función puede ser todo!

Encuentra si la variable dada es numérica. Las cadenas numéricas consisten en signos opcionales, cualquier número de dígitos, parte decimal opcional y parte exponencial opcional. Por lo tanto, + 0123.45e6 es un valor numérico válido. La notación hexadecimal (por ejemplo, 0xf4c3b00c) y binaria (por ejemplo, 0b10100111001) no está permitida.

is_numeric function

import ast
import numbers              
def is_numeric(obj):
    if isinstance(obj, numbers.Number):
        return True
    elif isinstance(obj, str):
        nodes = list(ast.walk(ast.parse(obj)))[1:]
        if not isinstance(nodes[0], ast.Expr):
            return False
        if not isinstance(nodes[-1], ast.Num):
            return False
        nodes = nodes[1:-1]
        for i in range(len(nodes)):
            #if used + or - in digit :
            if i % 2 == 0:
                if not isinstance(nodes[i], ast.UnaryOp):
                    return False
            else:
                if not isinstance(nodes[i], (ast.USub, ast.UAdd)):
                    return False
        return True
    else:
        return False

prueba:

>>> is_numeric("54")
True
>>> is_numeric("54.545")
True
>>> is_numeric("0x45")
True

función is_float

Encuentra si la variable dada es flotante. las cadenas flotantes consisten en signos opcionales, cualquier número de dígitos, ...

import ast

def is_float(obj):
    if isinstance(obj, float):
        return True
    if isinstance(obj, int):
        return False
    elif isinstance(obj, str):
        nodes = list(ast.walk(ast.parse(obj)))[1:]
        if not isinstance(nodes[0], ast.Expr):
            return False
        if not isinstance(nodes[-1], ast.Num):
            return False
        if not isinstance(nodes[-1].n, float):
            return False
        nodes = nodes[1:-1]
        for i in range(len(nodes)):
            if i % 2 == 0:
                if not isinstance(nodes[i], ast.UnaryOp):
                    return False
            else:
                if not isinstance(nodes[i], (ast.USub, ast.UAdd)):
                    return False
        return True
    else:
        return False

prueba:

>>> is_float("5.4")
True
>>> is_float("5")
False
>>> is_float(5)
False
>>> is_float("5")
False
>>> is_float("+5.4")
True

lo que es ast ?


2- Si estás seguro de que el contenido variable es String :

use el método str.isdigit ()

>>> a=454
>>> a.isdigit()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute 'isdigit'
>>> a="454"
>>> a.isdigit()
True

Entrada 3-numérica:

detectar el valor int:

>>> isinstance("54", int)
False
>>> isinstance(54, int)
True
>>> 

detectar flotador:

>>> isinstance("45.1", float)
False
>>> isinstance(45.1, float)
True
Bastian
fuente
¿Qué es " ast"?
4

Hice alguna prueba de velocidad. Digamos que si es probable que la cadena sea ​​un número, la estrategia try / except es la más rápida posible. Si no es probable que la cadena sea ​​un número y usted esté interesado en la verificación de enteros , vale la pena hacer alguna prueba (es decir, el título más el título '-'). Si está interesado en verificar el número flotante, debe usar el código try / except sin escape.

FxIII
fuente
4

Necesitaba determinar si una cadena se convertía en tipos básicos (float, int, str, bool). Después de no encontrar nada en Internet, creé esto:

def str_to_type (s):
    """ Get possible cast type for a string

    Parameters
    ----------
    s : string

    Returns
    -------
    float,int,str,bool : type
        Depending on what it can be cast to

    """    
    try:                
        f = float(s)        
        if "." not in s:
            return int
        return float
    except ValueError:
        value = s.upper()
        if value == "TRUE" or value == "FALSE":
            return bool
        return type(s)

Ejemplo

str_to_type("true") # bool
str_to_type("6.0") # float
str_to_type("6") # int
str_to_type("6abc") # str
str_to_type(u"6abc") # unicode       

Puedes capturar el tipo y usarlo

s = "6.0"
type_ = str_to_type(s) # float
f = type_(s) 
astrodsg
fuente
3

RyanN sugiere

Si desea devolver False para un NaN e Inf, cambie la línea a x = flotante (s); return (x == x) y (x - 1! = x). Esto debería devolver True para todas las carrozas excepto Inf y NaN

Pero esto no funciona del todo, porque para flotadores suficientemente grandes, x-1 == xdevuelve verdadero. Por ejemplo,2.0**54 - 1 == 2.0**54

philh
fuente
3

Creo que la solución está muy bien, pero no es una implementación correcta de expresiones regulares.

Parece haber mucho odio regexp hacia estas respuestas, lo que creo que no está justificado, las expresiones regulares pueden ser razonablemente limpias, correctas y rápidas. Realmente depende de lo que intentes hacer. La pregunta original era cómo puede "verificar si una cadena se puede representar como un número (flotante)" (según su título). Presumiblemente, querrá usar el valor numérico / flotante una vez que haya verificado que es válido, en cuyo caso su intento / excepción tiene mucho sentido. Pero si, por alguna razón, solo desea validar que una cadena es un númeroentonces una expresión regular también funciona bien, pero es difícil de corregir. Creo que la mayoría de las respuestas de expresiones regulares hasta ahora, por ejemplo, no analizan correctamente las cadenas sin una parte entera (como ".7") que es flotante en lo que respecta a Python. Y eso es un poco difícil de verificar en una sola expresión regular donde no se requiere la porción fraccional. He incluido dos expresiones regulares para mostrar esto.

Plantea la interesante pregunta de qué es un "número". ¿Incluye "inf" que es válido como flotante en Python? ¿O incluye números que son "números" pero que tal vez no se puedan representar en Python (como los números que son más grandes que el máximo flotante).

También hay ambigüedades en cómo analiza los números. Por ejemplo, ¿qué pasa con "--20"? ¿Es este un "número"? ¿Es esta una forma legal de representar "20"? Python le permitirá hacer "var = --20" y establecerlo en 20 (aunque en realidad esto se debe a que lo trata como una expresión), pero float ("- 20") no funciona.

De todos modos, sin más información, aquí hay una expresión regular que creo que cubre todas las entradas y flotadores a medida que Python las analiza .

# Doesn't properly handle floats missing the integer part, such as ".7"
SIMPLE_FLOAT_REGEXP = re.compile(r'^[-+]?[0-9]+\.?[0-9]+([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56"      # sign (-)
                            #     integer (12)
                            #           mantissa (34)
                            #                    exponent (E+56)

# Should handle all floats
FLOAT_REGEXP = re.compile(r'^[-+]?([0-9]+|[0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56"      # sign (-)
                            #     integer (12)
                            #           OR
                            #             int/mantissa (12.34)
                            #                            exponent (E+56)

def is_float(str):
  return True if FLOAT_REGEXP.match(str) else False

Algunos valores de prueba de ejemplo:

True  <- +42
True  <- +42.42
False <- +42.42.22
True  <- +42.42e22
True  <- +42.42E-22
False <- +42.42e-22.8
True  <- .42
False <- 42nope

La ejecución del código de evaluación comparativa en la respuesta de @ ron-reiter muestra que esta expresión regular es en realidad más rápida que la expresión regular normal y es mucho más rápida en el manejo de valores incorrectos que la excepción, lo que tiene sentido. Resultados:

check_regexp with good floats: 18.001921
check_regexp with bad floats: 17.861423
check_regexp with strings: 17.558862
check_correct_regexp with good floats: 11.04428
check_correct_regexp with bad floats: 8.71211
check_correct_regexp with strings: 8.144161
check_replace with good floats: 6.020597
check_replace with bad floats: 5.343049
check_replace with strings: 5.091642
check_exception with good floats: 5.201605
check_exception with bad floats: 23.921864
check_exception with strings: 23.755481
David Ljung Madison Stellar
fuente
Espero que sea correcto, me encantaría saber sobre cualquier contraejemplo. :)
David Ljung Madison Stellar
2
import re
def is_number(num):
    pattern = re.compile(r'^[-+]?[-0-9]\d*\.\d*|[-+]?\.?[0-9]\d*$')
    result = pattern.match(num)
    if result:
        return True
    else:
        return False


​>>>: is_number('1')
True

>>>: is_number('111')
True

>>>: is_number('11.1')
True

>>>: is_number('-11.1')
True

>>>: is_number('inf')
False

>>>: is_number('-inf')
False
xin.chen
fuente
2
¿No consideras 1e6representar un número?
Mark Dickinson el
1

Aquí está mi manera simple de hacerlo. Digamos que estoy recorriendo algunas cadenas y quiero agregarlas a una matriz si resultan ser números.

try:
    myvar.append( float(string_to_check) )
except:
    continue

Reemplace myvar.apppend con cualquier operación que desee hacer con la cadena si resulta ser un número. La idea es intentar usar una operación float () y usar el error devuelto para determinar si la cadena es o no un número.


fuente
Debe mover la parte anexa de esa función a una instrucción else para evitar activar accidentalmente la excepción en caso de que haya algo mal con la matriz.
DarwinSurvivor
1

También utilicé la función que mencionaste, 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 su función, que devolverá falso en ese tipo de entrada y no fallará 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
mathfac
fuente
1

Este código maneja los exponentes, flotantes y enteros, sin usar expresiones regulares.

return True if str1.lstrip('-').replace('.','',1).isdigit() or float(str1) else False
ravi tanwar
fuente
1

Función de ayuda del usuario:

def if_ok(fn, string):
  try:
    return fn(string)
  except Exception as e:
    return None

entonces

if_ok(int, my_str) or if_ok(float, my_str) or if_ok(complex, my_str)
is_number = lambda s: any([if_ok(fn, s) for fn in (int, float, complex)])
Samantha Atkins
fuente
0

Puede generalizar la técnica de excepción de una manera útil devolviendo valores más útiles que Verdadero y Falso. Por ejemplo, esta función pone comillas alrededor de las cadenas pero deja solo los números. Que es justo lo que necesitaba para un filtro rápido y sucio para hacer algunas definiciones variables para R.

import sys

def fix_quotes(s):
    try:
        float(s)
        return s
    except ValueError:
        return '"{0}"'.format(s)

for line in sys.stdin:
    input = line.split()
    print input[0], '<- c(', ','.join(fix_quotes(c) for c in input[1:]), ')'
Thruston
fuente
0

Estaba trabajando en un problema que me llevó a este hilo, a saber, cómo convertir una colección de datos en cadenas y números de la manera más intuitiva. Después de leer el código original, me di cuenta de que lo que necesitaba era diferente de dos maneras:

1 - Quería un resultado entero si la cadena representaba un entero

2 - Quería que un número o un resultado de cadena se pegara a una estructura de datos

así que adapté el código original para producir esta derivada:

def string_or_number(s):
    try:
        z = int(s)
        return z
    except ValueError:
        try:
            z = float(s)
            return z
        except ValueError:
            return s
usuario1508746
fuente
0

Prueba esto.

 def is_number(var):
    try:
       if var == int(var):
            return True
    except Exception:
        return False
TheRedstoneLemon
fuente
No responde conis_number('10')
geotheory
@geotheory, ¿qué quieres decir con "no responde"?
Solomon Ucko
0
def is_float(s):
    if s is None:
        return False

    if len(s) == 0:
        return False

    digits_count = 0
    dots_count = 0
    signs_count = 0

    for c in s:
        if '0' <= c <= '9':
            digits_count += 1
        elif c == '.':
            dots_count += 1
        elif c == '-' or c == '+':
            signs_count += 1
        else:
            return False

    if digits_count == 0:
        return False

    if dots_count > 1:
        return False

    if signs_count > 1:
        return False

    return True
Amir Saniyan
fuente