Verificando se uma string pode ser convertida para flutuar no Python

182

Eu tenho algum código Python que percorre uma lista de seqüências de caracteres e os converte em números inteiros ou números de ponto flutuante, se possível. Fazer isso para números inteiros é muito fácil

if element.isdigit():
  newelement = int(element)

Os números de ponto flutuante são mais difíceis. No momento, estou usando partition('.')para dividir a string e verificando se um ou os dois lados são dígitos.

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)

Isso funciona, mas, obviamente, a declaração if é um pouco difícil. A outra solução que considerei é apenas agrupar a conversão em um bloco try / catch e ver se ela é bem-sucedida, conforme descrito nesta pergunta .

Alguém tem outras ideias? Opiniões sobre os méritos relativos da partição e abordagens try / catch?

Chris Upchurch
fonte

Respostas:

305

Eu usaria ..

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

..é simples, e funciona

Outra opção seria uma expressão regular:

import re
if re.match(r'^-?\d+(?:\.\d+)?$', element) is None:
    print "Not float"
dbr
fonte
3
@ S.Lott: A maioria das strings às quais é aplicada acabará sendo ints ou floats.
21720 Chris Upchurch
10
Seu regex não é o ideal. "^ \ d + \. \ d + $" falhará na correspondência na mesma velocidade que acima, mas terá êxito mais rápido. Além disso, uma maneira mais correta seria: "^ [+ -]? \ D (>? \. \ D +)? $" No entanto, isso ainda não corresponde a números como: + 1.0e-10
John Gietzen
86
Exceto que você esqueceu de nomear sua função "will_it_float".
desmontado
3
A segunda opção não captura a expressão nan e exponencial - como 2e3.
Patrick B.
4
Eu acho que o regex não está analisando números negativos.
Carlos
191

Método Python para verificar se há float:

def isfloat(value):
  try:
    float(value)
    return True
  except ValueError:
    return False

Não fique mordido pelos goblins que se escondem no barco flutuador! FAÇA O TESTE DA UNIDADE!

O que é e não é um flutuador pode surpreendê-lo:

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
Eric Leschinski
fonte
6
Ótima resposta. Apenas adicionando mais 2 onde float = True: isfloat(" 1.23 ")e isfloat(" \n \t 1.23 \n\t\n"). Útil em solicitações da web; não é necessário aparar espaços em branco primeiro.
BareNakedCoder
22
'1.43'.replace('.','',1).isdigit()

que retornará trueapenas se houver um ou não '.' na sequência de dígitos.

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

retornará false

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

retornará false

TulasiReddy
fonte
3
Não é o ideal, mas é bastante inteligente. Não manipula +/- e expoentes.
Sep16
Anos atrasado, mas este é um bom método. Trabalhou para mim usando o seguinte em um dataframe de pandas:[i for i in df[i].apply(lambda x: str(x).replace('.','').isdigit()).any()]
Mark Moretto
1
@MarkMoretto Você ficará chocado ao saber da existência de números negativos
David Heffernan
Melhor one-liner para o meu cenário, onde eu só preciso verificar números ou flutuadores positivos. Eu gosto.
MJohnyJ 17/04
8

TL; DR :

  • Se sua entrada for principalmente strings que podem ser convertidas em flutuantes, o try: except:método é o melhor método nativo do Python.
  • Se sua entrada for principalmente strings que não podem ser convertidas em flutuantes, expressões regulares ou o método de partição serão melhores.
  • Se você 1) não tem certeza da sua entrada ou precisa de mais velocidade e 2) não se importa e pode instalar uma extensão C de terceiros, os números rápidos funcionam muito bem.

Existe outro método disponível por meio de um módulo de terceiros chamado número rápido (divulgação, eu sou o autor); fornece uma função chamada isfloat . Eu peguei o exemplo mais unido descrito por Jacob Gabrielson nesta resposta , mas adicionei o fastnumbers.isfloatmétodo. Devo também observar que o exemplo de Jacob não fazia justiça à opção regex porque na maioria das vezes esse exemplo era gasto em pesquisas globais por causa do operador de ponto ... Modifiquei essa função para fazer uma comparação mais justa 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()

Na minha máquina, a saída é:

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

Como você pode ver, o regex na verdade não é tão ruim quanto parecia originalmente e, se você tem uma necessidade real de velocidade, o fastnumbersmétodo é muito bom.

SethMMorton
fonte
os números rápido check funciona tão bem se você tiver uma maioria de cordas que não podem ser convertidos em carros alegóricos, realmente agiliza as coisas, thankyou
ragardner
5

Se você se preocupou com o desempenho (e eu não estou sugerindo que você deveria), a abordagem baseada em tentativas é a vencedora (em comparação com a abordagem baseada em partições ou a abordagem regexp), desde que você não espere muito seqüências inválidas, nesse caso, é potencialmente mais lento (provavelmente devido ao custo do tratamento de exceções).

Novamente, não estou sugerindo que você se preocupe com o desempenho, apenas fornecendo os dados para o caso de você fazer isso 10 bilhões de vezes por segundo, ou algo assim. Além disso, o código baseado em partição não manipula pelo menos uma sequência válida.

