14

In Python, what is the simplest way to convert a number enclosed in parentheses (string) to a negative integer (or float)?

For example, '(4,301)' to -4301, as commonly encountered in accounting applications.

silvernightstar
  • 1,885
  • 3
  • 19
  • 25
  • 1
    I don't know of a built-in way to do it. You can parse the comma-separated integer with [`locale`](http://stackoverflow.com/questions/1779288/how-do-i-use-python-to-convert-a-string-to-a-number-if-it-has-commas-in-it-as-th), but you'll need to handle the negativity yourself. – Katriel May 23 '13 at 16:04
  • 1
    Strange, there is `locale.currency` to go the other way, but you want the inverse of that which I can't seem to find .. same question was asked here already and no satisfactory answers http://stackoverflow.com/q/8421922/674039 – wim May 23 '13 at 16:16

8 Answers8

13

The simplest way is:

my_str = "(4,301)"
num = -int(my_str.translate(None,"(),"))
Crowman
  • 25,242
  • 5
  • 48
  • 56
  • 1
    Wow, translate is awesome! – ecline6 May 23 '13 at 16:19
  • 2
    Thanks, this led me to `int('(4,301)'.replace('(','-').translate(None,'), '))` which was enough for my purposes. – silvernightstar May 27 '13 at 01:14
  • Hi there. this answer assumes that i'm only going to deal with negative numbers. Any solution for when I'm not sure whether it'll be negative or positive ? – Pedro Braz Jan 20 '16 at 11:50
  • 2
    @PedroBraz: It doesn't assume that. If there are no parentheses, it just won't translate any, and will just remove any commas. – Crowman Jan 20 '16 at 12:24
  • 1
    true say, but the minus is outside the function, thus it'll be negative even though parenthesis abscense. I mean, i know a if will solve that, I was just looking for a clean solution. Thanks a lot for the answers ! – Pedro Braz Jan 20 '16 at 12:35
  • @PedroBraz: OK, you may have misunderstood the premise of the question, which deals with numbers presented in the accounting convention where negative numbers are shown enclosed in parentheses, and not with a minus sign. There is no "function" here - if your number has a minus sign, then you just have an incorrectly formatted number, at least in terms of the format that this question is asking about. For a number like `-4,301`, just get rid of the comma and convert it with `Int()`. – Crowman Jan 20 '16 at 12:56
  • 2
    @PaulGriffiths, I think PedroBraz is pointing out that if you set my_str to '4,301', num will be set to -4301. It's clear from a comment above that this is not the behavior silvernightstar wants, and it's probably not the behavior Pedro Braz wants either (http://stackoverflow.com/questions/16718648/convert-a-number-enclosed-in-parentheses-string-to-a-negative-integer-or-floa#comment24153742_16718742). – Vectornaut May 30 '16 at 08:36
  • 1
    @PedroBraz, in case you're still interested, I think silvernightstar's comment above gives the kind of clean solution you're looking for (http://stackoverflow.com/questions/16718648/convert-a-number-enclosed-in-parentheses-string-to-a-negative-integer-or-floa#comment24153742_16718742). – Vectornaut May 30 '16 at 08:37
8

Since you are reading from a system that put in thousands separators, it's worth mentioning that we are not using them the same way all around the world, which is why you should consider using a locale system. Consider:

import locale
locale.setlocale( locale.LC_ALL, 'en_US.UTF-8' )
my_str = "(4,301)"
result = -locale.atoi(my_str.translate(None,"()"))
mogul
  • 4,441
  • 1
  • 18
  • 22
  • 3
    Thank you Sir. I am in Denmark, and experience all kinds of crap due to "interesting" locales... – mogul May 23 '13 at 16:15
5

Assuming just removing the , is safe enough, and you may wish to apply the same function to values that may contain negative numbers or not, then:

import re
print float(re.sub(r'^\((.*?)\)$', r'-\1', a).replace(',',''))

You could then couple that with using locale as other answers have shown, eg:

import locale, re

locale.setlocale(locale.LC_ALL, 'en_GB.UTF-8')
print locale.atof(re.sub('^\((.*?)\)$', r'-\1', a))
Jon Clements
  • 138,671
  • 33
  • 247
  • 280
4

For Python 3.6, and also handles '-' as 0, and strips excess empty spaces:

def clean_num(num_string):
    bad_chars = '(),-'
    translator = str.maketrans('', '', bad_chars)
    clean_digits = num_string.translate(translator).strip()

    if clean_digits == '':
        return 0
    elif '(' in num_string:
        return -float(clean_digits)
    else:
        return float(clean_digits) 
yl_low
  • 1,209
  • 2
  • 17
  • 26
  • Worked for me !! we can update bad_chars with additional bad charcs to get desired result – Tokci May 27 '21 at 12:15
2

Presumably you want to handle positive numbers as well as negative, which is missing from many of the answers thus far. I'm going to add a bit to the answer from mogul.

import locale
locale.setlocale( locale.LC_ALL, '')
my_str = '( 4,301 )'
positive = my_str.translate(None, '()')
result = locale.atoi(positive) if positive == my_str else -locale.atoi(positive)
Community
  • 1
  • 1
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
2

For localized numbers, I use this:

def clean_number(text):
    import locale
    locale.setlocale(locale.LC_NUMERIC, "Portuguese_Brazil.1252")
    tbl = str.maketrans('(', '-', 'R$ )')
    return locale.atof(text.translate(tbl))

Works with Python 3.8. The first parenthesis is replaced with the minus sign, the second is removed. Also removes spaces and the monetary sign.

Julio Batista Silva
  • 1,861
  • 19
  • 19
0

This code could be a little bit longer, but straight forward and easy to maintain

from pyparsing import Word, nums, OneOrMore

integer = Word(nums)

text = "blah blah (4,301) blah blah " 

parser = OneOrMore(integer)

iterator = parser.scanString( text )

try:
    while True:
        part1 =  iterator.next()
        part2 =  iterator.next()
except:
    x =  part1[0][0][0] + '.' +part2[0][0][0]
    print -float(x)

Produces: -4.301

securecurve
  • 5,589
  • 5
  • 45
  • 80
0

As of today, the top answer is specific to Python 2. I tried editing it, but the queue is full.

For my_str = "(4,301)", the top answer advises:

num = -int(my_str.translate(None,"(),"))

For Python 3, you would instead use one of the following:

translate_table = dict({ord(i): None for i in '(),'})
num = -int(my_str.translate(translate_table))

or, more simply

num = -int(my_str.translate({ord(i): None for i in "(),"}))

If you're curious, the change is necessary for two reasons:

  1. str.translate() now expects a single argument.

  2. The str type contains Unicode. This is why each key in the dictionary is first passed to ord: this function accepts a given Unicode character and returns the Unicode code point for it:

     >>> ord('(')
     40
    
iff_or
  • 880
  • 1
  • 11
  • 24