Formatting number in Python according to culture code - python

Im trying to find a way to format number in Python according to the culture code used.
In R, for example, there is a function called 'formatC' that does exactly what i need:
if the culture code is 'en-US' the decimal mark will be ',' and the decimal mark, '.' (ex: 1,000.00) and, if the culture code is 'pt-BR', we would have 1.000,00.
Now, Im trying to do this in Python.. idk if there is a function that already does that... Can someone help me?

You can use python's built in 'locale' module. I would have a look through the docs here: https://docs.python.org/2/library/locale.html, but for your example you can do something like this:
import locale
#note the underscore rather than -
locale.setlocale(locale.LC_ALL, 'pt_BR')
print(locale.currency(1000.00, False))
#prints '1000,00'
Using locale you will be able to format a wide range of values to suit specific locations, not just currency!

You want the locale package, it has functions for doing locale specific formating:
https://docs.python.org/3.8/library/locale.html

This is a function which takes the number, d1 =',' and d2 ='.' for example and outputs the number in the desired format as a string
def formatC (number, d1, d2):
real_part = int (number)
rest = str(number-int(number))[2:]
real_part_as_string = str (real_part)
k = 0
result = ''
for i in range (len(real_part_as_string)-1, -1, -1):
k = k+1
result = real_part_as_string [i] + result
if (k % 3 == 0 and i != 0):
result = d1+result
k = 0
if (not rest):
return result
return result + d2 + str (rest)
I don't know if this is exactly what is asked for because this returns a string.
[edited]
Formatting an array:
def formatArray (array, d1, d2):
for element in array:
print (formatC (element, d1, d2))
Example input:
formatArray ([1200.0, 12346], ',', '.')

Related

Trying to convert a string to a list of complex numbers

I am trying to convert a string to a list of complex numbers. (If you were to read it without quotes, it would be a list of complex numbers.) I've written a function to do this, but I'm getting this error:
Traceback (most recent call last):
File "complex.py", line 26, in <module>
print(listCmplx('[1.111 + 2.222j, 3.333 + 4.444j]'))
File "complex.py", line 10, in listCmplx
while (not isDigit(listIn[count])) and (listIn[count] != '.'):
IndexError: string index out of range
What am I doing wrong here?
def isDigit(char):
return char in '0123456789'
def listCmplx(listIn):
listOut = []
count = 0
real = '0'
imag = '0'
while count < len(listIn):
while (not isDigit(listIn[count])) and (listIn[count] != '.'):
count += 1
start = count
while (isDigit(listIn[count])) or (listIn[count] == '.'):
count += 1
end = count
if listIn[count] == 'j':
imag = listIn[start:end]
else:
real = listIn[start:end]
if listIn[count] == ',':
listOut += [float(real) + float(imag) * 1j]
real = '0'
imag = '0'
return listOut
print(listCmplx('[1.111 + 2.222j, 3.333 + 4.444j]'))
Thank you in advance.
Amazingly, this is something Python can do without needing any functions written, with its inbuilt complex number class.
listIn = '1.111 + 2.222j, 3.333 + 4.444j'
listOut = eval(listIn)
print(listOut[0])
print(listOut[0].imag,listOut[0].real)
Your original parsing problem is a good example because it highlights the importance, whenever possible, of using the simplest, highest-level parsing tools available. Simple, high-level tools include basic things like splitting, stripping, and string indexing. Regex might be considered a mid-level tool, and it's certainly a more complex one. The lowest-level tool -- and the one you chose -- was character by character analysis. Never do that unless you are absolutely forced to by the problem at hand.
Here's one way to parse your example input with simple tools:
# Helper function to take a string a return a complex number.
def s2complex(s):
r, _, i = s.split()
return complex(float(r), float(i[:-1]))
# Parse the input.
raw = '[1.111 + 2.222j, 3.333 + 4.444j]'
xs = raw[1:-1].split(', ')
nums = [s2complex(x) for x in xs]
# Check.
for n in nums:
print(n)

Format a large integer with commas without using .format()

