Проверка возможности преобразования строки в число с плавающей запятой в Python

У меня есть код 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?


person Chris Upchurch    schedule 09.04.2009    source источник
comment
Если быть точным, в Python нет такой вещи, как преобразование типов. Таким образом, тег type-conversion вводит в заблуждение, что является четко определенным термином в некоторых языках.   -  person bombs    schedule 22.06.2021


Ответы (20)


я бы просто воспользовалась..

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

.. это просто, и это работает. Обратите внимание, что он по-прежнему будет вызывать OverflowError, если элемент, например. 1‹‹1024.

Другим вариантом может быть регулярное выражение:

import re
if re.match(r'^-?\d+(?:\.\d+)$', element) is None:
    print "Not float"
person dbr    schedule 09.04.2009
comment
@S.Lott: Большинство строк, к которым это применяется, окажутся целыми или с плавающей запятой. - person Chris Upchurch; 10.04.2009
comment
Нет ли функции tryfloat (элемент) или другой эквивалентной функции, такой как C # float.TryParse (элемент). Обычно исключение не очень эффективно. Не то, чтобы это происходило очень часто, но если это происходит в тесном цикле, это может быть проблемой. - person dustyburwell; 10.04.2009
comment
Ваше регулярное выражение не является оптимальным. ^\d+\.\d+$ не совпадет с той же скоростью, что и выше, но быстрее. Кроме того, более правильным было бы: ^[+-]?\d(›?\.\d+)?$ Однако это все еще не соответствует числам вроде: +1.0e-10 - person John Gietzen; 10.04.2009
comment
За исключением того, что вы забыли назвать свою функцию will_it_float. - person unmounted; 10.04.2009
comment
Второй вариант не поймает nan и экспоненциальное выражение, например 2e3. - person Patrick B.; 08.01.2015
comment
Regex намного медленнее и требует больше памяти, и в этом случае его нельзя использовать в производственном коде. - person Gewthen; 30.12.2015
comment
Я думаю, что регулярное выражение не анализирует отрицательные числа. - person Carlos; 09.06.2016
comment
Не могли бы вы указать, какой из них быстрее? - person Ébe Isaac; 05.09.2018
comment
прямо не работает для меня и просто закрывает терминал RIP - person madladzen; 09.04.2020
comment
Обратите внимание, что float() также может вызывать TypeError: аргумент float() должен быть строкой или числом. Недостаточно проверить ValueError. - person Erik Wognsen; 28.01.2021

Метод 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
person Eric Leschinski    schedule 05.01.2014
comment
Отличный ответ. Просто добавьте еще 2, где float=True: isfloat(" 1.23 ") и isfloat(" \n \t 1.23 \n\t\n"). Полезно в веб-запросах; нет необходимости сначала обрезать пробелы. - person BareNakedCoder; 06.07.2017
comment
Еще одно дополнение, которое может быть лишним: isfloat("1True3.4") -> True - person dariober; 08.10.2020
comment
isfloat(1<<1024) вызывает OverflowError... - person Janus Troelsen; 16.02.2021

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

который вернет true только в том случае, если есть один или нет '.' в строке цифр.

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

вернет false

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

вернется false

person TulasiReddy    schedule 12.07.2016
comment
Не оптимально, но на самом деле довольно умно. Не будет обрабатывать +/- и показатели степени. - person Mad Physicist; 06.09.2016
comment
С опозданием на годы, но это хороший метод. Работал для меня, используя следующее в кадре данных pandas: [i for i in df[i].apply(lambda x: str(x).replace('.','').isdigit()).any()] - person Mark Moretto; 30.08.2019
comment
@MarkMoretto Вы будете в шоке, когда узнаете о существовании отрицательных чисел. - person David Heffernan; 24.01.2020
comment
Лучший однострочный вариант для моего сценария, где мне просто нужно проверить положительные значения с плавающей запятой или числа. Мне нравится. - person MJohnyJ; 17.04.2020

