with - python convert string to float




Come posso verificare se una stringa è un numero(float)? (20)

Qual è il miglior modo possibile per verificare se una stringa può essere rappresentata come un numero in Python?

La funzione che attualmente ho al momento è:

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

Che, non solo è brutto e lento, sembra goffo. Tuttavia non ho trovato un metodo migliore perché chiamare float nella funzione principale è ancora peggio.


Che, non solo è brutto e lento, sembra goffo.

Ci vorrà un po 'di tempo per abituarsi, ma questo è il modo pitonico per farlo. Come è stato già sottolineato, le alternative sono peggiori. Ma c'è un altro vantaggio nel fare le cose in questo modo: il polimorfismo.

L'idea centrale alla base della digitazione anatra è che "se cammina e parla come un'anatra, allora è un'anatra". Cosa succede se decidi di dover creare una sottoclasse di stringhe in modo da poter cambiare il modo in cui determini se qualcosa può essere convertito in un float? O se decidessi di testare completamente qualche altro oggetto? Puoi fare queste cose senza dover modificare il codice precedente.

Altre lingue risolvono questi problemi utilizzando le interfacce. Salverò l'analisi di quale soluzione è migliore per un altro thread. Il punto, però, è che Python è decisamente sul lato di digitazione dell'equazione, e probabilmente dovresti abituarti alla sintassi come questa se pensi di fare molta programmazione in Python (ma questo non significa devi piacerti ovviamente).

Un'altra cosa che dovresti prendere in considerazione: Python è piuttosto veloce nel lanciare e catturare eccezioni rispetto a molti altri linguaggi (30 volte più veloce di .Net per esempio). Diamine, il linguaggio stesso genera anche eccezioni per comunicare condizioni di programma normali non eccezionali (ogni volta che usi un ciclo for). Quindi, non mi preoccuperei troppo degli aspetti prestazionali di questo codice fino a quando non noterai un problema significativo.


Quale, non solo è brutto e lento

Disputerò entrambi.

Una regex o altra analisi delle stringhe sarebbe più brutta e più lenta.

Non sono sicuro che nulla potrebbe essere più veloce di quanto sopra. Chiama la funzione e restituisce. Try / Catch non introduce molti overhead perché l'eccezione più comune viene catturata senza una ricerca estesa di frame stack.

Il problema è che qualsiasi funzione di conversione numerica ha due tipi di risultati

  • Un numero, se il numero è valido
  • Un codice di stato (ad esempio, tramite errno) o un'eccezione per mostrare che non è stato possibile analizzare un numero valido.

C (ad esempio) si aggira intorno a questo in un certo numero di modi. Python lo espone chiaramente ed esplicitamente.

Penso che il tuo codice per farlo sia perfetto.


Aggiornato dopo che Alfe ha sottolineato che non è necessario verificare la presenza del float separatamente, dato che maniglie complesse sono entrambe:

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

    return True

Precedentemente detto: alcuni casi rari potrebbero essere necessari anche per verificare numeri complessi (ad esempio 1 + 2i), che non possono essere rappresentati da un float:

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

C'è un'eccezione che potresti voler prendere in considerazione: la stringa "NaN"

Se vuoi che is_number restituisca FALSE per 'NaN', questo codice non funzionerà quando Python lo converte alla sua rappresentazione di un numero che non è un numero (parla di problemi di identità):

>>> float('NaN')
nan

Altrimenti, in realtà dovrei ringraziarti per il pezzo di codice che ora uso estesamente. :)

G.


Ho fatto un test di velocità. Diciamo che se la stringa è probabile che sia un numero la strategia try / except è la più veloce possibile.Se la stringa non è probabile che sia un numero e sei interessato al controllo Integer , vale la pena di fare qualche test (isdigit più intestazione '-'). Se sei interessato a verificare il numero float, devi usare il codice try / except tranne che escape.


Il lancio su float e la cattura di ValueError è probabilmente il modo più veloce, dal momento che float () è specificamente pensato per questo. Tutto il resto che richiede l'analisi delle stringhe (regex, ecc.) Sarà probabilmente più lento a causa del fatto che non è sintonizzato per questa operazione. I miei $ 0,02.


Nel caso in cui si stiano cercando gli interi (positivi, non firmati) anziché i float, è possibile utilizzare la funzione isdigit() per gli oggetti stringa.

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

Metodi di stringa: isdigit()

C'è anche qualcosa sulle stringhe Unicode, che non ho molta familiarità con Unicode - È decimale / decimale


Per le stringhe di numeri diversi, try: except: è in realtà più lento delle espressioni regolari. Per le stringhe di numeri validi, l'espressione regolare è più lenta. Quindi, il metodo appropriato dipende dal tuo input.