I'm trying to format any number by inserting ',' every 3 numbers from the end by not using format()
123456789 becomes 123,456,789
1000000 becomes 1,000,000
What I have so far only seems to go from the start, I've tried different ideas to get it to reverse but they seem to not work as I hoped.
def format_number(number):
s = [x for x in str(number)]
for a in s[::3]:
if s.index(a) is not 0:
s.insert(s.index(a), ',')
return ''.join(s)
print(format_number(1123456789))
>> 112,345,678,9
But obviously what I want is 1,123,456,789
I tried reversing the range [:-1:3] but I get 112,345,6789
Clarification: I don't want to use format to structure the number, I'd prefer to understand how to do it myself just for self-study's sake.
Here is a solution for you, without using built-in functions:
def format_number(number):
s = list(str(number))[::-1]
o = ''
for a in range(len(s)):
if a and a % 3 == 0:
o += ','
o += s[a]
return o[::-1]
print(format_number(1123456789))
And here is the same solution using built-in functions:
def format_number(number):
return '{:,}'.format(number)
print(format_number(1123456789))
I hope this helps. :D
One way to do it without built-in functions at all...
def format_number(number):
i = 0
r = ""
while True:
r = "0123456789"[number % 10] + r
number //= 10
if number == 0:
return r
i += 1
if i % 3 == 0:
r = "," + r
Here's a version that's almost free of built-in functions or methods (it does still have to use str)
def format_number(number):
i = 0
r = ""
for character in str(number)[::-1]:
if i > 0 and i % 3 == 0:
r = "," + r
r = character + r
i += 1
return r
Another way to do it without format but with other built-ins is to reverse the number, split it into chunks of 3, join them with a comma, and reverse it again.
def format_number(number):
backward = str(number)[::-1]
r = ",".join(backward[i:i+3] for i in range(0, len(backward), 3))
return r[::-1]
Your current approach has following drawbacks
checking for equality/inequality in most cases (especially for int) should be made using ==/!= operators, not is/is not ones,
using list.index returns first occurence from the left end (so s.index('1') will be always 0 in your example), we can iterate over range if indices instead (using range built-in).
we can have something like
def format_number(number):
s = [x for x in str(number)]
for index in range(len(s) - 3, 0, -3):
s.insert(index, ',')
return ''.join(s)
Test
>>> format_number(1123456789)
'1,123,456,789'
>>> format_number(6789)
'6,789'
>>> format_number(135)
'135'
If range, list.insert and str.join are not allowed
We can replace
range with while loop,
list.insert using slicing and concatenation,
str.join with concatenation,
like
def format_number(number):
s = [x for x in str(number)]
index = len(s) - 3
while index > 0:
s = s[:index] + [','] + s[index:]
index -= 3
result = ''
for character in s:
result += character
return result
Using str.format
Finally, following docs
The ',' option signals the use of a comma for a thousands separator. For a locale aware separator, use the 'n' integer presentation type instead.
your function can be simplified to
def format_number(number):
return '{:,}'.format(number)
and it will even work for floats.

Python (lambda?) random number generation

