ตรวจสอบว่าสตริงสามารถแปลงเป็นทศนิยมใน Python


183

ฉันมีรหัสไพ ธ อนที่วิ่งผ่านรายการสตริงและแปลงเป็นจำนวนเต็มหรือตัวเลขทศนิยมถ้าเป็นไปได้ การทำเช่นนี้กับจำนวนเต็มนั้นค่อนข้างง่าย

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

ตัวเลขจุดลอยตัวยากกว่า ตอนนี้ฉันกำลังใช้partition('.')เพื่อแยกสตริงและตรวจสอบเพื่อให้แน่ใจว่าหนึ่งหรือทั้งสองข้างเป็นตัวเลข

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)

มันใช้งานได้ แต่เห็นได้ชัดว่าคำสั่ง if สำหรับสิ่งนั้นเป็นความอดทน โซลูชันอื่นที่ฉันพิจารณาคือเพียงแค่แปลงการแปลงในบล็อกลอง / catch และดูว่ามันประสบความสำเร็จตามที่อธิบายไว้ในคำถามนี้

ใครมีแนวคิดอื่นบ้าง ความคิดเห็นเกี่ยวกับข้อดีของพาร์ทิชันและลอง / จับวิธี?

คำตอบ:


306

ฉันแค่จะใช้ ..

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

.. มันง่ายและใช้งานได้

ตัวเลือกอื่นจะเป็นการแสดงออกปกติ:

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

3
@ S.Lott: สตริงส่วนใหญ่ที่ใช้กับจะกลายเป็น ints หรือลอย
Chris Upchurch

10
regex ของคุณไม่เหมาะสม "^ \ d + \. \ d + $" จะล้มเหลวในการแข่งขันที่ความเร็วเดียวกับข้างต้น แต่จะประสบความสำเร็จเร็วขึ้น นอกจากนี้วิธีที่ถูกต้องมากขึ้นคือ: "^ [+ -]? \ d (>? \. \ d +)? $" อย่างไรก็ตามนั่นยังไม่ตรงกับหมายเลขที่ชอบ: + 1.0e-10
John Gietzen

86
ยกเว้นว่าคุณลืมตั้งชื่อฟังก์ชั่นของคุณ "will_it_float"
unmounted

3
ตัวเลือกที่สองจะไม่จับการแสดงออกของนินและเอ็กซ์โพเนนเชียลเช่น 2e3
แพทริค B.

4
ฉันคิดว่า regex ไม่ได้แยกตัวเลขที่เป็นลบ
Carlos

191

วิธี Python เพื่อตรวจสอบการลอย:

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

อย่ารับบิตโดยก๊อบลินซ่อนตัวอยู่ในเรือลอย! ทำการทดสอบ!

อะไรและไม่ใช่ลอยอาจทำให้คุณประหลาดใจ:

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
คำตอบที่ดี เพียงแค่เพิ่มอีก 2 ที่ลอย = true: และisfloat(" 1.23 ") isfloat(" \n \t 1.23 \n\t\n")มีประโยชน์ในการร้องขอทางเว็บ ไม่จำเป็นต้องตัดช่องว่างสีขาวก่อน
BareNakedCoder

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

ซึ่งจะส่งกลับtrueเฉพาะในกรณีที่มี 'หรือ' หนึ่ง ' ในสตริงของตัวเลข

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

จะกลับมา false

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

จะกลับมา false


3
ไม่ดีที่สุด แต่ก็ฉลาดจริงๆ จะไม่จัดการ +/- และเลขชี้กำลัง
นักฟิสิกส์บ้า

หลายปีที่ผ่านมา แต่นี่เป็นวิธีที่ดี ทำงานให้ฉันโดยใช้สิ่งต่อไปนี้ใน dataframe แพนด้า:[i for i in df[i].apply(lambda x: str(x).replace('.','').isdigit()).any()]
Mark Moretto

1
@MarkMoretto คุณจะต้องตกตะลึงเมื่อคุณเรียนรู้การมีอยู่ของจำนวนลบ
David Heffernan

หนึ่งซับที่ดีที่สุดสำหรับสถานการณ์ของฉันที่ฉันเพียงแค่ต้องตรวจสอบลอยหรือตัวเลขที่เป็นบวก ฉันชอบ.
MJohnyJ

8

TL; DR :

  • หากอินพุตของคุณส่วนใหญ่เป็นสตริงที่สามารถแปลงเป็นโฟลทtry: except:วิธีนั้นเป็นวิธีไพ ธ อนดั้งเดิมที่ดีที่สุด
  • หากอินพุตของคุณส่วนใหญ่เป็นสตริงที่ไม่สามารถแปลงเป็นแบบลอยได้นิพจน์ทั่วไปหรือวิธีการแบ่งจะดีกว่า
  • หากคุณเป็น 1) ไม่แน่ใจอินพุตของคุณหรือต้องการความเร็วที่มากขึ้นและ 2) ไม่ต้องสนใจและสามารถติดตั้งส่วนขยาย C ของบุคคลที่สามfastnumbersทำงานได้ดี

