Skip to content

Commit f53d855

Browse files
committed
update currency, measurements, calc tools, numeral modules
1 parent 9c38d07 commit f53d855

File tree

7 files changed

+341
-68
lines changed

7 files changed

+341
-68
lines changed

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
__pycache__/
2+
*.pyc
3+
*.pyo
4+
*.pyd
5+
*.pyw
6+
*.pyz
7+
*.pywz
8+
*.pyzw
9+
*.pyzwz

numt/calc_tools.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
2+
def format_percentage(n, precision=1):
3+
"""
4+
Format number as percentage.
5+
6+
Args:
7+
n (float): Number to format (0-1)
8+
precision (int): Number of decimal places
9+
10+
Returns:
11+
str: Formatted percentage
12+
"""
13+
return f"{n * 100:.{precision}f}%"
14+
15+
def percent_of(value, percent):
16+
"""
17+
Calculate the percentage of a given value.
18+
19+
Args:
20+
value (float): The base number.
21+
percent (float): The percentage to calculate (e.g., 25 for 25%).
22+
23+
Returns:
24+
float: Result of the percentage calculation.
25+
"""
26+
return (percent / 100) * value
27+
28+
29+
def percentile(data, percentile_rank):
30+
"""
31+
Calculate the value at a given percentile.
32+
33+
Args:
34+
data (list): Sorted list of numbers.
35+
percentile_rank (float): Percentile (0-100).
36+
37+
Returns:
38+
float: Value at the specified percentile.
39+
"""
40+
if not data:
41+
raise ValueError("Data list is empty")
42+
k = (len(data) - 1) * (percentile_rank / 100)
43+
f = int(k)
44+
c = f + 1
45+
if c >= len(data):
46+
return data[f]
47+
d0 = data[f] * (c - k)
48+
d1 = data[c] * (k - f)
49+
return d0 + d1
50+
51+
def percentage_change(old, new):
52+
"""
53+
Calculate the percentage change between two values.
54+
55+
Args:
56+
old (float): Original value.
57+
new (float): New value.
58+
59+
Returns:
60+
float: Percentage change (positive or negative).
61+
"""
62+
if old == 0:
63+
raise ValueError("Old value cannot be zero")
64+
return ((new - old) / old) * 100
65+
66+
def percent_rank(data, value):
67+
"""
68+
Compute the percentage rank of a value within a dataset.
69+
70+
Args:
71+
data (list): List of values.
72+
value (float): Value to find rank for.
73+
74+
Returns:
75+
float: Percent rank (0-100).
76+
"""
77+
if not data:
78+
raise ValueError("Data list is empty")
79+
count = sum(1 for x in data if x < value)
80+
return (count / len(data)) * 100
81+
82+
def value_percentiles_with_duplicates(data):
83+
"""
84+
Calculate the percentile rank for each value in a dataset,
85+
handling duplicates appropriately (equal values get equal percentile ranks).
86+
Uses the "midrank" strategy for ties (like NumPy/SciPy’s rankdata(method='average')).
87+
88+
Args:
89+
data (list): List of numeric values.
90+
91+
Returns:
92+
list of dict: Each dict contains 'value' and 'percentile'.
93+
"""
94+
if not data:
95+
raise ValueError("Data list is empty")
96+
97+
sorted_data = sorted(data)
98+
n = len(sorted_data)
99+
100+
# Precompute percentile rank for each unique value
101+
value_to_percentile = {}
102+
for i, val in enumerate(sorted_data):
103+
if val not in value_to_percentile:
104+
# All values strictly less than val
105+
count_less = sum(1 for x in sorted_data if x < val)
106+
count_equal = sorted_data.count(val)
107+
avg_rank = count_less + (count_equal - 1) / 2
108+
percentile = (avg_rank / (n - 1)) * 100 if n > 1 else 100.0
109+
value_to_percentile[val] = percentile
110+
111+
# Build result for original data
112+
return [{'value': val, 'percentile': value_to_percentile[val]} for val in data]
113+
114+
115+
def apply_discount(price, discount_percent):
116+
"""
117+
Calculate the price after a discount.
118+
119+
Args:
120+
price (float): Original price.
121+
discount_percent (float): Discount percentage (e.g., 20 for 20%).
122+
123+
Returns:
124+
float: Discounted price.
125+
"""
126+
return price - (price * discount_percent / 100)
127+
128+
def calculate_discount_amount(list_price, net_price):
129+
"""
130+
Calculate the discount value given the original and net prices.
131+
132+
Args:
133+
list_price (float): Original price before discount.
134+
net_price (float): Final price after discount.
135+
136+
Returns:
137+
float: Discount amount.
138+
"""
139+
if list_price < 0 or net_price < 0:
140+
raise ValueError("Prices cannot be negative")
141+
if net_price > list_price:
142+
raise ValueError("Net price cannot be greater than list price")
143+
144+
return list_price - net_price

