Проверка, можно ли преобразовать строку в float в Python

193

У меня есть код 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 и посмотреть, удастся ли оно, как описано в этом вопросе .

У кого-нибудь есть другие идеи? Мнения об относительных достоинствах раздела и подходов "попробуй / поймай"?

Крис Апчерч
источник
Решение: stackoverflow.com/a/64132078/8321339
Вишал Гупта,

Ответы:

320

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

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

..это просто, и это работает

Другой вариант - регулярное выражение:

import re
if re.match(r'^-?\d+(?:\.\d+)?$', element) is None:
    print "Not float"
dbr
источник
3
@ S.Lott: Большинство строк, к которым это применяется, окажутся целыми или плавающими.
Крис Апчерч,
10
Ваше регулярное выражение не оптимально. «^ \ d + \. \ d + $» приведет к сбою соответствия с той же скоростью, что и выше, но выполнится быстрее. Кроме того, более правильным способом было бы: "^ [+ -]? \ D (>? \. \ D +)? $" Однако это все еще не соответствует числам вроде: + 1.0e-10
Джон Гитцен,
94
За исключением того, что вы забыли назвать свою функцию "will_it_float".
демонтировано
3
Второй вариант не улавливает nan и экспоненциальные выражения, такие как 2e3.
Патрик Б.
4
Я думаю, что регулярное выражение не анализирует отрицательные числа.
Карлос
198

Метод 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
Эрик Лещински
источник
8
Отличный ответ. Просто добавляем еще 2, где float = True: isfloat(" 1.23 ")и isfloat(" \n \t 1.23 \n\t\n"). Полезно в веб-запросах; не нужно сначала обрезать пробелы.
BareNakedCoder
1
Еще одно дополнение, которое может быть превосходным: isfloat("1_2_3.4")->True
dariober
23
'1.43'.replace('.','',1).isdigit()

который вернется, trueтолько если он есть или нет '.' в строке цифр.

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

вернется false

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

вернется false

ТуласиРедди
источник
3
Не оптимально, но на самом деле довольно умно. Не будет обрабатывать +/- и экспоненты.
Безумный физик
Спустя годы, но это хороший метод. Работал для меня, используя следующее в [i for i in df[i].apply(lambda x: str(x).replace('.','').isdigit()).any()]
Марк Моретто
2
@MarkMoretto. Вас ждет шок, когда вы узнаете о существовании отрицательных чисел,
Дэвид Хеффернан,
Лучший однострочник для моего сценария, где мне просто нужно проверить положительные числа с плавающей запятой или числа. Мне нравится.
MJohnyJ
8

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метод вполне подойдет.

СетММортон
источник
быстрая проверка чисел работает так хорошо, если у вас есть большинство строк, которые не могут быть преобразованы в числа с плавающей запятой, действительно ускоряет работу, спасибо
ragardner 02
5

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

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

$ ./floatstr.py
F ..
раздел sad: 3.1102449894
раздел счастлив: 2.09208488464
..
повторно грустно: 7.76906108856
повторно счастлив: 7.09421992302
..
попробуйте грустно: 12.1525540352
попробуйте счастливы: 1.44165301323
.
================================================== ====================
FAIL: test_partition (__main __. ConvertTests)
-------------------------------------------------- --------------------
Отслеживание (последний вызов последний):
  Файл "./floatstr.py", строка 48, в test_partition
    self.failUnless (is_float_partition ("20e2"))
AssertionError

-------------------------------------------------- --------------------
Выполнить 8 тестов за 33,670 с.

ВЫПОЛНЕНО (отказов = 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()
Джейкоб Габриэльсон
источник
5

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

>>> 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 является float и 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 выглядит плохим выбором, поскольку она возвращает true для различных символов Unicode, например дробей. В документах говорится: «Числовые символы включают в себя цифры и все символы, которые имеют свойство числового значения Unicode, например, U + 2155, VULGAR FRACTION ONE
FIFTH
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()
simhumileco
источник
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
mathfac
источник
1
Не могли бы мы сразу начать с if text.isalpha():чека?
Csaba Toth
Кстати, мне нужно то же самое: я не хочу принимать 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'
edW
источник
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, и если да, преобразовать его в число с плавающей запятой.

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 или float

мукс
источник
In [2]: '123,123'.isdigit () Out [2]: False
Даниил Машкин
1
Это не работает с отрицательными числами буквально, исправьте свой ответ
RandomEli
'39 .1'.isdigit ()
Ohad the Lad
all ([x.isdigit () for x in str (VAR) .strip ('-'). replace (',', '.'). split ('.')]) Если вы ищете более полный реализация.
lotrus28