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


1609

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

La funzione che ho attualmente in questo momento è:

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

Il che, non solo è brutto e lento, sembra goffo. Tuttavia non ho trovato un metodo migliore perché chiamare floatnella funzione principale è anche peggio.


61
Cosa c'è che non va nella tua attuale soluzione? È breve, veloce e leggibile.
Colonnello Panic,

5
E non devi solo restituire Vero o Falso. È possibile invece restituire il valore opportunamente modificato, ad esempio è possibile utilizzarlo per inserire i non numeri tra virgolette.
Thruston,

7
Non sarebbe meglio restituire il risultato di float nel caso di una conversione riuscita? Hai ancora il controllo per il successo (il risultato è falso) e in realtà DEVI la conversione, che probabilmente vorrai comunque.
Jiminion

8
Anche se questa domanda è più antica, volevo solo dire che questo è un modo elegante che è documentato come EAFP . Quindi probabilmente la soluzione migliore per questo tipo di problema.
thiruvenkadam,

7
Non restituire il risultato di float (s) o None in caso di errore. se poi lo usi perché x = float('0.00'); if x: use_float(x);ora hai un bug nel tuo codice. I valori veritieri sono il motivo per cui queste funzioni sollevano un'eccezione piuttosto che tornare Nonein primo luogo. Una soluzione migliore è semplicemente quella di evitare la funzione di utilità e circondare la chiamata in modo che fluttui try catchquando si desidera utilizzarla.
Ovangle

Risposte:


699

Che, non solo è brutto e lento

Controverei entrambi.

Un regex o un altro metodo di analisi delle stringhe sarebbe più brutto e più lento.

Non sono sicuro che qualcosa di molto potrebbe essere più veloce di quanto sopra. Chiama la funzione e ritorna. Try / Catch non introduce molto overhead perché l'eccezione più comune viene rilevata senza una vasta ricerca 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 es. Tramite errno) o un'eccezione per mostrare che non è stato possibile analizzare alcun numero valido.

C (come esempio) aggira questo in diversi modi. Python lo espone in modo chiaro ed esplicito.

Penso che il tuo codice per farlo sia perfetto.


21
Non penso che il codice sia perfetto (ma penso che sia molto vicino): è più normale mettere solo la parte che viene "testata" nella tryclausola, quindi inserirò return Trueuna elseclausola di try. Uno dei motivi è che con il codice nella domanda, se dovessi rivederlo, dovrei verificare che la seconda affermazione nella tryclausola non possa generare un ValueError: garantito, ciò non richiede troppo tempo o potenza del cervello, ma perché usarne uno quando non è necessario?
Eric O Lebigot,

4
La risposta sembra avvincente, ma mi chiedo perché non sia fornito pronto all'uso ... Copierò questo e lo userò in ogni caso.
salvia,

9
Che schifo. Che ne dite se non mi interessa che cosa il numero è solo che è un numero (che è quello che mi ha portato qui)? Invece di una 1 riga IsNumeric()o finisco con un tentativo / cattura o un altro che avvolge un tentativo / cattura. Ugh
Basic

6
Non è fornito "out of the box" perché ha if is_number(s): x = float(x) else: // faillo stesso numero di righe di codice di try: x = float(x) catch TypeError: # fail. Questa funzione di utilità è un'astrazione del tutto inutile.
Ovangle

12
Ma l'astrazione è il punto centrale delle biblioteche. Avere una funzione 'isNumber' (in qualsiasi lingua) aiuta moltissimo perché puoi incorporarlo direttamente in istruzioni if ​​e avere un codice molto più leggibile e gestibile che si basa su try - catch blocks. Inoltre, se è necessario utilizzare il codice più di una volta in più di una classe / modulo, sono state utilizzate più righe di codice rispetto a una funzione incorporata.
JamEngulfer,

1612

Nel caso in cui tu stia cercando numeri interi (positivi, senza segno) invece di float, puoi usare la isdigit()funzione per oggetti stringa.

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

Metodi di stringa isdigit(): Python2 , Python3

C'è anche qualcosa sulle stringhe Unicode, che non conosco troppo bene Unicode - È decimale / decimale


232
È un
aspetto

22
Non riesce anche con esponenziali: '1e3'.isdigit () -> False
ssc,

35
Mentre Numero! = Cifra, le persone che sono alla ricerca di modi per verificare se una stringa contiene un numero intero potrebbero imbattersi in questa domanda e l'approccio isDigit potrebbe essere perfettamente adatto alla loro applicazione.
Adam Parkin,

