Can I make this iteration easier by using a loop? - python

I'm relatively new to python but I have used loops in previous programs I have made but they were relatively simple.
I was wondering if there was a method that involved a loop or some other way to make this iteration more concise.
Basically is there a way for me to have a variable called 't' that holds values for t1,t2 and t3 in like a list which is then used in 'eq' and 'der' instead of copying and pasting the same piece of code?
I have already tried to look for an answer on here and other places but the solutions I have found so far don't seem to work with what I have got/I'm not python literate enough to understand them.
import numpy as np
U235_Decay_Constant = 9.72e-10
U238_Decay_Constant = 1.54e-10
t0 = 4.1e9
eq = ((np.exp(U238_Decay_Constant*t0)-1)-(0.0167*
((np.exp(U235_Decay_Constant*t0)-1)))-0.0094)
der = (U238_Decay_Constant*(np.exp(U238_Decay_Constant*t0)))-(0.0167*
(U235_Decay_Constant*(np.exp(U235_Decay_Constant*t0))))
t1 = t0 - (eq/der)
eq = ((np.exp(U238_Decay_Constant*t1)-1)-(0.0167*
((np.exp(U235_Decay_Constant*t1)-1)))-0.0094)
der = (U238_Decay_Constant*(np.exp(U238_Decay_Constant*t1)))-(0.0167*
(U235_Decay_Constant*(np.exp(U235_Decay_Constant*t1))))
t2 = t1 - (eq/der)
eq = ((np.exp(U238_Decay_Constant*t2)-1)-(0.0167*
((np.exp(U235_Decay_Constant*t2)-1)))-0.0094)
der = (U238_Decay_Constant*(np.exp(U238_Decay_Constant*t2)))-(0.0167*
(U235_Decay_Constant*(np.exp(U235_Decay_Constant*t2))))
t3 = t2 - (eq/der)
print(t3)

Might be a little easier to read as:
import numpy as np
U235_Decay_Constant = 9.72e-10
U238_Decay_Constant = 1.54e-10
t = [4.1e9, None, None, None]
t[0] = 4.1e9
for i in range(3):
eq = ((np.exp(U238_Decay_Constant*t[i])-1)-(0.0167*
((np.exp(U235_Decay_Constant*t[i])-1)))-0.0094)
der = (U238_Decay_Constant*(np.exp(U238_Decay_Constant*t[0])))-(0.0167*
(U235_Decay_Constant*(np.exp(U235_Decay_Constant*t[0]))))
t[i+1] = t[i] - (eq/der)
print(t[3])

Yes, iteration can help here. Add your values to a list, then t? is the last value in the list so far; replacing your t? references with t[-1] gives:
t = [4.1e9]
for _ in range(3):
eq = (
(np.exp(U238_Decay_Constant * t[-1]) - 1)
- (0.0167 * ((np.exp(U235_Decay_Constant * t[-1]) - 1)))
- 0.0094
)
der = (U238_Decay_Constant * (np.exp(U238_Decay_Constant * t[-1]))) - (
0.0167 * (U235_Decay_Constant * (np.exp(U235_Decay_Constant * t[-1])))
)
t.append(t[-1] - (eq / der))
The general principle is one of accumulation, where you produce the running output of a repeated application of a function. So the itertools.accumulate() function could help here too:
from itertools import accumulate, chain, repeat
def u238_decay(t, _):
eq = (
(np.exp(U238_Decay_Constant * t) - 1)
- (0.0167 * ((np.exp(U235_Decay_Constant * t) - 1)))
- 0.0094
)
der = (U238_Decay_Constant * (np.exp(U238_Decay_Constant * t))) - (
0.0167 * (U235_Decay_Constant * (np.exp(U235_Decay_Constant * t)))
)
return t - (eq / der)
series = accumulate(chain([4.1e9], repeat(None)), u238_decay)
The above produces an unending series of decay values:
>>> series = accumulate(chain([4.1e9], repeat(None)), u238_decay)
>>> next(series)
4100000000.0
>>> next(series)
4081406102.7439713
>>> next(series)
4081163259.5641546
>>> next(series)
4081163218.6509323
>>> next(series)
4081163218.650931
You could look into creating a numpy universal function so you can do the same with the numpy.ufunc.accumulate() method.
However, I suspect that your formula can be re-cast to not depend on the previous input, but only as a formula of starting amount and t as time, at which point you can use full numpy vectorised calculations.

