it-swarm.com.de

Prüfen, ob eine Zeichenfolge in Python in Float umgewandelt werden kann

Ich habe einen Python-Code, der eine Liste von Strings durchläuft und diese, falls möglich, in Ganzzahlen oder Gleitkommazahlen konvertiert. Dies für ganze Zahlen zu tun ist ziemlich einfach

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

Fließkommazahlen sind schwieriger. Im Moment benutze ich partition('.'), um die Zeichenfolge zu teilen und zu überprüfen, ob eine oder beide Seiten Ziffern sind.

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)

Das funktioniert, aber natürlich ist die if-Aussage ein bisschen bärig. Die andere Lösung, die ich in Betracht zog, ist, die Konvertierung einfach in einen try/catch-Block zu packen und zu sehen, ob sie erfolgreich ist, wie in diese Frage beschrieben.

Hat noch jemand andere Ideen? Meinungen zu den relativen Vorzügen der Partition und Try/Catch-Ansätze?

128
Chris Upchurch

Ich würde nur verwenden ..

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

..es ist einfach und es funktioniert

Eine andere Option wäre ein regulärer Ausdruck:

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

Python-Methode zum Prüfen auf Float:

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

Lassen Sie sich nicht von den Kobolden beben, die sich im Schwimmerboot verstecken! UNIT TESTEN!

Was ist und ist kein Schwimmer kann Sie überraschen:

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

was true nur zurückgibt, wenn es eine oder keine '.' in der Ziffernfolge .

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

gibt false.__ zurück.

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

wird false zurückgeben

11
TulasiReddy

Wenn Sie Wert auf Leistung gelegt haben (und ich schlage nicht vor, dass Sie dies tun sollten), ist der try-basierte Ansatz der klare Gewinner (verglichen mit dem partitionierten Ansatz oder dem regexp-Ansatz), sofern Sie nicht viel davon erwarten ungültige Zeichenfolgen. In diesem Fall ist sie möglicherweise langsamer (vermutlich aufgrund der Kosten für die Ausnahmebehandlung).

Ich schlage nicht vor, dass Sie sich für die Leistung interessieren, sondern Ihnen lediglich die Daten zur Verfügung stellen, falls Sie dies 10 Milliarden Mal pro Sekunde tun. Der partitionsbasierte Code verarbeitet auch nicht mindestens eine gültige Zeichenfolge.

 $ ./floatstr.py
F..
partition traurig: 3.1102449894 
 Partition glücklich: 2.09208488464 
..
 re traurig: 7.76906108856
re glücklich: 7.09421992302 
..
 Versuch traurig: 12.1525540352 
 Versuch glücklich: 1.44165301323 
.
 ================================================ =============================================__. FAIL: test_partition (__main __. ConvertTests) 
-------------------------------------- ----------------------------
 Traceback (letzter Anruf zuletzt): 
 Datei "./floatstr.py", Zeile 48, in test_partition 
 self.failUnless (is_float_partition ("20e2")) 
 AssertionError 

----------------------------- -----------------------------------------
 8 Tests in 33,670 Sekunden durchgeführt 

 FAILED (Ausfälle = 1) 

Hier ist der Code (Python 2.6, regexp aus John Gietzen answer ):

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 __== '__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()
5

TL; DR :

  • Wenn Ihre Eingabe hauptsächlich aus Strings besteht, die can in Floats konvertiert werden können, ist die Methode try: except: Die beste native Python) -Methode.
  • Wenn Ihre Eingabe hauptsächlich aus Zeichenfolgen besteht, die nicht möglich in Gleitkommazahlen konvertiert werden können, sind reguläre Ausdrücke oder die Partitionsmethode besser.
  • Wenn Sie 1) unsicher sind oder mehr Geschwindigkeit benötigen und 2) nichts dagegen haben und eine C-Erweiterung eines Drittanbieters installieren können, funktioniert fastnumbers sehr gut.

Über ein Drittanbieter-Modul namens fastnumbers (Offenlegung, ich bin der Autor) ist eine andere Methode verfügbar. Es bietet eine Funktion namens isfloat . Ich habe das unanständigste Beispiel genommen, das Jacob Gabrielson in diese Antwort skizziert hat, aber die Methode fastnumbers.isfloat Hinzugefügt. Ich sollte auch beachten, dass Jacobs Beispiel der Regex-Option nicht gerecht wurde, da die meiste Zeit in diesem Beispiel wegen des Punktoperators in globalen Suchvorgängen verbracht wurde ... Ich habe diese Funktion geändert, um einen faireren Vergleich mit 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 __== '__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()

Auf meinem Computer lautet die Ausgabe:

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

Wie Sie sehen können, ist Regex tatsächlich nicht so schlecht, wie es ursprünglich schien, und wenn Sie ein echtes Bedürfnis nach Geschwindigkeit haben, ist die fastnumbers -Methode ziemlich gut.

5
SethMMorton

Nur zur Abwechslung gibt es hier eine andere Methode.

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

Edit: Ich bin sicher, dass es nicht für alle Fälle von Float geeignet ist, insbesondere wenn es einen Exponenten gibt. Um das zu lösen sieht es so aus. Dies gibt True zurück. Nur Val ist ein Float und False für Int, ist aber wahrscheinlich weniger performant als 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
4
Peter Moore

Wenn Sie sich nicht um wissenschaftliche oder andere Ausdrücke von Zahlen kümmern müssen und nur mit Zeichenfolgen arbeiten, bei denen es sich um Zahlen mit oder ohne Punkt handeln kann:

Funktion

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

Lambda-Version

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

Beispiel

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

Auf diese Weise konvertieren Sie nicht aus Versehen, was ein Int sein sollte, in einen Float.

2
kodetojoy

Diese Regex prüft, ob wissenschaftliche Gleitkommazahlen vorhanden sind:

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

Ich glaube jedoch, dass Sie am besten versuchen, den Parser zu verwenden.

2
John Gietzen

Versuchen Sie, in Float umzuwandeln. Wenn ein Fehler auftritt, drucken Sie die ValueError-Ausnahme.

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

Ausgabe:

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

Ich habe die bereits erwähnte Funktion verwendet, aber bald bemerke ich, dass Strings als "Nan", "Inf" und deren Variation als Zahl betrachtet werden. Ich schlage also eine verbesserte Version der Funktion vor, die bei dieser Art der Eingabe false zurückgibt und "1e3" -Varianten nicht versagt:

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

Ich habe nach etwas ähnlichem Code gesucht, aber es sieht so aus, als ob Try/Exceptions der beste Weg ist. Sie enthält eine Wiederholungsfunktion, wenn die Eingabe ungültig ist. Ich musste überprüfen, ob die Eingabe größer als 0 war, und wenn ja, konvertiere sie in einen 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
Lockey
str(strval).isdigit()

scheint einfach zu sein. 

Verarbeitet Werte, die als String oder Int oder Float gespeichert sind

0
muks

Vereinfachte Version der Funktion is_digit(str), die in den meisten Fällen ausreicht (Exponentialschreibweise und "NaN") nicht berücksichtigt:

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