I have two 3-d vectors originating from the origin with
v1 = array([ 0.20297736, -0.19208957, -0.63320655])
v2 = array([-0.63721771, 0.17457218, 0.12666251])
These two vectors are orthogonal to the vector axis_vector = array([ 0.21708059, 0.95127211, -0.21899175])
I am trying to determine the angle between v1 and v2 with the condition that I always start from v1. This means that V1 will be the point at which 0-degrees exists. Moving counterclockwise from v1, I want to determine the angle between v1 and v2.
Currently, I have been using the following:
angle=np.arccos(np.dot(vec2,vec1)/(np.linalg.norm(vec1)*np.linalg.norm(vec2))) *180/np.pi
but this particular line of code does not let me dictate which vector takes priority as the starting vector. As a result, it always returns the same angle without regard for which vector I wish to start from.
Any help would be appreciated!
The trick seemed to be to understand that the orthogonal axis vector is also representative of a plane. Once that is understood, you can solve this problem as below:
import numpy as np
import math
v2 = np.array([0.20297736, -0.19208957, -0.63320655])
v1 = np.array([-0.63721771, 0.17457218, 0.12666251])
axis_vector = np.array([ 0.21708059, 0.95127211, -0.21899175])
def find_angle(v1, v2, vn):
x1 = v1[0]
y1 = v1[1]
z1 = v1[2]
x2 = v2[0]
y2 = v2[1]
z2 = v2[2]
xn = vn[0]
yn = vn[1]
zn = vn[2]
dot = x1 * x2 + y1 * y2 + z1 * z2
det = x1 * y2 * zn + x2 * yn * z1 + xn * y1 * z2 - z1 * y2 * xn - z2 * yn * x1 - zn * y1 * x2
angle = math.atan2(det, dot)*180/np.pi
return angle
angle = find_angle(v1, v2, axis_vector)
This answer was based off of: Direct way of computing clockwise angle between 2 vectors
Edit: For sake of completeness, If you have to do this computation multiple times with multiple vectors and planes.
def find_angle(v1, v2, vn):
if v1.shape[0] == 1:
x1 = v1[0]
y1 = v1[1]
z1 = v1[2]
x2 = v2[0]
y2 = v2[1]
z2 = v2[2]
xn = vn[0]
yn = vn[1]
zn = vn[2]
dot = x1 * x2 + y1 * y2 + z1 * z2
det = x1 * y2 * zn + x2 * yn * z1 + xn * y1 * z2 - z1 * y2 * xn - z2 * yn * x1 - zn * y1 * x2
angle = m.atan2(det, dot) * 180 / np.pi
angle = np.array([angle])
else:
elementWiseConcat = np.asarray(list((zip(v1, v2, vn))))
dot = np.einsum('ij, ij->i', v1, v2)
det = np.linalg.det(elementWiseConcat)
angle = np.arctan2(det, dot) * 180 / np.pi
return angle
I am trying to solve a system of ODE's related to a coupled mass and spring system using solve_ivp. I am getting a problem stating that "ValueError: setting an array element with a sequence". See below for the code I am trying to use.
from scipy.integrate import solve_ivp
import numpy as np
func(t, w, p):
#unpack the variables and parameters"
x1, y1, x2, y2 = w
m1, m2, k1, k2, L1, L2, b1, b2 = p
# Create the functions
y1 = (-b1 * y1 - k1 * (x1 - L1) + k2 * (x2 - x1 - L2)) / m1,
y2 = (-b2 * y2 - k2 * (x2 - x1 - L2)) / m2
return y1, y2
# Parameter values
# Masses:
m1 = 1.0
m2 = 1.5
# Spring constants
k1 = 8.0
k2 = 40.0
# Natural lengths
L1 = 0.5
L2 = 1.0
# Friction coefficients
b1 = 0.8
b2 = 0.5
# Initial conditions
# x1 and x2 are the initial displacements; y1 and y2 are the initial velocities
x1 = 0.5
y1 = 0.0
x2 = 2.25
y2 = 0.0
# Pack up the parameters and initial conditions:
p = [m1, m2, k1, k2, L1, L2, b1, b2]
w0 = [x1, y1, x2, y2]
t_span = np.linspace(0, 15, 1000)
Sol = solve_ivp(lambda t, w: func(t, w, p), [t_span[0], t_span[-1]], y0=w0, t_eval=t_span)
I am not really sure where I am going wrong because the error doesn't specify which line of code the problem is with. Has anyone experienced this before?
If x is the position and y the velocity, then name z the acceleration and return a vector of the derivatives of the inputs
def func(t, w, p):
#unpack the variables and parameters"
x1, y1, x2, y2 = w
m1, m2, k1, k2, L1, L2, b1, b2 = p
# Create the functions
z1 = (-b1 * y1 - k1 * (x1 - L1) + k2 * (x2 - x1 - L2)) / m1,
z2 = (-b2 * y2 - k2 * (x2 - x1 - L2)) / m2
return y1, z1, y2, z2
Everything else looks like it should work.
I am trying to create the equation in python.
Sorry in advance if this has already been asked! If so, I couldn't find it, so please share the post!
I run into the problem that I don't know how to code the part in the red square (see equation ).
As I understand it the "|u1|" stands for the absolute value of u1. However, if I code it like the equation is written i.e. abs(u1)abs(u2) I get a syntax error (which I kind of expected).
My problem is the line of code:
angle = np.arccos((Mu1*Mu2)/(abs(Mu1)abs(Mu2)))
My complete code is:
import numpy as np
from math import sqrt
#first direction vector
#punt 1, PQ
# [x,y]
P = (1,1)
Q = (5,3)
#punt 2, RS
R = (2,3)
S = (4,1)
#direction vector = arctan(yq-yp/xq-xp)
#create function to calc direction vector of line
def dirvec(coord1, coord2):
#pull coordinates into x and y variables
x1 , y1 = coord1[0], coord1[1]
x2 , y2 = coord2[0], coord2[1]
#calc vector see article
v = np.arctan((y2-y1)/(x2-x1))
#outputs in radians, not degrees
v = np.degrees(v)
return v
print(dirvec(P,Q))
print(dirvec(R,S))
Mu1 = dirvec(P,Q)
Mu2 = dirvec(R,S)
angle = np.arccos((Mu1*Mu2)/(abs(Mu1)abs(Mu2)))
print(angle)
Thins I tried:
multiply the two abs, but then I'll get the same number (pi) every time:
np.arccos((Mu1*Mu2)/(abs(Mu1)*abs(Mu2)))
+ and - but I cannot imagine these are correct:
np.arccos((Mu1Mu2)/(abs(Mu1)+abs(Mu2))) np.arccos((Mu1Mu2)/(abs(Mu1)-abs(Mu2)))
In the formula, the numerator is the dot product of two vectors, and the denominator is the product of the norms of the two vectors.
Here is a simple way to write your formula:
import math
def dot_product(u, v):
(x1, y1) = u
(x2, y2) = v
return x1 * x2 + y1 * y2
def norm(u):
(x, y) = u
return math.sqrt(x * x + y * y)
def get_angle(u, v):
return math.acos( dot_product(u,v) / (norm(u) * norm(v)) )
def make_vector(p, q):
(x1, y1) = p
(x2, y2) = q
return (x2 - x1, y2 - y1)
#first direction vector
#punt 1, PQ
# [x,y]
P = (1,1)
Q = (5,3)
#punt 2, RS
R = (2,3)
S = (4,1)
angle = get_angle(make_vector(p,q), make_vector(r,s))
print(angle)
From what I see, the result of your code would always be pi or 0. It will be pi if one of the mu1 or mu2 is negative and when both are negative or positive it will be zero.
If I remember vectors properly :
Given two vectors P and Q, with say P = (x, y) and Q = (a, b)
Then abs(P) = sqrt(x^2 + y^2) and P. Q = xa+yb. So that cos# = P. Q/(abs(P) *abs(Q)). If am not clear you can give an example of what you intend to do
Okay so apparently I made a mistake in my interpretation.
I want to thank everyone for your solutions!
After some puzzling it appears that:
import math
import numpy as np
#punt 1, PQ
# [x,y]
P = (1,1)
Q = (5,3)
x1 = P[0]
x2 = Q[0]
y1 = P[1]
y2 = Q[1]
#punt 2, RS
R = (0,2)
S = (4,1)
x3 = R[0]
x4 = S[0]
y3 = R[1]
y4 = S[1]
angle = np.arccos(((x2 - x1) * (x4 - x3) + (y2 - y1) * (y4 - y3)) / (math.sqrt((x2 - x1)**2 + (y2 - y1)**2) * math.sqrt((x4 - x3)**2 + (y4 - y3)**2)))
print(angle)
Is the correct way to calculate the angle between two vectors.
This is obviously not pretty code, but it is the essence of how it works!
Again I want to thank you all for you reaction and solutions!
I am implementing a bilinear interpolation as in How to perform bilinear interpolation in Python
I have a sorted list of points that are the vertexes of my regular grid.
[[x1,y1,z1],[x2,y2,z2],[x3,y3,z3],[x4,y4,z4],[x5,y5,z5],...]
I want to interpolate linearly on the point (x,y). I have written the following code
def f(x, y, points):
for i in range(len(points)-1, -1, -1):
if (x>points[i][0])and(y>points[i][1]):
break
try:
pp = [points[i], points[i+1]]
except IndexError:
pp = [points[i], points[i-1]]
for j in range(len(points)):
if (x<points[j][0])and(y<points[j][1]):
break
pp.append(points[j-1])
pp.append(points[j])
(x1, y1, q11), (_x1, y2, q12), (x2, _y1, q21), (_x2, _y2, q22) = pp
return (q11 * (x2 - x) * (y2 - y) +
q21 * (x - x1) * (y2 - y) +
q12 * (x2 - x) * (y - y1) +
q22 * (x - x1) * (y - y1)) / ((x2 - x1) * (y2 - y1))
but this code doesn't work on the boundaries. I would think this is common problem in interpolation, so I was wondering how I should select the smallest rectangle of points around (x,y) from my regular grid.
Your grid is regular, so you don't need to traverse all points to determine cell indexes. Just divide coordinates by cell size and round result to smaller integer. 1D example: if first point has coordinate 1 and cell size is 2, point 6 lies at int (6-1)/2 = 2-nd interval
Restrict result index to ensure that it is in grid limits - so points outside grid will use border cells
i = int((x - points[i][0]) / xsize) #not sure what is the best way in Python
if (i < 0):
i = 0
if (i >= XCount):
i = XCount - 1
// same for j and y-coordinate
Following the suggestions in the comments I have written the following code:
def f(x, y, points):
points = sorted(points)
xunique = np.unique([point[0] for point in points])
yunique = np.unique([point[1] for point in points])
xmax = np.max(xunique)
ymax = np.max(yunique)
deltax = xunique[1] - xunique[0]
deltay = yunique[1] - yunique[0]
x0 = xunique[0]
y0 = yunique[0]
ni = len(xunique)
nj = len(yunique)
i1 = int(np.floor((x-x0)/deltax))
if i1 == ni:
i1 = i1 - 1
i2 = int(np.ceil((x-x0)/deltax))
if i2 == ni:
i2 = i2 - 1
j1 = int(np.floor((y-y0)/deltay))
if j1 == nj:
j1 = j1 - 1
j2 = int(np.ceil((y-y0)/deltay))
if j2 == ni:
j2 = j2 - 1
pp=[]
if (i1==i2):
if i1>0:
i1=i1-1
else:
i2=i2+1
if (j1==j2):
if j1>0:
j1=j1-1
else:
j2=j2+1
pp=[points[i1 * nj + j1], points[i1 * nj + j2],
points[i2 * nj + j1], points[i2 * nj + j2]]
(x1, y1, q11), (_x1, y2, q12), (x2, _y1, q21), (_x2, _y2, q22) = pp
return (q11 * (x2 - x) * (y2 - y) +
q21 * (x - x1) * (y2 - y) +
q12 * (x2 - x) * (y - y1) +
q22 * (x - x1) * (y - y1)) / ((x2 - x1) * (y2 - y1))
I would like to perform blinear interpolation using python.
Example gps point for which I want to interpolate height is:
B = 54.4786674627
L = 17.0470721369
using four adjacent points with known coordinates and height values:
n = [(54.5, 17.041667, 31.993), (54.5, 17.083333, 31.911), (54.458333, 17.041667, 31.945), (54.458333, 17.083333, 31.866)]
z01 z11
z
z00 z10
and here's my primitive attempt:
import math
z00 = n[0][2]
z01 = n[1][2]
z10 = n[2][2]
z11 = n[3][2]
c = 0.016667 #grid spacing
x0 = 56 #latitude of origin of grid
y0 = 13 #longitude of origin of grid
i = math.floor((L-y0)/c)
j = math.floor((B-x0)/c)
t = (B - x0)/c - j
z0 = (1-t)*z00 + t*z10
z1 = (1-t)*z01 + t*z11
s = (L-y0)/c - i
z = (1-s)*z0 + s*z1
where z0 and z1
z01 z0 z11
z
z00 z1 z10
I get 31.964 but from other software I get 31.961.
Is my script correct?
Can You provide another approach?
2022 Edit:
I would like to thank everyone who, even more than a decade after publication of this question, gives new answers to it.
Here's a reusable function you can use. It includes doctests and data validation:
def bilinear_interpolation(x, y, points):
'''Interpolate (x,y) from values associated with four points.
The four points are a list of four triplets: (x, y, value).
The four points can be in any order. They should form a rectangle.
>>> bilinear_interpolation(12, 5.5,
... [(10, 4, 100),
... (20, 4, 200),
... (10, 6, 150),
... (20, 6, 300)])
165.0
'''
# See formula at: http://en.wikipedia.org/wiki/Bilinear_interpolation
points = sorted(points) # order points by x, then by y
(x1, y1, q11), (_x1, y2, q12), (x2, _y1, q21), (_x2, _y2, q22) = points
if x1 != _x1 or x2 != _x2 or y1 != _y1 or y2 != _y2:
raise ValueError('points do not form a rectangle')
if not x1 <= x <= x2 or not y1 <= y <= y2:
raise ValueError('(x, y) not within the rectangle')
return (q11 * (x2 - x) * (y2 - y) +
q21 * (x - x1) * (y2 - y) +
q12 * (x2 - x) * (y - y1) +
q22 * (x - x1) * (y - y1)
) / ((x2 - x1) * (y2 - y1) + 0.0)
You can run test code by adding:
if __name__ == '__main__':
import doctest
doctest.testmod()
Running the interpolation on your dataset produces:
>>> n = [(54.5, 17.041667, 31.993),
(54.5, 17.083333, 31.911),
(54.458333, 17.041667, 31.945),
(54.458333, 17.083333, 31.866),
]
>>> bilinear_interpolation(54.4786674627, 17.0470721369, n)
31.95798688313631
Not sure if this helps much, but I get a different value when doing linear interpolation using scipy:
>>> import numpy as np
>>> from scipy.interpolate import griddata
>>> n = np.array([(54.5, 17.041667, 31.993),
(54.5, 17.083333, 31.911),
(54.458333, 17.041667, 31.945),
(54.458333, 17.083333, 31.866)])
>>> griddata(n[:,0:2], n[:,2], [(54.4786674627, 17.0470721369)], method='linear')
array([ 31.95817681])
Inspired from here, I came up with the following snippet. The API is optimized for reusing a lot of times the same table:
from bisect import bisect_left
class BilinearInterpolation(object):
""" Bilinear interpolation. """
def __init__(self, x_index, y_index, values):
self.x_index = x_index
self.y_index = y_index
self.values = values
def __call__(self, x, y):
# local lookups
x_index, y_index, values = self.x_index, self.y_index, self.values
i = bisect_left(x_index, x) - 1
j = bisect_left(y_index, y) - 1
x1, x2 = x_index[i:i + 2]
y1, y2 = y_index[j:j + 2]
z11, z12 = values[j][i:i + 2]
z21, z22 = values[j + 1][i:i + 2]
return (z11 * (x2 - x) * (y2 - y) +
z21 * (x - x1) * (y2 - y) +
z12 * (x2 - x) * (y - y1) +
z22 * (x - x1) * (y - y1)) / ((x2 - x1) * (y2 - y1))
You can use it like this:
table = BilinearInterpolation(
x_index=(54.458333, 54.5),
y_index=(17.041667, 17.083333),
values=((31.945, 31.866), (31.993, 31.911))
)
print(table(54.4786674627, 17.0470721369))
# 31.957986883136307
This version has no error checking and you will run into trouble if you try to use it at the boundaries of the indexes (or beyond). For the full version of the code, including error checking and optional extrapolation, look here.
You can also refer to the interp function in matplotlib.
A numpy implementation of based on this formula:
def bilinear_interpolation(x,y,x_,y_,val):
a = 1 /((x_[1] - x_[0]) * (y_[1] - y_[0]))
xx = np.array([[x_[1]-x],[x-x_[0]]],dtype='float32')
f = np.array(val).reshape(2,2)
yy = np.array([[y_[1]-y],[y-y_[0]]],dtype='float32')
b = np.matmul(f,yy)
return a * np.matmul(xx.T, b)
Input:
Here,x_ is list of [x0,x1] and y_ is list of [y0,y1]
bilinear_interpolation(x=54.4786674627,
y=17.0470721369,
x_=[54.458333,54.5],
y_=[17.041667,17.083333],
val=[31.993,31.911,31.945,31.866])
Output:
array([[31.95912739]])
I think the point of doing a floor function is that usually you're looking to interpolate a value whose coordinate lies between two discrete coordinates. However you seem to have the actual real coordinate values of the closest points already, which makes it simple math.
z00 = n[0][2]
z01 = n[1][2]
z10 = n[2][2]
z11 = n[3][2]
# Let's assume L is your x-coordinate and B is the Y-coordinate
dx = n[2][0] - n[0][0] # The x-gap between your sample points
dy = n[1][1] - n[0][1] # The Y-gap between your sample points
dx1 = (L - n[0][0]) / dx # How close is your point to the left?
dx2 = 1 - dx1 # How close is your point to the right?
dy1 = (B - n[0][1]) / dy # How close is your point to the bottom?
dy2 = 1 - dy1 # How close is your point to the top?
left = (z00 * dy1) + (z01 * dy2) # First interpolate along the y-axis
right = (z10 * dy1) + (z11 * dy2)
z = (left * dx1) + (right * dx2) # Then along the x-axis
There might be a bit of erroneous logic in translating from your example, but the gist of it is you can weight each point based on how much closer it is to the interpolation goal point than its other neighbors.
This is the same solution as defined here but applied to some function and compared with interp2d available in Scipy. We use numba library to make the interpolation function even faster than Scipy implementation.
import numpy as np
from scipy.interpolate import interp2d
import matplotlib.pyplot as plt
from numba import jit, prange
#jit(nopython=True, fastmath=True, nogil=True, cache=True, parallel=True)
def bilinear_interpolation(x_in, y_in, f_in, x_out, y_out):
f_out = np.zeros((y_out.size, x_out.size))
for i in prange(f_out.shape[1]):
idx = np.searchsorted(x_in, x_out[i])
x1 = x_in[idx-1]
x2 = x_in[idx]
x = x_out[i]
for j in prange(f_out.shape[0]):
idy = np.searchsorted(y_in, y_out[j])
y1 = y_in[idy-1]
y2 = y_in[idy]
y = y_out[j]
f11 = f_in[idy-1, idx-1]
f21 = f_in[idy-1, idx]
f12 = f_in[idy, idx-1]
f22 = f_in[idy, idx]
f_out[j, i] = ((f11 * (x2 - x) * (y2 - y) +
f21 * (x - x1) * (y2 - y) +
f12 * (x2 - x) * (y - y1) +
f22 * (x - x1) * (y - y1)) /
((x2 - x1) * (y2 - y1)))
return f_out
We make it quite a large interpolation array to assess the performance of each method.
The sample function is,
x = np.linspace(0, 4, 13)
y = np.array([0, 2, 3, 3.5, 3.75, 3.875, 3.9375, 4])
X, Y = np.meshgrid(x, y)
Z = np.sin(np.pi*X/2) * np.exp(Y/2)
x2 = np.linspace(0, 4, 1000)
y2 = np.linspace(0, 4, 1000)
Z2 = bilinear_interpolation(x, y, Z, x2, y2)
fun = interp2d(x, y, Z, kind='linear')
Z3 = fun(x2, y2)
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(10, 6))
ax[0].pcolormesh(X, Y, Z, shading='auto')
ax[0].set_title("Original function")
X2, Y2 = np.meshgrid(x2, y2)
ax[1].pcolormesh(X2, Y2, Z2, shading='auto')
ax[1].set_title("bilinear interpolation")
ax[2].pcolormesh(X2, Y2, Z3, shading='auto')
ax[2].set_title("Scipy bilinear function")
plt.show()
Performance Test
Python without numba library
bilinear_interpolation function, in this case, is the same as numba version except that we change prange with python normal range in the for loop, and remove function decorator jit
%timeit bilinear_interpolation(x, y, Z, x2, y2)
Gives 7.15 s ± 107 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Python with numba numba
%timeit bilinear_interpolation(x, y, Z, x2, y2)
Gives 2.65 ms ± 70.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Scipy implementation
%%timeit
f = interp2d(x, y, Z, kind='linear')
Z2 = f(x2, y2)
Gives 6.63 ms ± 145 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Performance tests are performed on 'Intel(R) Core(TM) i7-8700K CPU # 3.70GHz'
I suggest the following solution:
def bilinear_interpolation(x, y, z01, z11, z00, z10):
def linear_interpolation(x, z0, z1):
return z0 * x + z1 * (1 - x)
return linear_interpolation(y, linear_interpolation(x, z01, z11),
linear_interpolation(x, z00, z10))