Question

I want to convert a string to the tightest possible datatype: int or float.

I have two strings:

value1="0.80"     #this needs to be a float
value2="1.00"     #this needs to be an integer.

How I can determine that value1 should be Float and value2 should be Integer in Python?

Was it helpful?

Solution

def isfloat(x):
    try:
        a = float(x)
    except (TypeError, ValueError):
        return False
    else:
        return True

def isint(x):
    try:
        a = float(x)
        b = int(a)
    except (TypeError, ValueError):
        return False
    else:
        return a == b

OTHER TIPS

Python float objects have an is_integer method:

from ast import literal_eval
def parses_to_integer(s):
    val = literal_eval(s)
    return isinstance(val, int) or (isinstance(val, float) and val.is_integer())
def coerce(x):
    try:
        a = float(x)
        b = int(x)
        if a != b:
            return a
        else:
            return b
    except:
        raise ValueError("failed to coerce str to int or float")

I had to handle the case of ensuring that '1.0' gets converted to '1' when I was trying to determine the differences between two XML documents. So I wrote this function to help me. I also think that some of the other solutions will fail when the string literal in question is 'True' or 'False'. At any rate this function works very well for me. I hope that it will help you too.

from ast import literal_eval

def convertString(s):
    '''
    This function will try to convert a string literal to a number or a bool
    such that '1.0' and '1' will both return 1.

    The point of this is to ensure that '1.0' and '1' return as int(1) and that
    'False' and 'True' are returned as bools not numbers.

    This is useful for generating text that may contain numbers for diff
    purposes.  For example you may want to dump two XML documents to text files
    then do a diff.  In this case you would want <blah value='1.0'/> to match
    <blah value='1'/>.

    The solution for me is to convert the 1.0 to 1 so that diff doesn't see a
    difference.

    If s doesn't evaluate to a literal then s will simply be returned UNLESS the
    literal is a float with no fractional part.  (i.e. 1.0 will become 1)

    If s evaluates to float or a float literal (i.e. '1.1') then a float will be
    returned if and only if the float has no fractional part.

    if s evaluates as a valid literal then the literal will be returned. (e.g.
    '1' will become 1 and 'False' will become False)
    '''


    if isinstance(s, str):
        # It's a string.  Does it represnt a literal?
        #
        try:
            val = literal_eval(s)
        except:
            # s doesn't represnt any sort of literal so no conversion will be
            # done.
            #
            val = s
    else:
        # It's already something other than a string
        #
        val = s

    ##
    # Is the float actually an int? (i.e. is the float 1.0 ?)
    #
    if isinstance(val, float):
        if val.is_integer(): 
            return int(val)

        # It really is a float
        return val

    return val

The output of the unit test of this function produces:

convertString("1")=1; we expect 1
convertString("1.0")=1; we expect 1
convertString("1.1")=1.1; we expect 1.1
convertString("010")=8; we expect 8
convertString("0xDEADBEEF")=3735928559; we expect 3735928559
convertString("hello")="hello"; we expect "hello"
convertString("false")="false"; we expect "false"
convertString("true")="true"; we expect "true"
convertString("False")=False; we expect False
convertString("True")=True; we expect True
convertString(sri.gui3.xmlSamples.test_convertString.A)=sri.gui3.xmlSamples.test_convertString.A; we expect sri.gui3.xmlSamples.test_convertString.A
convertString(<function B at 0x7fd9e2f27ed8>)=<function B at 0x7fd9e2f27ed8>; we expect <function B at 0x7fd9e2f27ed8>
convertString(1)=1; we expect 1
convertString(1.0)=1; we expect 1
convertString(1.1)=1.1; we expect 1.1
convertString(3735928559)=3735928559; we expect 3735928559
convertString(False)=False; we expect False
convertString(True)=True; we expect True

The unit test code follows:

import unittest

# just  class for testing that the class gets returned unmolested.
#
class A: pass

# Just a function
#
def B(): pass

