Python: scientific notation with superscript exponent - python

I'm attempting to format numbers in scientific notation with exponents of base 10, e.g. write 0.00123 as 1.23x10–3, using python 3.
I found this great function which prints 1.23x10^-3, but how can the caret-exponent be replaced with a superscript?
def sci_notation(number, sig_fig=2):
ret_string = "{0:.{1:d}e}".format(number, sig_fig)
a,b = ret_string.split("e")
b = int(b) # removed leading "+" and strips leading zeros too.
return a + "x10^" + str(b)
print(sci_notation(0.001234, sig_fig=2)) # Outputs 1.23x10^-3
The function is modified from https://stackoverflow.com/a/29261252/8542513.
I've attempted to incorporate the answer from https://stackoverflow.com/a/8651690/8542513 to format the superscript, but I'm not sure how sympy works with variables:
from sympy import pretty_print as pp, latex
from sympy.abc import a, b, n
def sci_notation(number, sig_fig=2):
ret_string = "{0:.{1:d}e}".format(number, sig_fig)
a,b = ret_string.split("e")
b = int(b) #removed leading "+" and strips leading zeros too.
b = str(b)
expr = a + "x10"**b #Here's my problem
pp(expr) # default
pp(expr, use_unicode=True)
return latex(expr)
print(latex(sci_notation(0.001234, sig_fig=2)))
This returns: TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'

Here's a simple solution:
def SuperScriptinate(number):
return number.replace('0','⁰').replace('1','¹').replace('2','²').replace('3','³').replace('4','⁴').replace('5','⁵').replace('6','⁶').replace('7','⁷').replace('8','⁸').replace('9','⁹').replace('-','⁻')
def sci_notation(number, sig_fig=2):
ret_string = "{0:.{1:d}e}".format(number, sig_fig)
a,b = ret_string.split("e")
b = int(b) # removed leading "+" and strips leading zeros too.
return a + "x10^" + SuperScriptinate(str(b))

I take it that your main problem is how can the caret-exponent be replaced with a superscript?
If you are using python in a Jupyter notebook, there is an easy way:
from IPython.display import display, Math, Latex
# if the number is in scientific format already
display(Math('2.14e-6'.replace('e', r'\times 10^{') + '}'))
# if it is not:
d = "%e" % (number)
# then use the above form: display(Math(d.replace('e', r'\times ...

The scinot package formats numbers in scientific notation
import scinot as sn
a=4.7e-8
print(scinot.format(a))
4.7 × 10⁻⁸
or in a string
print('The value is {0:s}'.format(scinot.format(a)))
The value is 4.7 × 10⁻⁸

Related

How to use sympy to convert all standalone integers in an expression that aren't exponents to 1

