Convert an amount to Indian Notation in Python - python

Problem: I need to convert an amount to Indian currency format
My code: I have the following Python implementation:
import decimal
def currencyInIndiaFormat(n):
d = decimal.Decimal(str(n))
if d.as_tuple().exponent < -2:
s = str(n)
else:
s = '{0:.2f}'.format(n)
l = len(s)
i = l-1;
res = ''
flag = 0
k = 0
while i>=0:
if flag==0:
res = res + s[i]
if s[i]=='.':
flag = 1
elif flag==1:
k = k + 1
res = res + s[i]
if k==3 and i-1>=0:
res = res + ','
flag = 2
k = 0
else:
k = k + 1
res = res + s[i]
if k==2 and i-1>=0:
res = res + ','
flag = 2
k = 0
i = i - 1
return res[::-1]
def main():
n = 100.52
print "INR " + currencyInIndiaFormat(n) # INR 100.52
n = 1000.108
print "INR " + currencyInIndiaFormat(n) # INR 1,000.108
n = 1200000
print "INR " + currencyInIndiaFormat(n) # INR 12,00,000.00
main()
My Question: Is there a way to make my currencyInIndiaFormat function shorter, more concise and clean ? / Is there a better way to write my currencyInIndiaFormat function ?
Note: My question is mainly based on Python implementation of the above stated problem. It is not a duplicate of previously asked questions regarding conversion of currency to Indian format.
Indian Currency Format:
For example, numbers here are represented as:
1
10
100
1,000
10,000
1,00,000
10,00,000
1,00,00,000
10,00,00,000
Refer Indian Numbering System

Too much work.
>>> import locale
>>> locale.setlocale(locale.LC_MONETARY, 'en_IN')
'en_IN'
>>> print(locale.currency(100.52, grouping=True))
₹ 100.52
>>> print(locale.currency(1000.108, grouping=True))
₹ 1,000.11
>>> print(locale.currency(1200000, grouping=True))
₹ 12,00,000.00

You can follow these steps.
Install Babel python package from pip
pip install Babel
In your python script
from babel.numbers import format_currency
format_currency(5433422.8012, 'INR', locale='en_IN')
Output:
₹ 54,33,422.80

def formatINR(number):
s, *d = str(number).partition(".")
r = ",".join([s[x-2:x] for x in range(-3, -len(s), -2)][::-1] + [s[-3:]])
return "".join([r] + d)
It's simple to use:
print(formatINR(123456))
Output
1,23,456
If you want to handle negative numbers
def negativeFormatINR(number):
negativeNumber = False
if number < 0:
number = abs(number)
negativeNumber = True
s, *d = str(number).partition(".")
r = ",".join([s[x-2:x] for x in range(-3, -len(s), -2)][::-1] + [s[-3:]])
value = "".join([r] + d)
if negativeNumber:
return '-' + value
return value
It's simple to use:
print(negativeFormatINR(100-10000))
output
-9,900

Note - THIS IS AN ALTERNATIVE SOLUTION FOR ACTUAL QUESTION
If anyone trying to convert in simple Indian terms like K, L, or Cr with 2 floating-point values, the following solution would work.
def format_cash(amount):
def truncate_float(number, places):
return int(number * (10 ** places)) / 10 ** places
if amount < 1e3:
return amount
if 1e3 <= amount < 1e5:
return str(truncate_float((amount / 1e5) * 100, 2)) + " K"
if 1e5 <= amount < 1e7:
return str(truncate_float((amount / 1e7) * 100, 2)) + " L"
if amount > 1e7:
return str(truncate_float(amount / 1e7, 2)) + " Cr"
Examples
format_cash(7843) --> '7.84 K'
format_cash(78436) --> '78.43 K'
format_cash(784367) --> '7.84 L'
format_cash(7843678) --> '78.43 L'
format_cash(78436789) --> '7.84 Cr'