class Test(unittest.TestCase):


    def setUp(self):
        self.conversions = [
            # input      | expected
            ('1'         ,1         ),
            ('1.0'       ,1         ), # float with no fractional part
            ('1.1'       ,1.1       ),
            ('010'       ,8         ), # octal
            ('0xDEADBEEF',0xDEADBEEF), # hex
            ('hello'     ,'hello'   ),
            ('false'     ,'false'   ),
            ('true'      ,'true'    ),
            ('False'     ,False     ), # bool
            ('True'      ,True      ), # bool
            (A           ,A         ), # class
            (B           ,B         ), # function
            (1           ,1         ),
            (1.0         ,1         ), # float with no fractional part
            (1.1         ,1.1       ),
            (0xDEADBEEF  ,0xDEADBEEF),
            (False       ,False     ),
            (True        ,True      ),
        ]


    def testName(self):
        for s,expected in self.conversions:
            rval = convertString(s)
            print 'convertString({s})={rval}; we expect {expected}'.format(**locals())
            self.assertEqual(rval, expected)


if __name__ == "__main__":
    #import sys;sys.argv = ['', 'Test.testName']
    unittest.main()

Another way to do this, is by using regex like this:

import re
def parse_str(num):
    """
    Parse a string that is expected to contain a number.
    :param num: str. the number in string.
    :return: float or int. Parsed num.
    """
    if not isinstance(num, str): # optional - check type
        raise TypeError('num should be a str. Got {}.'.format(type(num)))
    if re.compile('^\s*\d+\s*$').search(num):
        return int(num)
    if re.compile('^\s*(\d*\.\d+)|(\d+\.\d*)\s*$').search(num):
        return float(num)
    raise ValueError('num is not a number. Got {}.'.format(num)) # optional

Notes on Regex Pattern

^      beginning of string
$      end of string
\s*    none or more spaces
\d+    one or many digits
\d*    none or many digits
\.     literal dot
|      or

Test

print(parse_str('1'))
print(parse_str('999'))
print(parse_str('1.2'))
print(parse_str('.3'))
print(parse_str('4.'))
print(parse_str('12.34'))
print(parse_str('    0.5    '))
print(parse_str('XYZ'))

Result

1
999
1.2
0.3
4.0
12.34
0.5
ValueError: num is not a number. Got XYZ.
lineVal = ['1850', '-0.373', '-0.339', '-0.425']

lineVal2 = [ float(x) if re.search(r'\.',x) else int(x) for x in lineVal ]

LineVal2 output ==> [1850, -0.373, -0.339, -0.425]

I'm new bee, I tried it, seem to work for me.

Here's an interesting solution using eval(). Note: using eval is highly dangerous and not recommended in production environments or anywhere that eval() might receive user input! Consider this an academically interesting answer only.

def get_string_type(x):
    if type(x) != str:
        raise ValueError('Input must be a string!')
    try:
        string_type = type(eval(x))
    except NameError:
        string_type = str
    return string_type

Since Eval treats the string as raw code, this works for any type you can enter into a repl. Example

>>> from decimal import Decimal
>>> my_test_string = 'Decimal(0.5)'
>>> type(my_test_string)
<class 'str'>
>>> get_string_type(my_test_string)
<class 'decimal.Decimal'>

I got to thinking that the request was to convert a number stored as a string to wither an or , whichever is the tightest data type. The following function fulfills the request (but does not check if the input is a valid value, i.e. a number an not alpha characters).

The str_to_float_or_int() converts a number stored as a string to a <float> or an <int>. Although all integers can be floats, an <int> is returned wherever possible to meet the "convert to tightest data type" criteria as when:

  • input = a whole number.
  • input = a characteristic with a 0 mantissa ('1.0')
  • input = a characteristic without a mantissa ('1.')

This technique uses the str.isdecimal() to determine if the string is a value (vs. a null) and the <str>.split(".") method to parse the string value (candidate number) into two parts:

  1. integer = number before the decimal
  2. Mantissa = number after the decimal place

The built-in <str>.split(".") method returns a list. In this case, list is of the following format: [integer, mantissa]

