Verifica se una stringa può essere convertita in float in Python


182

Ho del codice Python che scorre attraverso un elenco di stringhe e le converte in numeri interi o numeri in virgola mobile, se possibile. Fare questo per gli interi è abbastanza facile

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

I numeri in virgola mobile sono più difficili. In questo momento sto usando partition('.')per dividere la stringa e controllare per assicurarsi che uno o entrambi i lati siano cifre.

partition = element.partition('.')
if (partition[0].isdigit() and partition[1] == '.' and partition[2].isdigit()) 
    or (partition[0] == '' and partition[1] == '.' and partition[2].isdigit()) 
    or (partition[0].isdigit() and partition[1] == '.' and partition[2] == ''):
  newelement = float(element)

Funziona, ma ovviamente l'istruzione if è un po 'orso. L'altra soluzione che ho preso in considerazione è semplicemente avvolgere la conversione in un blocco try / catch e vedere se ha esito positivo, come descritto in questa domanda .

Qualcuno ha altre idee? Opinioni sui meriti relativi della partizione e approcci try / catch?

Risposte:


305

Vorrei solo usare ..

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

..è semplice e funziona

Un'altra opzione sarebbe un'espressione regolare:

import re
if re.match(r'^-?\d+(?:\.\d+)?$', element) is None:
    print "Not float"

3
@ S.Lott: la maggior parte delle stringhe a cui viene applicato risulterà essere ints o float.
Chris Upchurch,

10
Il tuo regex non è ottimale. "^ \ d + \. \ d + $" fallirà una partita alla stessa velocità di cui sopra, ma avrà successo più velocemente. Inoltre, un modo più corretto sarebbe: "^ [+ -]? \ D (>? \. \ D +)? $" Tuttavia, ciò non corrisponde ancora a numeri come: + 1.0e-10
John Gietzen

86
Tranne che hai dimenticato di nominare la tua funzione "will_it_float".
smontato il

3
La seconda opzione non catturerà l'espressione nan ed esponenziale - come 2e3.
Patrick B.

4
Penso che la regex non stia analizzando i numeri negativi.
Carlos,

191

Metodo Python per verificare la presenza di float:

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

Non fatevi mordere dai folletti che si nascondono nella barca galleggiante! FARE IL TEST DELL'UNITÀ!

Ciò che è e non è un galleggiante può sorprenderti:

Command to parse                        Is it a float?  Comment
--------------------------------------  --------------- ------------
print(isfloat(""))                      False
print(isfloat("1234567"))               True 
print(isfloat("NaN"))                   True            nan is also float
print(isfloat("NaNananana BATMAN"))     False
print(isfloat("123.456"))               True
print(isfloat("123.E4"))                True
print(isfloat(".1"))                    True
print(isfloat("1,234"))                 False
print(isfloat("NULL"))                  False           case insensitive
print(isfloat(",1"))                    False           
print(isfloat("123.EE4"))               False           
print(isfloat("6.523537535629999e-07")) True
print(isfloat("6e777777"))              True            This is same as Inf
print(isfloat("-iNF"))                  True
print(isfloat("1.797693e+308"))         True
print(isfloat("infinity"))              True
print(isfloat("infinity and BEYOND"))   False
print(isfloat("12.34.56"))              False           Two dots not allowed.
print(isfloat("#56"))                   False
print(isfloat("56%"))                   False
print(isfloat("0E0"))                   True
print(isfloat("x86E0"))                 False
print(isfloat("86-5"))                  False
print(isfloat("True"))                  False           Boolean is not a float.   
print(isfloat(True))                    True            Boolean is a float
print(isfloat("+1e1^5"))                False
print(isfloat("+1e1"))                  True
print(isfloat("+1e1.3"))                False
print(isfloat("+1.3P1"))                False
print(isfloat("-+1"))                   False
print(isfloat("(1)"))                   False           brackets not interpreted

6
Bella risposta. Aggiungo solo altri 2 dove float = True: isfloat(" 1.23 ")e isfloat(" \n \t 1.23 \n\t\n"). Utile nelle richieste web; non è necessario tagliare prima gli spazi bianchi.
BareNakedCoder

22
'1.43'.replace('.','',1).isdigit()