Here is the other way around:
import re
def in_for(value):
value,b=str(value),''
value=''.join(map(lambda va:va if re.match(r'[0-9,.]',va) else '',value))
val=value
if val.count(',')==0:
v,c,a,cc,ii=val,0,[3,2,2],0,0
val=val[:val.rfind('.')] if val.rfind('.')>=0 else val
for i in val[::-1]:
if c==ii and c!=0:
ii+=a[cc%3]
b=','+i+b
cc+=1
else:
b=i+b
c+=1
b=b[1:] if b[0]==',' else b
val=b+v[value.rfind('.'):] if value.rfind('.')>=0 else b
else:
val=str(val).strip('()').replace(' ','')
v=val.rfind('.')
if v>0:
val=val[:v+3]
return val.rstrip('0').rstrip('.') if '.' in val else val
print(in_for('1000000000000.5445'))
Output will be:
10,000,00,00,000.54
(As mentioned in wikipedia indian number system Ex:67,89,000,00,00,000)

def format_indian(t):
dic = {
4:'Thousand',
5:'Lakh',
6:'Lakh',
7:'Crore',
8:'Crore',
9:'Arab'
}
y = 10
len_of_number = len(str(t))
save = t
z=y
while(t!=0):
t=int(t/y)
z*=10
zeros = len(str(z)) - 3
if zeros>3:
if zeros%2!=0:
string = str(save)+": "+str(save/(z/100))[0:4]+" "+dic[zeros]
else:
string = str(save)+": "+str(save/(z/1000))[0:4]+" "+dic[zeros]
return string
return str(save)+": "+str(save)
This code will Convert Yout Numbers to Lakhs, Crores and arabs in most simplest way. Hope it helps.
for i in [1.234567899 * 10**x for x in range(9)]:
print(format_indian(int(i)))
Output:
1: 1
12: 12
123: 123
1234: 1234
12345: 12.3 Thousand
123456: 1.23 Lakh
1234567: 12.3 Lakh
12345678: 1.23 Crore
123456789: 12.3 Crore

Another way:
def formatted_int(value):
# if the value is 100, 10, 1
if len(str(value)) <= 3:
return value
# if the value is 10,000, 1,000
elif 3 < len(str(value)) <= 5:
return f'{str(value)[:-3]},{str(value)[-3:]} ₹'
# if the value is greater the 10,000
else:
cut = str(value)[:-3]
o = []
while cut:
o.append(cut[-2:]) # appending from 1000th value(right to left)
cut = cut[:-2]
o = o[::-1] # reversing list
res = ",".join(o)
return f'{res},{str(value)[-3:]} ₹'
value1 = 1_00_00_00_000
value2 = 10_00_00_00_000
value3 = 100
print(formatted_int(value1))
print(formatted_int(value2))
print(formatted_int(value3))
Ouput:
1,00,00,00,000 ₹
10,00,00,00,000 ₹
100 ₹

As pgksunilkumar's answer, a little improvement is done in case the the number is in between 0 to -1000
def formatINR(number):
if number < 0 and number > -1000:
return number
else:
s, *d = str(number).partition(".")
r = ",".join([s[x-2:x] for x in range(-3, -len(s), -2)][::-1] + [s[-3:]])
return "".join([r] + d)
now if the number is between 0 to -1000, the format will not disturb the user.
i.e
a = -600
b = -10000000
c = 700
d = 8000000
print(formatINR(a))
print(formatINR(b))
print(formatINR(c))
print(formatINR(d))
output will be:
-600
-1,00,00,000
700
80,00,000

Couldn't make the other two solutions work for me, so I made something a little more low-tech:
def format_as_indian(input):
input_list = list(str(input))
if len(input_list) <= 1:
formatted_input = input
else:
first_number = input_list.pop(0)
last_number = input_list.pop()
formatted_input = first_number + (
(''.join(l + ',' * (n % 2 == 1) for n, l in enumerate(reversed(input_list)))[::-1] + last_number)
)
if len(input_list) % 2 == 0:
formatted_input.lstrip(',')
return formatted_input
This doesn't work with decimals. If you need that, I would suggest saving the decimal portion into another variable and adding it back in at the end.

num=123456789
snum=str(num)
slen=len(snum)
result=''
if (slen-3)%2 !=0 :
snum='x'+snum
for i in range(0,slen-3,2):
result=result+snum[i:i+2]+','
result+=snum[slen-3:]
print(result.replace('x',''))

Related

How can I find the intersection of two lines more efficiently? (nested for loops in Python)