มีวิธีการอื่นผ่านทางโมดูลบุคคลที่สามที่เรียกว่าfastnumbers (เปิดเผยฉันเป็นผู้เขียน); ก็มีฟังก์ชั่นที่เรียกว่าisfloat ฉันได้นำตัวอย่างที่ไม่ย่อท้อซึ่งจาค็อบกาเบรียลสรุปไว้ในคำตอบนี้แต่เพิ่มfastnumbers.isfloatวิธีการ ฉันยังควรทราบว่าตัวอย่างของยาโคบไม่ได้ทำเพื่อความยุติธรรมตัวเลือก regex เพราะส่วนใหญ่ของเวลาในตัวอย่างที่ถูกใช้ในการค้นหาทั่วโลกเนื่องจากผู้ประกอบการจุด ... 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()

บนเครื่องของฉันผลลัพธ์คือ:

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

อย่างที่คุณเห็น regex ไม่ได้เลวร้ายอย่างที่มันเคยเห็นมาก่อนและถ้าคุณต้องการความเร็วที่แท้จริงfastnumbersวิธีนี้ค่อนข้างดี


การตรวจสอบตัวเลขอย่างรวดเร็วทำงานได้ดีถ้าคุณมีสายส่วนใหญ่ที่ไม่สามารถแปลงเป็นลอยตัวได้เร็วขึ้นจริงๆขอบคุณ
ragardner

5

หากคุณใส่ใจเกี่ยวกับประสิทธิภาพ (และฉันไม่แนะนำให้คุณควร) วิธีการทดสอบแบบลองคือผู้ชนะที่ชัดเจน (เทียบกับวิธีการแบ่งพาร์ติชันของคุณหรือวิธี regexp) ตราบใดที่คุณไม่คาดหวังมาก สตริงที่ไม่ถูกต้องซึ่งในกรณีนี้อาจช้ากว่านี้ (น่าจะเกิดจากค่าใช้จ่ายในการจัดการข้อยกเว้น)

อีกครั้งฉันไม่ได้แนะนำให้คุณใส่ใจกับประสิทธิภาพเพียงให้ข้อมูลกับคุณในกรณีที่คุณทำสิ่งนี้ 10 พันล้านครั้งต่อวินาทีหรืออะไรบางอย่าง นอกจากนี้รหัสที่ใช้พาร์ติชันไม่ได้จัดการสตริงที่ถูกต้องอย่างน้อยหนึ่งรายการ

$ ./floatstr.py
F ..
พาร์ทิชันเศร้า: 3.1102449894
พาร์ติชั่นมีความสุข: 2.09208488464
..
กำลังเศร้า: 7.76906108856
มีความสุขอีกครั้ง: 7.09421992302
..
ลองเศร้า: 12.1525540352
ลองมีความสุข: 1.44165301323
.
================================================== ====================
FAIL: test_partition (__ Main __. ConvertTests)
-------------------------------------------------- --------------------
Traceback (การโทรล่าสุดครั้งล่าสุด):
  ไฟล์ "./floatstr.py" บรรทัดที่ 48 ใน test_partition
    self.failUnless (is_float_partition ( "20e2"))
AssertionError

-------------------------------------------------- --------------------
ทดสอบ 8 ครั้งใน 33.670 ครั้ง

ล้มเหลว (ความล้มเหลว = 1)

