Check if all points in a set lie on the same line - python

I'm given a list with coordinates of n points, let's say:
points = [(1, 2), (2, 3), (3, 4)]
And I need to check if all of them lie on the same line. I also decided to consider 3 cases to avoid dividing by zero when x1 == x2.
So here's my code in Python:
# 2 points always lie on a line
if n <= 2:
print("yes")
else:
# leave only unique points
points = list(set(points))
x1, y1 = points[0]
x2, y2 = points[1]
# if two points have the same x coordinate
# then they lie on a vertical line and
# all other points have to have the same x coordinate too
if x2 == x1:
for i in range(2, len(points)):
if points[i][0] != x1:
print("no")
break
else: print("yes")
# same with a horizontal line
elif y2 == y1:
for i in range(2, len(points)):
if points[i][1] != y1:
print("no")
break
else: print("yes")
else:
a = (y2-y1)/(x2-x1)
b = y2 - a * x2
for i in range(2, len(points)):
x, y = points[i]
if (y != a * x + b):
print("no")
break
else: print("yes")
It seems that I have a mistake somewhere in the code but I don't really understand what it is.

Using cross product of vectors eliminates the complexity of having to deal with special cases where division by zero might happen. Three points are collinear if the cross product of the vectors formed by the two vectors defined by the 3 points is equal to zero:
import math
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y)
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def cross(self, other):
return self.x * other.y - self.y * other.x
def are_collinear(three_points):
a, b, c = three_points
# better use math.isclose than == to check for floats
return math.isclose((b-a).cross(c-a), 0.0)
points = [Point(1, 2), Point(2, 3), Point(3, 4)]
print(are_collinear(points))
# True
points = [Point(1, 2), Point(3, 3), Point(3, 4)]
print(are_collinear(points))
# False

From any point in the list (e.g. first one) if all other points have the same slope with that one, then they are on the same line.
def sameLine(points):
x0,y0 = points[0]
points = [ (x,y) for x,y in points if x != x0 or y != y0 ] # Other points
slopes = [ (y-y0)/(x-x0) if x!=x0 else None for x,y in points ] # None for vertical Line
return all( s == slopes[0] for s in slopes)

Related

Jaccard Index between timestamps in Python

I have UNIX timestamps being converted to strings as well as given time string inputs that I need to get Jaccard index from. Following are stored in 2D arrays as time intervals.
unix_converted = [['00:00:00', '00:00:03'], ['00:00:03', '00:00:06'], ['00:00:12', '00:00:15']]
input_timestamps = [['00:00:00', '00:00:03'], ['00:00:03', '00:00:06'], ['00:00:06', '00:00:09']]
def jaccard_index(s1, s2):
raise NotImplementedError
Do I have to convert these intervals to datetime objects or there is a way to be a straightforward? And how to get index itself?
You could exploit Python's native support for sets to calculate your Jaccard Index.
unix_converted = [['00:00:00', '00:00:03'], ['00:00:03', '00:00:06'], ['00:00:12', '00:00:15']]
input_timestamps = [['00:00:00', '00:00:03'], ['00:00:03', '00:00:06'], ['00:00:06', '00:00:09']]
def jaccard_index(s1, s2):
s1 = set({'-'.join(each) for each in s1})
s2 = set({'-'.join(each) for each in s2})
return len(s1.intersection(s2))/len(s1.union(s2))
print(jaccard_index(unix_converted, input_timestamps)) #outputs 0.5
Edit: I'm assuming by Jaccard Index you meant Jaccard similarity i.e. intersection over union of the given lists.
This code calculates Jaccard similarity in situations where timestamps are not necessarily calculated in the same invervals. O(len(s1)^2 + len(s2)^2) time complexity.
unix_converted = [(1, 3), (6, 10), (11, 12)]
input_timestamps = [(1, 3), (4, 7)]
def jaccard_index(s1, s2):
def _set_sum(start1, end1, start2, end2):
""" returns sum if there is an overlap and None otherwise """
if start2 <= start1 <= end2:
return start2, max(end1, end2)
if start1 <= start2 <= end1:
return start1, max(end1, end2)
return None # separate sets
def _set_intersection(start1, end1, start2, end2):
""" returns intersection if there is an overlap and None otherwise """
if start2 <= start1 <= end2:
return start1, min(end1, end2)
if start1 <= start2 <= end1:
return start2, min(end1, end2)
return None # separate sets
# Calculate A u B
sum = []
for x, y in s1 + s2:
matched_elem = False
for i, (x2, y2) in enumerate(sum):
set_sum = _set_sum(x, y, x2, y2)
if set_sum is not None:
sum[i] = set_sum
matched_elem = True
break
if not matched_elem:
sum.append((x, y))
# join overlapping timestamps
element_is_joined = [False for _ in sum]
for i, (x, y) in enumerate(sum):
if not element_is_joined[i]:
for j, (x2, y2) in enumerate(sum):
if element_is_joined[j] or i == j:
continue
set_sum = _set_sum(x, y, x2, y2)
if set_sum is not None: # overlap is found
sum[j] = set_sum
element_is_joined[i] = True
break
sum_ = 0
for (x, y), is_joined in zip(sum, element_is_joined):
if not is_joined:
sum_ += y - x
if sum_ == 0:
raise ValueError('Division by zero')
# calculate A ^ B
intersection = 0
for x, y in s1:
for x2, y2 in s2:
set_intersection = _set_intersection(x, y, x2, y2)
if set_intersection is not None:
intersection += set_intersection[1] - set_intersection[0]
return intersection / sum_
print(jaccard_index(unix_converted, input_timestamps)) #outputs 0.333333

