Calculate the angle between two lines (2 options) and efficiency - python

The task: calculate the angle between two lines
input:
float x1, y1 (starting point)
float x2, y2 (end point)
output:
float phi (angle in deg, between the lines)
additionally:
one line is parallel to the x-axis, and phi is 0° ≤ phi < 359°
Task Picture
option 1:
import math
rad_to deg = lambda x: 180.0/math.pi * x
# start-point
x1 = float(input("x1: "))
y1 = float(input("y1: "))
# end-point
x2 = float(input("x2: "))
y2 = float(input("y2: "))
# slope of one line (parallel to the x-axis)
m1 = 0
# slope between the start-point and end-point
# special-case: division with zero
if x1 == x2:
m2 = "Not defined!"
if y2 > y1: # end-point over start-point
phi = 90
elif y2 < y1: # end-point under start-point
phi = 270
else:
m2 = (y2 - y2) / (x2 - x1)
# angle between two lines (smaller angle: 0° < phi < 90°)
angle = rad_to_deg(math.atan(abs((m1 - m2) / (1 + m1 * m2))))
if x1 < x2 and y1 < y2: # 1. quadrant
phi = sw
elif x1 > x2 and y1 < y2: # 2. quadrant
phi = 180 - sw
elif x1 > x2 and y1 > y2: # 3. quadrant
phi = 180 + sw
elif x1 < x2 and y1 > y2: # 4. quadrant
phi = 360 - sw
elif y1 == y2 and x1 > x2: # end-point left from start-point
phi = 180
elif y1 == y2 and x1 < x2 : # end-point right from start-point
phi = 0
elif x1 == x2 and y1 == y2:
phi = "Error, start-point is end-point"
print("angle phi: " + str(phi))
or should be the special-case with try/except?
try:
m2 = (y2 - y2) / (x2 - x1)
except ZeroDivisionError: # x1 == x2
m2 = "Not defined!"
if y2 > y1: # end-point over start-point
phi = 90
elif y2 < y1: # end-point under start-point
phi = 270
option 2: with vectors
for math import sqrt, acos, pi
rad_to_deg = lambda x: 180.0/pi * x
x1 = float(input("x1: "))
y1 = float(input("y1: "))
x2 = float(input("x1: "))
y2 = float(input("x2: "))
# vectors
u = [1, 0] # start-point and (a point right from the start-point)
v = [x2 - x1, y2 - y1]
# calculation
u_scalar_v = u[0] * v[0] + u[1] * v[1]
u_amount = sqrt(u[0] * u[0] + u[1] * u[1])
v_amount = sqrt(v[0] * v[0] + v[1] * v[1])
# scalar product
phi = round(rad_to_deg(acos(u_scalar_v / (u_amount * v_amount))), 5)
if y2 >= y1:
pass
else:
phi = 360 - phi
print(phi)
option 3:
is to see the start-point, the end-point and a point right from s, as a triangle and to calculate over the cosine theorem
What is the most efficient way to calculate this and how can I decide it?

Find the Angle between three points from 2D using python provides a simple solution.
import math
def getAngle(a, b, c):
ang = math.degrees(math.atan2(c[1]-b[1], c[0]-b[0]) - math.atan2(a[1]-b[1], a[0]-b[0]))
return ang + 360 if ang < 0 else ang
# starting-point
x1 = float(input("x1: "))
y1 = float(input("y1: "))
# middle-point (intersection point)
x2 = float(input("x2: "))
y2 = float(input("y2: "))
# ending point of horizontal line
x3 = x2 + 1 # i.e. horizontal offset from mid-point (x2, y2)
y3 = y2
a = (x1, y1)
b = (x2, y2)
c = (x3, y3)
angle = getAngle(a, b, c)
Example
a = (5, 0)
b = (0, 0)
c = (0, 5)
print(getAngle(a, b, c)) # result 90.0
Example 2--test with random points
from random import randrange, sample
radius = 10
points = []
for i1 in range(10):
for i2 in range(10):
points.append((randrange(-radius, radius+1), randrange(-radius, radius+1)))
x1y1 = sample(points[:50], 10)
x2y2 = sample(points[50:], 10)
x3y3 = [(x+1, y) for x, y in x2y2]
for i in range(len(x1y1)):
a, b, c = x1y1[i], x2y2[i], x3y3[i]
angle = getAngle(a, b, c)
print(i, ": ", a, b, c, '=> ', angle)
Result
0 : (10, -6) (8, -10) (9, -10) => 296.565051177078
1 : (0, -9) (-4, -3) (-3, -3) => 56.309932474020215
2 : (-6, 10) (5, 9) (6, 9) => 185.1944289077348
3 : (0, 1) (-2, 1) (-1, 1) => 0.0
4 : (2, -1) (-3, 7) (-2, 7) => 57.9946167919165
5 : (2, -3) (-10, -8) (-9, -8) => 337.3801350519596
6 : (2, -6) (-10, 5) (-9, 5) => 42.510447078000844
7 : (7, 8) (7, 3) (8, 3) => 270.0
8 : (2, -2) (-4, 4) (-3, 4) => 45.0
9 : (1, -2) (-2, 7) (-1, 7) => 71.56505117707799