8
@AdamParkin: isdigit()e int()hanno opinioni diverse su ciò che è un numero intero, ad esempio, per il carattere Unicode u'\u00b9': u'¹'.isdigit()è Truema int(u'¹')genera ValueError.
jfs,

6
+1: isdigit () potrebbe non essere quello che stava cercando l'OP, ma è esattamente quello che volevo. Potrebbe non essere il caso che questa risposta e questo metodo non coprano tutti i tipi di numeri, ma è comunque altamente pertinente, contrariamente agli argomenti sulla sua accuratezza. Mentre "Numero! = Cifra", la cifra è ancora un sottoinsieme di numeri, in particolare numeri positivi, non negativi e che usano la base 1-10. Inoltre, questo metodo è particolarmente utile e breve per i casi in cui si desidera verificare se una stringa è un ID numerico o meno, che spesso rientra nel sottoinsieme di numeri che ho appena descritto.
Justin Johnson,

161

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

Ho fatto alcuni benchmark 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 di eccezione è piuttosto lento. Ma ancora più importante, il metodo try-tranne è 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 float ".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()

inserisci qui la descrizione dell'immagine


15
per bei grafici +1. Ho visto benchmark e ho visto il grafico, tutto il TL; la cosa DR è diventata chiara e intuitiva.
jcchuks,

Sono d'accordo con @JCChuks: il grafico aiuta molto a ottenere rapidamente tutti i TL; DR. Ma penso che un TL; DR (come: TL; DR : la soluzione migliore sia s.replace('.','',1).isdigit()) dovrebbe apparire all'inizio di questa risposta. In ogni caso dovrebbe essere quello accettato. Grazie!
Simon C.

10
Questo metodo non gestisce i numeri negativi (trattini). Consiglierei di usare solo il metodo float in quanto è meno soggetto a errori e funzionerà ogni volta.
Urchin,

3
La cosa importante da notare è che, anche sul presupposto che non ci possa essere un trattino, il metodo di sostituzione-isdigit è solo più veloce per i non numeri (risultato falso), mentre il metodo try-tranne è più veloce per i numeri (risultato vero). Se la maggior parte dei tuoi input è valida, stai meglio con la soluzione try-tranne!
Markus von Broady,

1
Non funziona su notazioni esponenziali come '1.5e-9'o su negativi.
EL_DON,

68

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à poiché Python lo converte nella sua rappresentazione di un numero che non è un numero (parla di problemi di identità):

>>> float('NaN')
nan

Altrimenti, dovrei davvero ringraziarti per il pezzo di codice che ora uso ampiamente. :)

G.


2
In realtà, NaNpotrebbe essere un buon valore restituire (piuttosto che False) se il testo passato non è in realtà una rappresentazione di un numero. Il controllo è una specie di dolore (il floattipo di Python ha davvero bisogno di un metodo per farlo) ma puoi usarlo nei calcoli senza produrre un errore e devi solo controllare il risultato.
kindall

7
Un'altra eccezione è la stringa 'inf'. Uno info NaNpuò anche essere preceduto da un +o -ed essere ancora accettato.
AGF

4
Se si desidera restituire False per NaN e Inf, modificare la riga in x = float (s); return (x == x) e (x - 1! = x). Ciò dovrebbe restituire True per tutti i galleggianti tranne Inf e NaN
RyanN

5
x-1 == xè vero per i grandi galleggianti più piccoli di inf. Da Python 3.2 è possibile utilizzare math.isfiniteper verificare la presenza di numeri che non sono né NaN né infiniti, oppure controllare entrambi math.isnane math.isinfprima di quello.
Steve Jessop,

56

cosa ne pensi di questo:

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

che tornerà vero solo se c'è uno o nessun '.' nella stringa di cifre.

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

restituirà false