Related

Using sympy to solve an equation, python

I want to solve the below using Pythons sympy module instead of adapting what if have hand written. The result is x=26.8.
Any help appreciated.
Tks
EDIT:
my equation without sympy:
def cal_equ(total_vel=1.23, vel_vent_mg=17.74, vel_vent_tg=28.45, const=105):
a = (const / vel_vent_tg / const) + (1 / vel_vent_mg)
b = (total_vel - const / vel_vent_tg)
return b / a
from sympy import *
# create a symbol: it represents your unkown
x = symbols("x")
# create your constants
total_vel=1.23
vel_vent_mg=17.74
vel_vent_tg=28.45
const=105
# there are many ways to create an equation.
# here we create an equality, something that states that
# the left hand side is equal to the right hand side
eq = Eq((const - x) / vel_vent_tg - x / vel_vent_mg, total_vel)
# finally, we use the solve function to solve for the equation for x
sol = solve(eq, x)
sol[0].n()
# out: 26.8871034856029

iteration using it.chain

I'm stuck on returning the result from the function which is checking samples for A/B test and gave the result. The calculation is correct, but somehow I'm getting the result twice. The code and output below.
def test (sample1, sample2):
for i in it.chain (range(len(sample1)), range(len(sample2))):
alpha = .05
difference = (sample1['step_conversion'][i] - sample2['step_conversion'][i])/100
if (i > 0):
p_combined = (sample1['unq_user'][i] + sample2['unq_user'][i]) / (sample1['unq_user'][i-1] + sample2['unq_user'][i-1])
z_value = difference / mth.sqrt(
p_combined * (1 - p_combined) * (1 / sample1['unq_user'][i-1] + 1 / sample2['unq_user'][i-1]))
distr = st.norm(0, 1)
p_value = (1 - distr.cdf(abs(z_value))) * 2
print( sample1['event_name'][i], 'p-value: ', p_value)
if p_value < alpha:
print('Deny H0')
else:
print('Accept H0')
return
So I need the result in output just once (tagged in the box), but I get it twice from both samples.
When using Pandas dataframes, you should avoid most for loops, and use the standard vectorised approach. Use NumPy where applicable.
First, I've reset the indexes (indices) of the dataframes, to be sure .loc can be used with a standard numerical index.
sample1 = sample1.reset_index()
sample2 = sample2.reset_index()
The below does what I think you for loop does.
I can't test it, and without a clear description, example dataframes and expected outcome, it is anyone's guess if the code below does what you want. But it may get close, and mostly serves as an example of the vectorised approach.
import numpy as np
difference = (sample1['step_conversion'] - sample2['step_conversion']) / 100
n = len(sample1)
# Note that Pandas uses `n` as the highest *valid* index when using `.loc`, `n-1` is one lower
p_combined = ((sample1.loc[1:, 'unq_user'] + sample2.loc[1:, 'unq_user']).reset_index(drop=True) /
(sample1.loc[:n-1, 'unq_user'] + sample2.loc[:n-1, 'unq_user'])).reset_index(drop=True)
z_value = difference / np.sqrt(
p_combined * (1 - p_combined) * (
1 / sample1.loc[:n-1, 'unq_user'] + 1 / sample2.loc[:n-1, 'unq_user']))
distr = st.norm(0, 1) # ??
p_value = (1 - distr.cdf(np.abs(z_value))) * 2
sample1['p_value'] = p_value
print(sample1)
# The below prints a list of True values for elements for which the condition is valid.
# You can also use e.g. `print(sample1[p_value < alpha])`.
alpha = 0.05
print('Deny H0:')
print(p_value < alpha)
print('Accept H0:')
print(p_value > alpha)
No for loop needed, and for a large dataframe, the above will be notably faster.
Note that the .reset_index(drop=True) is a bit ugly. But if that is not there, Pandas will divide the two dataframes by equal indices, which is not what we want. This way, that is avoided.

Replacing Sympy indexed symbols with numeric values

