Memeriksa apakah string dapat dikonversi menjadi float dengan Python

182

Saya punya beberapa kode Python yang berjalan melalui daftar string dan mengubahnya menjadi bilangan bulat atau angka floating point jika memungkinkan. Melakukan ini untuk bilangan bulat cukup mudah

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

Angka titik mengambang lebih sulit. Saat ini saya menggunakan partition('.')untuk membagi string dan memeriksa untuk memastikan bahwa satu atau kedua sisi adalah digit.

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)

Ini bekerja, tetapi jelas pernyataan if untuk itu adalah sedikit beruang. Solusi lain yang saya pertimbangkan adalah hanya membungkus konversi dalam blok coba / tangkap dan lihat apakah berhasil, seperti dijelaskan dalam pertanyaan ini .

Adakah yang punya ide lain? Pendapat tentang manfaat relatif dari partisi dan mencoba / menangkap pendekatan?

Chris Upchurch
sumber

Jawaban:

305

Saya hanya akan menggunakan ..

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

..itu sederhana, dan berfungsi

Pilihan lain adalah ekspresi reguler:

import re
if re.match(r'^-?\d+(?:\.\d+)?$', element) is None:
    print "Not float"
dbr
sumber
3
@ S.Lott: Sebagian besar string yang diterapkan akan berubah menjadi int atau mengapung.
Chris Upchurch
10
Regex Anda tidak optimal. "^ \ d + \. \ d + $" akan gagal mencocokkan dengan kecepatan yang sama seperti di atas, tetapi akan berhasil lebih cepat. Juga, cara yang lebih benar adalah: "^ [+ -]? \ D (>? \. \ D +)? $" Namun, itu masih tidak cocok dengan angka-angka seperti: + 1.0e-10
John Gietzen
86
Kecuali Anda lupa memberi nama fungsi Anda "will_it_float".
terpasang
3
Opsi kedua tidak akan menangkap ekspresi nan dan eksponensial - seperti 2e3.
Patrick B.
4
Saya pikir regex tidak menguraikan angka negatif.
Carlos
191

Metode python untuk memeriksa float:

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

Jangan terkecoh oleh para goblin yang bersembunyi di kapal yang mengapung! LAKUKAN UNIT MENGUJI!

Apa itu, dan bukan pelampung mungkin akan mengejutkan Anda:

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
Eric Leschinski
sumber
6
Jawaban yang bagus Hanya menambahkan 2 lagi di mana float = True: isfloat(" 1.23 ")and isfloat(" \n \t 1.23 \n\t\n"). Berguna dalam permintaan web; tidak perlu memangkas ruang putih terlebih dahulu.
BareNakedCoder
22
'1.43'.replace('.','',1).isdigit()

yang akan kembali truehanya jika ada satu atau tidak ada '.' dalam deretan digit.

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

akan kembali false

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

akan kembali false

TulasiReddy
sumber
3
Tidak optimal tapi sebenarnya cukup pintar. Tidak akan menangani +/- dan eksponen.
Fisikawan Gila
Beberapa tahun terlambat, tetapi ini adalah metode yang bagus. Bekerja untuk saya menggunakan yang berikut dalam bingkai data panda:[i for i in df[i].apply(lambda x: str(x).replace('.','').isdigit()).any()]
Mark Moretto
1
@MarkMoretto Anda akan terkejut ketika Anda mengetahui keberadaan angka negatif
David Heffernan
Satu-liner terbaik untuk skenario saya, di mana saya hanya perlu memeriksa float atau angka positif. Saya suka.
MJohnyJ
8

TL; DR :

  • Jika input Anda sebagian besar adalah string yang dapat dikonversi menjadi float, try: except:metode ini adalah metode Python asli terbaik.
  • Jika sebagian besar input Anda adalah string yang tidak dapat dikonversi menjadi float, ekspresi reguler atau metode partisi akan lebih baik.
  • Jika Anda 1) tidak yakin dengan input Anda atau membutuhkan kecepatan lebih dan 2) tidak keberatan dan dapat menginstal ekstensi-C pihak ketiga, fastnumber bekerja dengan sangat baik.