modifica: ho appena visto un altro commento ... è possibile aggiungere un .replace(badstuff,'',maxnum_badstuff)per altri casi. se stai passando sale e condimenti non arbitrari (rif: xkcd # 974 ) questo andrà bene: P


7
Questo, tuttavia, non tiene conto dei numeri negativi.
Michael Barton,

5
O numeri con esponenti come 1.234e56(che potrebbero anche essere scritti come +1.234E+56e diverse altre varianti).
Alfe,

re.match(r'^[+-]*(0[xbo])?[0-9A-Fa-f]*\.?[0-9A-Fa-f]*(E[+-]*[0-9A-Fa-f]+)$', 'str')dovrebbe fare un lavoro migliore nel determinare un numero (ma non tutti, non lo sto sostenendo). Non consiglio di usare questo, molto meglio usare il codice originale del Questioner.
Baldrickk,

se non ti piace questa soluzione, leggi questo prima di effettuare il downgrade!
aloisdg si trasferisce su codidact.com il

amico, questa è la soluzione più intelligente che abbia mai visto in questo sito!
Karam Qusai,

41

Il che, non solo è brutto e lento, sembra goffo.

Potrebbe volerci un po 'di tempo per abituarsi, ma questo è il modo pitonico di farlo. Come è già stato 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 dattilografia è che "se cammina e parla come un'anatra, allora è un'anatra". Che cosa succede se decidi che devi sottoclassare la stringa in modo da poter cambiare il modo in cui determini se qualcosa può essere convertito in un float? O se decidessi di provare completamente qualche altro oggetto? Puoi fare queste cose senza dover cambiare il codice sopra.

Altre lingue risolvono questi problemi utilizzando le interfacce. Salverò l'analisi di quale soluzione è migliore per un altro thread. Il punto, tuttavia, è che python è decisamente dalla parte dell'equazione digitando l'anatra, e probabilmente dovrai abituarti a una sintassi come questa se pensi di fare molta programmazione in Python (ma ciò non significa devi amarlo ovviamente).

Un'altra cosa che potresti prendere in considerazione: Python è piuttosto veloce nel lanciare e catturare eccezioni rispetto a molte altre lingue (30 volte più veloce di .Net per esempio). Diamine, la stessa lingua genera anche eccezioni per comunicare condizioni di programma normali non eccezionali (ogni volta che usi un ciclo for). Pertanto, non mi preoccuperei troppo degli aspetti prestazionali di questo codice fino a quando non noterai un problema significativo.


1
Un altro luogo comune in cui Python utilizza le eccezioni per le funzioni di base è in hasattr()cui è solo una getattr()chiamata racchiusa in un try/except. Tuttavia, la gestione delle eccezioni è più lenta del normale controllo del flusso, quindi l'utilizzo per qualcosa che sarà vero per la maggior parte del tempo può comportare una penalità delle prestazioni.
kindall

Sembra che se vuoi un one-liner, sei SOL
Basic

Anche Pythonic è l'idea che "sia meglio chiedere perdono che permesso", riguardo all'impatto di avere eccezioni economiche.
heltonbiker,

40

Aggiornato dopo che Alfe ha sottolineato che non è necessario controllare il float separatamente come handle complessi entrambi:

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

    return True

Precedentemente detto: in alcuni rari casi potresti anche dover verificare la presenza di numeri complessi (ad es. 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

14
Non sono d'accordo. È MOLTO improbabile nell'uso normale, e sarebbe meglio costruire una chiamata is_complex_number () per quando le stai usando, piuttosto che caricare una chiamata con un'operazione extra per una probabilità dello 0,0001% di malfunzionamento.
Jiminion

3
Puoi float()rimuovere completamente le cose e controllare che la complex()chiamata abbia successo. Tutto analizzato da float()può essere analizzato da complex().
Alfe,

Questa funzione restituirà i valori NaN e Inf di Pandas come valori numerici.
fixxxer,

complex('(01989)')tornerà (1989+0j). Ma float('(01989)')fallirà. Quindi penso che usare complexnon sia una buona idea.
plhn,

26

Per intusare questo:

>>> "1221323".isdigit()
True

Ma perché floatabbiamo bisogno di alcuni trucchi ;-). Ogni numero float ha un punto ...

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

Anche per i numeri negativi basta aggiungere lstrip():

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

E ora otteniamo un modo universale:

>>> '-12.34'.lstrip('-').replace('.','',1).isdigit()
True
>>> '.-234'.lstrip('-').replace('.','',1).isdigit()
False

2
Non gestisce cose come 1.234e56e simili. Inoltre, sarei interessato a come potresti scoprire che 99999999999999999999e99999999999999999999non è un numero. Cercando di analizzarlo lo scopre rapidamente.
Alfe,

Funziona circa il 30% più velocemente rispetto alla soluzione accettata in un elenco di stringhe da 50m e il 150% più veloce in un elenco di stringhe da 5k. 👏
Zev Averbach,

15

Solo Mimic C #

In C # ci sono due diverse funzioni che gestiscono l'analisi dei valori scalari:

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

float.parse ():

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

Nota: se ti stai chiedendo perché ho modificato l'eccezione in TypeError, ecco la documentazione .

float.try_parse ():

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

Nota: non si desidera restituire il booleano 'False' perché è ancora un tipo di valore. Nessuno è migliore perché indica un errore. Naturalmente, se si desidera qualcosa di diverso, è possibile modificare il parametro fail in quello che si desidera.

Per estendere float per includere 'parse ()' e 'try_parse ()' dovrai aggiungere la classe "float" per aggiungere questi metodi.

Se vuoi rispettare le funzioni preesistenti, il codice dovrebbe essere qualcosa del tipo:

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

Nota a margine: Personalmente preferisco chiamarlo Monkey Punching perché mi sento come se stessi abusando della lingua quando lo faccio, ma 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

E il grande Saggio Pitone disse alla Santa Sede Sharpisus: "Qualsiasi cosa tu possa fare, posso fare di meglio; posso fare di meglio di te".


Ultimamente ho codificato principalmente JS e in realtà non l'ho testato, quindi potrebbero esserci degli errori minori. Se ne vedi, sentiti libero di correggere i miei errori.
Evan Plaice,

Per aggiungere supporto per numeri complessi, vedi la risposta di @Matthew Wilcoxson. stackoverflow.com/a/3335060/290340 .
Evan Plaice,

1
L'uso al !posto di notpotrebbe essere un errore minore, ma sicuramente non è possibile assegnare attributi al floatCPython integrato .
BlackJack,

15

Per le stringhe di non numeri, try: except:è in realtà più lento delle espressioni regolari. Per stringhe di numeri validi, regex è più lento. Quindi, il metodo appropriato dipende dal tuo input.

Se ti accorgi che sei in un bind prestazionale, puoi usare un nuovo modulo di terze parti chiamato fastnumbers che fornisce una funzione chiamata isfloat . Divulgazione completa, sono l'autore. Ho incluso i suoi risultati nei tempi 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 input numerico ma molto lento per input non valido
  • regex è molto efficiente quando l'input non è valido
  • fastnumbers vince in entrambi i casi

Sono corretto: -} non sembrava proprio che lo facesse. Forse usare nomi come prep_code_basise prep_code_re_methodavrebbe impedito il mio errore.
Alfe,