Note: Technically, the use of the term 'integer' here really refers to the 'characteristic'. I used 'integer' because it has less characters and thus easier to use in coding.

def str_to_float_or_int(value_str, ShowExtended=False):
    # Convert a number stored as a string to a <float> or an <int> 
    # whichever is the "tightest" data type. 

    # Default condition is that the number is a <float>
    isfloat = True
    value = float(value_str)
    numberParsed = value_str.split(".")
    if len(numberParsed) > 1:
        integer = numberParsed[0]
        mantissa = numberParsed[1]
        if integer.isdecimal() and mantissa.isdecimal():
            if int(mantissa) == 0:
                # value is an integer; mantissa is 0
                isfloat = False
                value = int(integer)
            elif integer.isdecimal():
                # value is an integer because a value is only
                # returned for 'integer' variable by .split(), 
                # the returned mantissa value is null.
                isfloat = False
                value = int(integer)
        else:
            # value is an integer because .split() returned
            # a single value list.
            isfloat = False
            value = int(value_str)
        if ShowExtended:
            print("testValue: " + value_str + " | splits into: ", 
                   numberParsed,"\n value: ", value)
            if isfloat:
                print("It's a <float> (;o)\n")
            else:
                print("It's an <int> {:o)~\n")
        return value

Run Script from the Console to test str_to_float_or_int()


testValues = ["0.80", "1.00", "5", ".1", "4."]
print("\n-----------------------------------------------\n" +
        "| Testcase:  ", testValues, " |\n" +
        "-----------------------------------------------")
for number in testValues:
    str_to_float_or_int(number, ShowExtended=True)

Output Results (copied from the console)


>   ---------------------------------------------------
>   |  Testcase:   ['0.80', '1.00', '5', '.1', '4.']  |
>   ---------------------------------------------------
>   testValue: 0.80 | splits into:  ['0', '80']
>   value:  0.8
>   It's a <float> (;o)
>   
>   testValue: 1.00 | splits into:  ['1', '00']
>   value:  1
>   It's an <int> {:o)~
>   
>   testValue: 5 | splits into:  ['5']
>   value:  5
>   It's an <int> {:o)~
>   
>   testValue: .1 | splits into:  ['', '1']
>   value:  0.1
>   It's a <float> (;o)
>
>   testValue: 4. | splits into:  ['4', '']
>   value:  4
>   It's an <int> {:o)~

this simple function can tackle the problem, you just need the "Solution" block of code.

intOrfloat.py:

import sys
def NumberType(argv):
    Number = argv[1]
    try:
        float(Number)   # Exception if not a number
        ################ Solution ################
        if '.' not in Number:
            return '%s is Integer'%(Number)
        if int(Number.split('.')[1]) > 0:
            return '%s is Float'%(Number)
        else:
            return '%s is Integer'%(Number)
        ##########################################
    except Exception as e:
        return '%s is Text...'%(Number)
if __name__ == '__main__':
    print(NumberType(sys.argv))

tests:

>python intOrfloat.py 0.80
0.80 is Float

>python intOrfloat.py 1.00
1.00 is Integer

>python intOrfloat.py 9999999999999999
9999999999999999 is Integer

So, no need to worry about the size of integer number.

'.' not in Number              # number without decimal must be an integer
Number.split('.')              # split into [integer String, decimal String]
Number.split('.')[1]           # get decimal String
int(Number.split('.')[1])      # convert it into decimal Number
int(Number.split('.')[1]) > 0  # decimal Number > 0 = Float; Otherwise, Integer

Example for a short function: Returns the numeric type of a string (float or int), for non-numeric strings the str type is returned.

def numeric_type_of_string(string: str):
    if string.isnumeric():
        return int
    try:
        val = float(string)
        return int if val == int(val) else float
    except (TypeError, ValueError):
        return str

In case you directly want the converted value simply modify the return value:

def string_to_numeric_if_possible(string: str):
    if string.isnumeric():
        return int(string)
    try:
        val = float(string)
        return int(val) if val == int(val) else val
    except (TypeError, ValueError):
        return string
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top