Domanda

Ho un po 'di codice Python che attraversa una lista di stringhe e li converte in numeri interi o numeri in virgola mobile, se possibile. Fare questo per gli interi è abbastanza facile

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

Numeri in virgola mobile sono più difficili. In questo momento sto usando partition('.') di dividere la corda e il controllo per assicurarsi che una o entrambe le parti sono 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)

Questo funziona, ma, ovviamente, l'istruzione if per che è un po 'di un orso. L'altra soluzione ho considerato è quello di avvolgere solo la conversione in un blocco try / catch e vedere se riesce, come descritto in questa domanda .

Qualcuno ha altre idee? approcci opinioni sui meriti relativi di partizione e try / catch?

È stato utile?

Soluzione

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("^\d+?\.\d+?$", element) is None:
    print "Not float"

Altri suggerimenti

metodo di Python per verificare float:

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

Non ottenere po 'dai folletti nascosti nella barca galleggiante! FARE Unit Testing!

Che cosa è, e non è un galleggiante potrebbe sorprendervi:

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
'1.43'.replace('.','',1).isdigit()

che restituirà true solo se c'è uno o no '' nella stringa di cifre.

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

restituirà false

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

restituirà false

Se ti importasse sulle prestazioni (e non sto suggerendo si dovrebbe), l'approccio try-based è il chiaro vincitore (rispetto al tuo approccio basato sulla partizione o l'approccio regexp), a patto che non ti aspetti un sacco di stringhe non valide, nel qual caso è potenzialmente più lento (presumibilmente a causa del costo di gestione delle eccezioni).

Anche in questo caso, non sto suggerendo che ti interessano le prestazioni, solo dando i dati nel caso in cui si sta facendo questo 10 miliardi di volte al secondo, o qualcosa del genere. Inoltre, il codice di partizione basata non gestisce almeno una stringa valida.

$ ./floatstr.py
F..
partition sad: 3.1102449894
partition happy: 2.09208488464
..
re sad: 7.76906108856
re happy: 7.09421992302
..
try sad: 12.1525540352
try happy: 1.44165301323
.
======================================================================
FAIL: test_partition (__main__.ConvertTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./floatstr.py", line 48, in test_partition
    self.failUnless(is_float_partition("20e2"))
AssertionError

----------------------------------------------------------------------
Ran 8 tests in 33.670s

FAILED (failures=1)

Ecco il codice (Python 2.6, regexp preso da John Gietzen di risposta ):

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

TL; DR :

  • Se l'input è per lo più stringhe che possono essere convertito in carri, il metodo try: except: è il miglior metodo di Python nativo.
  • Se l'input è per lo più stringhe che non può essere convertito in carri allegorici, le espressioni regolari o il metodo della partizione sarà migliore.
  • Se siete 1) sicuri del vostro input o bisogno di più velocità e 2) non mente e in grado di installare un terzo C-estensione, fastnumbers funziona molto bene.

C'è un altro metodo disponibile tramite un modulo di terze parti chiamato fastnumbers (divulgazione, io sono il autore); fornisce una funzione chiamata isfloat . Ho preso l'esempio unittest delineato da Jacob Gabrielson in questa risposta , ma ha aggiunto il metodo fastnumbers.isfloat. Vorrei anche notare che l'esempio di Giacobbe non ha fatto giustizia alla possibilità regex perché la maggior parte del tempo in quell'esempio è stato speso in ricerche globali a causa del l'operatore punto ... ho modificato quella funzione di dare un confronto più equo per 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 si può vedere, regex in realtà non è così male come in origine sembrava, e se si dispone di un vero e proprio bisogno di velocità, il metodo fastnumbers è abbastanza buono.

Proprio per la varietà qui è 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 può contenere fino a tutti i casi di galleggiante, anche se soprattutto quando v'è un esponente. Per risolvere che assomiglia a questo. Ciò restituirà Vero solo val è un galleggiante e False per int, ma è probabilmente meno performante rispetto 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

Questa espressione regolare verifica per i numeri in virgola mobile scientifica:

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

Tuttavia, credo che la soluzione migliore è quella di utilizzare il parser in una prova.

Se non è necessario preoccuparsi di espressioni scientifici o di altro di numeri e si lavora solo con le stringhe che potrebbero essere i numeri con o senza un periodo:

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 si è accidentalmente convertendo quello che dovrebbe essere un int, in un galleggiante.

Versione semplificata della funzione is_digit(str), che basta in molti casi (non considera notazione esponenziale e "nan" di valore):

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

Ho usato la funzione già accennato, ma ben presto mi accorgo che le stringhe come "Nan", "Inf", ed è la variazione sono considerati come numero. Quindi vi propongo versione migliorata della funzione, che restituirà falso su quelli tipo di ingresso e non mancherà 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

Prova a convertire a galleggiare. Se c'è 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)

Output:

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

Ero alla ricerca di un codice simile, ma sembra che con try / excepts è il modo migliore. Ecco il codice che sto utilizzando. Esso comprende una funzione di reset automatico se l'ingresso non è valido. Ho bisogno di controllare se l'ingresso è maggiore di 0 e se così convertirlo in un galleggiante.

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: ")
str(strval).isdigit()

sembra essere semplice.

Maniglie valori memorizzati come una stringa o int o float

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top