Related

3D points from Numpy meshgrid coordinates

I have a 3 sets of meshgrid coordinates of a 3D cylinder:
X, Y, Z = [p1[i] + v[i] * t1 + r * np.sin(theta2) * n1[i] + r * np.cos(theta2) * n2[i] for i in [0, 1, 2]]
here the definition of p1, v, t1, r and theta2:
r = 3
start = [30, 45, 60]
end = [40, 58, 70]
p1 = np.array(start)
p2 = np.array(end)
v = p2 - p1
mag = scipy.linalg.norm(v)
v = v / mag
not_v = np.array([1, 0, 0])
if (v == not_v).all():
not_v = np.array([0, 1, 0])
n1 = np.cross(v, not_v)
n1 /= scipy.linalg.norm(n1)
n2 = np.cross(v, n1)
t = np.linspace(0, mag, 100)
theta = np.linspace(0, 2 * np.pi, 100)
t1, theta2 = np.meshgrid(t, theta)
I don't know if I didn't understand correctly how numpy.meshgrid works, but I'm looking for a way to get the (x,y,z) points of the polyhedron.

How to loop through lists from pandas dataframe in a function

Here is my dataframe,
df = pd.DataFrame({'Id': [102,103,104,303,305],'ExpG_Home':[1.8,1.5,1.6,1.8,2.9],
'ExpG_Away':[2.2,1.3,1.2,2.8,0.8],
'HomeG_Time':[[93, 109, 187],[169], [31, 159],[176],[16, 48, 66, 128]],
'AwayG_Time':[[90, 177],[],[],[123,136],[40]]})
First, I need to create an array y, for a given Id number, it takes values from same row (ExpG_Home & ExpG_Away).
y = [1 - (ExpG_Home + ExpG_Away), ExpG_Home, ExpG_Away]
Second, I found this much harder, for the Id used in creating y, the function below takes the corresponding lists from HomeG_Time & AwayG_Time and creates an array. Unfortunately, my function takes one row at a time. I need to do this for a large dataset.
x1 = [1,0,0]
x2 = [0,1,0]
x3 = [0,0,1]
total_timeslot = 200 # number of timeslot per game.
k = 1 # constant
#For Id=102 with ExpG_Home=2.2 and ExpG_Away=1.8
HomeG_Time = [93, 109, 187]
AwayG_Time = [90, 177]
y = np.array([1-(2.2 + 1.8)/k, 2.2/k, 1.8/k])
# output of y = [0.98 , 0.011, 0.009]
def squared_diff(x1, x2, x3, y):
ssd = []
for k in range(total_timeslot):
if k in HomeG_Time:
ssd.append(sum((x2 - y) ** 2))
elif k in AwayG_Time:
ssd.append(sum((x3 - y) ** 2))
else:
ssd.append(sum((x1 - y) ** 2))
return ssd
sum(squared_diff(x1, x2, x3, y))
Out[37]: 7.880400000000012
This output is for the first row only.
Here is the complete snippet given,
>>> import numpy as np
>>> x1 = np.array( [1,0,0] )
>>> x2 = np.array( [0,1,0] )
>>> x3 = np.array( [0,0,1] )
>>> total_timeslot = 200
>>> HomeG_Time = [93, 109, 187]
>>> AwayG_Time = [90, 177]
>>> ExpG_Home=2.2
>>> ExpG_Away=1.8
>>> y = np.array( [1 - (ExpG_Home + ExpG_Away), ExpG_Home, ExpG_Away] )
>>> def squared_diff(x1, x2, x3, y):
... ssd = []
... for k in range(total_timeslot):
... if k in HomeG_Time:
... ssd.append(sum((x2 - y) ** 2))
... elif k in AwayG_Time:
... ssd.append(sum((x3 - y) ** 2))
... else:
... ssd.append(sum((x1 - y) ** 2))
... return ssd
...
>>> sum(squared_diff(x1, x2, x3, y))
4765.599999999989
Assuming this. Calculate y as (N,3) using pandas.DataFrame.apply
>>> y = np.array( df.apply(lambda row: [1 - (row.ExpG_Home + row.ExpG_Away),
... row.ExpG_Home, row.ExpG_Away ],
... axis=1).tolist() )
>>> y.shape
(5, 3)
Now calcualte squared error for a given x
>>> def squared_diff(x, y):
... return np.sum( np.square(x - y), axis=1)
In your case, if error2 is squared_diff(x2,y) you are adding this the number of occuerences of HomeG_Time
>>> n3 = df.AwayG_Time.apply(len)
>>> n2 = df.HomeG_Time.apply(len)
>>> n1 = 200 - (n2 + n3)
The final sum of squared error is (as per your calculation)
>>> squared_diff(x1, y) * n1 + squared_diff(x2, y) * n2 + squared_diff(x3, y) * n3
0 4766.4
1 2349.4
2 2354.4
3 6411.6
4 4496.2
dtype: float64
>>>
try this,
import pandas as pd
import numpy as np
df = pd.DataFrame({'Id': [102,103,104,303,305],'ExpG_Home':[1.8,1.5,1.6,1.8,2.9],
'ExpG_Away':[2.2,1.3,1.2,2.8,0.8],
'HomeG_Time':[[93, 109, 187],[169], [31, 159],[176],[16, 48, 66, 128]],
'AwayG_Time':[[90, 177],[],[],[123,136],[40]]})
x1 = [1,0,0]
x2 = [0,1,0]
x3 = [0,0,1]
k=1
total_timeslot = 200 # number of timeslot per game.
def squared_diff(x1, x2, x3,AwayG_Time,HomeG_Time, y):
ssd = []
for k in range(total_timeslot):
if k in HomeG_Time:
ssd.append(sum((x2 - y) ** 2))
elif k in AwayG_Time:
ssd.append(sum((x3 - y) ** 2))
else:
ssd.append(sum((x1 - y) ** 2))
return ssd
s=pd.DataFrame( pd.concat([df,1-(df['ExpG_Home']+df['ExpG_Away'])/k,df['ExpG_Home']/k,df['ExpG_Away']/k],axis=1).values)
df['res']=s.apply(lambda x: sum(squared_diff(x1,x2,x3,x[0],x[3],np.array([x[5],x[6],x[7]]))),axis=1)
del s
print df
Output:
AwayG_Time ExpG_Away ExpG_Home HomeG_Time Id res
0 [90, 177] 2.2 1.8 [93, 109, 187] 102 4766.4
1 [] 1.3 1.5 [169] 103 2349.4
2 [] 1.2 1.6 [31, 159] 104 2354.4
3 [123, 136] 2.8 1.8 [176] 303 6411.6
4 [40] 0.8 2.9 [16, 48, 66, 128] 305 4496.2
def squared_diff(row):
y = np.array([1 - (row.ExpG_Home + row.ExpG_Away), row.ExpG_Home, row.ExpG_Away])
HomeG_Time = row.HomeG_Time
AwayG_Time = row.AwayG_Time
x1 = np.array([1, 0, 0])
x2 = np.array([0, 1, 0])
x3 = np.array([0, 0, 1])
total_timeslot = 200
ssd = []
for k in range(total_timeslot):
if k in HomeG_Time:
ssd.append(sum((x2 - y) ** 2))
elif k in AwayG_Time:
ssd.append(sum((x3 - y) ** 2))
else:
ssd.append(sum((x1 - y) ** 2))
return sum(ssd)
df.apply(squared_diff, axis=1)
Out[]:
0 4766.4
1 2349.4
2 2354.4
3 6411.6
4 4496.2

