SymPy is a great tool for doing units conversions in Python:
>>> from sympy.physics import units
>>> 12. * units.inch / units.m
0.304800000000000
You can easily roll your own:
>>> units.BTU = 1055.05585 * units.J
>>> units.BTU
1055.05585*m**2*kg/s**2
However, I cannot implement this into my application unless I can convert degrees C (absolute) to K to degrees F to degrees R, or any combo thereof.
I thought maybe something like this would work:
units.degC = <<somefunc of units.K>>
But clearly that is the wrong path to go down. Any suggestions for cleanly implementing "offset"-type units conversions in SymPy?
Note: I'm open to trying other units conversion modules, but don't know of any besides Unum, and found it to be cumbersome.
Edit: OK, it is now clear that what I want to do is first determine if the two quantities to be compared are in the same coordinate system. (like time units reference to different epochs or time zones or dB to straight amplitude), make the appropriate transformation, then make the conversion. Are there any general coordinate system management tools? That would be great.
I would make the assumption that °F and °C always refer to Δ°F Δ°C within an expression but refer to absolute when standing alone. I was just wondering if there was a way to make units.degF a function and slap a decorator property() on it to deal with those two conditions.
But for now, I'll set units.C == units.K and try to make it very clear in the documentation to use functions convertCtoK(...) and convertFtoR(...) when dealing with absolute units. (Just kidding. No I won't.)
The Unum documentation has a pretty good writeup on why this is hard:
Unum is unable to handle reliably conversions between °Celsius and Kelvin. The issue is referred as the 'false origin problem' : the 0°Celsius is defined as 273.15 K. This is really a special and annoying case, since in general the value 0 is unaffected by unit conversion, e.g. 0 [m] = 0 [miles] = ... . Here, the conversion Kelvin/°Celsius is characterized by a factor 1 and an offset of 273.15 K. The offset is not feasible in the current version of Unum.
Moreover it will presumably never be integrated in a future version because there is also a conceptual problem : the offset should be applied if the quantity represents an absolute temperature, but it shouldn't if the quantity represents a difference of temperatures. For instance, a raise of temperature of 1° Celsius is equivalent to a raise of 1 K. It is impossible to guess what is in the user mind, whether it's an absolute or a relative temperature. The question of absolute vs relative quantities is unimportant for other units since the answer does not impact the conversion rule. Unum is unable to make the distinction between the two cases.
It's pretty easy to conceptually see the problems with trying to represent absolute temperature conversion symbolically. With any normal relative unit, (x unit) * 2 == (x * 2) unit—unit math is commutative. With absolute temperatures, that breaks down—it's difficult to do anything more complex than straight temperature conversions with no other unit dimensions. You're probably best off keeping all calculations in Kelvin, and converting to and from other temperature units only at the entry and exit points of your code.
I personally like Quantities thanks to its NumPy integration, however it only does relative temperatures, not absolute.
Example, how it could work:
>>> T(0*F) + 10*C
T(265.37222222222221*K) # or T(47767/180*K)
>>> T(0*F + 10*C)
T(283.15*K)
>>> 0*F + T(10*C)
T(283.15*K)
>>> 0*F + 10*C
10*K
>>> T(0*F) + T(10*C)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'absolute_temperature' and \
'absolute_temperature'
>>> T(0*F) - T(10*C)
T(245.37222222222223*K) # or T(44167/180*K)
>>> 0*F - 10*C
-10*K
The natu package handles units of temperature. For instance, you can do this:
>>> from natu.units import K, degC, degF
>>> T = 25*degC
>>> T/K
298.1500
>>> T/degF
77.0000
>>> 0*degC + 100*K
100.0 degC
Prefixes are supported too:
>>> from natu.units import mdegC
>>> 100*mdegC/K
273.2500
natu also handles nonlinear units such as the decibel, not just those with offsets like degree Celsius and degree Fahrenheit.
Relating to the first example you gave, you can do this:
>>> from natu import units
>>> 12*units.inch/units.m
0.3048
BTU is already built in. You can change its display unit to m**2*kg/s**2, but by default natu simplifies the unit to J:
>>> from natu.units import BTU
>>> BTU.display = 'm2*kg/s2'
>>> 1*BTU
1055.05585262 J
Related
First I want to point out that I'm a total sympy noob.
I'm trying to create a Custom Formula-based Measurement class with this sympy expression:
from sympy import Symbol, S, floor, sympify, Float
SU = Symbol('millimeter')
exp = S(20.0) + floor((((SU-S(212.5)) / S(10.0))) / S(0.5)) * S(0.5)
The problem I face is that for the same SU I get different result based on the way the expression is evaluated. Here is what I mean:
>>> exp.subs(SU, 215)
20.0000000000000
>>> exp.evalf(subs={SU: 215})
0.e+1 #This is actually 16.0 when: float(exp.evalf(subs={SU: 215}))
More interestingly the problem exists only when SU is between [213:217] (when I expect the result to be 20.0)
For the rest of the values its fine (AFAIK)
>>> exp.subs(SU, 212)
19.5000000000000
>>> exp.evalf(subs={SU: 212})
19.50
>>> exp.subs(SU, 218)
20.5000000000000
>>> exp.evalf(subs={SU: 218})
20.50
Any Ideas for this strange behavior ?
This was due to incorrect precision values. The bug was reported and is already corrected in the current version of SymPy available on GitHub; the versions of SymPy > 1.1.1 will not have this bug.
Using srepr on the output of subs provides some explanation:
x = Symbol('x')
srepr((floor(x)+20).evalf(subs={x:0.5}))
The output is Float('16.0', precision=1). This is binary precision. SymPy thinks that the output of floor, when it happens to be zero, has only one bit of precision. So it subsequently truncates the added +20 accordingly, to nearest power of 2.
Of course, this is a bug. There are several open issues related to Float class and rounding, such as this one; they may be related.
The workaround is to avoid evalf(subs=dict) construction (is it even documented?). Using the methods in the natural order: substitute, then evaluate, gives correct results:
srepr((floor(x)+20).subs({x:0.5}).evalf())
"Float('20.0', precision=53)"
so I am having issues trying to do basic math in Python. I can do basic math, but when I add in exponents, square roots, etc, I have errors with the IDE. How do I do this?
Here are a few of my problems that I am having issues with:
(n(n-1))/2)
(4)* pi * r ** 2=
(r(cos(a)**2) + r(sin(b))**2)**(.5)
((y**2) - (y**1))/((x**2) - (x**1))=
(n*(n-1))/2 should work if you have already given n a numeric value (e.g. n=2 or something). Your original expression has unbalanced parentheses (three closing parens and only two opening parens) and it is missing the multiplication sign (*) which is necessary in python, otherwise n(n-1) would be interpreted as the function n supplied with the input n-1, in which case you get a message like "TypeError: 'int' object is not callable", assuming you had previously defined n=2 or the like. It's telling you that the integer n cannot be called like a function, which is how it interprets n().
To get pi (3.14159...) in python, you should import the math package and then use math.pi like this:
import math
r = 2
x = 4*math.pi*r**2
You don't need parentheses around the 4. Parentheses are used for grouping, when you need operations to be done in a different order than the standard order of operations. You don't need the trailing equal sign (that's a syntax error).
In the third expression you are using implicit multiplication notation which is fine for pencil and paper but in python you need to use * every time you multiply. Also, you need to import the math package and then use math.sin and math.cos.
import math
a = 90
b = 45
x = (r*(math.cos(a)**2) + r*(math.sin(b))**2)**(.5)
There doesn't appear to be anything wrong with the last expression except the trailing = sign which should be removed. Store the result of this expression in a variable if you want to keep it for future use:
z = ((y**2) - (y**1))/((x**2) - (x**1))
If you just type the expression at the command line it will print the result immediately:
x = 3
y = 2
((y**2) - (y**1))/((x**2) - (x**1))
but if you are using this expression in a script you want to save the result in a variable so you can use it later or print it out or something:
z = ((y**2) - (y**1))/((x**2) - (x**1))
As was previously pointed out in the comments, x**1 is the same, mathematically, as x so it's not clear why you would want to write it this way, but it's not wrong.
You can use math.pow() or the simpler ** syntax:
There are two ways to complete basic maths equations in python either:
use the ** syntax. e.g:
>>> 2 ** 4
16
>>> 3 ** 3
27
or use math.pow(). e.g:
>>> import math
>>> math.pow(5, 2)
25.0
>>> math.pow(36, 0.5)
6.0
As you can see, with both of these functions you can use any real power so negative for inverse or decimals for roots.
In general, for these types of equations, you want to look into the math module. It has lost of useful functions and defined constants that you may find useful. In particular for your specific problems: math.pi and these trig. functions.
I hope these examples and the links I made are useful for you :)
I'm using numerical method to find the cube root of 1. My method works, i.e. converging at the three roots from different starting point, judging by eyes.
However, when I use numpy.unique() to get the three roots, the slightly different complex part prevents me from extracting the roots.
Right now I'm separating each complex number into real and imaginary part, round the parts and compare. However, that is quite clunky. Is there a better way?
I would calculate the absolute value of their difference and compare to float's epsilon:
import math
import sys
def almost_equal(a, b):
d = a - b
return math.sqrt(d.real ** 2 + d.imag ** 2) < sys.float_info.epsilon
I have two ways of deriving the probability of a normally (say) distributed random variable to be within an interval. The first and most straight-forward is the following:
import scipy.stats
print scipy.stats.norm.cdf(6) - scipy.stats.norm.cdf(5)
# 2.85664984223e-07
And the second is by integrating the pdf:
import scipy.integrate
print scipy.integrate.quad(scipy.stats.norm.pdf, 5, 6)[0]
# 2.85664984234e-07
The difference in this case is really tiny, but it doesn't mean it can't grow larger for other distributions or integration limits. Can you tell which is more accurate and why?
By the way, the first alternative seems to be at least 10 times faster, so if it is also more accurate (which would be my guess, since it is somewhat specialized), then it is perfect.
In this particular case, given those particular numbers, the quad approach will actually be more accurate. The CDF itself can be computed quickly and accurately, of course, but look at the actual numbers:
>>> scipy.stats.norm.cdf(6), scipy.stats.norm.cdf(5)
(0.9999999990134123, 0.99999971334842808)
When you're differencing two very similar quantities, you lose accuracy. Similar problems can be mitigated somewhat during integration if the coders are careful with their summations.
Anyway, we can check this against a high-resolution calculation using mpmath:
>>> via_cdf = scipy.stats.norm.cdf(6)-scipy.stats.norm.cdf(5)
>>> via_quad = scipy.integrate.quad(scipy.stats.norm.pdf, 5, 6)[0]
>>> import mpmath
>>> mpmath.mp.dps = 100
>>> def cdf(x): return 0.5 * (1 + mpmath.erf(x/mpmath.sqrt(2)))
>>> highres = cdf(6)-cdf(5)
>>> highres
mpf('0.0000002856649842341562135330514687422473118357532223619105443630157837185833042478210791954518847897468442097')
>>> float((highres - via_quad)/highres)
-2.3824773334590333e-16
>>> float((highres - via_cdf)/highres)
3.86659439572868e-11
The first calls an implementation of the cdf included in scipy.special. The latter actually does the integration. The former is probably more accurate (as it is limited only by the computer's ability do evaluate the CDF and not by any errors introduced by numerical integration). In practice, unless you need results that are good to better than 6 decimal places, you're probably fine.
This question already has answers here:
Python cos(90) and cos(270) not 0
(3 answers)
Closed 9 years ago.
Is there a way to get the exact Tangent/Cosine/Sine of an angle (in radians)?
math.tan()/math.sin()/math.cos() does not give the exact for some angles:
>>> from math import *
>>> from decimal import Decimal
>>> sin(pi) # should be 0
1.2246467991473532e-16
>>> sin(2*pi) # should be 0
-2.4492935982947064e-16
>>> cos(pi/2) # should be 0
6.123233995736766e-17
>>> cos(3*pi/2) # 0
-1.8369701987210297e-16
>>> tan(pi/2) # invalid; tan(pi/2) is undefined
1.633123935319537e+16
>>> tan(3*pi/2) # also undefined
5443746451065123.0
>>> tan(2*pi) # 0
-2.4492935982947064e-16
>>> tan(pi) # 0
-1.2246467991473532e-16
I tried using Decimal(), but this does not help either:
>>> tan(Decimal(pi)*2)
-2.4492935982947064e-16
numpy.sin(x) and the other trigonometric functions also have the same issue.
Alternatively, I could always create a new function with a dictionary of values such as:
def new_sin(x):
sin_values = {math.pi: 0, 2*math.pi: 0}
return sin_values[x] if x in sin_values.keys() else math.sin(x)
However, this seems like a cheap way to get around it. Is there any other way? Thanks!
It is impossible to store the exact numerical value of pi in a computer. math.pi is the closest approximation to pi that can be stored in a Python float. math.sin(math.pi) returns the correct result for the approximate input.
To avoid this, you need to use a library that supports symbolic arithmetic. For example, with sympy:
>>> from sympy import *
>>> sin(pi)
0
>>> pi
pi
>>>
sympy will operate on an object that represents pi and can give exact results.
When you're dealing with inexact numbers, you need to deal with error explicitly. math.pi (or numpy.pi) isn't exactly π, it's, e.g., the closest binary rational number in 56 digits to π. And the sin of that number is not 0.
But it is very close to 0. And likewise, tan(pi/2) is not infinity (or NaN), but huge, and asin(1)/pi is very close to 0.5.
So, even if the algorithms were somehow exact, the results still wouldn't be exact.
If you've never read What Every Computer Scientist Should Know About Floating-Point Arithmetic, you should do so now.
The way to deal with this is to use epsilon-comparisons rather than exact comparisons everywhere, and explicitly round things when printing them out, and so on.
Using decimal.Decimal numbers instead of float numbers makes this easier. First, you probably think in decimal rather than binary, so it's easier for you to understand and make decisions about the error. Second, you can explicitly set precision and other context information on Decimal values, while float are always IEEE double values.
The right way to do it is to do full error analysis on your algorithms, propagate the errors appropriately, and use that information where it's needed. The simple way is to just pick some explicit absolute or relative epsilon (and the equivalent for infinity) that's "good enough" for your application, and use that everywhere. (You'll probably also want to use the appropriate domain-specific knowledge to treat some values as multiples of pi instead of just raw values.)