Se si scopre di essere in un bind di prestazioni, è possibile utilizzare un nuovo modulo di terze parti chiamato fastnumbers che fornisce una funzione chiamata isfloat . Completa divulgazione, io sono l'autore. Ho incluso i suoi risultati nelle tempistiche seguenti.

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

Come potete vedere

  • try: except: era veloce per l'input numerico ma molto lento per un input non valido
  • regex è molto efficiente quando l'input non è valido
  • fastnumbers vince in entrambi i casi

Prova questo.

 def is_number(var):
    try:
       if var == int(var):
            return True
    except Exception:
        return False


Quindi, per mettere tutto insieme, verificando Nan, infinito e numeri complessi (sembrerebbe che siano specificati con j, non io, cioè 1 + 2j) si traduca in:

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

So che questo è particolarmente vecchio, ma vorrei aggiungere una risposta, credo che copra le informazioni mancanti dalla risposta più votata che potrebbe essere molto utile per chiunque la trovi:

Per ciascuno dei seguenti metodi, collegarli con un conteggio se è necessario accettare qualsiasi input. (Supponendo che stiamo usando definizioni vocali di numeri interi piuttosto che 0-255, ecc.)

x.isdigit() funziona bene per verificare se x è un intero.

x.replace('-','').isdigit() funziona bene per verificare se x è un negativo. (Check - in prima posizione)

x.replace('.','').isdigit() funziona bene per verificare se x è un decimale.

x.replace(':','').isdigit() funziona bene per verificare se x è un rapporto.

x.replace('/','',1).isdigit() funziona bene per verificare se x è una frazione.


cosa ne pensi di questo:

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

che restituirà true solo se ce n'è uno o nessun '.' nella stringa di cifre.

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

restituirà false

modifica: appena visto un altro commento ... aggiungendo un .replace(badstuff,'',maxnum_badstuff) per altri casi può essere fatto. se stai passando sale e non condimenti arbitrari (ref: xkcd#974 ) questo andrà bene: P


TL; DR La soluzione migliore è s.replace('.','',1).isdigit()

Ho fatto alcuni benchmarks confrontando i diversi approcci

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

Se la stringa non è un numero, il blocco except è piuttosto lento. Ma ancora più importante, il metodo try-except è l'unico approccio che gestisce correttamente le notazioni scientifiche.

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 notazione floating ".1234" non è supportata da:
- 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 notazione scientifica "1.000000e + 50" non è supportata da:
- is_number_regex
- is_number_repl_isdigit
La notazione scientifica "1e50" non è supportata da:
- is_number_regex
- is_number_repl_isdigit

EDIT: i risultati del benchmark

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

dove sono state testate le seguenti funzioni

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


RyanN suggerisce

Se vuoi restituire False per un NaN e Inf, cambia linea in x = float (s); return (x == x) e (x - 1! = x). Questo dovrebbe restituire True per tutti i float tranne Inf e NaN

Ma questo non funziona, perché per i float sufficientemente grandi, x-1 == xrestituisce true. Per esempio,2.0**54 - 1 == 2.0**54


usa seguendo si gestisce tutti i casi: -

import re
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.3') 
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.')
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '.3')
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.3sd')
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.3')

Avevo bisogno di determinare se una stringa venisse convertita in tipi di base (float, int, str, bool). Dopo non aver trovato nulla su internet ho creato questo:

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)

Esempio

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       

Puoi catturare il tipo e usarlo

s = "6.0"
type_ = str_to_type(s) # float
f = type_(s) 

Ecco il mio modo semplice di farlo. Diciamo che sto eseguendo il looping di alcune stringhe e voglio aggiungerle a un array se risultano essere numeri.

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

Sostituisci myvar.apppend con qualsiasi operazione tu voglia fare con la stringa se risulta essere un numero. L'idea è di provare a utilizzare un'operazione float () e utilizzare l'errore restituito per determinare se la stringa è o meno un numero.


Se vuoi sapere se l' intera stringa può essere rappresentata come un numero, ti consigliamo di usare un'espressione regolare (o magari convertire il float in una stringa e confrontarlo con la stringa sorgente, ma suppongo che non sia molto veloce ).


Stavo lavorando su un problema che mi ha portato a questo thread, ovvero come convertire una raccolta di dati in stringhe e numeri nel modo più intuitivo. Mi sono reso conto dopo aver letto il codice originale che quello di cui avevo bisogno era diverso in due modi:

1 - Volevo un risultato intero se la stringa rappresentava un numero intero

2 - Volevo un numero o un risultato di una stringa da inserire in una struttura dati

quindi ho adattato il codice originale per produrre questa derivata:

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




type-conversion