I need to find the intersection point of two data sets, as illustrated here:
I have used the nested loops below to achieve this, but it takes impractically long to run for a dataframe with more (~1000) rows. How can I do this more efficiently?
For clarity, here is a screenshot of the CSV used in the example (len=20):
import pandas as pd
data = pd.read_csv("Less_Data.csv")
#Intersection of line A (points 1 & 2) and line B (points 3 & 4)
def findIntersection(x1,y1,x2,y2,x3,y3,x4,y4):
px= (( (x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4) )
/ ( (x1-x2)*(y3-y4)-(y1-y2)*(x3-x4) ))
py= (( (x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4) )
/ ( (x1-x2)*(y3-y4)-(y1-y2)*(x3-x4) ))
return [px, py]
#Find intersection of two series
intersections = {}
error_x = {}
error_y = {}
count = 0
print('All intersections found:')
for i in range(len(data)):
i_storage_modulus = data.iloc[i]['Storage_Modulus_Pa']
i_storage_stress = data.iloc[i]['Storage_Oscillation_Stress_Pa']
for j in range(len(data)):
j_storage_modulus = data.iloc[j]['Storage_Modulus_Pa']
j_storage_stress = data.iloc[j]['Storage_Oscillation_Stress_Pa']
if i == j + 1:
for k in range(len(data)):
k_loss_stress = data.iloc[k]['Loss_Oscillation_Stress_Pa']
k_loss_modulus = data.iloc[k]['Loss_Modulus_Pa']
for l in range(len(data)):
l_loss_stress = data.iloc[l]['Loss_Oscillation_Stress_Pa']
l_loss_modulus = data.iloc[l]['Loss_Modulus_Pa']
if k == l + 1:
if (max(k_loss_modulus, l_loss_modulus)
<= min(i_storage_modulus, j_storage_modulus)):
continue
else:
sample_intersect = findIntersection(i_storage_stress,
i_storage_modulus,
j_storage_stress,
j_storage_modulus,
k_loss_stress,
k_loss_modulus,
l_loss_stress,
l_loss_modulus)
if (min(i_storage_stress, j_storage_stress)
<= sample_intersect[0]
<= max(i_storage_stress, j_storage_stress)):
if (min(k_loss_stress, l_loss_stress)
<= sample_intersect[0]
<= max(k_loss_stress, l_loss_stress)):
print(sample_intersect)
intersections[count] = sample_intersect
error_x[count] = ([i_storage_stress,
j_storage_stress,
k_loss_stress,
l_loss_stress])
error_y[count] = ([i_storage_modulus,
j_storage_modulus,
k_loss_modulus,
l_loss_modulus])
count += 1
#Determine error bars
min_x_poss = []
max_x_poss = []
for i in error_x[0]:
if i < intersections[0][0]:
min_x_poss.append(i)
if i > intersections[0][0]:
max_x_poss.append(i)
x_error = (min(max_x_poss) - max(min_x_poss)) / 2
min_y_poss = []
max_y_poss = []
for i in error_y[0]:
if i < intersections[0][1]:
min_y_poss.append(i)
if i > intersections[0][1]:
max_y_poss.append(i)
y_error = (min(max_y_poss) - max(min_y_poss)) / 2
#Print results
print('\n', 'Yield Stress: ' + str(int(intersections[0][0])) + ' ± ' +
str(int(x_error)) + ' Pa (' +
str(int(x_error*100/intersections[0][0]))+'%)')
print('\n', 'Yield Modulus: ' + str(int(intersections[0][1])) + ' ± ' +
str(int(y_error)) + ' Pa (' +
str(int(y_error*100/intersections[0][1]))+'%)')
Can you create a new function y = (Storage Modulus - Loss Modulus) vs Oscillation Stress? The point of intersection is where y changes sign from positive to negative. The secant method should find this point in a few iterations.
https://en.wikipedia.org/wiki/Secant_method

Python - How to decrease big O and increase efficiency in multiple nested for loops?