TL;DR:

  • Если вы вводите в основном строки, которые можно преобразовать в числа с плавающей запятой, метод try: except: — лучший собственный метод Python.
  • Если вы вводите в основном строки, которые нельзя преобразовать в числа с плавающей запятой, лучше использовать регулярные выражения или метод разделения.
  • Если вы 1) не уверены в своем вводе или вам нужно больше скорости и 2) не возражаете и можете установить стороннее расширение C, fastnumbers работает очень хорошо.

Существует еще один метод, доступный через сторонний модуль под названием fastnumbers (раскрытие информации, я автор); он предоставляет функцию под названием isfloat. Я взял пример 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 __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 вполне подойдет.

person SethMMorton    schedule 14.08.2014
comment
быстрая проверка чисел работает так хорошо, если у вас есть большинство строк, которые не могут быть преобразованы в числа с плавающей запятой, действительно ускоряет работу, спасибо - person ragardner; 02.06.2017

Просто для разнообразия вот еще один способ сделать это.

>>> 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
person Peter Moore    schedule 23.05.2018
comment
Функция isnumeric выглядит плохим выбором, так как она возвращает true для различных символов Unicode, таких как дроби. Документы говорят: числовые символы включают цифровые символы и все символы, которые имеют свойство числового значения Unicode, например. U + 2155, Вульгарная дробь ОДНА ПЯТАЯ - person gwideman; 25.01.2019

Если вы заботитесь о производительности (а я не предлагаю вам этого делать), подход, основанный на попытках, является явным победителем (по сравнению с вашим подходом на основе разделов или подходом с регулярными выражениями), если вы не ожидаете многого. недопустимые строки, и в этом случае это потенциально медленнее (предположительно из-за стоимости обработки исключений).

Опять же, я не предлагаю вам заботиться о производительности, просто предоставляю вам данные на случай, если вы делаете это 10 миллиардов раз в секунду или что-то в этом роде. Кроме того, код на основе секций не обрабатывает хотя бы одну допустимую строку.

