Kiểm tra nếu một chuỗi có thể được chuyển đổi thành float trong Python


182

Tôi đã nhận được một số mã Python chạy qua danh sách các chuỗi và chuyển đổi chúng thành số nguyên hoặc số dấu phẩy động nếu có thể. Làm điều này cho số nguyên là khá dễ dàng

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

Số điểm nổi là khó khăn hơn. Ngay bây giờ tôi đang sử dụng partition('.')để phân tách chuỗi và kiểm tra để đảm bảo rằng một hoặc cả hai bên là chữ số.

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)

Điều này hoạt động, nhưng rõ ràng tuyên bố if cho đó là một chút của một con gấu. Giải pháp khác tôi đã xem xét là chỉ gói chuyển đổi trong một khối thử / bắt và xem liệu nó có thành công hay không, như được mô tả trong câu hỏi này .

Bất cứ ai có ý tưởng nào khác? Ý kiến ​​về giá trị tương đối của phân vùng và phương pháp thử / bắt?

Câu trả lời:


305

Tôi sẽ chỉ sử dụng ..

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

.. thật đơn giản, và nó hoạt động

Một tùy chọn khác sẽ là một biểu thức chính quy:

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

3
@ S.Lott: Hầu hết các chuỗi này được áp dụng sẽ hóa ra là ints hoặc float.
Chris Up nhẫn

10
Regex của bạn không tối ưu. "^ \ d + \. \ d + $" sẽ thất bại một trận đấu ở cùng tốc độ như trên, nhưng sẽ thành công nhanh hơn. Ngoài ra, một cách chính xác hơn sẽ là: "^ [+ -]? \ D (>? \. \ D +)? $" Tuy nhiên, điều đó vẫn không khớp với các số như: + 1.0e-10
John Gietzen

86
Ngoại trừ việc bạn quên đặt tên cho chức năng của mình là "will_it_float".
không đếm được

3
Tùy chọn thứ hai sẽ không bắt được biểu thức nan và hàm mũ - chẳng hạn như 2e3.
Patrick B.

4
Tôi nghĩ rằng regex không phân tích số âm.
Carlos

191

Phương pháp Python để kiểm tra float:

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

Đừng nhận được một chút bởi những con yêu tinh trốn trong thuyền phao! ĐƠN VỊ KIỂM TRA!

Cái gì là và không phải là một cái phao có thể làm bạn ngạc nhiên:

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
Câu trả lời chính xác. Chỉ cần thêm 2 trong đó float = True: isfloat(" 1.23 ")isfloat(" \n \t 1.23 \n\t\n"). Hữu ích trong các yêu cầu web; không cần phải cắt khoảng trắng trước.
BareNakenCoder

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

sẽ truechỉ trở lại nếu có một hoặc không '.' trong chuỗi các chữ số.

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

sẽ trở lại false

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

sẽ trở lại false


3
Không tối ưu nhưng thực sự khá thông minh. Không xử lý +/- và số mũ.
Nhà vật lý điên

Nhiều năm muộn, nhưng đây là một phương pháp tốt đẹp. Làm việc cho tôi bằng cách sử dụng các mục sau trong khung dữ liệu gấu trúc:[i for i in df[i].apply(lambda x: str(x).replace('.','').isdigit()).any()]
Mark Moretto

1
@MarkMoretto Bạn sẽ bị sốc khi biết về sự tồn tại của số âm
David Heffernan

Một lớp lót tốt nhất cho kịch bản của tôi, nơi tôi chỉ cần kiểm tra các số float hoặc số dương. Tôi thích.
MJohnyJ

8

TL; DR :

  • Nếu đầu vào của bạn chủ yếu là các chuỗi có thể được chuyển đổi thành float, thì try: except:phương thức này là phương thức Python gốc tốt nhất.
  • Nếu đầu vào của bạn chủ yếu là các chuỗi không thể chuyển đổi thành float, biểu thức chính quy hoặc phương thức phân vùng sẽ tốt hơn.
  • Nếu bạn là 1) không chắc chắn về đầu vào của mình hoặc cần thêm tốc độ và 2) đừng bận tâm và có thể cài đặt tiện ích mở rộng C của bên thứ ba, fastnumbers hoạt động rất tốt.

