What is this piece of Python code doing? - python

This following is a snippet of Python code I found that solves a mathematical problem. What exactly is it doing? I wasn't too sure what to Google for.
x, y = x + 3 * y, 4 * x + 1 * y
Is this a special Python syntax?

x, y = x + 3 * y, 4 * x + 1 * y
is the equivalent of:
x = x + 3 * y
y = 4 * x + 1 * y
EXCEPT that it uses the original values for x and y in both calculations - because the new values for x and y aren't assigned until both calculations are complete.
The generic form is:
x,y = a,b
where a and b are expressions the values of which get assigned to x and y respectively. You can actually assign any tuple (set of comma-separated values) to any tuple of variables of the same size - for instance,
x,y,z = a,b,c
would also work, but
w,x,y,z = a,b,c
would not because the number of values in the right-hand tuple doesn't match the number of variables in the left-hand tuple.

It's an assignment to a tuple, also called sequence unpacking. Probably it's clearer when you add parenthesis around the tuples:
(x, y) = (x + 3 * y, 4 * x + 1 * y)
The value x + 3 * y is assigned to x and the value 4 * x + 1 * y is assigned to y.
It is equivalent to this:
x_new = x + 3 * y
y_new = 4 * x + 1 * y
x = x_new
y = y_new

I also recently saw this referred to as "simultaneous assignment", which seems to capture the spirit of several of the answers.

Related

How to write a for loop for 500 values of theta between 0 and 2pi

I am trying to write a for-loop for the 500 values of theta between 0 and 2pi using these formulas:
x = r cos(θ)
y = sin(3θ) −2 cos(2(θ + 0.2)) + 2 sin(7θ)
z = r sin(θ)**
This is what I have:
for i in range(500):
x = r*np.cos(i)
y = (np.sin(3*i)) - 2*np.cos(2*(i + 0.2)) + (2*np.sin(7*i))
z = r*np.sin(i)
Can someone please help me?
Since you use numpy anyway, you can get rid of for loops altogether, because numpy is designed to perform the same operation on a whole array of numbers:
thetas = np.linspace(0, 2*np.pi, 500)
r = 1
x = r*np.cos(thetas)
y = (np.sin(3*thetas)) - 2*np.cos(2*(thetas + 0.2)) + (2*np.sin(7*thetas))
z = r*np.sin(thetas)
Here thetas variable will contain an array of 500 numbers between 0 and 2pi.
And next x, y, and z variables will also be arrays with 500 numbers, containing the values calculated with all the given thetas.

Function with a choice of parameters used

I have a two functions where I set parameters for mathematical function. I would like to create one if possible where I would type:
x, y = one_demo_fun(a=1,
b=2,
c=5,
power_of_the_first_x=2,
power_of_the_second_x=1))
or
x, y = one_demo_fun(y = 1 * (x ** 2) + (2 * x) + 5)
and get the same result.
I have two functions which I would like to make one, where user can type one of above:
def function_demo_parameters( # type function settings ax^2+bx+c
a=1,
b=1,
c=1,
power_of_the_first_x=2,
power_of_the_second_x=1):
x = np.linspace(-2, 2, 100)
y = a * (x ** power_of_the_first_x) + b * (x ** power_of_the_second_x) + c
return x, y
def function_demo_equation( # type function
y=1 * (x ** 2) + (2 * x) + 5):
x = np.linspace(-2, 2, 100)
y = y
return x, y
x, y = function_demo_parameters()
x_1, y_1 = function_demo_equation()
Is there some way to merge them or other way to look at the problem?
You don't want an equation; you want the function that the equation implies.
function_demo_equation(lambda x: 1*(x**2) + 2*x + 5)

Simplify in sympy with custom symbols

Suppose that I have sympy variables x, y and z
I have the expression (x + y) (x + y) + x
Is it possible that I am able to define z as x + y so that I can "simplify" the expression above to z^2 + x?
That is, I want to use z wherever I can in the simplified expression.
Yes, use subs as in
>>> var('x:z')
(x, y, z)
>>> ((x + y)*(x + y) + x).subs(x+y,z)
x + z**2
The subs routine will sometimes try to be smart about substitutions, but works best when the substitution target is a symbol (and hence unambiguous). So if you want to try the replacement x+y->z in a more ambiguous expression, try replacing x->z-y, simplify, and then resubstitute z-y->x to restore anything that didn't change:
>>> ((x*x + 2 * x * y + y * y) + x).subs(x,z-y).expand().subs(z-y,x)
x + z**2

