Überprüfen, ob eine Zeichenfolge in Python in float konvertiert werden kann


180

Ich habe Python-Code, der eine Liste von Zeichenfolgen durchläuft und diese nach Möglichkeit in Ganzzahlen oder Gleitkommazahlen konvertiert. Dies für ganze Zahlen zu tun ist ziemlich einfach

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

Gleitkommazahlen sind schwieriger. Im Moment teile ich partition('.')die Zeichenfolge und überprüfe, 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 offensichtlich ist die if-Aussage dafür ein bisschen wie ein Bär. Die andere Lösung, die ich in Betracht gezogen habe, besteht darin, die Konvertierung einfach in einen Try / Catch-Block zu packen und zu prüfen, ob sie erfolgreich ist, wie in dieser Frage beschrieben .

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

Antworten:


303

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

3
@ S.Lott: Die meisten Zeichenfolgen, auf die dies angewendet wird, sind Ints oder Floats.
Chris Upchurch

9
Ihre Regex ist nicht optimal. "^ \ d + \. \ d + $" schlägt ein Match mit der gleichen Geschwindigkeit wie oben fehl, ist aber schneller erfolgreich. Ein korrekterer Weg wäre auch: "^ [+ -]? \ D (>? \. \ D +)? $" Das stimmt jedoch immer noch nicht mit Zahlen wie: + 1.0e-10
John Gietzen

86
Nur dass Sie vergessen haben, Ihre Funktion "will_it_float" zu nennen.
Unmontiert

3
Die zweite Option erfasst keine nan- und exponentiellen Ausdrücke - wie z. B. 2e3.
Patrick B.

4
Ich denke, der reguläre Ausdruck analysiert keine negativen Zahlen.
Carlos

190

Python-Methode zur Überprüfung auf float:

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

Lassen Sie sich nicht von den Kobolden beißen, die sich im Schwimmboot verstecken! TESTEN SIE DIE EINHEIT!

Was ist und was nicht, 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

6
Gute Antwort. Fügen Sie einfach 2 weitere hinzu, wobei float = True: isfloat(" 1.23 ")und isfloat(" \n \t 1.23 \n\t\n"). Nützlich bei Webanfragen; Es ist nicht erforderlich, zuerst Leerzeichen zu kürzen.
BareNakedCoder

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

Dies wird truenur zurückgegeben, wenn es ein oder kein '.' gibt. in der Ziffernfolge.

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

wird zurückkehren false

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

wird zurückkehren false


3
Nicht optimal, aber eigentlich ziemlich schlau. Behandelt nicht +/- und Exponenten.
Mad Physicist

Jahre zu spät, aber das ist eine schöne Methode. Arbeitete für mich mit den folgenden in einem Pandas-Datenrahmen:[i for i in df[i].apply(lambda x: str(x).replace('.','').isdigit()).any()]
Mark Moretto

1
@MarkMoretto Sie werden einen Schock erleben, wenn Sie von der Existenz negativer Zahlen erfahren
David Heffernan

Bester Einzeiler für mein Szenario, in dem ich nur nach positiven Floats oder Zahlen suchen muss. Ich mag.
MJohnyJ

8

TL; DR :

  • Wenn Ihre Eingabe hauptsächlich Zeichenfolgen sind , die in Floats konvertiert werden können, ist die try: except:Methode die beste native Python-Methode.
  • Wenn es sich bei Ihrer Eingabe hauptsächlich um Zeichenfolgen handelt, die nicht in Floats konvertiert werden können, sind reguläre Ausdrücke oder die Partitionsmethode besser.
  • Wenn Sie 1) sich Ihrer Eingabe nicht sicher sind oder mehr Geschwindigkeit benötigen und 2) nichts dagegen haben und eine C-Erweiterung eines Drittanbieters installieren können, funktionieren Fastnumbers sehr gut.

Es gibt eine andere Methode, die über ein Drittanbieter-Modul namens Fastnumbers verfügbar ist (Offenlegung, ich bin der Autor). Es bietet eine Funktion namens isfloat . Ich habe das von Jacob Gabrielson in dieser Antwort skizzierte Beispiel genommen , aber die fastnumbers.isfloatMethode hinzugefügt . Ich sollte auch beachten, dass Jacobs Beispiel der Regex-Option nicht gerecht wurde, da die meiste Zeit in diesem Beispiel aufgrund des Punktoperators für globale Suchvorgänge aufgewendet wurde ... Ich habe diese Funktion geändert, um einen faireren Vergleich zu ermöglichen 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()

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 nicht so schlecht, wie es ursprünglich schien, und wenn Sie ein echtes Bedürfnis nach Geschwindigkeit haben, ist die fastnumbersMethode ziemlich gut.


Die schnelle Überprüfung der Zahlen funktioniert so gut, wenn Sie eine Mehrheit von Zeichenfolgen haben, die nicht in Floats konvertiert werden können, was die Dinge wirklich beschleunigt, danke
Ragardner

5