iterating through multiple equations [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I have a problem simplified into the following:
Xn+1 = Xn + Yn
Yn+1= Yn + Zn
Zn+1= Zn+ Xn
I know the values of X0,Y0,Z0 to be equal to 1.
I want to tell python to find the values of X1,Y1,Z1 and then X2,Y2,Z2,...etc. Can anyone help me with that? I think I have to use a nested loop but I am not sure exactly how to go about it. Thanks!
Are we talking about something as simple as:
x, y, z = 1, 1, 1
for i in range(10):
print("X{i} = {x}, Y{i} = {y}, Z{i} = {z}".format(**locals()))
x, y, z = x + y, y + z, z + x
which doesn't seem right as the output is so uninteresting:
X0 = 1, Y0 = 1, Z0 = 1
X1 = 2, Y1 = 2, Z1 = 2
X2 = 4, Y2 = 4, Z2 = 4
X3 = 8, Y3 = 8, Z3 = 8
X4 = 16, Y4 = 16, Z4 = 16
X5 = 32, Y5 = 32, Z5 = 32
X6 = 64, Y6 = 64, Z6 = 64
X7 = 128, Y7 = 128, Z7 = 128
X8 = 256, Y8 = 256, Z8 = 256
X9 = 512, Y9 = 512, Z9 = 512
Below is the sample function to achieve it:
def solve_equation(n):
X = {0: 1}
Y = {0: 1}
Z = {0: 1}
for i in range(n):
print 'For n: ', i+1
X[i+1] = X[i] + Y[i]
Y[i+1] = Y[i] + Z[i]
Z[i+1] = Z[i] + X[i]
print 'X = ', X[i+1], ' Y = ', Y[i+1], ' Z = ', Z[i+1]
Sample run:
>>> solve_equation(3)
For n: 1
X = 2 Y = 2 Z = 2
For n: 2
X = 4 Y = 4 Z = 4
For n: 3
X = 8 Y = 8 Z = 8
You can use recursive functions. For a faster and better code you can consider using yield
def Cvalue(c,xyz , n):
if n == 0 :
res = c
else :
if xyz == 1: res = Cvalue(c,1, n-1) + Cvalue(c,2, n-1)
elif xyz == 2: res = Cvalue(c,2, n-1) + Cvalue(c,3, n-1)
elif xyz == 3: res = Cvalue(c,3, n-1) + Cvalue(c,1, n-1)
else: print("Error\n")
return res
def XYZvalues(x0, y0, z0, n):
x = Cvalue(x0,1, n)
y = Cvalue(y0,2, n)
z = Cvalue(z0,3, n)
return (x, y , z)
print(XYZvalues(1,1,1, 3))

Scipy optimize.minimize function

I try to solve nonlinear programming task using scipy.optimize.minimize
max r
x1**2 + y1**2 <= (1-r)**2
(x1-x2)**2 + (y1-y2)**2 >= 4*r**2
0 <= r <= 1
So I've wrote next code:
r = np.linspace(0, 1, 100)
x1 = np.linspace(0, 1, 100)
y1 = np.linspace(0, 1, 100)
x2 = np.linspace(0, 1, 100)
y2 = np.linspace(0, 1, 100)
fun = lambda r: -r
cons = ({'type': 'ineq',
'fun': lambda x1, r: [x1[0] ** 2 + x1[1] ** 2 - (1 - r) ** 2],
'args': (r,)},
{'type': 'ineq',
'fun': lambda x2, r: [x2[0] ** 2 + x2[1] ** 2 - (1 - r) ** 2],
'args': (r,)},
{'type': 'ineq',
'fun': lambda x1, x2, r: [(x1[0] - x2[0]) ** 2 + (x1[1] - x2[1]) ** 2 - 4 * r ** 2],
'args': (x2, r,)})
bnds = ((0, 1), (-1, 1), (-1, 1), (-1, 1), (-1, 1))
x0 = [0, 0, 0, 0, 0]
minimize(fun, x0, bounds=bnds, constraints=cons)
But I've got next error
File "C:\Anaconda2\lib\site-packages\scipy\optimize\slsqp.py", line 377, in _minimize_slsqp
c = concatenate((c_eq, c_ieq))
ValueError: all the input arrays must have same number of dimensions
Please, help me to find out my mistakes and write correct code
UPD:
Thx to #unutbu i've understand how to build it correctly.
fun = lambda x: -x[0]
cons = ({'type': 'ineq',
'fun': lambda x: -x[1] ** 2 - x[2] ** 2 + (1 - x[0]) ** 2},
{'type': 'ineq',
'fun': lambda x: -x[3] ** 2 - x[4] ** 2 + (1 - x[0]) ** 2},
{'type': 'ineq',
'fun': lambda x: (x[1] - x[3]) ** 2 + (x[1] - x[4]) ** 2 - 4 * x[0] ** 2})
bnds = ((0, 1), (-1, 1), (-1, 1), (-1, 1), (-1, 1))
x0 = [0.5, 0.3, 0.5, 0.3, 0.5]
answer = minimize(fun, x0, bounds=bnds, constraints=cons)
In task of minimization we have to lead constraints to such form:
g(x) >= 0
that's why constraints look like in that way.
Your parameter space appears to be 5-dimensional. A point in your parameter
space would be z = (r, x1, y1, x2, y2). Therefore the function to be minimized
-- and also the constraint functions -- should accept a point z and
return a scalar value.
Thus instead of
fun = lambda r: -r
use
def func(z):
r, x1, y1, x2, y2 = z
return -r
and instead of
lambda x1, r: [x1[0] ** 2 + x1[1] ** 2 - (1 - r) ** 2]
use
def con1(z):
r, x1, y1, x2, y2 = z
return x1**2 + y1**2 - (1-r)**2
and so on.
Note that simple constraints such as 0 <= r <= 1 can be handled by setting the bounds parameter instead of defining a constraint. And if the bounds for x1, y1, x2, y2 are from -1 to 1, then you might also want change
x1 = np.linspace(0, 1, 100)
...
to
x1 = np.linspace(-1, 1, 100)
...
However, the arrays r, x1, y1, x2, y2 are not needed to minimize func, so you could just as well eliminate them from the script entirely.
import numpy as np
import scipy.optimize as optimize
"""
max r
x1**2 + y1**2 <= (1-r)**2
(x1-x2)**2 + (y1-y2)**2 >= 4*r**2
0 <= r <= 1
"""
def func(z):
r, x1, y1, x2, y2 = z
return -r
def con1(z):
r, x1, y1, x2, y2 = z
return x1**2 + y1**2 - (1-r)**2
def con2(z):
r, x1, y1, x2, y2 = z
return 4*r**2 - (x1-x2)**2 - (y1-y2)**2
cons = ({'type': 'ineq', 'fun': con1}, {'type': 'ineq', 'fun': con2},)
bnds = ((0, 1), (-1, 1), (-1, 1), (-1, 1), (-1, 1))
guess = [0, 0, 0, 0, 0]
result = optimize.minimize(func, guess, bounds=bnds, constraints=cons)
print(result)
yields
fun: -1.0
jac: array([-1., 0., 0., 0., 0., 0.])
message: 'Optimization terminated successfully.'
nfev: 14
nit: 2
njev: 2
status: 0
success: True
x: array([ 1., 0., 0., 0., 0.])

Pythonic way to select elements of an array based on values?

I'm looking for a pythonic (1-line) way to extract a range of values from an array
Here's some sample code that will extract the array elements that are >2 and <8 from x,y data, and put them into a new array. Is there a way to accomplish this on a single line? The code below works but seems kludgier than it needs to be. (Note I'm actually working with floats in my application)
import numpy as np
x0 = np.array([0,3,9,8,3,4,5])
y0 = np.array([2,3,5,7,8,1,0])
x1 = x0[x0>2]
y1 = y0[x0>2]
x2 = x1[x1<8]
y2 = y1[x1<8]
print x2, y2
This prints
[3 3 4 5] [3 8 1 0]
Part (b) of the problem would be to extract values say 1 < x < 3 and 7 < x < 9 as well as their corresponding y values.
You can chain together boolean arrays using & for element-wise logical and and | for element-wise logical or, so that the condition 2 < x0 and x0 < 8 becomes
mask = (2 < x0) & (x0 < 8)
For example,
import numpy as np
x0 = np.array([0,3,9,8,3,4,5])
y0 = np.array([2,3,5,7,8,1,0])
mask = (2 < x0) & (x0 < 8)
x2 = x0[mask]
y2 = y0[mask]
print(x2, y2)
# (array([3, 3, 4, 5]), array([3, 8, 1, 0]))
mask2 = ((1 < x0) & (x0 < 3)) | ((7 < x0) & (x0 < 9))
x3 = x0[mask2]
y3 = y0[mask2]
print(x3, y3)
# (array([8]), array([7]))
import numpy as np
x0 = np.array([0,3,9,8,3,4,5])
y0 = np.array([2,3,5,7,8,1,0])
list( zip( *[(x,y) for x, y in zip(x0, y0) if 1<=x<=3 or 7<=x<=9] ) )
# [(3, 9, 8, 3), (3, 5, 7, 8)]

Categories

Resources