Có một phương pháp khác có sẵn thông qua một mô-đun của bên thứ ba được gọi là fastnumbers (tiết lộ, tôi là tác giả); nó cung cấp một hàm gọi là isfloat . Tôi đã lấy ví dụ nhỏ nhất được nêu ra bởi Jacob Gabrielson trong câu trả lời này , nhưng đã thêm fastnumbers.isfloatphương pháp. Tôi cũng nên lưu ý rằng ví dụ của Jacob đã không công bằng với tùy chọn regex vì phần lớn thời gian trong ví dụ đó được dành cho việc tra cứu toàn cầu vì toán tử dấu chấm ... Tôi đã sửa đổi chức năng đó để so sánh công bằng hơn 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()

Trên máy của tôi, đầu ra là:

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

Như bạn có thể thấy, regex thực sự không tệ như ban đầu và nếu bạn có nhu cầu thực sự về tốc độ, fastnumbersphương pháp này khá tốt.


kiểm tra số nhanh hoạt động rất tốt nếu bạn có phần lớn các chuỗi không thể chuyển đổi thành phao, thực sự tăng tốc mọi thứ, thankyou
ragardner

5

Nếu bạn quan tâm đến hiệu suất (và tôi không khuyên bạn nên), cách tiếp cận dựa trên thử là chiến thắng rõ ràng (so với cách tiếp cận dựa trên phân vùng hoặc cách tiếp cận regrec), miễn là bạn không mong đợi nhiều chuỗi không hợp lệ, trong trường hợp đó có khả năng chậm hơn (có lẽ là do chi phí xử lý ngoại lệ).

Một lần nữa, tôi không đề nghị bạn quan tâm đến hiệu suất, chỉ cung cấp cho bạn dữ liệu trong trường hợp bạn thực hiện việc này 10 tỷ lần một giây hoặc một cái gì đó. Ngoài ra, mã dựa trên phân vùng không xử lý ít nhất một chuỗi hợp lệ.

$ ./floatstr.py
F ..
phân vùng buồn: 3.1102449894
phân vùng hạnh phúc: 2.09208488464
..
lại buồn: 7.76906108856
hạnh phúc: 7.09421992302
..
thử buồn: 12.1525540352
cố gắng hạnh phúc: 1.44165301323
.
================================================== ====================
FAIL: test_partition (__main __. ConvertTests)
-------------------------------------------------- --------------------
TracBack (cuộc gọi gần đây nhất vừa qua):
  Tệp "./floatstr.py", dòng 48, trong test_partition
    self.failUnless (is_float_partition ("20e2"))
Khẳng địnhError

-------------------------------------------------- --------------------
Chạy thử nghiệm 8 trong 33.670

FAILED (thất bại = 1)