I have a string where I want to output random ints of differing size using Python's built-in format function.
IE: "{one_digit}:{two_digit}:{one_digit}"
Yields: "3:27:9"
I'm trying:
import random
"{one_digit}:{two_digit}:{one_digit}".format(one_digit=random.randint(1,9),two_digits=random.randint(10,99))
but this always outputs...
"{one_digit}:{two_digit}:{one_digit}".format(one_digit=random.randint(1,9),two_digit=random.randint(10,99))
>>>'4:22:4'
"{one_digit}:{two_digit}:{one_digit}".format(one_digit=random.randint(1,9),two_digit=random.randint(10,99))
>>>'7:48:7'
"{one_digit}:{two_digit}:{one_digit}".format(one_digit=random.randint(1,9),two_digit=random.randint(10,99))
>>>'2:28:2'
"{one_digit}:{two_digit}:{one_digit}".format(one_digit=random.randint(1,9),two_digit=random.randint(10,99))
>>>'1:12:1'
Which is as expected since the numbers are evaluated before hand. I'd like them to all be random, though. I tried using a lambda function but only got this:
"test{number}:{number}".format(number=lambda x: random.randint(1,10))
But that only yields
"test{number}:{number}".format(number=lambda x: random.randint(1,10))
>>>'test<function <lambda> at 0x10aa14e18>:<function <lambda> at 0x10aa14e18>'
First off: str.format is the wrong tool for the job, because it doesn't allow you to generate a different value for each replacement.
The correct solution is therefore to implement your own replacement function. We'll replace the {one_digit} and {two_digit} format specifiers with something more suitable: {1} and {2}, respectively.
format_string = "{1}:{2}:{1}"
Now we can use regex to substitute all of these markers with random numbers. Regex is handy because re.sub accepts a replacement function, which we can use to generate a new random number every time:
import re
def repl(match):
num_digits = int(match.group(1))
lower_bound = 10 ** (num_digits - 1)
upper_bound = 10 * lower_bound - 1
random_number = random.randint(lower_bound, upper_bound)
return str(random_number)
result = re.sub(r'{(\d+)}', repl, format_string)
print(result) # result: 5:56:1
How about this?
import random
r = [1,2,3,4,5]
','.join(map(str,(random.randint(-10**i,10**i) for i in r)))
The first two params(-10** i, 10**i) are low and upper bound meanwhile size=10 is the amount of numbers).
Example output: '-8,45,-328,7634,51218'
Explanation:
It seems you are looking to join random numbers with ,. This can simply be done using ','.join([array with strings]), e.g. ','.join(['1','2']) which would return '1,2'.
What about This?
'%s:%s:%s' % (random.randint(1,9),random.randint(10,99),random.randint(1,9))
EDIT : meeting requirements.
a=[1,2,2,1,3,4,5,9,0] # our definition of the pattern (decimal range)
b= ''
for j in enumerate(a):
x=random.randint(10**j,10**(j+1)-1)
b = b + '%s:' % x
print(b)
sample:
print (b)
31:107:715:76:2602:99021:357311:7593756971:1:

Python: How to do several float formattings with 1 lambda function

I have a numpy array
[2.15295647e+01, 8.12531501e+00, 3.97113829e+00, 1.00777250e+01]
and would like to format it so that it looks like this
[21.53, 08.13, 03.97, 10.08]
float_formatter = lambda x: "%.2f" % x
np.set_printoptions(formatter={'float_kind':float_formatter})
How can I adjust this to contain 2 float manipulations?
I need something like %02d %.2f % (x,y) but don't know how to change the lambda function to do this.
From what I understand, you just want your array values to be padded with two leading zeros and printed to two decimal places. All you need to do is modify the formatting string:
In [1]: "%05.2f" %np.pi
Out[1]: '03.14'
To break this down, the 5 means that the total length of the formatted string is 5 characters (including the decimal point), the 0 means that you want to pad with leading zeros, and the .2 means that you want two figures after the decimal point.
So if you wanted, say, 3 leading zeros and 4 decimal places you would do:
In [2]: "%08.4f" %np.pi
Out[2]: '003.1416
If you just wanted to pad with leading spaces rather than zeros, you can omit the 0 in front of the total length:
In [3]: "%8.4f" %np.pi
Out[3]: ' 3.1416'
There are other options that you can read about in the table of conversion flags here.
To set this as your new formatter, you would just do:
In [4]: float_formatter = lambda x: "%05.2f" %x
In [5]: np.set_printoptions(formatter={'float_kind':float_formatter})
If you want to work with two floats in your lambda, you need two input arguments like this:
float_formatter = lambda x, y: "%.2f %.2f" % (x, y)
You can define multiple inputs to lambda expressions, not just a single and the name is arbitrary. Here's the same thing with different argument names.
float_formatter = lambda float_one, float_two: "%.2f %.2f" % (float_one, float_two)
To change the values in the list you could use a list comprehension. Python 3 example below.
import numpy as np
precision = 2
formatter = "{0:."+str(precision)+"f}"
a = np.array([2.15295647e+01, 8.12531501e+00, 3.97113829e+00, 1.00777250e+01])
aFormat = np.array([float(formatter.format(i)) for i in a])
print(a, aFormat)
I first tried epileptic fish's option, but the printer function doesn't give you an array. It just gives you a float. So if you really want to change the print option for a 2d numpy matrix you will have to define your own function with an external counter on what to format which value with. The example below shows this with a class, because you don't want the external counter to be a global.
import numpy
class npPrint(object):
def __init__(self):
self.counter = 0
self.precision = 2
# end Constructor
def printer(self, x):
self.precision = 2
if self.counter == 0:
formatter = "{0:."+str(self.precision)+"f}"
self.counter += 1
else:
formatter = "{0:0"+str(self.precision)+"d}"
x = int(round(x))
self.counter = 0
return formatter.format(x)
# end printer
formatter = npPrint()
a = numpy.array([[2.15295647e+01, 8.12531501e+00], [3.97113829e+00, 1.00777250e+01]])
numpy.set_printoptions(formatter={"float_kind": lambda x, f=formatter: f.printer(x)})
print(a)
You have to have a counter, because you don't know if you are handed the x value or the y value. You just receive a float. This is really ugly. If I were you I would handle the printing myself and break apart the 2d matrix.

