web-dev-qa-db-ja.com

Pythonで文字列をfloatに変換できるかどうかを確認する

文字列のリストを実行し、可能であれば整数または浮動小数点数に変換するPythonコードがあります。整数に対してこれを行うのはとても簡単です

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ステートメントは少し弱点です。私が検討した他の解決策は、変換をtry/catchブロックにラップして、成功するかどうかを確認することです この質問 で説明しています。

誰か他のアイデアがありますか?パーティションとtry/catchアプローチの相対的なメリットに関する意見?

150
Chris Upchurch

私はただ使用します。

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

..それは簡単です、そしてそれは動作します

別のオプションは正規表現です:

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

フロートをチェックする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
161
Eric Leschinski
'1.43'.replace('.','',1).isdigit()

trueが返されるのは、 '。'が1つまたはない場合のみです。数字の文字列で。

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

falseを返します

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

falseを返します

16
TulasiReddy

あなたがパフォーマンスを気にかけているなら(そして、私はあなたがそうすることを提案していない)、トライベースのアプローチは明確な勝者です(あなたのパーティションベースのアプローチまたは正規表現アプローチと比較して)、あなたが多くを期待しない限り無効な文字列。この場合、潜在的に遅くなります(おそらく例外処理のコストが原因です)。

繰り返しになりますが、パフォーマンスを気にすることはお勧めしません。1秒に100億回などを行う場合に備えてデータを提供するだけです。また、パーティションベースのコードは、少なくとも1つの有効な文字列を処理しません。

 $ ./floatstr.py
F..
partition sad:3.1102449894 
 partition happy:2.09208488464 
 .. 
 re悲しい:7.76906108856 
 re happy:7.09421992302 
 .. 
 try悲しい:12.1525540352 
 try happy:1.44165301323 
。
 == ================================================== ================== 
 FAIL:test_partition(__main __。ConvertTests)
 -------------- -------------------------------------------------- ------ 
トレースバック(最後の最後の呼び出し):
ファイル "./floatstr.py"、行48、test_partition 
 self.failUnless(is_float_partition( " 20e2 "))
 AssertionError 
 
 ------------------------------ ---------------------------------------- 
 33.670で8回のテストを実施s 
 
 FAILED(failures = 1)

コードは次のとおりです(Python 2.6、John Gietzenの answer から取られた正規表現):

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 __== '__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()
5

TL; DR

  • 入力の大部分が文字列で、canがfloatに変換される場合、try: except:メソッドが最適なネイティブPythonメソッドです。
  • 入力が主に文字列で、cannotがfloatに変換される場合、正規表現またはパーティション方式の方が適しています。
  • 1)入力がわからない場合、またはより高速にする必要がある場合、2)サードパーティのC拡張機能をインストールしても問題ない場合、 fastnumbers は非常にうまく機能します。

fastnumbers (開示、私は著者です)というサードパーティのモジュールを介して利用できる別の方法があります。 isfloat と呼ばれる関数を提供します。 Jacob Gabrielsonが この回答 で概説したunittestの例を取り上げましたが、fastnumbers.isfloatメソッドを追加しました。また、ジェイコブの例では正規表現オプションに正義を行っていないことに注意する必要があります。この例のほとんどの時間はドット演算子のためにグローバルルックアップに費やされたためです... 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 __== '__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

ご覧のとおり、正規表現は実際には当初のように悪くはありません。実際に速度が必要な場合、fastnumbersメソッドは非常に優れています。

5
SethMMorton

多様性のために、別の方法があります。

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

編集:特に指数がある場合でも、フロートのすべてのケースに耐えられるとは限りません。それを解決するには、このように見えます。これは、valがfloatであり、intに対してFalseである場合にのみ、Trueを返しますが、おそらく正規表現よりもパフォーマンスが低下します。

>>> 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
4
Peter Moore

科学的な数値やその他の数値表現を心配する必要がなく、ピリオドの有無にかかわらず数値になり得る文字列のみを使用している場合:

関数

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

Lambdaバージョン

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であるべきものをfloatに変換することはありません。

2
kodetojoy

この正規表現は、科学的な浮動小数点数をチェックします。

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

ただし、パーサーを試してみることをお勧めします。

2
John Gietzen

フロートに変換してみてください。エラーがある場合は、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
edW

既に述べた関数を使用しましたが、すぐに「Nan」、「Inf」などの文字列とそのバリエーションが数字と見なされることに気付きました。したがって、これらのタイプの入力でfalseを返し、「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
mathfac

関数の簡易版is_digit(str)、ほとんどの場合で十分です(指数表記および "NaN"値):

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

同様のコードを探していましたが、try/exceptsを使用するのが最善の方法のようです。これが私が使用しているコードです。入力が無効な場合、再試行機能が含まれます。入力が0より大きいかどうかを確認する必要があり、もしそうなら、それを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
Lockey
str(strval).isdigit()

簡単そうです。

文字列、int、またはfloatとして格納された値を処理します

0
muks