che tornerà truesolo se ce n'è uno o nessun '.' nella stringa di cifre.

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

sarà di ritorno false

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

sarà di ritorno false


3
Non ottimale ma in realtà abbastanza intelligente. Non gestirà +/- e gli esponenti.
Fisico pazzo,

Anni di ritardo, ma questo è un bel metodo. Ha funzionato per me utilizzando quanto segue in un data frame panda:[i for i in df[i].apply(lambda x: str(x).replace('.','').isdigit()).any()]
Mark Moretto,

1
@MarkMoretto Stai per essere scioccato quando apprendi dell'esistenza di numeri negativi
David Heffernan,

Il miglior one-liner per il mio scenario, in cui ho solo bisogno di controllare numeri o float positivi. Mi piace.
MJohnyJ,

8

TL; DR :

  • Se l'input è per lo più stringhe che possono essere convertite in float, il try: except:metodo è il metodo Python nativo migliore.
  • Se l'input è per lo più stringhe che non possono essere convertite in float, le espressioni regolari o il metodo di partizione saranno migliori.
  • Se non sei sicuro del tuo input o hai bisogno di maggiore velocità e 2) non preoccuparti e puoi installare un'estensione C di terze parti, i fastnumbers funzionano molto bene.

Esiste un altro metodo disponibile tramite un modulo di terze parti chiamato fastnumbers (divulgazione, sono l'autore); fornisce una funzione chiamata isfloat . Ho preso l'esempio più semplice delineato da Jacob Gabrielson in questa risposta , ma ho aggiunto il fastnumbers.isfloatmetodo. Dovrei anche notare che l'esempio di Jacob non ha reso giustizia all'opzione regex perché la maggior parte del tempo in quell'esempio è stato speso in ricerche globali a causa dell'operatore punto ... Ho modificato quella funzione per fornire un confronto più equo try: except:.


def is_float_try(str):
    try:
        float(str)
        return True
    except ValueError:
        return False

import re
_float_regexp = re.compile(r"^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+\b)(?:[eE][-+]?[0-9]+\b)?$").match
def is_float_re(str):
    return True if _float_regexp(str) else False

def is_float_partition(element):
    partition=element.partition('.')
    if (partition[0].isdigit() and partition[1]=='.' and partition[2].isdigit()) or (partition[0]=='' and partition[1]=='.' and partition[2].isdigit()) or (partition[0].isdigit() and partition[1]=='.' and partition[2]==''):
        return True
    else:
        return False

from fastnumbers import isfloat


if __name__ == '__main__':
    import unittest
    import timeit

    class ConvertTests(unittest.TestCase):

        def test_re_perf(self):
            print
            print 're sad:', timeit.Timer('ttest.is_float_re("12.2x")', "import ttest").timeit()
            print 're happy:', timeit.Timer('ttest.is_float_re("12.2")', "import ttest").timeit()

        def test_try_perf(self):
            print
            print 'try sad:', timeit.Timer('ttest.is_float_try("12.2x")', "import ttest").timeit()
            print 'try happy:', timeit.Timer('ttest.is_float_try("12.2")', "import ttest").timeit()

        def test_fn_perf(self):
            print
            print 'fn sad:', timeit.Timer('ttest.isfloat("12.2x")', "import ttest").timeit()
            print 'fn happy:', timeit.Timer('ttest.isfloat("12.2")', "import ttest").timeit()


        def test_part_perf(self):
            print
            print 'part sad:', timeit.Timer('ttest.is_float_partition("12.2x")', "import ttest").timeit()
            print 'part happy:', timeit.Timer('ttest.is_float_partition("12.2")', "import ttest").timeit()

    unittest.main()

Sulla mia macchina, l'output è:

fn sad: 0.220988988876
fn happy: 0.212214946747
.
part sad: 1.2219619751
part happy: 0.754667043686
.
re sad: 1.50515985489
re happy: 1.01107215881
.
try sad: 2.40243887901
try happy: 0.425730228424
.
----------------------------------------------------------------------
Ran 4 tests in 7.761s

OK

Come puoi vedere, regex non è poi così male come sembrava in origine, e se hai un reale bisogno di velocità, il fastnumbersmetodo è abbastanza buono.


il controllo veloce dei numeri funziona così bene se hai la maggior parte delle stringhe che non possono essere convertite in float, accelera davvero le cose, grazie
ragardner

5

Se ti preoccupavi delle prestazioni (e non sto suggerendo di farlo), l'approccio basato sul tentativo è il chiaro vincitore (rispetto all'approccio basato sulla partizione o all'approccio regexp), purché non ti aspetti molto stringhe non valide, nel qual caso è potenzialmente più lenta (presumibilmente a causa del costo della gestione delle eccezioni).

Ancora una volta, non sto suggerendo che ti interessi delle prestazioni, ti do solo i dati nel caso in cui lo fai 10 miliardi di volte al secondo, o qualcosa del genere. Inoltre, il codice basato su partizioni non gestisce almeno una stringa valida.

$ ./floatstr.py
F ..
partizione triste: 3.1102449894
partizione felice: 2.09208488464
..
re triste: 7.76906108856
ri felice: 7.09421992302
..
prova triste: 12.1525540352
prova felice: 1.44165301323
.
================================================== ====================
FAIL: test_partition (__main __. ConvertTests)
-------------------------------------------------- --------------------
Traceback (ultima chiamata più recente):
  File "./floatstr.py", riga 48, in test_partition
    self.failUnless (is_float_partition ( "20e2"))
AssertionError

-------------------------------------------------- --------------------
Ho eseguito 8 test in 33.670s

FAILED (guasti = 1)

Ecco il codice (Python 2.6, regexp tratto dalla risposta di John Gietzen ):

def is_float_try(str):
    try:
        float(str)
        return True
    except ValueError:
        return False

import re
_float_regexp = re.compile(r"^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+\b)(?:[eE][-+]?[0-9]+\b)?$")
def is_float_re(str):
    return re.match(_float_regexp, str)


def is_float_partition(element):
    partition=element.partition('.')
    if (partition[0].isdigit() and partition[1]=='.' and partition[2].isdigit()) or (partition[0]=='' and partition[1]=='.' and pa\
rtition[2].isdigit()) or (partition[0].isdigit() and partition[1]=='.' and partition[2]==''):
        return True

if __name__ == '__main__':
    import unittest
    import timeit

    class ConvertTests(unittest.TestCase):
        def test_re(self):
            self.failUnless(is_float_re("20e2"))

        def test_try(self):
            self.failUnless(is_float_try("20e2"))

        def test_re_perf(self):
            print
            print 're sad:', timeit.Timer('floatstr.is_float_re("12.2x")', "import floatstr").timeit()
            print 're happy:', timeit.Timer('floatstr.is_float_re("12.2")', "import floatstr").timeit()

        def test_try_perf(self):
            print
            print 'try sad:', timeit.Timer('floatstr.is_float_try("12.2x")', "import floatstr").timeit()
            print 'try happy:', timeit.Timer('floatstr.is_float_try("12.2")', "import floatstr").timeit()

        def test_partition_perf(self):
            print
            print 'partition sad:', timeit.Timer('floatstr.is_float_partition("12.2x")', "import floatstr").timeit()
            print 'partition happy:', timeit.Timer('floatstr.is_float_partition("12.2")', "import floatstr").timeit()

        def test_partition(self):
            self.failUnless(is_float_partition("20e2"))

        def test_partition2(self):
            self.failUnless(is_float_partition(".2"))

        def test_partition3(self):
            self.failIf(is_float_partition("1234x.2"))

    unittest.main()

4

Solo per varietà ecco un altro metodo per farlo.

>>> all([i.isnumeric() for i in '1.2'.split('.',1)])
True
>>> all([i.isnumeric() for i in '2'.split('.',1)])
True
>>> all([i.isnumeric() for i in '2.f'.split('.',1)])
False

Modifica: sono sicuro che non resisterà a tutti i casi di float, soprattutto se c'è un esponente. Per risolverlo sembra così. Ciò restituirà True solo val è float e False per int ma probabilmente è meno performante di regex.

>>> def isfloat(val):
...     return all([ [any([i.isnumeric(), i in ['.','e']]) for i in val],  len(val.split('.')) == 2] )
...
>>> isfloat('1')
False
>>> isfloat('1.2')
True
>>> isfloat('1.2e3')
True
>>> isfloat('12e3')
False

La funzione isnumerica sembra una scelta sbagliata, poiché ritorna vera su vari caratteri Unicode come le frazioni. I documenti dicono: "I caratteri numerici includono caratteri numerici e tutti i caratteri che hanno la proprietà del valore numerico Unicode, ad esempio U + 2155, VULGAR FRACTION ONE FIFTH"
gwideman

3

Questa regex verificherà la presenza di numeri scientifici in virgola mobile:

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

Tuttavia, credo che la soluzione migliore sia utilizzare il parser in una prova.


2

Se non devi preoccuparti di espressioni scientifiche o di altri numeri e stai lavorando solo con stringhe che potrebbero essere numeri con o senza punto:

Funzione

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

Versione Lambda

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

Esempio

if is_float(some_string):
    some_string = float(some_string)
elif some_string.isdigit():
    some_string = int(some_string)
else:
    print "Does not convert to int or float."

In questo modo non stai convertendo accidentalmente quello che dovrebbe essere un int in un float.


2

Versione semplificata della funzione is_digit(str) , che nella maggior parte dei casi è sufficiente (non considera la notazione esponenziale e il valore "NaN" ):

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

1

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

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

1
Non potremmo iniziare subito con l' if text.isalpha():assegno?
Csaba Toth,

A proposito, ho bisogno dello stesso: non voglio accettare NaN, Inf e roba del genere
Csaba Toth,

1

Prova a convertire in float. Se si verifica un errore, stampare l'eccezione ValueError.

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

Produzione:

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

1

Passando il dizionario come argomento, convertirà le stringhe che possono essere convertite in float e ne lasceranno altre

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

0

Stavo cercando un codice simile, ma sembra che usare try / excepts sia il modo migliore. Ecco il codice che sto usando. Include una funzione di nuovo tentativo se l'input non è valido. Ho dovuto verificare se l'input era maggiore di 0 e in tal caso convertirlo in un float.

def cleanInput(question,retry=False): 
    inputValue = input("\n\nOnly positive numbers can be entered, please re-enter the value.\n\n{}".format(question)) if retry else input(question)
    try:
        if float(inputValue) <= 0 : raise ValueError()
        else : return(float(inputValue))
    except ValueError : return(cleanInput(question,retry=True))


willbefloat = cleanInput("Give me the number: ")

0
def try_parse_float(item):
  result = None
  try:
    float(item)
  except:
    pass
  else:
    result = float(item)
  return result

2
Sebbene questo codice possa risolvere la domanda, inclusa una spiegazione di come e perché questo risolva il problema, contribuirebbe davvero a migliorare la qualità del tuo post e probabilmente darebbe più voti positivi. Ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo per la persona che chiede ora. Si prega di modificare la risposta per aggiungere spiegazioni e dare un'indicazione di ciò si applicano le limitazioni e le assunzioni.
doppio segnale acustico

0

Ho provato alcune delle opzioni semplici sopra, usando un test di prova per la conversione in float, e ho scoperto che c'è un problema nella maggior parte delle risposte.

Test semplice (sulla falsariga delle risposte sopra):

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

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

Il problema si presenta quando:

  • Inserisci '-' per iniziare un numero negativo:

Stai quindi provando float('-')quale fallisce

  • Immettere un numero, ma quindi provare a eliminare tutte le cifre

Stai quindi provando float('')che allo stesso modo fallisce

La soluzione rapida che ho avuto è:

def _test_num(P):
    if P == '' or P == '-': return True
    try: 
        float(P)
        return True
    except ValueError:
        return False

-2
str(strval).isdigit()

sembra essere semplice.

Gestisce i valori memorizzati come stringa o int o float


In [2]: '123.123'.isdigit () Out [2]: False
Daniil Mashkin,

1
Non funziona per i numeri negativi letterali, correggi la tua risposta
RandomEli

'39 .1'.isdigit ()
Ohad the Lad

all ([x.isdigit () per x in str (VAR) .strip ('-'). sostituisci (',', '.'). split ('.')]) Se stai cercando un più completo implementazione.
lotrus28,
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.