Encoding a numeric string into a shortened alphanumeric string, and back again

Quick question. I'm trying to find or write an encoder in Python to shorten a string of numbers by using upper and lower case letters. The numeric strings look something like this:
20120425161608678259146181504021022591461815040210220120425161608667
The length is always the same.
My initial thought was to write some simple encoder to utilize upper and lower case letters and numbers to shorten this string into something that looks more like this:
a26Dkd38JK
That was completely arbitrary, just trying to be as clear as possible.
I'm certain that there is a really slick way to do this, probably already built in. Maybe this is an embarrassing question to even be asking.
Also, I need to be able to take the shortened string and convert it back to the longer numeric value.
Should I write something and post the code, or is this a one line built in function of Python that I should already know about?
Thanks!
This is a pretty good compression:
import base64
def num_to_alpha(num):
num = hex(num)[2:].rstrip("L")
if len(num) % 2:
num = "0" + num
return base64.b64encode(num.decode('hex'))
It first turns the integer into a bytestring and then base64 encodes it. Here's the decoder:
def alpha_to_num(alpha):
num_bytes = base64.b64decode(alpha)
return int(num_bytes.encode('hex'), 16)
Example:
>>> num_to_alpha(20120425161608678259146181504021022591461815040210220120425161608667)
'vw4LUVm4Ea3fMnoTkHzNOlP6Z7eUAkHNdZjN2w=='
>>> alpha_to_num('vw4LUVm4Ea3fMnoTkHzNOlP6Z7eUAkHNdZjN2w==')
20120425161608678259146181504021022591461815040210220120425161608667
There are two functions that are custom (not based on base64), but produce shorter output:
chrs = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = len(chrs)
def int_to_cust(i):
result = ''
while i:
result = chrs[i % l] + result
i = i // l
if not result:
result = chrs[0]
return result
def cust_to_int(s):
result = 0
for char in s:
result = result * l + chrs.find(char)
return result
And the results are:
>>> int_to_cust(20120425161608678259146181504021022591461815040210220120425161608667)
'9F9mFGkji7k6QFRACqLwuonnoj9SqPrs3G3fRx'
>>> cust_to_int('9F9mFGkji7k6QFRACqLwuonnoj9SqPrs3G3fRx')
20120425161608678259146181504021022591461815040210220120425161608667L
You can also shorten the generated string, if you add other characters to the chrs variable.
Do it with 'class':
VALID_CHRS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
BASE = len(VALID_CHRS)
MAP_CHRS = {k: v
for k, v in zip(VALID_CHRS, range(BASE + 1))}
class TinyNum:
"""Compact number representation in alphanumeric characters."""
def __init__(self, n):
result = ''
while n:
result = VALID_CHRS[n % BASE] + result
n //= BASE
if not result:
result = VALID_CHRS[0]
self.num = result
def to_int(self):
"""Return the number as an int."""
result = 0
for char in self.num:
result = result * BASE + MAP_CHRS[char]
return result
Sample usage:
>> n = 4590823745
>> tn = TinyNum(a)
>> print(n)
4590823745
>> print(tn.num)
50GCYh
print(tn.to_int())
4590823745
(Based on Tadeck's answer.)
>>> s="20120425161608678259146181504021022591461815040210220120425161608667"
>>> import base64, zlib
>>> base64.b64encode(zlib.compress(s))
'eJxly8ENACAMA7GVclGblv0X4434WrKFVW5CtJl1HyosrZKRf3hL5gLVZA2b'
>>> zlib.decompress(base64.b64decode(_))
'20120425161608678259146181504021022591461815040210220120425161608667'
so zlib isn't real smart at compressing strings of digits :(

Categories

Resources