Ada metode lain yang tersedia melalui modul pihak ketiga yang disebut fastnumber (pengungkapan, saya penulisnya); ini menyediakan fungsi yang disebut isfloat . Saya telah mengambil contoh unittest yang diuraikan oleh Jacob Gabrielson dalam jawaban ini , tetapi menambahkan fastnumbers.isfloatmetode. Saya juga harus mencatat bahwa contoh Jacob tidak melakukan keadilan terhadap opsi regex karena sebagian besar waktu dalam contoh tersebut dihabiskan dalam pencarian global karena operator titik ... Saya telah memodifikasi fungsi itu untuk memberikan perbandingan yang lebih adil 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()

Di mesin saya, hasilnya adalah:

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

Seperti yang Anda lihat, regex sebenarnya tidak seburuk yang terlihat pada awalnya, dan jika Anda benar-benar membutuhkan kecepatan, fastnumbersmetode ini cukup baik.

SethMMorton
sumber
pemeriksaan angka cepat berfungsi dengan baik jika Anda memiliki sebagian besar string yang tidak dapat dikonversi menjadi float, benar-benar mempercepat, terima kasih
ragardner
5

Jika Anda peduli dengan kinerja (dan saya tidak menyarankan Anda seharusnya), pendekatan berbasis coba adalah pemenang yang jelas (dibandingkan dengan pendekatan berbasis partisi atau pendekatan regexp), selama Anda tidak mengharapkan banyak string tidak valid, dalam hal ini berpotensi lebih lambat (mungkin karena biaya penanganan pengecualian).

Sekali lagi, saya tidak menyarankan Anda peduli dengan kinerja, hanya memberi Anda data jika Anda melakukan ini 10 miliar kali per detik, atau sesuatu. Juga, kode berbasis partisi tidak menangani setidaknya satu string yang valid.

$ ./floatstr.py
F ..
partisi sedih: 3.1102449894
partisi senang: 2.09208488464
..
sedih: 7.76906108856
kembali bahagia: 7.09421992302
..
coba sedih: 12.1525540352
coba bahagia: 1.44165301323
.
================================================== ====================
GAGAL: test_partition (__main __. ConvertTests)
-------------------------------------------------- --------------------
Traceback (panggilan terakhir terakhir):
  File "./floatstr.py", baris 48, di test_partition
    self.failUnless (is_float_partition ("20e2"))
AssertionError

-------------------------------------------------- --------------------
Berlari 8 tes dalam 33,670-an

GAGAL (kegagalan = 1)