Draw perpendicular line of fixed length at a point of another line

I have two points A (10,20) and B (15,30). The points generate a line AB. I need to draw a perpendicular line, CD, on point B with a length of 6 (each direction 3 units) in Python.
I already have some properties of line AB using the following code:
from scipy import stats
x = [10,15]
y = [20,30]
slope, intercept, r_value, p_value, std_err = stats.linregress(x,y)
How can I calculate the location of C and D. I need their X and Y value.
The value of C and D will be used for accomplishing another objective using the Shapely library.
Since you are interested in using Shapely, the easiest way to get the perpendicular line that I can think of, is to use parallel_offset method to get two parallel lines to AB, and connect their endpoints:
from shapely.geometry import LineString
a = (10, 20)
b = (15, 30)
cd_length = 6
ab = LineString([a, b])
left = ab.parallel_offset(cd_length / 2, 'left')
right = ab.parallel_offset(cd_length / 2, 'right')
c = left.boundary[1]
d = right.boundary[0] # note the different orientation for right offset
cd = LineString([c, d])
And the coordinates of CD:
>>> c.x, c.y
(12.316718427000252, 31.341640786499873)
>>> d.x, d.y
(17.683281572999746, 28.658359213500127)
If slope is the slope of AB, then the slope of CD is -1/slope. This is equal to vertical change over horizontal change: dy/dx = -1/slope. This gives that dx = -slope*dx. And by Pythagorean Theorem, you have 3**2 = dy**2+dx**2. Substitute for dx, and you get
3**2 = (-slope*dy)**2+dy**2
3**2 = (slope**2 + 1)*dy**2
dy**2 = 3**2/(slope**2+1)
dy = math.sqrt(3**2/(slope**2+1))
Then you can get dx = -slope*dy. Finally, you can use dx and dy to get C and D. So the code would be:
import math
dy = math.sqrt(3**2/(slope**2+1))
dx = -slope*dy
C[0] = B[0] + dx
C[1] = B[1] + dy
D[0] = B[0] - dx
D[1] = B[1] - dy
(Note that although math.sqrt returns only one number, in general there is a positive and negative square root. C corresponds to the positive square root, and D to the negative).
You probably should use vectors to calculate the position of the points.
create the vector AB
calculate its normalized perpendicular
add or subtract 3 times this to B
with the help of a simple, reusable Vector class, the calculation is trivial, and reads like English:
Find the points perpendicular to AB at distance 3 from point B:
P1 = B + (B-A).perp().normalized() * 3
P2 = B + (B-A).perp().normalized() * 3
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y)
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def dot(self, other):
return self.x * other.x + self.y * other.y
def norm(self):
return self.dot(self)**0.5
def normalized(self):
norm = self.norm()
return Vector(self.x / norm, self.y / norm)
def perp(self):
return Vector(1, -self.x / self.y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
def __str__(self):
return f'({self.x}, {self.y})'
A = Vector(10, 20)
B = Vector(15, 30)
AB = B - A
AB_perp_normed = AB.perp().normalized()
P1 = B + AB_perp_normed * 3
P2 = B - AB_perp_normed * 3
print(f'Point{P1}, and Point{P2}')
output:
Point(17.683281572999746, 28.658359213500127), and Point(12.316718427000252, 31.341640786499873)
If we have a line parallel to x-axis or y-axis, then we can add the following two methods to the class Vector.
def perptoy(self):
return Vector(1, 0)
def perptox(self):
return Vector(1, -10000) // any big number will do instead of -inf
AB = B - A
if (A.y == B.y):
AB_perp_normed = AB.perptox().normalized();
elif (A.x == B.x):
AB_perp_normed = AB.perptoy().normalized();
else:
AB_perp_normed = AB.perp().normalized();
And the rest follows the same as in the earlier post.

The faculty of two numbers python 3

def choose (x, y):
if y > x:
print ("False")
elif y == 0 or y == x:
return 1
elif y == 1:
return x
else:
if (x-y) > y:
biggest = x-y
smallest = y
else:
biggest = y
smallest = x-y
resultatet = x * choose (x-1, biggest)
res = resultatet // smallest
return res
My function is working perfectly with whatever x input I insert but with bigger Y inputs like 8000 for example I'm getting
File "/home/nazel607/labb3b_2.py", line 20, in choose
resultatet = x * choose (x-1, biggest)
File "/home/nazel607/labb3b_2.py", line 3, in choose
if y > x:
RuntimeError: maximum recursion depth exceeded in comparison
Is there a way I can overcome this problem or is it impossible in python due to its limits? Is there another way than increasing the limit?
It seems that you can get rid of the recursion:
def choose2(x, y):
if y > x:
raise ValueError()
if y == 0 or y == x:
return 1
if y == 1:
return x
result = 1
while y != x:
big, small = max(x-y, y), min(x-y, y)
result *= x // small
x -= 1
y = big
return result
I've tested it over few examples:
for x, y in [
(4, 2),
(17, 9),
(125, 79),
(8005, 13),
(9005, 13),
# (19005, 7004) # exceeds max recursion depth on my machine
]:
assert choose(x, y) == choose2(x, y)
and seems to work fine.
You are not exiting the program ...
def choose (x, y):
if y > x:
print ("False")
return
# ...rest of your program

Is there a better way to find points along a curve than Bresenham's line algorithm

I have a travel time map, I want to get the integer points along the shortest path from source to receiver.
My present solution is that I make a runge-kutta integration from the receiver location and get a series of float points. Then I sample every 5 or some number of points and assume it a straight line between in order to use the Bresenham's line algorithm. With this approach, I will get the integer points.
However, it's not enough fast. Because I need to calculate a lot of receivers' shortest path, the sum of time will be very large.
I used line_profiler to analysis the time-consuming, which shows the major part of time is for function ruge-kutta and its calling function get_velocity
codes are below
def optimal_path_2d(gradx_interp,
grady_interp,
starting_point,
dx,
N=100):
"""
Find the optimal path from starting_point to the zero contour
of travel_time. dx is the grid spacing
Solve the equation x_t = - grad t / | grad t |
"""
def get_velocity(position):
""" return normalized velocity at pos """
x, y = position
vel = np.array([gradx_interp(y, x)[0][0], grady_interp(y, x)[0][0]])
return vel / np.linalg.norm(vel)
def runge_kutta(pos, ds):
""" Fourth order Runge Kutta point update """
k1 = ds * get_velocity(pos)
k2 = ds * get_velocity(pos - k1 / 2.0)
k3 = ds * get_velocity(pos - k2 / 2.0)
k4 = ds * get_velocity(pos - k3)
return pos - (k1 + 2 * k2 + 2 * k3 + k4) / 6.0
x = runge_kutta(starting_point, dx)
xl, yl = [], []
for i in range(N):
xl.append(x[0])
yl.append(x[1])
x = runge_kutta(x, dx)
distance = ((x[0] - xl[-1])**2 +
(x[1] - yl[-1])**2)**0.5
if distance < dx*0.9:
break
return yl, xl
def get_curve(x_curve, y_curve, num_interval):
"""Curve Algorithm based on Bresenham's Line Algorithm
Produces a list of tuples
"""
num = len(x_curve)
if num < num_interval:
print("num_interval is too large.")
ret_set = set()
x0 = x_curve[0]
y0 = y_curve[0]
for i in range(num_interval, num, num_interval):
x1 = x_curve[i]
y1 = y_curve[i]
points_on_line = get_line((x0, y0), (x1, y1))
ret_set.update(points_on_line)
x0 = x1
y0 = y1
if num % num_interval != 0:
n = int(num/num_interval)*num_interval
x0 = x_curve[n]
y0 = y_curve[n]
x1 = x_curve[-1]
y1 = y_curve[-1]
points_on_line = get_line((x0, y0), (x1, y1))
ret_set.update(points_on_line)
return list(ret_set)
def get_line(start, end):
"""modifed version of Bresenham's Line Algorithm
Produces a list of tuples from start and end
>>> points1 = get_line((0, 0), (3, 4))
>>> points2 = get_line((3, 4), (0, 0))
>>> assert(set(points1) == set(points2))
>>> print points1
[(0, 0), (1, 1), (1, 2), (2, 3), (3, 4)]
>>> print points2
[(3, 4), (2, 3), (1, 2), (1, 1), (0, 0)]
"""
# Setup initial conditions
x1, y1 = (int(x) for x in start)
x2, y2 = (int(x) for x in end)
dx = x2 - x1
dy = y2 - y1
# Determine how steep the line is
is_steep = abs(dy) > abs(dx)
# Rotate line
if is_steep:
x1, y1 = y1, x1
x2, y2 = y2, x2
# Swap start and end points if necessary and store swap state
swapped = False
if x1 > x2:
x1, x2 = x2, x1
y1, y2 = y2, y1
swapped = True
# Recalculate differentials
dx = x2 - x1
dy = y2 - y1
# Calculate error
error = int(dx / 2.0)
ystep = 1 if y1 < y2 else -1
# Iterate over bounding box generating points between start and end
y = y1
points = []
for x in range(x1, x2 + 1):
coord = (y, x) if is_steep else (x, y)
points.append(coord)
error -= abs(dy)
if error < 0:
y += ystep
error += dx
# Reverse the list if the coordinates were swapped
if swapped:
points.reverse()
return points
nx = 100
ny = 100
num_interval = 5
loc_src = (10, 10)
loc_rec = (70, 90)
coordx = np.arange(nx)
coordy = np.arange(ny)
X, Y = np.meshgrid(coordx, coords)
travel_time = (X-loc_src[0])**2/5 + (Y-loc_src[1])**2/10 # for simplicity
grad_t_y, grad_t_x = np.gradient(travel_time, dx)
if isinstance(travel_time, np.ma.MaskedArray):
grad_t_y[grad_t_y.mask] = 0.0
grad_t_y = grad_t_y.data
grad_t_x[grad_t_x.mask] = 0.0
grad_t_x = grad_t_x.data
gradx_interp = RectBivariateSpline(coordy, coordx, grad_t_x)
grady_interp = RectBivariateSpline(coordy, coordx, grad_t_y)
yl, xl = optimal_path(gradx_interp, grady_interp, loc_rec, dx)
grid_indx = get_curve(xl, yl, num_interval)
I hear that Cython will be faster, then I learn a little recently and try it. the result is only 2 faster than codes above because I'm really new to Cython. The code below is incomplete, and I just wrote it for testing.
import numpy as np
from numpy.core.umath_tests import inner1d
def func(X_interp, Y_interp):
def get_velocity(double x, double y ):
""" return normalized velocity at pos """
cdef double vel[2], norm
a = X_interp(y, x)
vel[0] = a[0][0]
b = Y_interp(y, x)
vel[1] = b[0][0]
# norm = (vel[0]**2 + vel[1]**2)**0.5
# vel[0] = vel[0]/norm
# vel[1] = vel[1]/norm
return vel
def runge_kutta(double x, double y, double ds):
""" Fourth order Runge Kutta point update """
cdef double k1[2], k2[2], k3[2], k4[2], r[2], pos[2]
pos[0] = x; pos[1] = y
k1 = get_velocity(pos[0], pos[1])
k2 = get_velocity(pos[0] - k1[0]/2.0*ds,pos[1] - k1[1]/2.0*ds)
k3 = get_velocity(pos[0] - k2[0]/2.0*ds,pos[1] - k2[1]/2.0*ds)
k4 = get_velocity(pos[0] - k3[0]/2.0*ds,pos[1] - k3[1]/2.0*ds)
cdef size_t i
for i in range(2):
r[i] = pos[i] - ds * (k1[i] + 2*k2[i] + 2*k3[i] + k4[i])/6.0
return r
for i in range(50):
runge_kutta(0, 0, 1.)
# print(runge_kutta(0, 0, 1.))

Python checking if a point is in sphere with center x, y ,z

I'm trying to check if a point is within a sphere with a center point of (x, y, z) where (x, y, z) is not (0, 0, 0).
This code I'm using to generate the points I want to check:
def generatecoords(self, i):
x, y, z = generatepoint()
if i >= 1:
valid = False
while valid == False:
coords = self.checkpoint(x, y, z)
for b in world.starlist:
if coords == world.starlist[b].coords:
coords = self.checkpoint(x, y, z)
else:
valid = True
else:
coords = self.checkpoint(x, y, z)
return coords
def checkpoint(self, x, y, z):
d = math.sqrt(x * x + y * y + z * z)
while d >= self.radius:
x, y, z = generatepoint()
d = math.sqrt(x * x + y * y + z * z)
coords = (int(x), int(y), int(z))
return coords
def generatepoint():
x, y, z = [int(random.uniform(-self.radius, self.radius)) \
for b in range(3)]
return x, y, z
These function are called in a for loop to generate the points in a dictionary, while also checking the unlikely chance that points aren't placed on top of another(mostly because I can).
I trying to figure out what I need to add to math.sqrt(x * x + y * y + z * z) so that it accounts for a center that isn't (0, 0, 0). I do know of one way to do it, but it would require several lines of code and I'd rather do it in one. I would have asked this in the comments of the answer in another question, but I'm not allowed to comment on answers yet.
The formula is:
A point (x,y,z) is inside the sphere with center (cx,cy,cz) and radius r if
(x - cx)^2 + (y - cy)^2 + (z - cz)^2 < r^2
Here is a very short function that returns True if the point is in the sphere, and False if not.
The inputs are two numpy arrays: point = [x,y,z] and ref = [x,y,z] and the radius should be a float.
import numpy as np
def inSphere(self, point, ref, radius):
# Calculate the difference between the reference and measuring point
diff = np.subtract(point, ref)
# Calculate square length of vector (distance between ref and point)^2
dist = np.sum(np.power(diff, 2))
# If dist is less than radius^2, return True, else return False
return dist < radius ** 2

Categories

Resources