I have a sympy expression I want to put numerical values in after differentiating it. The variables I want to replace are all the x[i], y[i] and R_abs[i] in the last expression and are numpy arrays a la
rx=np.array([-0.357, -0.742, -1.078, 0.206])
But trying subs or replace either doesn't do anything or raises the error that Symbols dont allow indexation for for example e1.subs(x[1],rx[0]). I pretty much went through every iteration I could think of to no avail.
import sympy as sp
r0,ge_x,ge_y,bx,by = sp.symbols('r0,ge_x,ge_y,bx,by', real=True) #Main symbols
i,x,y,R_abs = sp.symbols('i,x,y,R_abs', real=True) #Helper symbols
n=4
s2=sp.Sum((bx+r0*sp.Indexed('x',i)/sp.Indexed('R_abs',i)+ge_x*sp.Indexed('x',i)+ge_y*sp.Indexed('y',i)-sp.Indexed('x',i))**2+(by+r0*sp.Indexed('y',i)/sp.Indexed('R_abs',i)-ge_x*sp.Indexed('y',i)+ge_y*sp.Indexed('x',i)-sp.Indexed('y',i))**2,(i,1,n))
e1=sp.Eq(sp.diff(s2,bx).doit(),0)
With e1 then being
Eq(8*bx + 2*ge_x*x[1] + 2*ge_x*x[2] + 2*ge_x*x[3] + 2*ge_x*x[4] + 2*ge_y*y[1] + 2*ge_y*y[2] + 2*ge_y*y[3] + 2*ge_y*y[4] + 2*r0*x[4]/R_abs[4] + 2*r0*x[3]/R_abs[3] + 2*r0*x[2]/R_abs[2] + 2*r0*x[1]/R_abs[1] - 2*x[1] - 2*x[2] - 2*x[3] - 2*x[4], 0)
In here I would like to replace all the x, y, and R_abs with their numerical values.
I've always struggled with indexing in SymPy. Turns out, making Function instances are way easier than indexing instances of Symbol. It also makes notation simpler.
Also note that by using strings in your expression, I think SymPy makes its own symbols with those same string names but they can't be accessed with yours since your symbols are different. At least that's what happens sometimes to me.
Here is a working sample:
import sympy as sp
r0, ge_x, ge_y, bx, by = sp.symbols("r0 ge_x ge_y bx by", real=True) # main symbols
# define functions that will take the role of indexed symbols
x = sp.Function("x")
y = sp.Function("y")
R_abs = sp.Function("R_abs")
i = sp.Symbol("i", positive=True, integer=True)
n = 4
s2 = sp.Sum((bx + r0 * x(i) / R_abs(i) + ge_x * x(i) + ge_y * y(i) - x(i)) ** 2 +
(by + r0 * y(i) / R_abs(i) - ge_x * y(i) + ge_y * x(i) - y(i)) ** 2, (i, 1, n))
s2_prime = sp.diff(s2, bx).doit().simplify()
print(s2_prime)
# whatever lists you want. Can even be an instance of `np.ndarray`
# note that you summed from 1 to n so the 0th element will not be used
x_array = [0, 1, 2, 3, 4]
y_array = [4, 3, 2, 1, 0]
R_abs_array = [-10, 10, 5, 4, 3]
# define a function to access these array elements
x_function = lambda index: x_array[index]
y_function = lambda index: y_array[index]
R_abs_function = lambda index: R_abs_array[index]
# no idea why subs does not work and you MUST keep the same name for the variable.
# you can't have for example `evaluated_s2_prime = ...`.
# Probably something to do with forcing sp to remove references to `x`?
s2_prime = s2_prime.replace(x, x_function).replace(y, y_function).replace(R_abs, R_abs_function)
print(s2_prime)
Producing:
8*bx + 2*ge_x*x(1) + 2*ge_x*x(2) + 2*ge_x*x(3) + 2*ge_x*x(4) + 2*ge_y*y(1) + 2*ge_y*y(2) + 2*ge_y*y(3) + 2*ge_y*y(4) + 2*r0*x(4)/R_abs(4) + 2*r0*x(3)/R_abs(3) + 2*r0*x(2)/R_abs(2) + 2*r0*x(1)/R_abs(1) - 2*x(1) - 2*x(2) - 2*x(3) - 2*x(4)
8*bx + 20*ge_x + 12*ge_y + 31*r0/6 - 20