I got an empty list when solving two equation

I tried to solve two simple equations but I got nothing.
from sympy import *
x, y = symbols('x y')
eq1=Function('eq1')
eq2=Function('eq2')
eq1 = Eq(x + y , 1) # x + y = 1
eq2 = Eq(x + y ,3) # x + y = 3
ans = solve([eq1, eq2] , [x, y])
print(ans)
I got
[]
You set everything up ok. The empty list is the way that solve tells you that it could not find a solution for x and y that satisfied the equations. And, indeed, there are no values which, when added, will give two different results (as others have noted).

how do I make a numpy.piecewise function of arbitrary length? (having lambda issues)

I'm trying to plot a piecewise fit to my data, but I need to do it with an arbitrary number of line segments. Sometimes there are three segments; sometimes there are two. I'm storing the coefficients of the fit in actable and the bounds on the segments in btable.
Here are example values of my bounds:
btable = [[0.00499999989, 0.0244274978], [0.0244275965, 0.0599999987]]
Here are example values of my coefficients:
actable = [[0.0108687987, -0.673182865, 14.6420775], [0.00410866373, -0.0588355861, 1.07750032]]
Here's what my code looks like:
rfig = plt.figure()
<>various other plot specifications<>
x = np.arange(0.005, 0.06, 0.0001)
y = np.piecewise(x, [(x >= btable[i][0]) & (x <= btable[i][1]) for i in range(len(btable))], [lambda x=x: np.log10(actable[j][0] + actable[j][2] * x + actable[j][2] * x**2) for j in list(range(len(actable)))])
plt.plot(x, y)
The problem is that lambda sets itself to the last instance of the list, so it uses the coefficients for the last segment for all the segments. I don't know how to do a piecewise function without using lambda.
Currently, I'm cheating by doing this:
if len(btable) == 2:
y = np.piecewise(x, [(x >= btable[i][0]) & (x <= btable[i][1]) for i in range(len(btable))], [lambda x: np.log10(actable[0][0] + actable[0][1] * x + actable[0][2] * x**2), lambda x: np.log10(actable[1][0] + actable[1][1] * x + actable[1][2] * x**2)])
else if len(btable) == 3:
y = np.piecewise(x, [(x >= btable[i][0]) & (x <= btable[i][1]) for i in range(len(btable))], [lambda x: np.log10(actable[0][0] + actable[0][1] * x + actable[0][2] * x**2), lambda x: np.log10(actable[1][0] + actable[1][1] * x + actable[1][2] * x**2), lambda x: np.log10(actable[2][0] + actable[2][1] * x + actable[2][2] * x**2)])
else
print('Oh no! You have fewer than 2 or more than 3 segments!')
But this makes me feel icky on the inside. I know there must be a better solution. Can someone help?
This issue is common enough that Python's official documentation has an article Why do lambdas defined in a loop with different values all return the same result? with a suggested solution: create a local variable to be initialized by the loop variable, to capture the changing values of the latter within the function.
That is, in the definition of y it suffices to replace
[lambda x=x: np.log10(actable[j][0] + actable[j][1] * x + actable[j][2] * x**2) for j in range(len(actable))]
by
[lambda x=x, k=j: np.log10(actable[k][0] + actable[k][1] * x + actable[k][2] * x**2) for j in range(len(actable))]
By the way, one can use one-sided inequalities to specify ranges for numpy.piecewise: the last of the conditions that evaluate to True will trigger the corresponding function. (This is a somewhat counterintuitive priority; using the first true condition would be more natural, like SymPy does). If the breakpoints are arranged in increasing order, then one should use "x>=" inequalities:
breaks = np.arange(0, 10) # breakpoints
coeff = np.arange(0, 20, 2) # coefficients to use
x = np.arange(0, 10, 0.1)
y = np.piecewise(x, [x >= b for b in breaks], [lambda x=x, a=c: a*x for c in coeff])
Here each coefficient will be used for the interval that begins with the corresponding breakpoint; e.g., coefficient c=0 is used in the range 0<=x<1, coefficient c=2 in the range 1<=x<2, and so on.

Categories