Pythonで文字列を浮動小数点数に変換できるかどうかを確認する


182

文字列のリストを実行し、可能であれば整数または浮動小数点数に変換する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アプローチの相対的なメリットに関する意見はありますか?

回答:


305

私は使うだろう。

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:これが適用されるほとんどの文字列は、intまたはfloatになります。
クリスアップチャーチ

10
あなたの正規表現は最適ではありません。「^ \ d + \。\ d + $」は、上記と同じ速度で一致に失敗しますが、より速く成功します。また、より正しい方法は次のようになります: "^ [+-]?\ d(>?\。\ d +)?$"ただし、それでも次のような数値には一致しません:+ 1.0e-10
John Gietzen

86
関数に「will_it_float」という名前を付けるのを忘れた場合を除きます。
マウント解除

3
2番目のオプションは、2e3などのナンおよび指数表現をキャッチしません。
Patrick B.

4
正規表現は負の数を解析していないと思います。
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
すばらしい答えです。float = True:isfloat(" 1.23 ")およびの場合、あと2つ追加するだけ isfloat(" \n \t 1.23 \n\t\n")です。Webリクエストで役立ちます。最初に空白を削除する必要はありません。
BareNakedCoder 2017

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

true'。'が1つあるか、ない場合にのみ返されます。数字列内。

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

戻ります false

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

戻ります false


3
最適ではありませんが、実際にはかなり賢いです。+/-および指数を処理しません。
Mad Physicist

数年遅れますが、これは良い方法です。パンダのデータフレームで以下を使用して私のために働きました:[i for i in df[i].apply(lambda x: str(x).replace('.','').isdigit()).any()]
Mark Moretto '30

1
@MarkMoretto負の数の存在を知ると、ショックを受けることになります
David Heffernan

正の浮動小数点数または数値を確認する必要がある、私のシナリオに最適なワンライナー。好き。
MJohnyJ

8

TL; DR

  • 入力が主に浮動小数点数に変換できる文字列である場合、このtry: except:メソッドは最高のネイティブPythonメソッドです。
  • 入力がフロートに変換できない文字列である場合は、正規表現またはパーティションメソッドが適しています。
  • あなたが1)入力がわからない、またはもっと速度が必要な場合、2)気にしないでサードパーティのC拡張機能をインストールできる場合、fastnumbersは非常にうまく機能します。

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

ご覧のように、正規表現は当初見た目ほど悪くはありませんが、速度が本当に必要な場合は、このfastnumbers方法は非常に優れています。


浮動小数点数に変換できない文字列の大部分がある場合、高速な数値チェックは非常にうまく機能し、本当にスピードアップします。
ありがとう

5

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

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

$ ./floatstr.py
F ..
悲しいパーティション:3.1102449894
パーティション幸せ:2.09208488464
..
悲しい:7.76906108856
再び幸せ:7.09421992302
..
悲しんでみてください:12.1525540352
ハッピートライ:1.44165301323
。
================================================== ====================
FAIL:test_partition(__main __。ConvertTests)
-------------------------------------------------- --------------------
トレースバック(最新の呼び出しが最後):
  test_partitionのファイル "./floatstr.py"、48行目
    self.failUnless(is_float_partition( "20e2"))
AssertionError

-------------------------------------------------- --------------------
33.670で8テストを実行

失敗(失敗= 1)

コードは次のとおりです(Python 2.6、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のみを返します。valは浮動小数点数で、Falseはintですが、おそらく正規表現よりもパフォーマンスが低くなります。

>>> 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文字でtrueを返すため、不適切な選択のように見えます。ドキュメントには、「数字には数字と、Unicodeの数値プロパティを持つすべての文字が含まれます。例:U + 2155、VULGAR FRACTION ONE FIFTH」
gwideman

3

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

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

しかし、私はあなたの最善の策はパーサーを試して使うことだと思います。


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であるはずのものが誤ってfloatに変換されるのを防ぐことができます。


2

is_digit(str)ほとんどの場合に十分な関数の簡略版指数表記「NaN」値は考慮されません):

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

1

すでに述べた関数を使用しましたが、文字列が「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
if text.isalpha():すぐに小切手から始められませんか?
Csaba Toth 2017年

ところで私は同じ必要があります:NaN、Infなどを受け入れたくありません
Csaba Toth

1

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

辞書を引数として渡すと、floatに変換できる文字列が変換され、他の文字列が残ります。

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

同様のコードを探していましたが、try / exceptsを使用するのが最善の方法のようです。これが私が使っているコードです。入力が無効な場合の再試行機能が含まれています。入力が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
このコードは問題を解決する可能性がありますが、これが問題を解決する方法と理由の説明含めると、投稿の品質が向上し、おそらくより多くの投票が得られます。あなたが今尋ねている人だけでなく、あなたが将来の読者のための質問に答えていることを忘れないでください。回答を編集して説明を追加し、適用される制限と前提を示してください。
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、floatとして格納された値を処理します


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

1
負の数値リテラルでは機能しません。回答を修正してください
RandomEli

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

all([x.isdigit()for x in str(VAR).strip( '-')。replace( '、'、 '。')。split( '。')])より完全なものを探しているなら実装。
lotrus28
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.