Đây là mã (Python 2.6, regrec lấy từ câu trả lời của 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()

4

Chỉ cần đa dạng ở đây là một phương pháp khác để làm điều đó.

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

Chỉnh sửa: Tôi chắc chắn rằng nó sẽ không giữ được tất cả các trường hợp nổi mặc dù đặc biệt là khi có số mũ. Để giải quyết nó trông như thế này. Điều này sẽ trả về True chỉ val là float và false cho int nhưng có lẽ ít hiệu suất hơn 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

Hàm isnumeric trông giống như một lựa chọn kém, vì nó trả về đúng trên các ký tự Unicode khác nhau như phân số. Tài liệu nói: "Các ký tự số bao gồm các ký tự chữ số và tất cả các ký tự có thuộc tính giá trị số Unicode, ví dụ: U + 2155, VULGAR FRACTION ONE
FINTH

3

Regex này sẽ kiểm tra các số dấu phẩy động khoa học:

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

Tuy nhiên, tôi tin rằng đặt cược tốt nhất của bạn là sử dụng trình phân tích cú pháp trong một lần thử.


2

Nếu bạn không cần lo lắng về các biểu thức khoa học hoặc các số khác và chỉ hoạt động với các chuỗi có thể là số có hoặc không có dấu chấm:

Chức năng

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

Phiên bản Lambda

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

Thí dụ

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

Bằng cách này, bạn không vô tình chuyển đổi những gì nên là một int, thành một float.


2

Phiên bản đơn giản hóa của hàm is_digit(str) , trong hầu hết các trường hợp (không xem xét ký hiệu số mũ và giá trị "NaN" ):

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

1

Tôi đã sử dụng hàm đã được đề cập, nhưng ngay sau đó tôi nhận thấy các chuỗi là "Nan", "Inf" và biến thể của nó được coi là số. Vì vậy, tôi đề xuất cho bạn phiên bản cải tiến của chức năng, sẽ trả về false trên các loại đầu vào đó và sẽ không thất bại với các biến thể "1e3":

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

1
Chúng ta không thể bắt đầu với việc if text.isalpha():kiểm tra ngay lập tức?
Csaba Toth

BTW Tôi cũng cần như vậy: Tôi không muốn chấp nhận NaN, Inf và những thứ khác
Csaba Toth

1

Cố gắng chuyển đổi để nổi. Nếu có lỗi, hãy in ngoại lệ ValueError.

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

Đầu ra:

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

1

Truyền từ điển làm đối số, nó sẽ chuyển đổi các chuỗi có thể được chuyển đổi thành float và sẽ để lại các chuỗi khác

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

Tôi đã tìm kiếm một số mã tương tự, nhưng có vẻ như sử dụng try / ex accept là cách tốt nhất. Đây là mã tôi đang sử dụng. Nó bao gồm chức năng thử lại nếu đầu vào không hợp lệ. Tôi cần kiểm tra xem đầu vào có lớn hơn 0 không và nếu có thì chuyển đổi nó thành 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
def try_parse_float(item):
  result = None
  try:
    float(item)
  except:
    pass
  else:
    result = float(item)
  return result

2
Mặc dù mã này có thể giải quyết câu hỏi, bao gồm giải thích về cách thức và lý do giải quyết vấn đề này thực sự sẽ giúp cải thiện chất lượng bài đăng của bạn và có thể dẫn đến nhiều lượt bình chọn hơn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai, không chỉ người hỏi bây giờ. Vui lòng chỉnh sửa câu trả lời của bạn để thêm giải thích và đưa ra dấu hiệu về những hạn chế và giả định được áp dụng.
tiếng bíp đôi

0

Tôi đã thử một số tùy chọn đơn giản ở trên, sử dụng thử nghiệm xung quanh chuyển đổi sang float và thấy rằng có một vấn đề trong hầu hết các câu trả lời.

Bài kiểm tra đơn giản (dọc theo dòng câu trả lời trên):

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

Vấn đề xảy ra khi:

  • Bạn nhập '-' để bắt đầu số âm:

Bạn đang cố gắng float('-')mà thất bại

  • Bạn nhập một số, nhưng sau đó cố gắng xóa tất cả các chữ số

Sau đó, bạn đang cố gắng float('')mà cũng thất bại

Giải pháp nhanh chóng tôi có là:

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

-2
str(strval).isdigit()

có vẻ đơn giản

Xử lý các giá trị được lưu trữ dưới dạng chuỗi hoặc int hoặc float


Trong [2]: '123,123'.itorigit () Out [2]: Sai
Daniil Mashkin

1
Nó không hoạt động đối với các số âm theo nghĩa đen, vui lòng sửa câu trả lời của bạn
RandomEli

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

all ([x. thẩmigit () cho x in str (VAR) .strip ('-'). thay thế (',', '.'). split ('.')]) Nếu bạn đang tìm kiếm một bản hoàn chỉnh hơn thực hiện.
lotrus28
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.