Ti dispiace spiegare come funziona il tuo modulo, almeno per la isfloatfunzione?
Solomon Ucko,

@SolomonUcko Ecco un link al codice sorgente per la parte di controllo della stringa: github.com/SethMMorton/fastnumbers/blob/v1.0.0/src/… . Fondamentalmente, cammina attraverso ogni carattere della stringa in ordine e conferma che segue un modello per un float valido. Se l'input è già un numero, utilizza solo il veloce PyFloat_Check .
SethMMorton,

1
Testato contro le migliori alternative in questo thread confermo che questa soluzione è di gran lunga la più veloce. Il secondo metodo più veloce è str(s).strip('-').replace('.','',1).isdigit()che è circa 10 volte più lento!
Alexander McFarlane il

14

So che questo è particolarmente vecchio ma aggiungerei una risposta che ritengo riguardi le informazioni mancanti dalla risposta più votata che potrebbero essere molto utili per chiunque lo trovi:

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

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

x.replace('-','').isdigit() funziona bene per verificare se x è 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.


1
Sebbene per le frazioni, probabilmente devi fare x.replace('/','',1).isdigit()o altrimenti date come il 4/7/2017 sarebbero interpretate male come numeri.
Yuxuan Chen

Per i modi migliori per concatenare le condizioni: stackoverflow.com/q/3411771/5922329
Daniel Braun

13

Questa risposta fornisce una guida passo-passo con funzione con esempi per trovare la stringa è:

  • Intero positivo
  • Positivo / negativo - intero / float
  • Come scartare le stringhe "NaN" (non un numero) mentre si controlla il numero?

Controlla se la stringa è un numero intero positivo

È possibile utilizzare str.isdigit()per verificare se una determinata stringa è un numero intero positivo .

Risultati del campione:

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

Controlla la stringa come positiva / negativa - intera / float

str.isdigit()ritorna Falsese la stringa è un numero negativo o un numero float. Per esempio:

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

