[Tutor] Python 3: string to decimal conversion
US
usaidov at gmail.com
Mon May 23 13:18:58 EDT 2016
Thanks everyone for all your help. This solved my problem with
parenthesis and $ signs in the data:
if not row[4]:
pass
else:
try:
expenses[ts.Date(row[0]).month] +=
decimal.Decimal(row[4].strip('()$ ,').replace(',',''))
except decimal.InvalidOperation as e:
print("unexpected expenses value: %r" % (row[4]))
On Mon, May 23, 2016 at 5:06 AM, Peter Otten <__peter__ at web.de> wrote:
> US wrote:
>
>> Thank you both for suggesting a way to handle errors. I have run the
>> suggested code. What I learned is that all the values (not only empty
>> cells) seem to be invalid for decimal.Decimal function.
>>
>> I tried the float() function instead of decimal.Decimal and got an
>> error message: could not convert string to float: '($75.59)'.
>>
>> I also checked the type of values in row[4]. All the values passed on
>> to decimal.Decimal () function are indeed of string type...
>>
>> Is there anything I can do to the formatting of the csv file to make
>> it 'readable' for decimal.Decimal function?
>>
>> Here is my updated code along with the output I got from running it:
>>
>> ----------------------------------------------------
>> code:
>> import numpy as np
>> import csv
>> import timestring as ts
>> import decimal
>>
>> months= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
>> expenses = {x: decimal.Decimal() for x in months}
>> income = {x: decimal.Decimal() for x in months}
>> test = []
>>
>> exp_cat = []
>> income_cat = []
>>
>> files =['export.csv']
>>
>> with open("budgetfile.csv","wt") as fw:
>> writer = csv.writer(fw)
>> for file in files:
>> with open(file, newline ='' ) as csvfile:
>> records = csv.reader(csvfile)
>> print("Processing file {}. \n" .format(file))
>> header = next(records)
>> for row in records:
>> print("row[4] =", repr(row[4]), "value type =",
>> type(row[4]))
>>
>> if not row[4]:
>> pass
>> else:
>> try:
>> expenses[ts.Date(row[0]).month] +=
>> decimal.Decimal(row[4])
>> except decimal.InvalidOperation as e:
>> print("unexpected expenses value: %r" %
>> (row[4],))
>
>> last 4 lines of output:
>> [4] = '($10.00)' value type = <class 'str'>
>> unexpected expenses value: '($10.00)'
>> row[4] = '($287.42)' value type = <class 'str'>
>> unexpected expenses value: '($287.42)'
>
> Perhaps you should use a custom conversion routine like to_decimal() below:
>
> class MissingValue(ValueError):
> pass
>
>
> @contextlib.contextmanager
> def expect(exc):
> """Ensure that an excption of type `exc` was raised.
>
> Helper for the doctests.
> """
> try:
> yield
> except exc:
> pass
> except:
> raise AssertionError(
> "Wrong exception type (expected {})".format(exc))
> else:
> raise AssertionError("Exception was not raised")
>
>
> def to_decimal(s):
> """
> >>> with expect(MissingValue):
> ... to_decimal(" ")
> >>> with expect(ValueError):
> ... to_decimal("42")
> >>> to_decimal("$12.34")
> Decimal('12.34')
> >>> to_decimal("($34.56)")
> Decimal('-34.56')
> >>> with expect(ValueError):
> ... to_decimal("foo")
> >>> with expect(ValueError):
> ... to_decimal("$bar")
> >>> with expect(ValueError):
> ... to_decimal("($baz)")
> """
> s = s.strip() # remove leading/trailing whitespace
> if not s:
> raise MissingValue("Empty amount")
> if s.startswith("(") and s.endswith(")"):
> sign = -1
> s = s[1:-1] # remove parens
> else:
> sign = 1
> if not s.startswith("$"):
> raise ValueError("No leading $ found")
> s = s[1:] # remove $
> try:
> value = decimal.Decimal(s)
> except decimal.InvalidOperation as err:
> raise ValueError(err.args[0]) from None
> return sign * value
>
>
> That way you can spell out explicitly what the allowed values may look like,
> and if (e. g.) you want to allow for grouping you can easily add another
> preprocessing step to remove the commas. Use it like so in your code:
>
> ...
> for row in records:
> try:
> amount = to_decimal(row[4])
> except ValueError as err:
> print(err, file=sys.stderr)
> else:
> ... # add amount to expenses
> ...
>
> _______________________________________________
> Tutor maillist - Tutor at python.org
> To unsubscribe or change subscription options:
> https://mail.python.org/mailman/listinfo/tutor
More information about the Tutor
mailing list