$ ./floatstr.py
F ..
partição triste: 3.1102449894
partição feliz: 2.09208488464
..
re sad: 7.76906108856
feliz: 7.09421992302
..
tente triste: 12.1525540352
tente feliz: 1.44165301323
.
==================================================== ====================
FAIL: test_partition (__main __. ConvertTests)
-------------------------------------------------- --------------------
Traceback (última chamada mais recente):
  Arquivo "./floatstr.py", linha 48, na partição test_
    self.failUnless (is_float_partition ("20e2"))
AssertionError

-------------------------------------------------- --------------------
Ran 8 testes em 33.670s

FAILED (falhas = 1)

Aqui está o código (Python 2.6, regexp retirado da resposta de 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()
Jacob Gabrielson
fonte
4

Apenas por variedade, aqui está outro método para fazê-lo.

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

Edit: Tenho certeza de que ele não aguentará todos os casos de flutuação, especialmente quando houver um expoente. Para resolver isso, fica assim. Isso retornará True only val é um float e False para int, mas provavelmente tem menos desempenho que regex.

>>> 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
Peter Moore
fonte
A função isnumeric parece uma má escolha, pois retorna true em vários caracteres Unicode, como frações. Os documentos dizem: "Os caracteres numéricos incluem caracteres de dígito e todos os caracteres que possuem a propriedade de valor numérico Unicode, por exemplo, U + 2155, FRACÇÃO DE VULGAR QUINTA QUINTA"
gwideman
3

Este regex procurará números científicos de ponto flutuante:

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

No entanto, acredito que sua melhor aposta é usar o analisador em uma tentativa.

John Gietzen
fonte
2

Se você não precisar se preocupar com expressões científicas ou outras expressões de números e estiver trabalhando apenas com strings que poderiam ser números com ou sem um período:

Função

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

Versão Lambda

is_float = lambda x: x.replace('.','',1).isdigit() and "." in x

Exemplo

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

Dessa forma, você não está convertendo acidentalmente o que deveria ser um int, em um float.

kodetojoy
fonte
2

Versão simplificada da função is_digit(str) , que é suficiente na maioria dos casos (não considera notação exponencial e valor "NaN" ):

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

Eu usei a função já mencionada, mas logo noto que cadeias de caracteres como "Nan", "Inf" e sua variação são consideradas como número. Então, proponho a você uma versão aprimorada da função, que retornará false nesse tipo de entrada e não falhará nas variantes "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
fonte
1
Não poderíamos começar com o if text.isalpha():cheque imediatamente?
precisa
BTW eu preciso a mesma: Eu não quero aceitar NaN, Inf e outras coisas
Csaba Toth
1

Tente converter para flutuar. Se houver um erro, imprima a exceção ValueError.

try:
    x = float('1.23')
    print('val=',x)
    y = float('abc')
    print('val=',y)
except ValueError as err:
    print('floatErr;',err)

Resultado:

val= 1.23
floatErr: could not convert string to float: 'abc'
edW
fonte
1

Passando o dicionário como argumento, ele converterá seqüências de caracteres que podem ser convertidas em float e deixarão outras

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

Eu estava procurando por um código semelhante, mas parece que usar try / excpts é a melhor maneira. Aqui está o código que estou usando. Inclui uma função de repetição se a entrada for inválida. Eu precisava verificar se a entrada era maior que 0 e, se sim, convertê-la em um flutuador.

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: ")
Lockey
fonte
0
def try_parse_float(item):
  result = None
  try:
    float(item)
  except:
    pass
  else:
    result = float(item)
  return result
Tawanda Matereke
fonte
2
Embora esse código possa resolver a questão, incluir uma explicação de como e por que isso resolve o problema realmente ajudaria a melhorar a qualidade da sua postagem e provavelmente resultaria em mais votos positivos. Lembre-se de que você está respondendo à pergunta dos leitores no futuro, não apenas à pessoa que está perguntando agora. Por favor edite sua resposta para adicionar explicações e dar uma indicação do que limitações e premissas se aplicam.
beep duplo
0

Tentei algumas das opções simples acima, usando um teste try para converter para um float e descobri que há um problema na maioria das respostas.

Teste simples (seguindo as linhas das respostas acima):

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

O problema surge quando:

  • Você digita '-' para iniciar um número negativo:

Você está tentando então o float('-')que falha

  • Você digita um número, mas tenta excluir todos os dígitos

Você está tentando o float('')que também falha

A solução rápida que tive foi:

def _test_num(P):
    if P == '' or P == '-': return True
    try: 
        float(P)
        return True
    except ValueError:
        return False
Richard
fonte
-2
str(strval).isdigit()

parece ser simples.

Manipula valores armazenados como uma string ou int ou float

muks
fonte
In [2]: '123,123' .isdigit () Out [2]: False
Daniil Mashkin
1
Ele não funciona para números negativos literais, por favor corrija a sua resposta
RandomEli
'39 .1'.isdigit ()
Ohad the Lad
all ([x.isdigit () para x em str (VAR) .strip ('-'). replace (',', '.'). split ('.')]) Se você estiver procurando por uma descrição mais completa implementação.
lotrus28 30/01