numt/common/__init__.py

Whitespace-only changes.

numt/common/constants.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
currency_symbols = {
2+
'AMD': 'Դ', # Armenian Dram
3+
'AUD': '$', # Australian Dollar
4+
'AZN': '₼', # Azerbaijani Manat
5+
'BRL': 'R$', # Brazilian Real
6+
'CAD': '$', # Canadian Dollar
7+
'CHF': 'CHF', # Swiss Franc
8+
'CNY': '¥', # Chinese Yuan Renminbi
9+
'EGP': 'ج.م', # Egyptian Pound
10+
'EUR': '€', # Euro
11+
'GBP': '£', # British Pound Sterling
12+
'ILS': '₪', # Israeli Shekel
13+
'INR': '₹', # Indian Rupee
14+
'JOD': 'د.ا', # Jordanian Dinar
15+
'JPY': '¥', # Japanese Yen
16+
'KRW': '₩', # South Korean Won
17+
'KWD': 'د.ك', # Kuwaiti Dinar
18+
'KZT': '〒', # Kazakhstani Tenge
19+
'MNT': '₮', # Mongolian Tögrög
20+
'MXN': '$', # Mexican Peso
21+
'MYR': 'RM', # Malaysian Ringgit
22+
'NGN': '₦', # Nigerian Naira
23+
'PKR': '₨', # Pakistani Rupee
24+
'RUB': '₽', # Russian Ruble
25+
'SGD': '$', # Singapore Dollar
26+
'THB': '฿', # Thai Baht
27+
'TRY': '₺', # Turkish Lira
28+
'USD': '$', # US Dollar
29+
'ZAR': 'R', # South African Rand
30+
}
31+
32+
long_numeric_suffixes = [
33+
'', # 10^0 (ones)
34+
'thousand', # 10^3 (thousands)
35+
'million', # 10^6 (millions)
36+
'billion', # 10^9 (billions)
37+
'trillion', # 10^12 (trillions)
38+
'quadrillion', # 10^15 (quadrillions)
39+
'quintillion', # 10^18 (quintillions)
40+
'sextillion', # 10^21 (sextillions)
41+
'septillion', # 10^24 (septillions)
42+
'octillion', # 10^27 (octillions)
43+
'nonillion', # 10^30 (nonillions)
44+
'decillion', # 10^33 (decillions)
45+
'undecillion', # 10^36 (undecillions)
46+
'duodecillion', # 10^39 (duodecillions)
47+
'tredecillion', # 10^42 (tredecillions)
48+
'quattuordecillion', # 10^45 (quattuordecillions)
49+
'quindecillion', # 10^48 (quindecillions)
50+
'sexdecillion', # 10^51 (sexdecillions)
51+
'septendecillion', # 10^54 (septendecillions)
52+
'octodecillion', # 10^57 (octodecillions)
53+
'novemdecillion', # 10^60 (novemdecillions)
54+
'vigintillion' # 10^63 (vigintillions)
55+
]
56+
57+
short_numeric_suffixes = [
58+
'', # 10^0 (ones)
59+
'K', # 10^3 (thousands)
60+
'M', # 10^6 (millions)
61+
'B', # 10^9 (billions)
62+
'T', # 10^12 (trillions)
63+
'Qa', # 10^15 (quadrillions)
64+
'Qi', # 10^18 (quintillions)
65+
'Sx', # 10^21 (sextillions)
66+
'Sp', # 10^24 (septillions)
67+
'O', # 10^27 (octillions)
68+
'N', # 10^30 (nonillions)
69+
'D', # 10^33 (decillions)
70+
'Ud', # 10^36 (undecillions)
71+
'Dd', # 10^39 (duodecillions)
72+
'Td', # 10^42 (tredecillions)
73+
'Qad', # 10^45 (quattuordecillions)
74+
'Qid', # 10^48 (quindecillions)
75+
'Sxd', # 10^51 (sexdecillions)
76+
'Spd', # 10^54 (septendecillions)
77+
'Od', # 10^57 (octodecillions)
78+
'Nd', # 10^60 (novemdecillions)
79+
'V' # 10^63 (vigintillions)
80+
]
81+
82+
short_googol_suffixes = [
83+
'Gg', # 10^100 (googol)
84+
'Dg', # 10^200 (duogoogol)
85+
'Tg', # 10^300 (trigoogol)
86+
'Qag', # 10^400 (quattrogoogol)
87+
'Qig', # 10^500 (quinquagoogol)
88+
'Sxg', # 10^600 (sexagoogol)
89+
'Spg', # 10^700 (septengoogol)
90+
'Og', # 10^800 (octogoogol)
91+
'Ng', # 10^900 (nonagoogol)
92+
'Kkg' # 10^1000 (kilogoogol)
93+
]
94+
95+
long_googol_suffixes = [
96+
'googol', # 10^100 (googol)
97+
'duogoogol', # 10^200 (duogoogol)
98+
'trigoogol', # 10^300 (trigoogol)
99+
'quattuorgoogol', # 10^400 (quattuorgoogol)
100+
'quintagoogol', # 10^500 (quintagoogol)
101+
'sexagoogol', # 10^600 (sexagoogol)
102+
'septengoogol', # 10^700 (septengoogol)
103+
'octogoogol', # 10^800 (octogoogol)
104+
'nonagoogol', # 10^900 (nonagoogol)
105+
'kilogoogol' # 10^1000 (kilogoogol)
106+
]