นี่คือรหัส (Python 2.6, regexp นำมาจากคำตอบของ 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

เพียงเพื่อความหลากหลายที่นี่เป็นวิธีการอื่นที่จะทำ

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

แก้ไข: ฉันแน่ใจว่ามันจะไม่ถือได้ถึงทุกกรณีของการลอยแม้ว่าโดยเฉพาะอย่างยิ่งเมื่อมีการยกกำลัง เพื่อแก้ไขว่ามันดูเหมือนนี้ สิ่งนี้จะส่งคืนค่า True อย่างเดียวเป็นแบบลอยตัวและเป็นเท็จสำหรับ int แต่อาจมีประสิทธิภาพน้อยกว่า 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

ฟังก์ชั่น isnumeric นั้นดูเหมือนเป็นตัวเลือกที่ไม่ดีนักเนื่องจากมันจะคืนค่าจริงให้กับอักขระ Unicode ต่างๆเช่นเศษส่วน เอกสารพูดว่า: "ตัวอักษรตัวเลขรวมถึงตัวอักษรและตัวละครทั้งหมดที่มีคุณสมบัติค่าตัวเลข Unicode เช่น U + 2155, VULGAR FRACTION ONE FIFTH"
gwideman

3

Regex นี้จะตรวจสอบตัวเลขทางลอยตัวทางวิทยาศาสตร์:

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

อย่างไรก็ตามฉันเชื่อว่าทางออกที่ดีที่สุดของคุณคือการใช้ parser ในการลอง


2

หากคุณไม่ต้องกังวลเกี่ยวกับการแสดงออกทางวิทยาศาสตร์หรือตัวเลขอื่น ๆ และทำงานกับสตริงที่อาจเป็นตัวเลขที่มีหรือไม่มีจุด:

ฟังก์ชัน

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

รุ่นแลมบ์ดา

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

ตัวอย่าง

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

วิธีนี้คุณไม่ได้ตั้งใจแปลงสิ่งที่ควรจะเป็น int เป็นทุ่น


2

ฟังก์ชันรุ่นที่ง่าย is_digit(str)ขึ้นซึ่งพอเพียงในกรณีส่วนใหญ่ (ไม่ได้พิจารณารูปแบบเลขชี้กำลังและค่า"NaN" ):

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

1

ฉันใช้ฟังก์ชั่นที่กล่าวถึงแล้ว แต่ไม่นานฉันก็สังเกตเห็นว่าสตริงเป็น "น่าน", "Inf" และรูปแบบของมันจะถูกพิจารณาว่าเป็นตัวเลข ดังนั้นฉันขอเสนอให้คุณปรับปรุงเวอร์ชั่นของฟังก์ชั่นที่จะคืนค่าเท็จในประเภทอินพุตเหล่านั้นและจะไม่ล้มเหลว "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
เราไม่สามารถเริ่มด้วยการif text.isalpha():ตรวจสอบได้ทันทีหรือ
Csaba Toth

BTW ฉันต้องการเหมือนกัน: ฉันไม่ต้องการยอมรับ NaN, Inf และสิ่งของ
Csaba Toth

1

ลองแปลงให้ลอย หากมีข้อผิดพลาดให้พิมพ์ข้อยกเว้น ValueError

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

เอาท์พุท:

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

1

ผ่านพจนานุกรมเป็นอาร์กิวเมนต์มันจะแปลงสตริงซึ่งสามารถแปลงเป็นลอยและจะปล่อยให้คนอื่น

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

ฉันกำลังมองหารหัสที่คล้ายกัน แต่ดูเหมือนว่าการใช้ลอง / ยกเว้นเป็นวิธีที่ดีที่สุด นี่คือรหัสที่ฉันใช้ มันมีฟังก์ชั่นการลองอีกครั้งหากอินพุตไม่ถูกต้อง ฉันจำเป็นต้องตรวจสอบว่าอินพุตมีค่ามากกว่า 0 หรือไม่และหากแปลงให้เป็นลอย

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
แม้ว่ารหัสนี้อาจแก้ปัญหาได้รวมถึงคำอธิบายว่าทำไมและวิธีแก้ปัญหานี้จะช่วยปรับปรุงคุณภาพการโพสต์ของคุณได้อย่างไรและอาจส่งผลให้คะแนนโหวตสูงขึ้น จำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตไม่ใช่เพียงแค่คนที่ถามตอนนี้ โปรดแก้ไขคำตอบของคุณเพื่อเพิ่มคำอธิบายและระบุข้อ จำกัด และสมมติฐานที่ใช้
เสียงบีปสองครั้ง

0

ฉันลองตัวเลือกง่าย ๆ ข้างต้นโดยใช้การทดสอบลองรอบ ๆ การแปลงเป็นแบบลอยและพบว่ามีปัญหาในการตอบกลับส่วนใหญ่

การทดสอบอย่างง่าย (ตามคำตอบข้างต้น):

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

ปัญหาเกิดขึ้นเมื่อ:

  • คุณป้อน '-' เพื่อเริ่มต้นจำนวนลบ:

คุณกำลังลองfloat('-')ซึ่งล้มเหลว

  • คุณป้อนตัวเลข แต่พยายามลบตัวเลขทั้งหมด

คุณพยายามแล้ว float('')ที่จะล้มเหลวเช่นเดียวกัน

วิธีแก้ปัญหาอย่างรวดเร็วที่ฉันมีคือ:

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

-2
str(strval).isdigit()

ดูเหมือนจะง่าย

จัดการค่าที่เก็บไว้เป็นสตริงหรือ int หรือลอย


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

1
มันไม่ทำงานสำหรับตัวเลขที่เป็นลบโปรดแก้ไขคำตอบของคุณ
RandomEli

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

all ([x.isdigit () สำหรับ x in str (VAR) .strip ('-'). แทนที่ (',', '.'). split ('.')]) หากคุณกำลังมองหาที่สมบูรณ์กว่า การดำเนินงาน
lotrus28
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.