Manipulate return values in python

I am trying to convert decimal degrees to degrees, minutes and seconds using the following code:
def dd2dms(deg):
d = int(deg)
md = abs(deg - d) * 60
m = int(md)
sd = (md - m) * 60
return [d, m, sd]
full = 500/60
print(full)
print(dd2dms(full))
But I get the following output:
8.333333333333334 [8, 20, 0]
I would like to get the output as:
8.333333333333334 8:20:0
How can I achieve this? I am taking baby steps in learning Python. :)
Here's a little class built around your angle that has a conversion function for dms and also a nicely formatted string representation:
class Angle(float):
def __init__(self, degrees):
self.degrees = degrees
def __repr__(self):
return "<Angle of {}° ({})>".format(self.degrees, self.dms)
#property
def dms(self):
deg = self.degrees
d = int(deg)
md = abs(deg - d) * 60
m = int(md)
sd = (md - m) * 60
return "{:g}:{:g}:{:g}".format(d, m, sd)
Demo:
In[1]: a = Angle(90.173)
In[2]: print(a)
90.173
In[3]: print(a.dms)
90:10:22.8
In[4]: a
Out[4]: <Angle of 90.173° (90:10:22.8)>
Since it inherits from float, you can even calculate with it. Note though that the return value will be a normal float (not an Angle) without further adaption of the class.
If it is just for display purposes something like:
print("{0}:{1}:{2}".format(*dd2dms(full)))
If dd2dms returns an array, you just need to extract the array elements, convert them to strings and then concatenate. If you're just beginning to learn Python, try something easy like this:
result = dd2dms(full)
print(str(result[0]) + ":" + str(result[1]) + ":" + str(result[2]))
where result[i] returns the i-th element of array result, str() constructs a string from its argument, and then you can just concatenate the strings with +.
Remove square brackets from return statement (to return a tuple instead a list):
return d, m, sd
and format output string:
"%s:%s:%s" % dd2dms(full)
Full snippet:
def dd2dms(deg):
d = int(deg)
md = abs(deg - d) * 60
m = int(md)
sd = (md - m) * 60
return d, m, sd
full = 500/60
print(full)
print("%s:%s:%s" % dd2dms(full))
In python 3.6 you can use use literal string interpolation https://www.python.org/dev/peps/pep-0498/ to do the following:
def dd2dms(deg):
d = int(deg)
md = abs(deg - d) * 60
m = int(md)
sd = (md - m) * 60
return f'{d}:{m}:{sd}'
You can even do computations in the strings
for instance, you could do :
def dd2dms(deg):
d = int(deg)
md = abs(deg - d) * 60
m = int(md)
return f'{d}:{m}:{(md - m) * 60}'
However, it's usually better to declare your variables explicitly.

Non-sequential substitution in SymPy