Wenn Sie sich um die Leistung gekümmert haben (und ich schlage nicht vor, dass Sie dies tun sollten), ist der try-basierte Ansatz der klare Gewinner (im Vergleich zu Ihrem partitionbasierten Ansatz oder dem Regexp-Ansatz), solange Sie nicht viel erwarten ungültige Zeichenfolgen. In diesem Fall ist sie möglicherweise langsamer (vermutlich aufgrund der Kosten für die Ausnahmebehandlung).

Auch hier schlage ich nicht vor, dass Sie sich für die Leistung interessieren, sondern nur die Daten für den Fall, dass Sie dies 10 Milliarden Mal pro Sekunde tun, oder so. Außerdem verarbeitet der partitionbasierte Code nicht mindestens eine gültige Zeichenfolge.

$ ./floatstr.py
F ..
Partition traurig: 3.1102449894
Partition glücklich: 2.09208488464
..
Es ist traurig: 7.76906108856
Ich freue mich: 7.09421992302
..
versuche es traurig: 12.1525540352
versuche es 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.670er Jahren durchgeführt

FEHLGESCHLAGEN (Fehler = 1)

Hier ist der Code (Python 2.6, regulärer Ausdruck aus John Gietzens Antwort ):

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

Nur für Abwechslung ist hier eine andere Methode, um es zu tun.

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

Bearbeiten: Ich bin mir sicher, dass es nicht allen Fällen von Float standhält, besonders 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

Die isnumerische Funktion scheint eine schlechte Wahl zu sein, da sie bei verschiedenen Unicode-Zeichen wie Brüchen true zurückgibt. Die Ärzte sagen: "Numerische Zeichen enthalten Ziffernzeichen und alle Zeichen, die die numerische Unicode-Werteigenschaft haben, z. B. U + 2155, VULGAR FRACTION ONE FIFTH"
gwideman

3

Diese Regex prüft auf wissenschaftliche Gleitkommazahlen:

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

Ich glaube jedoch, dass Ihre beste Wette darin besteht, den Parser bei einem Versuch zu verwenden.


2

Wenn Sie sich nicht um wissenschaftliche oder andere Ausdrücke von Zahlen kümmern müssen und nur mit Zeichenfolgen arbeiten, die Zahlen mit oder ohne Punkt sein können:

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 versehentlich ein Int in einen Float.


2

Vereinfachte Version der Funktion is_digit(str) , die in den meisten Fällen ausreicht (ohne Berücksichtigung der Exponentialschreibweise und des "NaN" -Werts):

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

1

Ich habe die bereits erwähnte Funktion verwendet, aber bald stelle ich fest, dass Zeichenfolgen wie "Nan", "Inf" und ihre Variation als Zahl betrachtet werden. Daher schlage ich vor, dass Sie eine verbesserte Version der Funktion haben, die bei dieser Art von Eingabe false zurückgibt und "1e3" -Varianten nicht fehlschlägt:

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

1
Könnten wir nicht sofort mit dem if text.isalpha():Scheck beginnen?
Csaba Toth

Übrigens brauche ich das gleiche: Ich will NaN, Inf und so nicht akzeptieren
Csaba Toth

1

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

Wenn Sie das Wörterbuch als Argument übergeben, werden Zeichenfolgen konvertiert, die in float konvertiert werden können, und andere werden verlassen

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

Ich habe nach einem ähnlichen Code gesucht, aber es sieht so aus, als wäre die Verwendung von try / excepts der beste Weg. Hier ist der Code, den ich benutze. Es 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, sie in einen Float umwandeln.

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
Während dieser Code die Frage lösen kann, einschließlich einer Erklärung, wie und warum dies das Problem löst, würde dies wirklich dazu beitragen, die Qualität Ihres Beitrags zu verbessern, und wahrscheinlich zu mehr Up-Votes führen. Denken Sie daran, dass Sie in Zukunft die Frage für die Leser beantworten, nicht nur für die Person, die jetzt fragt. Bitte bearbeiten Sie Ihre Antwort, um Erklärungen hinzuzufügen und anzugeben, welche Einschränkungen und Annahmen gelten.
Doppelter Piepton

0

Ich habe einige der oben genannten einfachen Optionen ausprobiert und dabei einen Versuchstest zum Konvertieren in einen Float verwendet. Dabei stellte ich fest, dass in den meisten Antworten ein Problem vorliegt.

Einfacher Test (gemäß den obigen Antworten):

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

Das Problem tritt auf, wenn:

  • Sie geben '-' ein, um eine negative Zahl zu beginnen:

Sie versuchen dann, float('-')was fehlschlägt

  • Sie geben eine Nummer ein, versuchen dann aber, alle Ziffern zu löschen

Sie versuchen dann, float('')was ebenfalls ebenfalls fehlschlägt

Die schnelle Lösung, die ich hatte, ist:

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

-2
str(strval).isdigit()

scheint einfach zu sein.

Behandelt Werte, die als Zeichenfolge oder int oder float gespeichert sind


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

1
Es funktioniert nicht für negative Zahlen wörtlich, bitte
korrigieren

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

all ([x.isdigit () für x in str (VAR) .strip ('-'). replace (',', '.'). split ('.')]) Wenn Sie nach einem vollständigeren suchen Implementierung.
lotrus28
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.