Checking if a string can be converted to float in Python
Solution 1
I would just use..
try:
float(element)
except ValueError:
print "Not a float"
..it's simple, and it works. Note that it will still throw OverflowError if element is e.g. 1<<1024.
Another option would be a regular expression:
import re
if re.match(r'^-?\d+(?:\.\d+)$', element) is None:
print "Not float"
Solution 2
Python method to check for float:
def is_float(element: Any) -> bool:
try:
float(element)
return True
except ValueError:
return False
Always do unit testing. What is, and is not a float may surprise you:
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
Solution 3
'1.43'.replace('.','',1).isdigit()
which will return true
only if there is one or no '.' in the string of digits.
'1.4.3'.replace('.','',1).isdigit()
will return false
'1.ww'.replace('.','',1).isdigit()
will return false
Solution 4
TL;DR:
- If your input is mostly strings that can be converted to floats, the
try: except:
method is the best native Python method. - If your input is mostly strings that cannot be converted to floats, regular expressions or the partition method will be better.
- If you are 1) unsure of your input or need more speed and 2) don't mind and can install a third-party C-extension, fastnumbers works very well.
There is another method available via a third-party module called fastnumbers (disclosure, I am the author); it provides a function called isfloat. I have taken the unittest example outlined by Jacob Gabrielson in this answer, but added the fastnumbers.isfloat
method. I should also note that Jacob's example did not do justice to the regex option because most of the time in that example was spent in global lookups because of the dot operator... I have modified that function to give a fairer comparison to 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()
On my machine, the output is:
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
As you can see, regex is actually not as bad as it originally seemed, and if you have a real need for speed, the fastnumbers
method is quite good.
Solution 5
Just for variety here is another method to do it.
>>> 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: Im sure it will not hold up to all cases of float though especially when there is an exponent. To solve that it looks like this. This will return True only val is a float and False for int but is probably less performant than 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
Related videos on Youtube
Chris Upchurch
I am a professor at the University of South Carolina. I'm not a professional programmer, but I do some programming in the course of my research work. I like to use python when I can but I am occasionally forced to use VB.
Updated on April 19, 2022Comments
-
Chris Upchurch about 2 years
I've got some Python code that runs through a list of strings and converts them to integers or floating point numbers if possible. Doing this for integers is pretty easy
if element.isdigit(): newelement = int(element)
Floating point numbers are more difficult. Right now I'm using
partition('.')
to split the string and checking to make sure that one or both sides are digits.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)
This works, but obviously the if statement for that is a bit of a bear. The other solution I considered is to just wrap the conversion in a try/catch block and see if it succeeds, as described in this question.
Anyone have any other ideas? Opinions on the relative merits of the partition and try/catch approaches?
-
bombs almost 3 yearsTo be precise, there is no such thing as type-conversion in Python. So the tag
type-conversion
is misleading, which is a well-defined term in some languages. -
ggorlen over 2 years@bombs no type convesion? Then what do you call this:
print(type("1"));print(type(int("1")))
, output:<class 'str'>
<class 'int'>
? Is this not a type conversion fromstr
toint
? -
bombs over 2 years@ggorlen Not at all. You are just creating new objects from other objects. You allocate new memory spaces in the heap along the way. Nothing is converted.
-
ggorlen over 2 yearsI see what you mean, but that seems pedantic to me. If someone says "convert string to float" the intent seems pretty universally clear.
-
Charlie Parker over 2 yearswhy did you use
.isdigit()
and not.isnumeric():
for positive ints?
-
-
Chris Upchurch about 15 years@S.Lott: Most of the strings this is applied to will turn out to be ints or floats.
-
dustyburwell about 15 yearsIs there not a tryfloat(element) or otherwise equivalent function like C#'s float.TryParse(element). Typically excepting is not very performant. Not that this is something that will happen very often, but if it's in a tight loop, it could be an issue.
-
dhara tcrails about 15 yearsYour regex is not optimal. "^\d+\.\d+$" will fail a match at the same speed as above, but will succeed faster. Also, a more correct way would be: "^[+-]?\d(>?\.\d+)?$" However, that still doesn't match numbers like: +1.0e-10
-
Erusa Chan about 15 yearsExcept that you forgot to name your function "will_it_float".
-
Patrick B. over 9 yearsThe second option won't catch nan and exponential expression - such as 2e3.
-
Gewthen over 8 yearsRegex is much more slow and memory intensive to use and in this case not an option for use in production code.
-
Carlos almost 8 yearsI think the regex is not parsing negative numbers.
-
Mad Physicist over 7 yearsNot optimal but actually pretty clever. Won't handle +/- and exponents.
-
Csaba Toth about 7 yearsCouldn't we start with the
if text.isalpha():
check right away? -
Csaba Toth about 7 yearsBTW I need the same: I don't want accept NaN, Inf and stuff
-
ragardner almost 7 yearsthe fast numbers check works so well if you have a majority of strings that can't be converted to floats, really speeds things up, thankyou
-
BareNakedCoder almost 7 yearsGreat answer. Just adding 2 more where float=True:
isfloat(" 1.23 ")
andisfloat(" \n \t 1.23 \n\t\n")
. Useful in web requests; no need to trim white spaces first. -
Daniil Mashkin over 6 yearsIn [2]: '123,123'.isdigit() Out[2]: False
-
RandomEli over 6 yearsIt does not work for negative numbers literal, please fix your answer
-
Ébe Isaac over 5 yearsCould you please state which one is faster?
-
gwideman over 5 yearsThe isnumeric function looks like a poor choice, as it returns true on various Unicode characters like fractions. Docs say: "Numeric characters include digit characters, and all characters that have the Unicode numeric value property, e.g. U+2155, VULGAR FRACTION ONE FIFTH"
-
Ohad the Lad almost 5 years'39.1'.isdigit()
-
Mark Moretto over 4 yearsYears late, but this is a nice method. Worked for me using the following in a pandas dataframe:
[i for i in df[i].apply(lambda x: str(x).replace('.','').isdigit()).any()]
-
David Heffernan over 4 years@MarkMoretto You are going to be in for a shock when you learn of the existence of negative numbers
-
lotrus28 over 4 yearsall([x.isdigit() for x in str(VAR).strip('-').replace(',','.').split('.')]) If you are looking for a more complete implementation.
-
double-beep about 4 yearsWhile this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.
-
madladzen about 4 yearsstraight up isnt working for me and is just closing terminal RIP
-
MJohnyJ about 4 yearsBest one-liner for my scenario, where I just need to check for positive floats or numbers. I like.
-
dariober over 3 yearsAnother addition that may be surpsing:
isfloat("1_2_3.4")
->True
-
Erik Wognsen over 3 yearsNote that float() can also raise "TypeError: float() argument must be a string or a number". It is not enough to check for ValueError.
-
Gaurav Koradiya almost 3 yearsYou can also place and operator like this. def is_float(s): result = False if s.count(".") == 1 and s.replace(".", "").isdigit(): result = True return result
-
ggorlen over 2 yearsNaming a variable
str
overwrites a builtin function. -
ggorlen over 2 yearsWhat's the point of the
(?:)
? Isn'tr'^-?\d+\.\d+$'
the same thing? -
Levi over 2 yearsYou likely want to except type errors as well for
isfloat(None
andisfloat(False)
Edit: Removed my code example as the multiline formatting as quite mangled. -
Ralph over 2 years@dariober Why ?
-
étale-cohomology about 2 yearsvery nice, elegant
-
Hawkeye Parker about 2 years
am_i_a_number.strip().lstrip('-').replace('.', '', 1).isdigit()