I'm trying to use [SymPy][1] to substitute multiple terms in an expression at the same time. I tried the [subs function][2] with a dictionary as parameter, but found out that it substitutes sequentially.
In : a.subs({a:b, b:c})
Out: c
The problem is the first substitution resulted in a term that can be substituted by the second substitution, but it should not (for my cause).
Any idea on how to perform the substitutions simultaneously, without them interfering with each other?
Edit:
This is a real example
In [1]: I_x, I_y, I_z = Symbol("I_x"), Symbol("I_y"), Symbol("I_z")
In [2]: S_x, S_y, S_z = Symbol("S_x"), Symbol("S_y"), Symbol("S_z")
In [3]: J_is = Symbol("J_IS")
In [4]: t = Symbol("t")
In [5]: substitutions = (
(2 * I_x * S_z, 2 * I_x * S_z * cos(2 * pi * J_is * t) + I_y * sin(2 * pi * J_is * t)),
(I_x, I_x * cos(2 * pi * J_is * t) + 2 * I_x * S_z * sin(2 * pi * J_is * t)),
(I_y, I_y * cos(2 * pi * J_is * t) - 2 * I_x * S_z * sin(2 * pi * J_is * t))
)
In [6]: (2 * I_x * S_z).subs(substitutions)
Out[7]: (I_y*cos(2*pi*J_IS*t) - 2*I_x*S_z*sin(2*pi*J_IS*t))*sin(2*pi*J_IS*t) + 2*S_z*(I_x*cos(2*pi*J_IS*t) + 2*I_x*S_z*sin(2*pi*J_IS*t))*cos(2*pi*J_IS*t)
Only the appropriate substitution should happen, in this case only the first one. So the expected output should be the following:
In [6]: (2 * I_x * S_z).subs(substitutions)
Out[7]: I_y*sin(2*pi*J_IS*t) + 2*I_x*S_z*cos(2*pi*J_IS*t)
The current version of sympy provides the keyword simultaneous. The complicated operations in the previous answers are no more necessary:
In [1]: (x*sin(y)).subs([(x,y),(y,x)],simultaneous=True)
Out[1]: y⋅sin(x)
The subs(self,*args) method is defined (in part) this way:
In [11]: x.subs??
...
sequence = args[0]
if isinstance(sequence, dict):
return self._subs_dict(sequence)
elif isinstance(sequence, (list, tuple)):
return self._subs_list(sequence)
If you pass subs a dict, you lose control over the order of the substitutions.
While if you pass subs a list or tuple, you can control the order.
This doesn't allow you to do simultaneous substitutions. That would lead to difficulties if the user were to pass stuff like x.subs([(x,y),(y,x)]). So I doubt sympy has a method for doing simultaneous substitutions. Instead I believe all substutions are either unordered (if you pass a dict) or, at best, done by a 1-pass ordered substitution (if you pass a list or tuple):
In [17]: x.subs([(x,y),(y,z)])
Out[18]: z
In [19]: x.subs([(y,z),(x,y)])
Out[19]: y
PS. _subs_list(self, sequence) is defined (in part) like this:
In [14]: x._subs_list??
...
for old, new in sequence:
result = result.subs(old, new)
This nails down the order in which the subs are done.
Example for #~unutbu's answer:
>>> import ordereddict # collections.OrderedDict in Python 2.7+
>>> from sympy import *
>>> x,y,z = symbols('xyz')
>>> x.subs(ordereddict.OrderedDict([(x,y),(y,z)]))
y
>>> x.subs(ordereddict.OrderedDict([(y,z),(x,y)]))
z
Answering the edited question.
In your example you can use some temporary variables which will not be over-written be subsequent substitutions. Then, once all of the potentially overlapping substitutions have been made, you can replace the temporary variables with the real ones.
This example works for the question, if your full problem contains more complex substitutions, I think you should still be able to create temporary variables to avoid overlapping substitutions.
from sympy import Symbol, sin, cos, pi
I_x, I_y, I_z = Symbol("I_x"), Symbol("I_y"), Symbol("I_z")
S_x, S_y, S_z = Symbol("S_x"), Symbol("S_y"), Symbol("S_z")
J_is = Symbol("J_IS")
t = Symbol("t")
I_x_temp, I_y_temp, I_z_temp = Symbol("I_x_temp"), Symbol("I_y_temp"), Symbol("I_z_temp")
f = 2*I_x*S_z
answer = I_y*sin(2*pi*J_is*t) + 2*I_x*S_z*cos(2*pi*J_is*t)
subs1a = [
(2*I_x*S_z, 2*I_x_temp*S_z*cos(2*pi*J_is*t) + I_y_temp*sin(2*pi*J_is*t)),
(I_x, I_x_temp*cos(2* pi*J_is*t) + 2*I_x_temp*S_z*sin(2*pi*J_is*t)),
(I_y, I_y_temp*cos(2*pi*J_is*t) - 2*I_x_temp*S_z* sin(2*pi*J_is*t))
]
subs_temp = [(I_x_temp, I_x), (I_y_temp, I_y), (I_z_temp, I_z)]
print f
f = f.subs(subs1a)
print f
f = f.subs(subs_temp)
print f
print f == answer # True
Note, you can also perform two substitutions back to back:
f.subs(subs1a).subs(subs_temp) == answer
The Keyword simultaneous will do non-clashing subs regardless of the input (dict or sequence):
>>> x.subs([(x,y),(y,z)],simultaneous=1)
y
>>> x.subs([(y,z),(x,y)],simultaneous=1)
y

Categories