Se si desidera verificare anche gli interi negativi efloat , è possibile scrivere una funzione personalizzata per verificarlo come:

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

Esempio di esecuzione:

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

Elimina le stringhe "NaN" (non un numero) mentre controlla il numero

Le funzioni di cui sopra restituiranno Trueper la stringa "NAN" (non un numero) perché per Python è un float valido che rappresenta non è un numero. Per esempio:

>>> is_number('NaN')
True

Per verificare se il numero è "NaN", è possibile utilizzare math.isnan()come:

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

>>> math.isnan(nan_num)
True

Oppure, se non si desidera importare libreria aggiuntiva per verificarlo, è possibile verificarlo semplicemente confrontandolo con se stesso mediante ==. Python ritorna Falsequando il nanfloat viene confrontato con se stesso. Per esempio:

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

Quindi, sopra funzione is_numberpuò essere aggiornato per tornare Falsea"NaN" come:

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

Esempio di esecuzione:

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

PS: ogni operazione per ciascun controllo a seconda del tipo di numero comporta un sovraccarico aggiuntivo. Scegli la versione della is_numberfunzione che si adatta alle tue esigenze.


12

Lanciare in float e catturare ValueError è probabilmente il modo più veloce, poiché float () è specificamente pensato per questo. Qualsiasi altra cosa che richieda l'analisi delle stringhe (regex, ecc.) Sarà probabilmente più lenta a causa del fatto che non è sintonizzata per questa operazione. I miei $ 0,02.


11
Anche i tuoi dollari "2e-2" sono float (un ulteriore argomento per l'utilizzo di float :)
tzot

8
@tzot MAI utilizzare un float per rappresentare un valore monetario.
Luca

6
@Luke: sono totalmente d'accordo con te, anche se non ho mai suggerito di usare i float per rappresentare i valori monetari; Ho appena detto che i valori monetari possono essere rappresentati come float :)
tzot


9

Volevo vedere quale metodo è il più veloce. Nel complesso, i risultati migliori e più coerenti sono stati forniti dalla check_replacefunzione. I risultati più veloci sono stati dati dalla check_exceptionfunzione, ma solo se non è stata generata alcuna eccezione, il che significa che il suo codice è il più efficiente, ma il sovraccarico di generare un'eccezione è piuttosto grande.

Si noti che la verifica della riuscita del cast è l'unico metodo accurato, ad esempio, funziona con, check_exceptionma le altre due funzioni di test restituiranno False per un float valido:

huge_number = float('1e+100')

Ecco il codice di riferimento:

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

Ecco i risultati con Python 2.7.10 su un 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

Ecco i risultati con Python 3.6.5 su un 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

Ecco i risultati con PyPy 2.7.13 su un 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

10
È inoltre necessario testare le prestazioni per casi non validi. Non viene sollevata alcuna eccezione con questi numeri, che è esattamente la parte "lenta".
Ugo Méda,

1
@ UgoMéda ho ricevuto il tuo consiglio dal 2013 e l'ho fatto :)
Ron Reiter,

"Nota che verificare la riuscita di un cast è l'unico metodo accurato" <- questo non è in realtà vero. Ho eseguito il test usando regexp nella mia risposta sopra, e in realtà viene eseguito più velocemente di regexp. Aggiungerò i risultati alla mia risposta sopra.
David Ljung Madison Stellar il

Per inciso, come punto divertente, il tuo creatore di numeri cattivi può effettivamente creare alcuni numeri legali, anche se sarebbe abbastanza raro. :)
David Ljung Madison Stellar il

8

Quindi, per mettere tutto insieme, controllando Nan, infinito e numeri complessi (sembrerebbe che siano specificati con j, non i, cioè 1 + 2j) si traduce 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

Finora la risposta migliore. Grazie
anish

6

L'input può essere il seguente:

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


1-input generale:

L'input di questa funzione può essere tutto!

Verifica se la variabile specificata è numerica. Le stringhe numeriche sono costituite da segno opzionale, qualsiasi numero di cifre, parte decimale opzionale e parte esponenziale opzionale. Pertanto + 0123.45e6 è un valore numerico valido. La notazione esadecimale (ad es. 0xf4c3b00c) e binaria (ad es. 0b10100111001) non è consentita.

funzione is_numeric

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

test:

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

funzione is_float

Verifica se la variabile specificata è float. le stringhe float sono costituite da un segno opzionale, un numero qualsiasi di cifre, ...

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

