Bir dizenin Python'da float'a dönüştürülüp dönüştürülemeyeceğini kontrol etme


183

Dizelerin bir listesi üzerinden çalışan ve mümkünse tamsayılara veya kayan nokta numaralarına dönüştüren bazı Python kodu var. Tamsayılar için bunu yapmak oldukça kolaydır

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

Kayan nokta sayıları daha zordur. Şu anda partition('.')dizeyi bölmek için kullanıyorum ve bir veya iki tarafın rakam olduğundan emin olmak için kontrol ediyorum .

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)

Bu işe yarıyor, ama açıkçası bunun için if ifadesi biraz ayı. Düşündüğüm diğer çözüm, dönüşümü bir try / catch bloğuna sarmak ve bu soruda açıklandığı gibi başarılı olup olmadığını görmek .

Başka fikri olan var mı? Bölümleme ve dene / yakala yaklaşımlarının göreceli esasına ilişkin görüşler?

Yanıtlar:


306

Ben sadece ...

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

.. bu basit ve işe yarıyor

Başka bir seçenek normal bir ifade olacaktır:

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

3
@ S.Lott: Bunun uygulandığı dizelerin çoğu ints veya float olur.
Chris Upchurch

10
Normal ifadeniz uygun değil. "^ \ d + \. \ d + $" yukarıdakiyle aynı hızda bir eşleşme başarısız olur, ancak daha hızlı başarılı olur. Ayrıca, daha doğru bir yol şöyle olur: "^ [+ -]? \ D (>? \. \ D +)? $" Ancak, yine de bu gibi sayılarla eşleşmiyor: +
1.0e

86
Bunun dışında "will_it_float" fonksiyonunu isimlendirmeyi unuttunuz.
monte edilmemiş

3
İkinci seçenek nan ve üstel ifadeyi yakalamaz - 2e3 gibi.
Patrick B.

4
Bence normal ifade negatif sayıları ayrıştırmıyor.
Carlos

191

Şamandıra için Python yöntemi:

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

Şamandıra teknesinde saklanan goblinler tarafından ısırmayın! TEST YAPMAK!

Bir şamandıra nedir ve olmayan bir şey sizi şaşırtabilir:

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
Mükemmel cevap. Sadece float = True: isfloat(" 1.23 ")ve isfloat(" \n \t 1.23 \n\t\n"). Web isteklerinde kullanışlıdır; önce beyaz boşlukları düzeltmeye gerek yok.
BareNakedCoder

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

bu truesadece bir tane ya da hiç '' varsa döner . rakamlar dizesinde.

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

dönecek false

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

dönecek false


3
Optimal değil ama aslında oldukça zeki. +/- ve üsleri ele almayacak.
Mad Physicist

Yıllar geç, ama bu güzel bir yöntem. Pandalar veri çerçevesinde aşağıdakileri kullanarak benim için çalıştı:[i for i in df[i].apply(lambda x: str(x).replace('.','').isdigit()).any()]
Mark Moretto

1
@MarkMoretto Negatif sayıların varlığını öğrendiğinizde şok olacaksınız
David Heffernan

Pozitif şamandıraları veya sayıları kontrol etmem gereken senaryom için en iyi tek astar. Severim.
MJohnyJ

8

TL; DR :

  • Girişinizi çoğunlukla dizeleri ise olabilir float dönüştürülebilir, try: except:yöntem en iyi yerli Python yöntemidir.
  • Girişinizi çoğunlukla dizeleri ise edemez yüzen, düzenli ifadeler veya bölme yöntemi daha iyi olacak dönüştürülebilir.
  • 1) girişinizden emin değilseniz veya daha fazla hıza ihtiyacınız varsa ve 2) aldırmayın ve üçüncü taraf bir C uzantısı yükleyebiliyorsanız , fastnumbers çok iyi çalışır.

Fastnumbers adı verilen bir üçüncü taraf modülü aracılığıyla başka bir yöntem daha mevcuttur (açıklama, ben yazarım); isfloat adlı bir işlev sağlar . Bu cevapta Jacob Gabrielson tarafından özetlenen birim test örneğini aldım , ancak fastnumbers.isfloatyöntemi ekledim . Jacob'un örneğinin normal ifade seçeneğine adalet yapmadığına da dikkat etmeliyim çünkü bu örnekteki çoğu zaman nokta operatörü nedeniyle küresel aramalarda geçiyordu ... Bu işlevi daha adil bir karşılaştırma yapmak için değiştirdim 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()

Makinemde çıktı:

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

Gördüğünüz gibi, normal ifade aslında göründüğü kadar kötü değildir ve hıza gerçek bir ihtiyacınız varsa, fastnumbersyöntem oldukça iyidir.


yüzen
sayılara

5

Performansı önemsediyseniz (ve yapmanız gerektiğini önermiyorum), çok fazla beklemediğiniz sürece, denemeye dayalı yaklaşım açık kazanır (bölüm tabanlı yaklaşımınız veya normal ifade yaklaşımınızla karşılaştırıldığında). geçersiz dizeler, bu durumda potansiyel olarak daha yavaştır (muhtemelen istisna işleme maliyeti nedeniyle).

Yine, performansı önemsemenizi önermiyorum, sadece saniyede 10 milyar kez ya da başka bir şey yapmanız durumunda size veri vermenizi önermiyorum. Ayrıca, bölüm tabanlı kod en az bir geçerli dize işlemez.