$ ./floatstr.py
F..
partition sad: 3.1102449894
partition happy: 2.09208488464
..
re sad: 7.76906108856
re happy: 7.09421992302
..
try sad: 12.1525540352
try happy: 1.44165301323
.
======================================================================
FAIL: test_partition (__main__.ConvertTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./floatstr.py", line 48, in test_partition
    self.failUnless(is_float_partition("20e2"))
AssertionError

----------------------------------------------------------------------
Ran 8 tests in 33.670s

FAILED (failures=1)

Вот код (Python 2.6, регулярное выражение взято из ответ):

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()
person Jacob Gabrielson    schedule 09.04.2009

Упрощенная версия функции is_digit(str), которой достаточно в большинстве случаев (не учитывает экспоненциальное представление и значение "NaN"):

def is_digit(str):
    return str.lstrip('-').replace('.', '').isdigit()
person simhumileco    schedule 15.09.2018

Это регулярное выражение будет проверять научные числа с плавающей запятой:

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

Тем не менее, я считаю, что лучше всего попробовать использовать синтаксический анализатор.

person John Gietzen    schedule 09.04.2009

Если вам не нужно беспокоиться о научных или других выражениях чисел и вы работаете только со строками, которые могут быть числами с точкой или без нее:

Функция

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, в число с плавающей запятой.

person kodetojoy    schedule 08.10.2015
comment
Вы также можете разместить и оператор, как это. def is_float(s): result = False, если s.count(.) == 1 и s.replace(., ).isdigit(): result = True возвращаемый результат - person Gaurav Koradiya; 17.05.2021

Я использовал уже упомянутую функцию, но вскоре замечаю, что строки типа "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
person mathfac    schedule 15.10.2016
comment
Не могли бы мы начать с проверки if text.isalpha(): прямо сейчас? - person Csaba Toth; 14.03.2017
comment
Кстати, мне нужно то же самое: я не хочу принимать NaN, Inf и прочее - person Csaba Toth; 14.03.2017

Вы можете использовать предложение try-except-else, это уловит любые ошибки преобразования/значения, возникающие, когда переданное значение не может быть преобразовано в число с плавающей запятой.


  def try_parse_float(item):
      result = None
      try:
        float(item)
      except:
        pass
      else:
        result = float(item)
      return result
person Tawanda Matereke    schedule 19.02.2020
comment
Хотя этот код может решить проблему, включая объяснение того, как и почему это решает проблему, действительно поможет улучшить качество вашего сообщение и, вероятно, приведет к большему количеству голосов. Помните, что вы отвечаете на вопрос для будущих читателей, а не только для того, кто задает сейчас. Пожалуйста, отредактируйте свой ответ, чтобы добавить пояснения и указать, какие ограничения и предположения применяются. - person double-beep; 19.02.2020

Я искал похожий код, но, похоже, лучше всего использовать try/excepts. Вот код, который я использую. Он включает функцию повторной попытки, если ввод недействителен. Мне нужно было проверить, был ли ввод больше 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: ")
person Lockey    schedule 18.03.2018

Попробуйте преобразовать в 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'
person edW    schedule 31.12.2018

Передавая словарь в качестве аргумента, он преобразует строки, которые можно преобразовать в числа с плавающей запятой, и оставит другие

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

Я попробовал некоторые из вышеперечисленных простых вариантов, используя пробный тест для преобразования в число с плавающей запятой, и обнаружил, что в большинстве ответов есть проблема.

Простой тест (в соответствии с приведенными выше ответами):

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
person Richard    schedule 03.04.2020

Кажется, что многие регулярные выражения пропускают то или иное. Это работало для меня до сих пор:

(?i)^\s*[+-]?(?:inf(inity)?|nan|(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)\s*$

Он допускает бесконечность (или inf) со знаком, nan, отсутствием цифры перед десятичной дробью и начальными/конечными пробелами (при желании). ^ и $ необходимы, чтобы не допустить частичного совпадения чего-то вроде 1.2f-2 с 1.2.

Вы можете использовать [ed] вместо e, если вам нужно проанализировать некоторые файлы, где D используется для экспоненциальной записи с двойной точностью. Вы хотели бы заменить его позже или просто заменить их перед проверкой, так как функция float() не позволит этого.

person Ryan Gray    schedule 26.02.2021

Я нашел способ, который также может работать. нужно это проверить. первый раз сюда что-то кладу.

def isfloat(a_str):
    try:
        x=float(a_str)
        if x%1 == 0:
            return False
        elif x%1 != 0: #an else also do
            return True
    except Exception as error:
            return False
person VIVEK SINGH LATWAL    schedule 30.04.2021

Это работает как шарм:

[dict([a,int(x) if isinstance(x, str)
 and x.isnumeric() else float(x) if isinstance(x, str)
 and x.replace('.', '', 1).isdigit() else x] for a, x in json_data.items())][0]
person Pol Clota    schedule 30.04.2021

Мы можем использовать регулярное выражение как: import re if re.match('[0-9]*.?[0-9]+', <your_string>): print("Its a float/int") else: print("Its something alien") позвольте мне объяснить регулярное выражение на английском языке,

  • * -› 0 или более вхождений
  • + -› 1 или более случаев
  • ? -› 0/1 появление

теперь давайте конвертировать

  • '[0-9]* -> пусть будет 0 или более вхождений цифр между 0-9
  • \.? -›, за которым следует 0 или один '.' (если вам нужно проверить, может ли это быть int/float, иначе мы также можем использовать вместо ?, используйте {1})
  • [0-9]+ -›, за которым следует 0 или более цифр в диапазоне от 0 до 9
person P.R.    schedule 20.12.2020

str(strval).isdigit()

кажется простым.

Обрабатывает значения, хранящиеся в виде строки, int или float.

person muks    schedule 24.09.2016
comment
В [2]: '123,123'.isdigit() Out[2]: False - person Daniil Mashkin; 10.10.2017
comment
Это не работает для литерала отрицательных чисел, исправьте свой ответ. - person RandomEli; 11.12.2017
comment
'39.1'.isdigit() - person Ohad the Lad; 13.06.2019
comment
all([x.isdigit() for x in str(VAR).strip('-').replace(',','.').split('.')]) Если вы ищете более полный выполнение. - person lotrus28; 30.01.2020