Перевірка, чи можна перетворити рядок на плаваючий у 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 для цього трохи несе. Інше рішення, яке я розглядав, - просто загорнути конверсію в блок "спробувати / ловити" і побачити, чи вдасться вона, як описано в цьому запитанні .

У когось є якісь інші ідеї? Думки про відносні достоїнства розділу та проби / підхоплення?

Відповіді:


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: Більшість рядків, до яких застосовано, виявляться ints або floats.
Кріс Upchurch

10
Ваш регулярний вираз не є оптимальним. "^ \ d + \. \ d + $" завершить збіг із тією ж швидкістю, що і вище, але матиме успіх швидше. Крім того, більш правильним способом було б: "^ [+ -]? \ D (>? \. \ D +)? $" Однак це все ще не відповідає числам на зразок: + 1.0e-10
John Gietzen

86
За винятком того, що ви забули назвати свою функцію "will_it_float".
відключено

3
Другий варіант не охопить нан та експоненціальний вираз - наприклад, 2e3.
Патрік Б.

4
Я думаю, що регулярний вираз не розбирає негативні числа.
Карлос

191

Метод Python для перевірки на float:

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, де float = 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
Не оптимально, але насправді досить розумно. Не буде обробляти +/- та показники.
Божевільний фізик

Роки запізнюються, але це приємний метод. Працював для мене, використовуючи наступне в рамці даних панди:[i for i in df[i].apply(lambda x: str(x).replace('.','').isdigit()).any()]
Марк Моретто

1
@MarkMoretto Ви отримаєте шок, коли дізнаєтесь про існування від’ємних чисел
Девід Хеффернан,

Найкращий однолінійний для мого сценарію, де мені просто потрібно перевірити наявність позитивних поплавків чи цифр. Мені подобається.
MJohnyJ

8

TL; DR :

  • Якщо ваш вхід - це в основному рядки, які можна перетворити на плаваючі, try: except:метод є найкращим нативним методом Python.
  • Якщо ваш вхід - це в основному рядки, які неможливо перетворити на плавки, регулярні вирази або метод розділів будуть кращими.
  • Якщо ви не впевнені в своєму вході або потребуєте більшої швидкості, і 2) не заперечуєте та можете встановити сторонне розширення C, fastnumbers працює дуже добре.

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

5

Якщо ви піклувались про ефективність (і я не пропоную вам це зробити), випробувальний підхід є явним переможцем (порівняно з вашим підходом на основі розділів або підходом регулярного вибору), якщо ви не очікуєте багато недійсні рядки, в цьому випадку це потенційно повільніше (імовірно, через витрати на обробку винятків).

Знову ж таки, я не пропоную вам дбати про ефективність, просто надаю вам дані на випадок, якщо ви робите це 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, у тестовій частині
    self.failUnless (is_float_partition ("20e2"))
AssertionError

-------------------------------------------------- --------------------
Пройшов 8 тестів у 33.670-х

FAILED (відмови = 1)

Ось код (Python 2.6, РегВир взяті з Джона 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 only val є float та False for int, але, ймовірно, менш ефективно, ніж rgex.

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

Імінометрична функція виглядає як поганий вибір, оскільки вона повертає істину для різних символів Unicode, таких як дроби. Документи кажуть: "Числові символи включають цифри символів, і всі символи, які мають властивість числового значення 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, у поплавок.


2

Спрощена версія функції is_digit(str) , якої достатньо у більшості випадків (не враховує експоненціальне позначення та значення "NaN" ):

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

1

Я використовував уже згадану функцію, але незабаром помічаю, що рядки як "Nan", "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

До речі, мені потрібно те саме: я не хочу приймати 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 або float


У [2]: '123,123'.isdigit () Out [2]: False
Даніїл Машкін

1
Це не працює для негативних чисел, буквально, виправте свою відповідь
RandomEli

'39 .1'.isdigit ()
Лад

all ([x.isdigit () для x in str (VAR) .strip ('-'). Замінити (',', '.'). split ('.')]) Якщо ви шукаєте більш повний реалізація.
lotrus28
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.