$ ./floatstr.py
F ..
üzgün bölme: 3.1102449894
bölüm mutlu: 2.09208488464
..
üzgün: 7.76906108856
mutlu: 7.09421992302
..
üzülmek: 12.1525540352
mutlu deneyin: 1.44165301323
.
================================================== ====================
BAŞARISIZ: test_partition (__main __. ConvertTests)
-------------------------------------------------- --------------------
Geri izleme (en son son arama):
  Test_partition'da "./floatstr.py" dosyası, satır 48,
    self.failUnless (is_float_partition ( "20e2"))
AssertionError

-------------------------------------------------- --------------------
33.670'lerde 8 test koştu

BAŞARISIZ (hatalar = 1)

İşte (Python 2.6, John Gietzen en alınan RegExp kod cevap ):

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

Sadece çeşitlilik için burada başka bir yöntem.

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

Düzenleme: Im emin özellikle bir üsse olsa bile tüm şamandıra vakaları kadar tutamaz. Bunu çözmek için böyle görünüyor. Bu, True için int için bir şamandıra ve False True döndürür ancak muhtemelen normal ifadeden daha az performans gösterir.

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

Kesirler gibi çeşitli Unicode karakterlerde true döndürdüğünden, isnumeric işlevi kötü bir seçim gibi görünür. Dokümanlar şunları söylüyor: "Sayısal karakterler basamaklı karakterleri ve Unicode sayısal değer özelliğine sahip tüm karakterleri içerir, örn. U + 2155, VULGAR FRACTION ONE FIFTH"
gwideman

3

Bu normal ifade bilimsel kayan nokta sayılarını kontrol edecektir:

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

Ancak, en iyi seçeneğin ayrıştırıcıyı denemede kullanmak olduğuna inanıyorum.


2

Bilimsel veya diğer sayı ifadeleri hakkında endişelenmeniz gerekmiyorsa ve yalnızca nokta olan veya olmayan rakamlar olabilecek dizelerle çalışıyorsanız:

fonksiyon

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

Lambda versiyonu

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

Misal

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

Bu şekilde yanlışlıkla bir int olması gereken şeyi bir şamandıraya dönüştürmezsiniz.


2

is_digit(str)Çoğu durumda yeterli olan işlevin basitleştirilmiş sürümü ( üstel gösterim ve "NaN" değerini dikkate almaz ):

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

1

Daha önce bahsedilen işlevi kullandım, ancak yakında dizelerin "Nan", "Inf" olarak değiştiğini ve varyasyonun sayı olarak kabul edildiğini fark ettim. Bu nedenle, girişin bu türünde yanlış döndürecek ve "1e3" varyantlarında başarısız olmayacak fonksiyonun geliştirilmiş sürümünü öneriyorum:

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

1
if text.isalpha():Çekle hemen başlayamaz mıyız ?
Csaba Toth

BTW Aynı ihtiyacım var: NaN, Inf ve şeyleri kabul etmek istemiyorum
Csaba Toth

1

Kayan noktaya dönüştürmeye çalışın. Bir hata varsa, ValueError istisnasını yazdırın.

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

Çıktı:

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

1

Sözlüğü argüman olarak iletmek, kayan noktaya dönüştürülebilecek dizeleri dönüştürecek ve başkalarını bırakacaktır.

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

Ben benzer bir kod arıyordum, ama try / excepts kullanarak en iyi yolu gibi görünüyor. İşte kullanıyorum kodu. Giriş geçersizse bir yeniden deneme işlevi içerir. Giriş 0'dan büyük olup olmadığını kontrol etmek gerekiyordu ve eğer öyleyse bir şamandıra dönüştürün.

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
Bu kod, sorunun nasıl ve neden çözüldüğüne dair bir açıklama da dahil olmak üzere soruyu çözebilir, ancak gönderinizin kalitesini artırmaya yardımcı olabilir ve muhtemelen daha fazla oyla sonuçlanır. Sadece şimdi soran kişi için değil, gelecekte okuyucular için soruyu cevapladığınızı unutmayın. Lütfen açıklama eklemek için cevabınızı düzenleyin ve hangi sınırlamaların ve varsayımların geçerli olduğunu belirtin.
çift ​​bip

0

Bir kayan noktaya dönüştürme etrafında bir deneme testi kullanarak yukarıdaki basit seçeneklerden bazılarını denedim ve cevapların çoğunda bir sorun olduğunu buldum.

Basit test (yukarıdaki cevaplar boyunca):

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

Sorun şu durumlarda ortaya çıkar:

  • Negatif bir sayı başlatmak için '-' girersiniz:

Daha sonra float('-')hangisinin başarısız olduğunu

  • Bir sayı giriyorsunuz, ancak tüm rakamları silmeye çalışıyorsunuz

Daha sonra float('')da aynı şekilde başarısız olan

Sahip olduğum hızlı çözüm:

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

-2
str(strval).isdigit()

basit görünüyor.

Dize veya int veya kayan nokta olarak depolanan değerleri işler


[2]: '123,123'.isdigit () Çıkan [2]: Yanlış
Daniil Mashkin

1
Negatif sayılar için işe yaramaz, lütfen cevabınızı düzeltin
RandomEli

'39 .1'.isdigit ()
Lad Ohad

all ([x.isdigit () str (VAR) .strip ('-') içindeki x için. (',', '.'). split ('.')]) Daha eksiksiz bir şey arıyorsanız uygulanması.
lotrus28
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.