Is there a way to use sympy to find/replace all standalone integers (that aren't exponents) to 1.
For example, converting the following:
F(x) = (2/x^2) + (3/x^3) + 4
To:
F(x) = (1/x^2) + (1/x^3) + 1
I've searched extensively on stackoverflow for sympy expression.match/replace/find solutions, and have tried using a Wildcard symbol to find and replace all numbers in the expression but I keep running into the issue of matching and replacing the exponents (2 and 3 in this example) as well as they are also considered numbers.
Is there a simple (pythonic) way to achieve the above?
Thanks!
setdefault used with replace is a nice way to go. The single expression below has 3 steps:
mask off powers and record
change Rationals to 1 (to handle integers in numer or denom)
restore powers
>>> from sympy.abc import x
>>> from sympy import Dummy
>>> eq = (2/x**2) + (3/x**3) + 4 + 1/x/8
>>> reps = {}
>>> eq = eq.replace(lambda x: x.is_Pow, lambda x: reps.setdefault(x, Dummy())
).replace(lambda x: x.is_Rational, lambda x: 1
).xreplace({v:k for k,v in reps.items()})
1 + 1/x + 1/x**2 + 1/x**3
You can write a function that will recurse into your expression. For any expression expr, expr.args will give you the components of that expression. expr.is_constant() will tell you if it's a constant. expr.is_Pow will tell you if it's an exponential expression, so you can choose not to drill down into these expressions.
import sympy
def get_constants(expr):
c = set()
for x in expr.args:
if x.is_constant(): c |= {x}
if not x.is_Pow:
c |= get_constants(x)
return c
Now, you can get all the constants in said expression, and replace each of these constants using expr.replace().
def replace_constants(expr, repl):
for const in get_constants(expr):
expr = expr.replace(const, repl)
return expr
With your example expression, we get:
x = sympy.symbols('x')
F = 2/x**2 + 3/x**3 + 4
G = replace_constants(F, 1)
print(F) # 4 + 2/x**2 + 3/x**3
print(G) # 1 + x**(-2) + x**(-3)

Optimized summing beween ints and strs [duplicate]

This question already has answers here:
How do I put a variable’s value inside a string (interpolate it into the string)?
(9 answers)
Closed 2 years ago.
print("ax^2 + bx + c = d what is your values for them? ")
a = int(input(">a = "))
b = int(input(">b = "))
c = int(input(">c = "))
d = int(input(">d = "))
given_parabola = str(a) + "x^2 + " + str(b) + "x + " + (str(c)) + " = " + str(d)
Is there any other way that I can merge integer variables with strings?
The "best" approach really depends on what you're trying to do.
1. Concatenating lists with variable number of items (numbers and strings)
If you simply want to form a string from numbers and strings, I would first create a generator with generator expression and then join the strings with the join() method.
In [1]: a = [2, 'a', 3, 'x', 'foo', 8, 55]
In [2]: g = (str(x) for x in a)
In [3]: ' '.join(g)
Out[3]: '2 a 3 x foo 8 55'
Pluses
Can be used to concatenate any amount of strings and numbers, which can be in any order
Minuses
Probably not the most speed optimized, if you know more about the variables you are going to concatenate
2. Literal String interpolation
If you know what amount of numeric variables you want to concatenate with what strings, the problem is called string interpolation.
In Python 3.6+ you can use so-called f-strings to form string using a string template and a fixed number of variables. For example:
In [1]: a, b, c, d = 3, 2, 1, 5
In [2]: f"{a}x^2 + {b}x + {c} = {d}"
Out[2]: '3x^2 + 2x + 1 = 5'
Pluses
Probably the most speed optimized way to create a string from a template.
Minuses
This is not a general approach to "sum"/concatenate any amount of strings and numbers.
3. Using sympy for expression generation
Since your problem looks like being very specific: You want to create string from mathematical formula, you might want to look at sympy.
Installation
pip install sympy
Simple example
In [1]: from sympy import symbols, Eq, mathematica_code
In [2]: x, a, b, c, d = symbols('x a b c d')
In [3]: expr = Eq(a*(x**2) + b*x + c, d)
In [4]: var_dict = dict(a=3, b=2, c=1, d=5)
In [5]: expr_with_numbers = expr.subs(var_dict)
In [6]: mathematica_code(expr_with_numbers).replace('==', '=')
Out[6]: '3*x^2 + 2*x + 1 = 5'
you can also solve for the expression easily:
In [7]: solve(expr_with_numbers, x)
Out[7]: [-1/3 + sqrt(13)/3, -sqrt(13)/3 - 1/3]
and you can print any kind of equation. For example
In [1]: from sympy import symbols, Eq, mathematica_code, sqrt, pretty, solve
In [2]: expr = Eq(a*(x**2)/(sqrt(x-c)), d)
In [3]: var_dict = dict(a=3, b=2, c=1, d=5)
In [4]: expr_with_numbers = expr.subs(var_dict)
In [5]: print(pretty(expr_with_numbers, use_unicode=False))
2
3*x
--------- = 5
_______
\/ x - 1
Pros
Useful, if you want to create complex mathematical expressions
Can also output pretty multiline output or even LaTeX output.
Can be useful if you want to actually solve the equation, too
Cons
Not speed-optimized for simple string formation.
You can avoid concatenating multiple strings using the format string python proposed.
Using Format strings vs concatenation to do a list of more performant to less performant
f-string as f"{a}x^2 + {b}x + {c} = {d}"
"%sx^2 + %sx + %s = %s" % (a,b,c,d)
"{}x^2 + {}x + {} = {}".format(a,b,c,d)
Might I suggest string interpolation?
given_parabola = "%sx^2 + %sx + %s = %s" % (a, b, c, d)
Or
given_parabola = f"{a}x^2 + {b}x + {c} = {d}"
Yes, hopefully, this is what you mean:
# This way the integer 10 will convert to a string automatically. Works in Print as well!
x = 10
y = "lemons"
z = "In the basket are %s %s" % (x, y)
print(z)
Output:
In the basket are 10 lemons

Concentrate two int to one decimal

What's the fastest way to concentrate two int into one decimal.
eg.
a=100
b=10
to get = 100.10
Thanks.
Convert them into strings and then add them and again convert them
c = float(str(a) + '.' + str(b))
Output:
100.10
Assuming python >= 3.6:
a = 100
b = 10
# if you want a string
c = f'{a}.{b}'
# if you want a float
d = float(f'{a}.{b}')
It should be a little bit faster than string concatenation, see here.
Play with string concatenation
y = float(str(a) + "." + str(b))
If for some reason you don't want to use string concatenation you can do the following:
from math import log10, floor, pow
c = a + (b/(pow(10, floor(log10(b) + 1))))
Not sure if it is any more efficient. You can also import from numpy or import all of either numpy or math:
import numpy
c = a + (b / (numpy.power(10, numpy.floor(numpy.log10(b) + 1))))

Rounding to 2 decimal places in Python

This isn't a duplicate because I have checked everything before this post on this site. I think I have managed to do the first two bullet points. The first one I will do through a string but I am willing to change that if you know another way. The 2nd one is using comma seperators for the $'s. So I will use a float but once again am willing to change if better way is found.
But I am stuck.
And the "print("%.2f") % str) is something I found but I need work on rounding to two decimal spaces and the last bullet point.
Code:
import random
def random_number():
random_dollars = random.uniform(1.00, 10000.00)
print(round(random_dollars, 2))
print("%.2f") % str
print(random_number())
Shell:
C:\Users\jacke\PycharmProjects\ASLevelHomeworkWeek18\venv\Scripts\python.exe C:/Users/jacke/PycharmProjects/ASLevelHomeworkWeek18/ASLevelHomeworkWeek18.py 6567.62 Traceback (most recent call last): %.2f File
C:/Users/jacke/PycharmProjects/ASLevelHomeworkWeek18/ASLevelHomeworkWeek18.py", line 10, in <module> print(random_number()) File
C:/Users/jacke/PycharmProjects/ASLevelHomeworkWeek18/ASLevelHomeworkWeek18.py", line 7, in random_number print("%.2f") % str TypeError: unsupported operand type(s) for %: 'NoneType' and 'type' Process finished with exit code 1
You can format currency like this:
def random_number():
random_dollars = random.uniform(1, 10000)
result = '$ {:0>9}'.format('{:,.2f}'.format(random_dollars))
print(result)
{:0>10} means: pad string left to width 9 with 0's.
{:,.2f} rounds to two decimal places (.2f) and adds a comma as thousands-separator.
Just one side note: by using random.uniform(1, 10000) most of your numbers will be large (>1000), if you want to test your script with small amounts you could use random_dollars = 10**random.uniform(0, 4) instead:
def random_number():
random_dollars = 10**random.uniform(0, 4)
result = '$ {:0>9}'.format('{:,.2f}'.format(random_dollars))
print(result)
If I get what you are saying you want to round a number to 2 decimal places. Here is how I would do it.
import random
def random_number():
random_dollars = random.uniform(1, 10000)
split = str(random_dollars).split(".")
if (len(split) == 2 ):
if (len(split[1]) == 1 ):# checks if 1 digit after decimal place
answer = split[0] + ".0"
split[1] = str(int(int(split[1]) / (10 ** (len(split[1]) - 3) )))
# Gets 3 decimal places
if int(split[1][-1:]) => 5: #Checks if last digit is above or equal to 5
split[1] = int(split[1][:-1])
split[1] += 1
else:
split[1] = int(split[1][:-1])
answer = split[0] + '.' + str(split[1])
else:
answer = split[0] + ".00"
print(answer)
random_number()
This makes it so if the random number is somehow 100 it will add 2 zeros. If the number is like 100.1 it will add one zero. It will also round it.
def random_number():
random_dollars = random.uniform (1.00, 10000.00)
n = round(random_dollars,2)
bd, d = str(n).split('.')
if len(d) == 1:
n = bd + "." + d + '0'
return n
else:
return n
for i in range(1, 20):
print(random_number())
7340.55
7482.70
3956.81
3044.50
4108.57
4864.90
235.00
9831.98
960.97
1172.28
5221.31
3663.50
5410.50
3448.52
8288.13
293.48
1390.68
9216.15
6493.65
TL;DR: you have to put the % directly after the string and you have to put a real variable there, not the type str
from the last line of your error message you can see that the problem is the % operator. You can also see that it tried to do the operation with two objects of types 'NoneType' and 'type'. Since you put the entire print statement in front of the % and print returns None (which is of type NoneType), the first operand is of type NoneType. then, the second operand is the type str, which is, as just said, a type. You can fix this by moving the % operator after the string and replacing str with your variable random_dollars since that is what you want to insert into the string.
import random
def random_number():
random_dollars = random.uniform(1.00, 10000.00)
print(round(random_dollars, 2))
# this:
print("%.2f" % random_dollars)
print(random_number())

drop trailing zeros from decimal

I have a long list of Decimals and that I have to adjust by factors of 10, 100, 1000,..... 1000000 depending on certain conditions. When I multiply them there is sometimes a useless trailing zero (though not always) that I want to get rid of. For example...
from decimal import Decimal
# outputs 25.0, PROBLEM! I would like it to output 25
print Decimal('2.5') * 10
# outputs 2567.8000, PROBLEM! I would like it to output 2567.8
print Decimal('2.5678') * 1000
Is there a function that tells the decimal object to drop these insignificant zeros? The only way I can think of doing this is to convert to a string and replace them using regular expressions.
Should probably mention that I am using python 2.6.5
EDIT
senderle's fine answer made me realize that I occasionally get a number like 250.0 which when normalized produces 2.5E+2. I guess in these cases I could try to sort them out and convert to a int
You can use the normalize method to remove extra precision.
>>> print decimal.Decimal('5.500')
5.500
>>> print decimal.Decimal('5.500').normalize()
5.5
To avoid stripping zeros to the left of the decimal point, you could do this:
def normalize_fraction(d):
normalized = d.normalize()
sign, digits, exponent = normalized.as_tuple()
if exponent > 0:
return decimal.Decimal((sign, digits + (0,) * exponent, 0))
else:
return normalized
Or more compactly, using quantize as suggested by user7116:
def normalize_fraction(d):
normalized = d.normalize()
sign, digit, exponent = normalized.as_tuple()
return normalized if exponent <= 0 else normalized.quantize(1)
You could also use to_integral() as shown here but I think using as_tuple this way is more self-documenting.
I tested these both against a few cases; please leave a comment if you find something that doesn't work.
>>> normalize_fraction(decimal.Decimal('55.5'))
Decimal('55.5')
>>> normalize_fraction(decimal.Decimal('55.500'))
Decimal('55.5')
>>> normalize_fraction(decimal.Decimal('55500'))
Decimal('55500')
>>> normalize_fraction(decimal.Decimal('555E2'))
Decimal('55500')
There's probably a better way of doing this, but you could use .rstrip('0').rstrip('.') to achieve the result that you want.
Using your numbers as an example:
>>> s = str(Decimal('2.5') * 10)
>>> print s.rstrip('0').rstrip('.') if '.' in s else s
25
>>> s = str(Decimal('2.5678') * 1000)
>>> print s.rstrip('0').rstrip('.') if '.' in s else s
2567.8
And here's the fix for the problem that #gerrit pointed out in the comments:
>>> s = str(Decimal('1500'))
>>> print s.rstrip('0').rstrip('.') if '.' in s else s
1500
Answer from the Decimal FAQ in the documentation:
>>> def remove_exponent(d):
... return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
>>> remove_exponent(Decimal('5.00'))
Decimal('5')
>>> remove_exponent(Decimal('5.500'))
Decimal('5.5')
>>> remove_exponent(Decimal('5E+3'))
Decimal('5000')
Answer is mentioned in FAQ (https://docs.python.org/2/library/decimal.html#decimal-faq) but does not explain things.
To drop trailing zeros for fraction part you should use normalize:
>>> Decimal('100.2000').normalize()
Decimal('100.2')
>> Decimal('0.2000').normalize()
Decimal('0.2')
But this works different for numbers with leading zeros in sharp part:
>>> Decimal('100.0000').normalize()
Decimal('1E+2')
In this case we should use `to_integral':
>>> Decimal('100.000').to_integral()
Decimal('100')
So we could check if there's a fraction part:
>>> Decimal('100.2000') == Decimal('100.2000').to_integral()
False
>>> Decimal('100.0000') == Decimal('100.0000').to_integral()
True
And use appropriate method then:
def remove_exponent(num):
return num.to_integral() if num == num.to_integral() else num.normalize()
Try it:
>>> remove_exponent(Decimal('100.2000'))
Decimal('100.2')
>>> remove_exponent(Decimal('100.0000'))
Decimal('100')
>>> remove_exponent(Decimal('0.2000'))
Decimal('0.2')
Now we're done.
Use the format specifier %g. It seems remove to trailing zeros.
>>> "%g" % (Decimal('2.5') * 10)
'25'
>>> "%g" % (Decimal('2.5678') * 1000)
'2567.8'
It also works without the Decimal function
>>> "%g" % (2.5 * 10)
'25'
>>> "%g" % (2.5678 * 1000)
'2567.8'
I ended up doing this:
import decimal
def dropzeros(number):
mynum = decimal.Decimal(number).normalize()
# e.g 22000 --> Decimal('2.2E+4')
return mynum.__trunc__() if not mynum % 1 else float(mynum)
print dropzeros(22000.000)
22000
print dropzeros(2567.8000)
2567.8
note: casting the return value as a string will limit you to 12 significant digits
Slightly modified version of A-IV's answer
NOTE that Decimal('0.99999999999999999999999999995').normalize() will round to Decimal('1')
def trailing(s: str, char="0"):
return len(s) - len(s.rstrip(char))
def decimal_to_str(value: decimal.Decimal):
"""Convert decimal to str
* Uses exponential notation when there are more than 4 trailing zeros
* Handles decimal.InvalidOperation
"""
# to_integral_value() removes decimals
if value == value.to_integral_value():
try:
value = value.quantize(decimal.Decimal(1))
except decimal.InvalidOperation:
pass
uncast = str(value)
# use exponential notation if there are more that 4 zeros
return str(value.normalize()) if trailing(uncast) > 4 else uncast
else:
# normalize values with decimal places
return str(value.normalize())
# or str(value).rstrip('0') if rounding edgecases are a concern
You could use :g to achieve this:
'{:g}'.format(3.140)
gives
'3.14'
This should work:
'{:f}'.format(decimal.Decimal('2.5') * 10).rstrip('0').rstrip('.')
Just to show a different possibility, I used to_tuple() to achieve the same result.
def my_normalize(dec):
"""
>>> my_normalize(Decimal("12.500"))
Decimal('12.5')
>>> my_normalize(Decimal("-0.12500"))
Decimal('-0.125')
>>> my_normalize(Decimal("0.125"))
Decimal('0.125')
>>> my_normalize(Decimal("0.00125"))
Decimal('0.00125')
>>> my_normalize(Decimal("125.00"))
Decimal('125')
>>> my_normalize(Decimal("12500"))
Decimal('12500')
>>> my_normalize(Decimal("0.000"))
Decimal('0')
"""
if dec is None:
return None
sign, digs, exp = dec.as_tuple()
for i in list(reversed(digs)):
if exp >= 0 or i != 0:
break
exp += 1
digs = digs[:-1]
if not digs and exp < 0:
exp = 0
return Decimal((sign, digs, exp))
Why not use modules 10 from a multiple of 10 to check if there is remainder? No remainder means you can force int()
if (x * 10) % 10 == 0:
x = int(x)
x = 2/1
Output: 2
x = 3/2
Output: 1.5

Categories