Berikut kodenya (Python 2.6, regexp diambil dari jawaban 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()
Jacob Gabrielson
sumber
4

Hanya untuk variasi di sini adalah metode lain untuk melakukannya.

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

Sunting: Saya yakin itu tidak akan berlaku untuk semua kasus float meskipun terutama ketika ada eksponen. Untuk menyelesaikannya terlihat seperti ini. Ini akan mengembalikan True only val adalah float dan False untuk int tetapi mungkin kurang berkinerja daripada 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
Peter Moore
sumber
Fungsi isnumerik terlihat seperti pilihan yang buruk, karena mengembalikan true pada berbagai karakter Unicode seperti pecahan. Documents mengatakan: "Karakter numerik termasuk karakter digit, dan semua karakter yang memiliki properti nilai numerik Unicode, misalnya U + 2155,
VULGAR FRACTION
3

Regex ini akan memeriksa nomor floating point ilmiah:

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

Namun, saya percaya bahwa cara terbaik Anda adalah menggunakan parser dalam mencoba.

John Gietzen
sumber
2

Jika Anda tidak perlu khawatir tentang ekspresi angka atau ilmiah dan hanya bekerja dengan string yang bisa berupa angka dengan atau tanpa tanda titik:

Fungsi

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

Versi lambda

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

Contoh

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

Dengan cara ini Anda tidak sengaja mengubah apa yang seharusnya menjadi int, menjadi pelampung.

Kodetojoy
sumber
2

Versi fungsi is_digit(str) yang disederhanakan , yang mencukupi dalam kebanyakan kasus (tidak mempertimbangkan notasi eksponensial dan nilai "NaN" ):

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

Saya menggunakan fungsi yang telah disebutkan, tetapi segera saya melihat bahwa string sebagai "Nan", "Inf" dan variasi itu dianggap sebagai angka. Jadi saya usulkan versi perbaikan fungsi, yang akan mengembalikan false pada tipe input tersebut dan tidak akan gagal varian "1e3":

def is_float(text):
    # check for nan/infinity etc.
    if text.isalpha():
        return False
    try:
        float(text)
        return True
    except ValueError:
        return False
mathfac
sumber
1
Tidak bisakah kita mulai dengan if text.isalpha():cek segera?
Csaba Toth
BTW Saya membutuhkan hal yang sama: Saya tidak ingin menerima NaN, Inf dan lainnya
Csaba Toth
1

Cobalah untuk mengkonversi ke float. Jika ada kesalahan, cetak pengecualian ValueError.

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

Keluaran:

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

Melewati kamus sebagai argumen, itu akan mengkonversi string yang dapat dikonversi menjadi float dan akan meninggalkan yang lain

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

Saya sedang mencari beberapa kode yang serupa, tetapi sepertinya menggunakan try / exception adalah cara terbaik. Ini kode yang saya gunakan. Ini mencakup fungsi coba lagi jika input tidak valid. Saya perlu memeriksa apakah input lebih besar dari 0 dan jika demikian konversikan ke 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: ")
Lockey
sumber
0
def try_parse_float(item):
  result = None
  try:
    float(item)
  except:
    pass
  else:
    result = float(item)
  return result
Tawanda Matereke
sumber
2
Meskipun kode ini dapat menyelesaikan pertanyaan, termasuk penjelasan tentang bagaimana dan mengapa ini menyelesaikan masalah akan sangat membantu untuk meningkatkan kualitas posting Anda, dan mungkin menghasilkan lebih banyak suara. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, bukan hanya orang yang bertanya sekarang. Harap edit jawaban Anda untuk menambahkan penjelasan dan berikan indikasi tentang batasan dan asumsi apa yang berlaku.
bip ganda
0

Saya mencoba beberapa opsi sederhana di atas, menggunakan uji coba sekitar mengkonversi ke float, dan menemukan bahwa ada masalah di sebagian besar balasan.

Tes sederhana (sesuai dengan jawaban di atas):

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

Masalahnya muncul ketika:

  • Anda memasukkan '-' untuk memulai angka negatif:

Anda kemudian mencoba float('-')yang gagal

  • Anda memasukkan nomor, tetapi kemudian mencoba untuk menghapus semua digit

Anda kemudian mencoba float('')yang juga gagal

Solusi cepat yang saya miliki adalah:

def _test_num(P):
    if P == '' or P == '-': return True
    try: 
        float(P)
        return True
    except ValueError:
        return False
Richard
sumber
-2
str(strval).isdigit()

tampaknya sederhana.

Menangani nilai yang disimpan sebagai string atau int atau float

muks
sumber
Dalam [2]: '123,123'.isdigit () Keluar [2]: Salah
Daniil Mashkin
1
Ini tidak berfungsi untuk angka negatif literal, harap perbaiki jawaban Anda
RandomEli
'39 .1'.isdigit ()
Ohad the Lad
all ([x.isdigit () untuk x in str (VAR) .strip ('-'). replace (',', '.'). split ('.')]) Jika Anda mencari yang lebih lengkap penerapan.
lotrus28