test:

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

cos'è ast ?


2- Se si è certi che il contenuto della variabile sia String :

usa il metodo 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

3 input numerici:

rilevare il valore int:

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

rilevare galleggiante:

>>> isinstance("45.1", float)
False
>>> isinstance(45.1, float)
True

cos'è " ast"?

4

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


4

Dovevo determinare se una stringa veniva lanciata in tipi base (float, int, str, bool). Dopo 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       

È possibile acquisire il tipo e utilizzarlo

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

3

RyanN suggerisce

Se si desidera restituire False per NaN e Inf, modificare la riga 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 del tutto, perché per float sufficientemente grandi, x-1 == xritorna vero. Per esempio,2.0**54 - 1 == 2.0**54


3

Credo che la soluzione va bene, ma non v'è un'implementazione regexp corretta.

Sembra che ci sia un sacco di odio regexp verso queste risposte che ritengo ingiustificato, regexps può essere ragionevolmente pulito, corretto e veloce. Dipende davvero da cosa stai cercando di fare. La domanda originale era come "verificare se una stringa può essere rappresentata come un numero (float)" (come da titolo). Presumibilmente vorrai usare il valore numerico / float dopo aver verificato che sia valido, nel qual caso il tuo tentativo / tranne ha molto senso. Ma se, per qualche motivo, vuoi solo confermare che una stringa è un numeroallora anche una regex funziona bene, ma è difficile da correggere. Penso che la maggior parte delle risposte regex finora, per esempio, non analizzino correttamente le stringhe senza una parte intera (come ".7") che è un float per quanto riguarda Python. E questo è leggermente complicato da verificare in una singola regex in cui non è richiesta la parte frazionaria. Ho incluso due regex per dimostrarlo.

Solleva l'interessante domanda su cosa sia un "numero". Includete "inf" che è valido come float in Python? Oppure includi numeri che sono "numeri" ma che forse non possono essere rappresentati in Python (come numeri che sono più grandi del float max).

Ci sono anche ambiguità nel modo in cui analizzi i numeri. Ad esempio, che dire di "--20"? È un "numero"? È un modo legale per rappresentare "20"? Python ti permetterà di fare "var = --20" e di impostarlo su 20 (anche se in realtà questo è perché lo tratta come un'espressione), ma float ("- 20") non funziona.

Ad ogni modo, senza ulteriori informazioni, ecco una regex che credo copre tutti gli ints e float mentre Python li analizza .

# 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

Alcuni valori di prova di esempio:

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

L'esecuzione del codice di benchmark nella risposta di @ ron-reiter mostra che questa regex è in realtà più veloce della regex normale ed è molto più veloce nella gestione di valori errati rispetto all'eccezione, il che ha un senso. risultati:

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

Spero che sia giusto - mi piacerebbe conoscere qualsiasi contro esempio. :)
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

2
Non consideri 1e6di rappresentare un numero?
Mark Dickinson,

1

Ecco il mio modo semplice di farlo. Diciamo che sto passando in rassegna alcune stringhe e voglio aggiungerle a un array se si rivelano 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.


È necessario spostare la parte di aggiunta di tale funzione in un'istruzione else per evitare l'attivazione accidentale dell'eccezione in caso di errori nell'array.
DarwinSurvivor,

1

Ho anche usato la funzione che hai citato, ma presto noto che le stringhe come "Nan", "Inf" e la sua variazione sono considerate come numeri. Quindi ti propongo una versione migliorata della tua funzione, che restituirà false su quel tipo di input e non fallirà le varianti "1e3":

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

1

Questo codice gestisce esponenti, float e interi, senza usare regex.

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

1

Funzione di aiuto dell'utente:

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

poi

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

0

È possibile generalizzare la tecnica dell'eccezione in modo utile restituendo valori più utili di Vero e Falso. Ad esempio questa funzione mette le virgolette attorno alle stringhe ma lascia numeri da soli. Il che è proprio quello di cui avevo bisogno per un filtro rapido e sporco per fare alcune definizioni variabili per 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:]), ')'

0

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. Dopo aver letto il codice originale mi sono reso conto che ciò di cui avevo bisogno era diverso in due modi:

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

2 - Volevo che un risultato di un numero o di una stringa rimanesse in una struttura di dati

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

0

Prova questo.

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

Non riesce a rispondere conis_number('10')
geotheory,

@geotheory, cosa intendi con "mancata risposta"?
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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.