numt/currency.py

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
def format_currency(amount, currency='USD', precision=2):
1+
from numt.common.constants import currency_symbols
2+
3+
def format_currency(amount, currency='USD', precision=2, format_type='western'):
24
"""
35
Format number as currency.
46
@@ -10,12 +12,32 @@ def format_currency(amount, currency='USD', precision=2):
1012
Returns:
1113
str: Formatted currency
1214
"""
13-
currency_symbols = {
14-
'USD': '$',
15-
'EUR': '€',
16-
'GBP': '£',
17-
'JPY': '¥',
18-
'INR': '₹'
19-
}
20-
symbol = currency_symbols.get(currency, currency)
21-
return f"{symbol}{amount:,.{precision}f}"
15+
16+
symbol = currency_symbols.get(currency, currency + ' ')
17+
18+
if format_type == 'western':
19+
formatted_amount = f"{amount:,.{precision}f}"
20+
elif format_type == 'indian':
21+
import math
22+
def indian_format(n, precision):
23+
s = f"{n:.{precision}f}"
24+
if '.' in s:
25+
integer_part, decimal_part = s.split('.')
26+
else:
27+
integer_part, decimal_part = s, ''
28+
rev = integer_part[::-1]
29+
groups = [rev[:3]]
30+
rev = rev[3:]
31+
while rev:
32+
groups.append(rev[:2])
33+
rev = rev[2:]
34+
formatted_int = ','.join(groups)[::-1]
35+
return formatted_int + ('.' + decimal_part if decimal_part else '')
36+
formatted_amount = indian_format(amount, precision)
37+
elif format_type == 'continental':
38+
formatted_amount = f"{amount:,.{precision}f}".replace(',', 'X').replace('.', ',').replace('X', '.')
39+
elif format_type == 'swiss':
40+
formatted_amount = f"{amount:,.{precision}f}".replace(',', 'X').replace('.', ',').replace('X', "'")
41+
else:
42+
formatted_amount = f"{amount:,.{precision}f}"
43+
return f"{symbol}{formatted_amount}"

numt/measurement_si.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# You can use conversion dictionaries to convert and format appropriately.
21

32
class SIUnit:
43
"""Base class for SI unit conversions"""

0 commit comments

Comments
 (0)