I wrote a python script that calculates all possibilities where the following conditions are met:
a^(2) + b^(2) + c^(2) + d^(2) + e^(2) = f^(2)
a,b,c,d,e,f are distinct and nonzero integers
a,b,c,d,e are even numbers between twin primes (e.g. 11 & 13 are twin primes, so 12 is a valid possibility)
f ≤ 65535
the sum of the digits of a == the sum of the digits of b == the sum of the digits of c == the sum of the digits of d == the sum of the digits of e == the sum of the digits of f
I'm not positive whether there will be any results when including criteria 5, but I'd like to find out in a timely manner at least. Ideally, the following conditions should also be met:
results that use the same values for a,b,c,d,e,f but in a different order should not be in the results; ideally they should be excluded from the for loops as well
results should be sorted by lowest value of a first, lowest value of b first and so and so forth
My question would be, how can I decrease the operating time and increase efficiency?
import itertools
import time
start_time = time.time()
def is_prime(n):
for i in range(2, n):
if n % i == 0:
return False
return True
def generate_twin_primes(start, end):
for i in range(start, end):
j = i + 2
if(is_prime(i) and is_prime(j)):
n = text_file2.write(str(i+1) + '\n')
def sum_digits(n):
r = 0
while n:
r, n = r + n % 10, n // 10
return r
def is_sorted(vals):
for i in range(len(vals)-2):
if vals[i] < vals[i+1]:
return False
return True
def pythagorean_sixlet():
valid = []
for a in x:
for b in x:
for c in x:
for d in x:
for e in x:
f = (a * a + b * b + c * c + d * d + e * e)**(1/2)
if f % 1 == 0 and all(x[0]!=x[1] for x in list(itertools.combinations([a, b, c, d, e], 2))):
if sum_digits(a) == sum_digits(b) == sum_digits(c) == sum_digits(d) == sum_digits(e) == sum_digits(int(f)):
valid.append([a, b, c, d, e, int(f)])
for valid_entry in valid:
if is_sorted(valid_entry):
with open("output.txt", "a") as text_file1:
text_file1.write(str(valid_entry[0]) + " " + str(valid_entry[1]) + " " + str(valid_entry[2]) + " " + str(valid_entry[3]) + " " + str(valid_entry[4]) + " | " + str(valid_entry[5]) + '\n')
text_file1.close()
#input #currently all even numbers between twin primes under 1000
text_file2 = open("input.txt", "w")
generate_twin_primes(2, 1000)
text_file2.close()
# counting number of lines in "input.txt" and calculating number of potential possibilities to go through
count = 0
fname = "input.txt"
with open(fname, 'r') as f:
for line in f:
count += 1
print("Number of lines:", count)
print("Number of potential possibilites:", count**5)
with open('input.txt', 'r') as f:
x = f.read().splitlines()
x = [int(px) for px in x]
pythagorean_sixlet()
print("--- %s seconds ---" % (time.time() - start_time))
Well, this smells a lot like a HW problem, so we can't give away the farm too easy... :)
A couple things to consider:
if you want to check unique combinations, the number of possibilities is reduced a good chunk from count**5, right?
You are doing all of your checking at the inner-most part of the loop. Can you do some checking along the way so that you don't have to generate and test all of the possibilities, which is "expensive."
If you do choose to keep your check for uniqueness in the inner portion, find a better way that making all the combinations...that is wayyyyy expensive. Hint: If you made a set of the numbers you have, what would it tell you?
Implementing some of the above:
Number of candidate twin primes between [2, 64152]: 846
total candidate solutions: 1795713740 [need to check f for these]
elapsed: 5.957056045532227
size of result set: 27546
20 random selections from the group:
(40086.0, [3852, 4482, 13680, 20808, 30852])
(45774.0, [6552, 10458, 17028, 23832, 32940])
(56430.0, [1278, 13932, 16452, 27108, 44532])
(64746.0, [15732, 17208, 20772, 32562, 46440])
(47610.0, [3852, 9432, 22158, 24372, 32832])
(53046.0, [3852, 17208, 20772, 23058, 39240])
(36054.0, [4518, 4932, 16452, 21492, 22860])
(18396.0, [3258, 4518, 5742, 9342, 13680])
(45000.0, [2970, 10890, 16650, 18540, 35730])
(59976.0, [2970, 9342, 20772, 35802, 42282])
(42246.0, [3528, 5652, 17208, 25308, 28350])
(39870.0, [3528, 7308, 16362, 23292, 26712])
(64656.0, [8820, 13932, 16452, 36108, 48312])
(61200.0, [198, 882, 22158, 35532, 44622])
(55350.0, [3168, 3672, 5652, 15732, 52542])
(14526.0, [1278, 3528, 7128, 7560, 9432])
(65106.0, [5652, 30852, 31248, 32832, 34650])
(63612.0, [2088, 16830, 26730, 33750, 43650])
(42066.0, [2088, 13932, 15642, 23832, 27540])
(31950.0, [828, 3582, 13932, 16452, 23292])
--- 2872.701852083206 seconds ---
[Finished in 2872.9s]

How to structure python programs? Tried making it more structured, now runs 13 times slower

Im very new to programming, I wrote a simple program for a school project and wanted to make the code "prettier" by not just having the program be one giant function but instead be made up of multiple smaller functions with a singe purpose. I seemed to have messed up royally since the program now runs 13 times slower. How should I structured the program to make it run faster and just in general make programs easier to write, read and edit?
Here are the two programs:
First program (for reference values runs in ≈0:20):
import numpy as np
import matplotlib.pyplot as plt
def graf(a,b,H,p):
GM = 39.5216489684
x_0 = a + np.sqrt(a**2 - b**2)
v_0 = np.sqrt(GM*(2/x_0 - 1/a))
konstant_period = np.sqrt(a**3)*H
h = 1/H
'''starting position given by an elliptic orbit '''
stor_x_lista = [x_0]
stor_y_lista = [0]
hastighet_x = [0]
hastighet_y = [v_0]
liten_x_lista = []
liten_y_lista = []
''' a loop that approximates the points of the orbit'''
t = 0
tid_lista = []
n = 0
while n < konstant_period:
hastighet_x.append(hastighet_x[n] - h*GM* stor_x_lista[n]/(np.sqrt(stor_x_lista[n]**2 + stor_y_lista[n]**2))**3)
stor_x_lista.append(stor_x_lista[n] + h*hastighet_x[n])
hastighet_y.append(hastighet_y[n] - h*GM*stor_y_lista[n]/(np.sqrt(stor_x_lista[n]**2 + stor_y_lista[n]**2))**3)
stor_y_lista.append(stor_y_lista[n] + h*hastighet_y[n])
'''smaller list of points to run faster'''
if n % p == 0:
liten_x_lista.append(stor_x_lista[n])
liten_y_lista.append(stor_y_lista[n])
tid_lista.append(t)
n += 1
t += h
''' function that finds the angle'''
vinkel = []
siffra = 0
while siffra < len(liten_x_lista):
if liten_y_lista[siffra ] >= 0:
vinkel.append( np.arccos( liten_x_lista[siffra]/np.sqrt( liten_x_lista[siffra]**2 + liten_y_lista[siffra]**2)))
siffra += 1
elif liten_y_lista[siffra] < 0 :
vinkel.append( np.pi + np.arccos( -liten_x_lista[siffra]/np.sqrt( liten_x_lista[siffra]**2 + liten_y_lista[siffra]**2) ))
siffra += 1
'''get rid of line to find periodic function'''
mod_lista = []
modn = 0
while modn < len(vinkel):
mod_lista.append(vinkel[modn] - (2*np.pi*tid_lista[modn])/np.sqrt(a**3))
modn += 1
'''make all inputs have period 1'''
squeeze_tid = []
squeezen = 0
while squeezen < len(tid_lista):
squeeze_tid.append(tid_lista[squeezen]/np.sqrt(a**3))
squeezen += 1
del mod_lista[-1:]
del tid_lista[-1:]
del squeeze_tid[-1:]
plt.plot(squeeze_tid,mod_lista)
plt.title('p(t) där a = ' + str(a) + ' och b = ' + str(b))
plt.show
Second more split up program (for reference values runs in ≈4:20):
import numpy as np
import matplotlib.pyplot as plt
'''function that generates the points of the orbit'''
def punkt(a,b,H,p):
GM = 39.5216489684
x_0 = a + np.sqrt(a**2 - b**2)
v_0 = np.sqrt(GM*(2/x_0 - 1/a))
konstant_period = np.sqrt(a**3)*H
h = 1/H
'''starting position given by an elliptic orbit '''
stor_x_lista = [x_0]
stor_y_lista = [0]
hastighet_x = [0]
hastighet_y = [v_0]
liten_x_lista = []
liten_y_lista = []
''' a loop that approximates the points of the orbit'''
t = 0
tid_lista = []
n = 0
while n < konstant_period:
hastighet_x.append(hastighet_x[n] - h*GM* stor_x_lista[n]/(np.sqrt(stor_x_lista[n]**2 + stor_y_lista[n]**2))**3)
stor_x_lista.append(stor_x_lista[n] + h*hastighet_x[n])
hastighet_y.append(hastighet_y[n] - h*GM*stor_y_lista[n]/(np.sqrt(stor_x_lista[n]**2 + stor_y_lista[n]**2))**3)
stor_y_lista.append(stor_y_lista[n] + h*hastighet_y[n])
'''smaller list of points to run faster'''
if n % p == 0:
liten_x_lista.append(stor_x_lista[n])
liten_y_lista.append(stor_y_lista[n])
tid_lista.append(t)
n += 1
t += h
return (liten_x_lista,liten_y_lista,tid_lista)
''' function that finds the angle'''
def vinkel(a,b,H,p):
'''import lists'''
liten_x_lista = punkt(a,b,H,p)[0]
liten_y_lista = punkt(a,b,H,p)[1]
tid_lista = punkt(a,b,H,p)[2]
'''find the angle'''
vinkel_lista = []
siffra = 0
while siffra < len(liten_x_lista):
if liten_y_lista[siffra ] >= 0:
vinkel_lista.append( np.arccos( liten_x_lista[siffra]/np.sqrt( liten_x_lista[siffra]**2 + liten_y_lista[siffra]**2)))
siffra += 1
elif liten_y_lista[siffra] < 0 :
vinkel_lista.append( np.pi + np.arccos( -liten_x_lista[siffra]/np.sqrt( liten_x_lista[siffra]**2 + liten_y_lista[siffra]**2) ))
siffra += 1
return (vinkel_lista, tid_lista)
def periodisk(a,b,H,p):
'''import lists'''
tid_lista = vinkel(a,b,H,p)[1]
vinkel_lista = vinkel(a,b,H,p)[0]
'''get rid of linear line to find p(t)'''
mod_lista = []
modn = 0
while modn < len(vinkel_lista):
mod_lista.append((vinkel_lista[modn] - (2*np.pi*tid_lista[modn])/np.sqrt(a**3)))
modn += 1
'''make all inputs have period 1'''
squeeze_tid = []
squeezen = 0
while squeezen < len(tid_lista):
squeeze_tid.append(tid_lista[squeezen]/np.sqrt(a**3))
squeezen += 1
del mod_lista[-1:]
del tid_lista[-1:]
del squeeze_tid[-1:]
return (squeeze_tid,mod_lista)
'''fixa 3d-punkt av p(a,b) a är konstant b varierar??? '''
def hitta_amp(a):
x_b = []
y_b = []
n_b = 0.1
while n_b <= a:
x_b.append(n_b)
y_b.append(punkt(a,n_b,10**5,10**3))
return 0
def graf(a,b,H,p):
plt.plot(periodisk(a,b,H,p)[0],periodisk(a,b,H,p)[1])
plt.show
I would assume the thing that is going wrong is that the program is running the same, slow code multiple times instead of just running it once and then accessing the data. Is the problem that everything is done locally and nothing is stored globally or is it something else?
Just as a heads up, the only thing I know about programming is basic syntax, I have no clue how to actually write and run programs. I ran all the code in spyder if that affects anything.
plt.plot(periodisk(a,b,H,p)[0],periodisk(a,b,H,p)[1])
This code runs periodisk twice with the same arguments, thus at this point we know we run things at least 2 times slower.
You should do some_var = periodisk(a,b,H,p) and then some_var[0], some_var[1]. Or just use unpacking:
plt.plot(*periodisk(a,b,H,p))
tid_lista = vinkel(a,b,H,p)[1]
vinkel_lista = vinkel(a,b,H,p)[0]
Again doing the same thing twice (total: 4*time of (current) vinkel function). Again, smart assignment to fix this:
vinkel_lista, tid_lista = vinkel(a,b,H,p)
liten_x_lista = punkt(a,b,H,p)[0]
liten_y_lista = punkt(a,b,H,p)[1]
tid_lista = punkt(a,b,H,p)[2]
And now you repeat yourself thrice. (total: 12 * time of current punkt function)
liten_x_lista, liten_y_lista, tid_lista = punkt(a,b,H,p)
punkt function is like in original, so we arrived as total being 12 times slower - which quite matches your time estimations. :)
You are calling the functions once per returned list, you should only call them once.
When a method returns multiple variables, (e.g. punkt):
def punkt(a,b,H,p):
# Here is all your code
return (liten_x_lista,liten_y_lista,tid_lista)
You must be careful to only call the function once:
result = punkt(a,b,H,p)
liten_x_lista = result[0]
liten_y_lista = result[1]
tid_lista = result[2]
# As opposed to:
liten_x_lista = punkt(a,b,H,p)[0] # 1st call, ignoring results 2 and 3
liten_y_lista = punkt(a,b,H,p)[1] # 2nd call, ignoring results 1 and 3
tid_lista = punkt(a,b,H,p)[2] # 3rd call, ignoring results 1 and 2
Note: I would personally not return a list, but use python's unpacking:
def punkt(a,b,H,p):
# Here is all your code
return liten_x_lista, liten_y_lista, tid_lista
And you'd access it:
liten_x_lista, liten_y_lista, tid_lista = punkt(a,b,H,p)

Pretty-printing physical quantities with automatic scaling of SI prefixes

I am looking for an elegant way to pretty-print physical quantities with the most appropriate prefix (as in 12300 grams are 12.3 kilograms). A simple approach looks like this:
def pprint_units(v, unit_str, num_fmt="{:.3f}"):
""" Pretty printer for physical quantities """
# prefixes and power:
u_pres = [(-9, u'n'), (-6, u'µ'), (-3, u'm'), (0, ''),
(+3, u'k'), (+6, u'M'), (+9, u'G')]
if v == 0:
return num_fmt.format(v) + " " + unit_str
p = np.log10(1.0*abs(v))
p_diffs = np.array([(p - u_p[0]) for u_p in u_pres])
idx = np.argmin(p_diffs * (1+np.sign(p_diffs))) - 1
u_p = u_pres[idx if idx >= 0 else 0]
return num_fmt.format(v / 10.**u_p[0]) + " " + u_p[1] + unit_str
for v in [12e-6, 3.4, .123, 3452]:
print(pprint_units(v, 'g', "{: 7.2f}"))
# Prints:
# 12.00 µg
# 3.40 g
# 123.00 mg
# 3.45 kg
Looking over units and Pint, I could not find that functionality. Are there any other libraries which typeset SI units more comprehensively (to handle special cases like angles, temperatures, etc)?
I have solved the same problem once. And IMHO with more elegance. No degrees or temperatures though.
def sign(x, value=1):
"""Mathematical signum function.
:param x: Object of investigation
:param value: The size of the signum (defaults to 1)
:returns: Plus or minus value
"""
return -value if x < 0 else value
def prefix(x, dimension=1):
"""Give the number an appropriate SI prefix.
:param x: Too big or too small number.
:returns: String containing a number between 1 and 1000 and SI prefix.
"""
if x == 0:
return "0 "
l = math.floor(math.log10(abs(x)))
if abs(l) > 24:
l = sign(l, value=24)
div, mod = divmod(l, 3*dimension)
return "%.3g %s" % (x * 10**(-l + mod), " kMGTPEZYyzafpnµm"[div])
CommaCalc
Degrees like that:
def intfloatsplit(x):
i = int(x)
f = x - i
return i, f
def prettydegrees(d):
degrees, rest = intfloatsplit(d)
minutes, rest = intfloatsplit(60*rest)
seconds = round(60*rest)
return "{degrees}° {minutes}' {seconds}''".format(**locals())
edit:
Added dimension of the unit
>>> print(prefix(0.000009, 2))
9 m
>>> print(prefix(0.9, 2))
9e+05 m
The second output is not very pretty, I know. You may want to edit the formating string.
edit:
Parse inputs like 0.000009 m². Works on dimensions less than 10.
import unicodedata
def unitprefix(val):
"""Give the unit an appropriate SI prefix.
:param val: Number and a unit, e.g. "0.000009 m²"
"""
xstr, unit = val.split(None, 2)
x = float(xstr)
try:
dimension = unicodedata.digit(unit[-1])
except ValueError:
dimension = 1
return prefix(x, dimension) + unit
The decimal module can help. Morover it prevents bad float rounding.
import decimal
prefix="yzafpnµm kMGTPEZY"
shift=decimal.Decimal('1E24')
def prettyprint(x,baseunit):
d=(decimal.Decimal(str(x))*shift).normalize()
m,e=d.to_eng_string().split('E')
return m + " " + prefix[int(e)//3] + baseunit
print(prettyprint (12300,'g'))
>>>> '12.3 kg'
it can be tuned to manage format.
If you're interested in using Pint, check out the to_compact method. This hasn't made it into the documentation, but I think it does what you're looking for!
Here is the implementation of the example from the OP:
import pint
ureg = pint.UnitRegistry()
for v in [12e-6, 3.4, .123, 3452]:
print('{:~7.2f}'.format((v * ureg('g')).to_compact()))
>>> 12.00 ug
>>> 3.40 g
>>> 123.00 mg
>>> 3.45 kg

How to cycle through the index of an array?

line 14 is where my main problem is.i need to cycle through each item in the array and use it's index to determine whether or not it is a multiple of four so i can create proper spacing for binary numbers.
def decimalToBinary(hu):
bits = []
h = []
while hu > 0:
kla = hu%2
bits.append(kla)
hu = int(hu/2)
for i in reversed(bits):
h.append(i)
if len(h) <= 4:
print (''.join(map(str,h)))
else:
for j in range(len(h)):
h.index(1) = h.index(1)+1
if h.index % 4 != 0:
print (''.join(map(str,h)))
elif h.index % 4 == 0:
print (' '.join(map(str,h)))
decimalToBinary( 23 )
If what you're looking for is the index of the list from range(len(h)) in the for loop, then you can change that line to for idx,j in enumerate(range(len(h))): where idx is the index of the range.
This line h.index(1) = h.index(1)+1 is incorrect. Modified your function, so at least it executes and generates an output, but whether it is correct, i dont know. Anyway, hope it helps:
def decimalToBinary(hu):
bits = []
h = []
while hu > 0:
kla = hu%2
bits.append(kla)
hu = int(hu/2)
for i in reversed(bits):
h.append(i)
if len(h) <= 4:
print (''.join(map(str,h)))
else:
for j in range(len(h)):
h_index = h.index(1)+1 # use h_index variable instead of h.index(1)
if h_index % 4 != 0:
print (''.join(map(str,h)))
elif h_index % 4 == 0:
print (' '.join(map(str,h)))
decimalToBinary( 23 )
# get binary version to check your result against.
print(bin(23))
This results:
#outout from decimalToBinary
10111
10111
10111
10111
10111
#output from bin(23)
0b10111
You're trying to join the bits to string and separate them every 4 bits. You could modify your code with Marcin's correction (by replacing the syntax error line and do some other improvements), but I suggest doing it more "Pythonically".
Here's my version:
def decimalToBinary(hu):
bits = []
while hu > 0:
kla = hu%2
bits.append(kla)
hu = int(hu/2)
h = [''.join(map(str, bits[i:i+4])) for i in range(0,len(bits),4)]
bu = ' '.join(h)
print bu[::-1]
Explanation for the h assignment line:
range(0,len(bits),4): a list from 0 to length of bits with step = 4, eg. [0, 4, 8, ...]
[bits[i:i+4] for i in [0, 4, 8]: a list of lists whose element is every four elements from bits
eg. [ [1,0,1,0], [0,1,0,1] ...]
[''.join(map(str, bits[i:i+4])) for i in range(0,len(bits),4)]: convert the inner list to string
bu[::-1]: reverse the string
If you are learning Python, it's good to do your way. As #roippi pointed out,
for index, value in enumerate(h):
will give you access to both index and value of member of h in each loop.
To group 4 digits, I would do like this:
def decimalToBinary(num):
binary = str(bin(num))[2:][::-1]
index = 0
spaced = ''
while index + 4 < len(binary):
spaced += binary[index:index+4]+' '
index += 4
else:
spaced += binary[index:]
return spaced[::-1]
print decimalToBinary(23)
The result is:
1 0111

Categories