diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/analytics.py b/analytics.py new file mode 100644 index 0000000..b4e3371 --- /dev/null +++ b/analytics.py @@ -0,0 +1,156 @@ +import math + +from .point import Point + +def mean_center(points): + """ + Given a set of points, compute the mean center + + Parameters + ---------- + points : list + A list of points in the form (x,y) + + Returns + ------- + x : float + Mean x coordinate + + y : float + Mean y coordinate + """ + x = None + y = None + sum_x=[] + sum_y=[] + for x_tmp,y_tmp in points: + sum_x.append(x_tmp) + sum_y.append(y_tmp) + + x=float(sum(sum_x)/len(sum_x)) + y=float(sum(sum_y)/len(sum_y)) + + return x, y + +def euclidean_distance(a, b): + """ + Compute the Euclidean distance between two points + + Parameters + ---------- + a : tuple + A point in the form (x,y) + + b : tuple + A point in the form (x,y) + + Returns + ------- + + distance : float + The Euclidean distance between the two points + """ + distance = math.sqrt((a[0] - b[0])**2 + (a[1] - b[1])**2) + return distance + +def average_nearest_neighbor_distance(points,mark=None): + """ + Given a set of points, compute the average nearest neighbor. + + Parameters + ---------- + points : list + A list of points + mark : str + + Returns + ------- + mean_d : float + Average nearest neighbor distance + + References + ---------- + Clark and Evan (1954 Distance to Nearest Neighbor as a + Measure of Spatial Relationships in Populations. Ecology. 35(4) + p. 445-453. + """ + mean_d = 0 + if mark==None: + points_tmp=points + else: + points_tmp=[ point for point in points if point.mark==mark] + + length=len(points_tmp) + nearest_distances=[] + for i in range(length): + distance=[] + for j in range(length): + if i==j: + continue + else: + distance.append(euclidean_distance((points_tmp[i].x,points_tmp[i].y),(points_tmp[j].x,points_tmp[j].y))) + nearest_distances.append(min(distance)) + + mean_d=float(sum(nearest_distances)/len(nearest_distances)) + return mean_d + + +def minimum_bounding_rectangle(points): + """ + Given a set of points, compute the minimum bounding rectangle. + + Parameters + ---------- + points : list + A list of points in the form (x,y) + + Returns + ------- + : list + Corners of the MBR in the form [xmin, ymin, xmax, ymax] + """ + + mbr = [0,0,0,0] + + x_list=[] + y_list=[] + for x,y in points: + x_list.append(x) + y_list.append(y) + mbr=[min(x_list),min(y_list),max(x_list),max(y_list)] + + return mbr + + +def mbr_area(mbr): + """ + Compute the area of a minimum bounding rectangle + """ + area = 0 + area=(mbr[2]-mbr[0])*(mbr[3]-mbr[1]) + return area + + +def expected_distance(area, n): + """ + Compute the expected mean distance given + some study area. + + This makes lots of assumptions and is not + necessarily how you would want to compute + this. This is just an example of the full + analysis pipe, e.g. compute the mean distance + and the expected mean distance. + + Parameters + ---------- + area : float + The area of the study area + + n : int + The number of points + """ + + expected = 0 + expected =float((math.sqrt(area/n))/2) + return expected diff --git a/io_geojson.py b/io_geojson.py new file mode 100644 index 0000000..76300f6 --- /dev/null +++ b/io_geojson.py @@ -0,0 +1,79 @@ +import json + + +def read_geojson(input_file): + """ + Read a geojson file + + Parameters + ---------- + input_file : str + The PATH to the data to be read + + Returns + ------- + gj : dict + An in memory version of the geojson + """ + # Please use the python json module (imported above) + # to solve this one. + gj = None + fp = open(input_file, 'r') + gj = json.loads(fp.read()) + fp.close() + return gj + +def find_largest_city(gj): + """ + Iterate through a geojson feature collection and + find the largest city. Assume that the key + to access the maximum population is 'pop_max'. + + Parameters + ---------- + gj : dict + A GeoJSON file read in as a Python dictionary + + Returns + ------- + city : str + The largest city + + population : int + The population of the largest city + """ + city = None + max_population = 0 + for feature in gj["features"]: + if feature["properties"]["pop_max"]>max_population: + max_population=feature["properties"]["pop_max"] + city=feature["properties"]["nameascii"] + + return city, max_population + + +def write_your_own(gj): + """ + Here you will write your own code to find + some attribute in the supplied geojson file. + + Take a look at the attributes available and pick + something interesting that you might like to find + or summarize. This is totally up to you. + + Do not forget to write the accompanying test in + tests.py! + + To find the average of pop_max and pop_min. + + """ + + sum_pop_max=0 + sum_pop_min=0 + num=0 + for feature in gj["features"]: + sum_pop_max+=feature["properties"]["pop_max"] + sum_pop_min+=feature["properties"]["pop_min"] + num+=1 + + return float(sum_pop_max/num),float(sum_pop_min/num) diff --git a/point.py b/point.py new file mode 100644 index 0000000..829745b --- /dev/null +++ b/point.py @@ -0,0 +1,28 @@ + + + +class Point(): + + def __init__(self,x,y,mark=None): + self.x=x + self.y=y + self.mark=mark + + def check_coincident(self, peer_p): + + return (self.x == peer_p.x and self.y == peer_p.y and self.mark == peer_p.mark) + + def shift_point(self, x_shift, y_shift): + + self.x += x_shift + self.y += y_shift + + def __eq__(self, other): + return self.x == other.x and self.y == other.y and self.mark == other.mark + + def __str__(self): + return "x=%f,y=%f,mark=%s"%(self.x,self.y,self.mark) + + def __add__(self, other): + return Point(self.x+other.x,self.y+other.y,self.mark) + diff --git a/point_pattern.py b/point_pattern.py new file mode 100644 index 0000000..df13b46 --- /dev/null +++ b/point_pattern.py @@ -0,0 +1,191 @@ +from point import Point +import math +import random +import numpy as np + +class PointPattern(): + + def __init__(self,points): + self.points = points[:] + + + def average_nearest_neighbor_distance(self,mark=None): + + """ + Given a set of points, compute the average nearest neighbor. + + Parameters + ---------- + points : list + A list of points + mark : str + + Returns + ------- + mean_d : float + Average nearest neighbor distance + + References + ---------- + Clark and Evan (1954 Distance to Nearest Neighbor as a + Measure of Spatial Relationships in Populations. Ecology. 35(4) + p. 445-453. + """ + mean_d = 0 + if mark==None: + points_tmp=self.points + else: + points_tmp=[ point for point in self.points if point.mark==mark] + + length=len(points_tmp) + nearest_distances=[] + for i in range(length): + distance=[] + for j in range(length): + if i==j: + continue + else: + distance.append(self.euclidean_distance((points_tmp[i].x,points_tmp[i].y),(points_tmp[j].x,points_tmp[j].y))) + nearest_distances.append(min(distance)) + + mean_d=float(sum(nearest_distances)/len(nearest_distances)) + return mean_d + + def number_of_coincident_points(self): + + length=len(self.points) + number_of_coincident_points=0 + for i in range(length): + for j in range(length): + if i==j: + continue + else: + if self.check_coincident((self.points[i].x,self.points[i].y),(self.points[j].x,self.points[j].y)): + number_of_coincident_points+=1 + return number_of_coincident_points + + def list_marks(self): + + points_marks=set() + for point in self.points: + if point.mark!=None: + points_marks.add(point.mark) + result=[] + for points_mark in points_marks: + result.append(points_mark) + return result + + def list_subset_of_points(self,mark): + + return [point for point in self.points if point.mark==mark] + + def create_random_marked_points(self, n=None,marks=[]): + + if n == None: + n=len(self.points) + points_list=[] + for i in range(n): + points_list.append(Point(random.uniform(0,1), random.uniform(0,1),random.choice(marks))) + + return points_list + + def create_realizations(self, k): + + return self.permutations(k) + + def permutations(self,p=99, n=100): + """ + Return the mean nearest neighbor distance of p permutations. + + Parameters + ---------- + p : integer + n : integer + + Returns + ------- + permutations : list + the mean nearest neighbor distance list. + + """ + permutation_list=[] + for i in range(p): + permutation_list.append(self.average_nearest_neighbor_distance(self.create_random_marked_points(n))) + return permutation_list + + def critical_points(self,permutations): + """ + Return the mean nearest neighbor distance of p permutations. + + Parameters + ---------- + permutations : list + the mean nearest neighbor distance list. + Returns + ------- + smallest : float + largest : float + + """ + + return min(permutations),max(permutations) + + def compute_g(self,nsteps): + + ds = np.linspace(0, 1, nsteps) + min_list=[] + for i_index,di in enumerate(ds): + tmp_list=[] + for j_index,dj in enumerate(ds): + if i_index != j_index: + tmp_list.append(abs(di-dj)) + + min_list.append(min(tmp_list)) + + return sum(min_list)/nsteps + + + + def check_coincident(self,a, b): + """ + Check whether two points are coincident + Parameters + ---------- + a : tuple + A point in the form (x,y) + + b : tuple + A point in the form (x,y) + + Returns + ------- + equal : bool + Whether the points are equal + """ + return a == b + + def euclidean_distance(self,a, b): + """ + Compute the Euclidean distance between two points + + Parameters + ---------- + a : tuple + A point in the form (x,y) + + b : tuple + A point in the form (x,y) + + Returns + ------- + + distance : float + The Euclidean distance between the two points + """ + distance = math.sqrt((a[0] - b[0])**2 + (a[1] - b[1])**2) + return distance + + +# p=PointPattern() +# points=[Point(1,2,'red'),Point(1,2,'red'),Point(1,2,'yellow'),Point(1,2)] +# print(p.list_marks(points)) \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/functional_test.py b/tests/functional_test.py new file mode 100644 index 0000000..1c77d50 --- /dev/null +++ b/tests/functional_test.py @@ -0,0 +1,125 @@ +import random +import unittest + +from .. import point +from .. import analytics +from .. import io_geojson +from .. import utils + + + +class TestFunctionalPointPattern(unittest.TestCase): + + def setUp(self): + random.seed(123) + self.marks=['red','blue','green'] + mark_list=[ random.choice(self.marks) for i in range(100)] + random.seed(12345) + i = 0 + self.points = [] + while i < 100: + seed = (round(random.random(),2), round(random.random(),2)) + self.points.append(point.Point(seed[0],seed[1],mark_list[i])) + n_additional = random.randint(5,10) + i += 1 + c = random.choice([0,1]) + if c: + for j in range(n_additional): + x_offset = random.randint(0,10) / 100 + y_offset = random.randint(0,10) / 100 + pt =point.Point(round(seed[0] + x_offset, 2), round(seed[1] + y_offset,2),mark_list[i]) + self.points.append(pt) + i += 1 + if i == 100: + break + if i == 100: + break + #print([(points.x,points.y)for points in self.points]) + def test_point_pattern(self): + """ + This test checks that the code can compute an observed mean + nearest neighbor distance and then use Monte Carlo simulation to + generate some number of permutations. A permutation is the mean + nearest neighbor distance computed using a random realization of + the point process. + """ + random.seed() # Reset the random number generator using system time + # I do not know where you have moved avarege_nearest_neighbor_distance, so update the point_pattern module + observed_avg = analytics.average_nearest_neighbor_distance(self.points) + self.assertAlmostEqual(0.027, observed_avg, 3) + + # Again, update the point_pattern module name for where you have placed the point_pattern module + # Also update the create_random function name for whatever you named the function to generate + # random points + rand_points = utils.generate_random_points(100) + self.assertEqual(100, len(rand_points)) + + # As above, update the module and function name. + permutations = utils.permutation(99) + self.assertEqual(len(permutations), 99) + self.assertNotEqual(permutations[0], permutations[1]) + + # As above, update the module and function name. + lower, upper = utils.critical_points(permutations) + self.assertTrue(lower > 0.03) + self.assertTrue(upper < 0.07) + self.assertTrue(observed_avg < lower or observed_avg > upper) + + # As above, update the module and function name. + significant = utils.is_observed_distance_significant(lower, upper, observed_avg) + self.assertTrue(significant) + + self.assertTrue(True) + + def test_mark_point_pattern(self): + + random.seed(12345) + # I do not know where you have moved avarege_nearest_neighbor_distance, so update the point_pattern module + observed_avg_red = analytics.average_nearest_neighbor_distance(self.points,self.marks[0]) + self.assertAlmostEqual(0.041, observed_avg_red, 3) + + observed_avg_blue = analytics.average_nearest_neighbor_distance(self.points,self.marks[1]) + self.assertAlmostEqual(0.045, observed_avg_blue, 3) + observed_avg_green = analytics.average_nearest_neighbor_distance(self.points,self.marks[2]) + self.assertAlmostEqual(0.059, observed_avg_green, 3) + + # Again, update the point_pattern module name for where you have placed the point_pattern module + # Also update the create_random function name for whatever you named the function to generate + # random points + rand_points = utils.create_random_marked_points(100,self.marks) + self.assertEqual(100, len(rand_points)) + + # As above, update the module and function name. + permutations = utils.permutation_mark(99,marks=self.marks) + self.assertEqual(len(permutations), 99) + self.assertNotEqual(permutations[0], permutations[1]) + + # As above, update the module and function name. + permutations = utils.permutation_mark(99,marks=self.marks,mark=self.marks[0]) + lower, upper = utils.critical_points(permutations) + self.assertTrue(lower > 0.03) + self.assertTrue(upper < 0.20) + self.assertTrue(observed_avg_red < lower or observed_avg_red > upper) + + significant = utils.is_observed_distance_significant(lower, upper, observed_avg_red) + self.assertTrue(significant) + + permutations = utils.permutation_mark(99,marks=self.marks,mark=self.marks[1]) + lower, upper = utils.critical_points(permutations) + self.assertTrue(lower > 0.03) + self.assertTrue(upper < 0.20) + self.assertTrue(observed_avg_blue < lower or observed_avg_blue > upper) + significant = utils.is_observed_distance_significant(lower, upper, observed_avg_blue) + self.assertTrue(significant) + + + permutations = utils.permutation_mark(99,marks=self.marks,mark=self.marks[2]) + lower, upper = utils.critical_points(permutations) + + self.assertTrue(lower > 0.03) + self.assertTrue(upper < 0.20) + self.assertTrue(observed_avg_green < lower or observed_avg_green > upper) + significant = utils.is_observed_distance_significant(lower, upper, observed_avg_green) + self.assertTrue(significant) + + self.assertTrue(True) \ No newline at end of file diff --git a/tests/point_test.py b/tests/point_test.py new file mode 100644 index 0000000..1e26977 --- /dev/null +++ b/tests/point_test.py @@ -0,0 +1,62 @@ +from .. import point +from .. import utils + +import unittest +import random + +class TestPointClass(unittest.TestCase): + + def set_up(self): + pass + + def test_set_xy(self): + + new_point=point.Point(1,1,'red') + self.assertEqual(new_point.x, 1) + self.assertEqual(new_point.y, 1) + + def test_coincident(self): + + new_point=point.Point(1,1,'red') + peer_point=point.Point(1,1,'red') + self.assertTrue(new_point.check_coincident(peer_point)) + peer_point=point.Point(1,2,'red') + self.assertFalse(new_point.check_coincident(peer_point)) + + def test_shift(self): + + new_point=point.Point(1,1,'red') + new_point.shift_point(1,1) + self.assertEqual(new_point.x, 2) + self.assertEqual(new_point.y, 2) + + def test_magic_methods(self): + + new_point=point.Point(1,1,'red') + peer_point=point.Point(1,1,'red') + self.assertEqual(str(new_point), "x=1.000000,y=1.000000,mark=red") + new_add_point=new_point+peer_point + self.assertEqual(new_add_point.x, 2) + self.assertEqual(new_add_point.y, 2) + self.assertEqual(new_point==peer_point, True) + + def test_mark_points(self): + + random.seed(12345) + marks=['red','yellow','blue','green'] + list_of_points = utils.create_random_marked_points(10,marks) + sum_color_list=[0,0,0,0] + for points in list_of_points: + if points.mark=='red': + sum_color_list[0]+=1 + elif points.mark=='yellow': + sum_color_list[1]+=1 + elif points.mark=='blue': + sum_color_list[2]+=1 + elif points.mark=='green': + sum_color_list[3]+=1 + + self.assertEqual(sum_color_list[0], 2) + self.assertEqual(sum_color_list[1], 4) + self.assertEqual(sum_color_list[2], 2) + self.assertEqual(sum_color_list[3], 2) diff --git a/tests/test_analytics.py b/tests/test_analytics.py new file mode 100644 index 0000000..36264d2 --- /dev/null +++ b/tests/test_analytics.py @@ -0,0 +1,67 @@ +import os +import sys +import unittest +import random +sys.path.insert(0, os.path.abspath('..')) + +from .. import analytics + +class TestAnalytics(unittest.TestCase): + + def setUp(self): + random.seed(12345) + # A list comprehension to create 50 random points + self.points = [(random.randint(0,100), random.randint(0,100)) for i in range(50)] + + + def test_mean_center(self): + """ + Something to think about - What values would you + expect to see here and why? Why are the values + not what you might expect? + """ + x, y = analytics.mean_center(self.points) + self.assertEqual(x, 47.52) + self.assertEqual(y, 45.14) + + def test_minimum_bounding_rectangle(self): + mbr = analytics.minimum_bounding_rectangle(self.points) + self.assertEqual(mbr, [0,0,94,98]) + + def test_mbr_area(self): + mbr = [0,0,94,98] + area = analytics.mbr_area(mbr) + self.assertEqual(area, 9212) + + def test_expected_distance(self): + area = 9212 + npoints = 50 + expected = analytics.expected_distance(area, npoints) + self.assertAlmostEqual(expected, 6.7867518, 5) + + def test_euclidean_distance(self): + """ + A test to ensure that the distance between points + is being properly computed. + You do not need to make any changes to this test, + instead, in point_pattern.py, you must complete the + `eucliden_distance` function so that the correct + values are returned. + Something to think about: Why might you want to test + different cases, e.g. all positive integers, positive + and negative floats, coincident points? + """ + point_a = (3, 7) + point_b = (1, 9) + distance = analytics.euclidean_distance(point_a, point_b) + self.assertAlmostEqual(2.8284271, distance, 4) + + point_a = (-1.25, 2.35) + point_b = (4.2, -3.1) + distance = analytics.euclidean_distance(point_a, point_b) + self.assertAlmostEqual(7.7074639, distance, 4) + + point_a = (0, 0) + point_b = (0, 0) + distance = analytics.euclidean_distance(point_b, point_a) + self.assertAlmostEqual(0.0, distance, 4) \ No newline at end of file diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..dbfe7e3 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,132 @@ +import os +import sys +import unittest +sys.path.insert(0, os.path.abspath('..')) + +from .. import utils + +class TestUtils(unittest.TestCase): + + def setUp(self): + pass + + def test_getx(self): + """ + A simple test to ensure that you understand how to access + the x coordinate in a tuple of coordinates. + You do not need to make any changes to this test, + instead, in point_pattern.py, you must complete the + `getx` function so that the correct + values are returned. + """ + point = (1,2) + x = utils.getx(point) + self.assertEqual(1, x) + + def test_gety(self): + """ + As above, except get the y coordinate. + You do not need to make any changes to this test, + instead, in point_pattern.py, you must complete the + `gety` function so that the correct + values are returned. + """ + point = (3,2.5) + y = utils.gety(point) + self.assertEqual(2.5, y) + + def test_shift_point(self): + """ + Test that a point is being properly shifted + when calling point_pattern.shift_point + """ + point = (0,0) + new_point = utils.shift_point(point, 3, 4) + self.assertEqual((3,4), new_point) + + point = (-2.34, 1.19) + new_point = utils.shift_point(point, 2.34, -1.19) + self.assertEqual((0,0), new_point) + + + + def test_manhattan_distance(self): + """ + A test to ensure that the distance between points + is being properly computed. + You do not need to make any changes to this test, + instead, in point_pattern.py, you must complete the + `eucliden_distance` function so that the correct + values are returned. + Something to think about: Why might you want to test + different cases, e.g. all positive integers, positive + and negative floats, coincident points? + """ + point_a = (3, 7) + point_b = (1, 9) + distance = utils.manhattan_distance(point_a, point_b) + self.assertEqual(4.0, distance) + + point_a = (-1.25, 2.35) + point_b = (4.2, -3.1) + distance = utils.manhattan_distance(point_a, point_b) + self.assertEqual(10.9, distance) + + point_a = (0, 0) + point_b = (0, 0) + distance = utils.manhattan_distance(point_b, point_a) + self.assertAlmostEqual(0.0, distance, 4) + + def test_check_coincident(self): + """ + As above, update the function in point_pattern.py + """ + point_a = (3, 7) + point_b = (3, 7) + coincident = utils.check_coincident(point_a, point_b) + self.assertEqual(coincident, True) + + point_b = (-3, -7) + coincident = utils.check_coincident(point_a, point_b) + self.assertEqual(coincident, False) + + point_a = (0, 0) + point_b = (0.0, 0.0) + coincident = utils.check_coincident(point_b, point_a) + self.assertEqual(coincident, True) + + def test_check_in(self): + """ + As above, update the function in point_pattern.py + """ + point_list = [(0,0), (1,0.1), (-2.1, 1), + (2,4), (1,1), (3.5, 2)] + + inlist = utils.check_in((0,0), point_list) + self.assertTrue(inlist) + + inlist = utils.check_in((6,4), point_list) + self.assertFalse(inlist) + + def test_generate_random_points(self): + + rand_points = utils.generate_random_points(20) + self.assertEqual(20, len(rand_points)) + + def test_permutation(self): + + permutations = utils.permutation(88) + self.assertEqual(len(permutations), 88) + self.assertNotEqual(permutations[0], permutations[1]) + + def test_critical_points(self): + + avg_des_list = [0.5,0.001,0.2,0.9,0.91] + lower,upper=utils.critical_points(avg_des_list) + self.assertEqual(0.001, lower) + self.assertEqual(0.91, upper) + + def test_is_observed_distance_significant(self): + lower,upper=(0.1,0.5) + result=utils.is_observed_distance_significant(lower,upper,0.51) + self.assertTrue(result) diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..8249792 --- /dev/null +++ b/utils.py @@ -0,0 +1,246 @@ +import math +import random +from .analytics import average_nearest_neighbor_distance +from .point import Point + +def generate_random_points(n): + """ + Return n random points. + + Parameters + ---------- + n : integer + + Returns + ------- + points_list : list + The random points list + + """ + return [Point(random.uniform(0,1), random.uniform(0,1)) for i in range(n)] + + +def create_random_marked_points(n, marks=[]): + + points_list=[] + for i in range(n): + points_list.append(Point(random.uniform(0,1), random.uniform(0,1),random.choice(marks))) + + return points_list + + + +def permutation(p=99, n=100): + """ + Return the mean nearest neighbor distance of p permutations. + + Parameters + ---------- + p : integer + n : integer + + Returns + ------- + permutations : list + the mean nearest neighbor distance list. + + """ + permutation_list=[] + for i in range(p): + permutation_list.append(average_nearest_neighbor_distance(generate_random_points(n))) + return permutation_list + + +def permutation_mark(p=99, n=100 ,marks=None,mark=None): + """ + Return the mean nearest neighbor distance of p permutations. + + Parameters + ---------- + p : integer + n : integer + marks : list + + Returns + ------- + permutations : list + the mean nearest neighbor distance list. + + """ + permutation_list=[] + for i in range(p): + permutation_list.append(average_nearest_neighbor_distance(create_random_marked_points(n,marks),mark)) + return permutation_list + +def critical_points(permutations): + """ + Return the mean nearest neighbor distance of p permutations. + + Parameters + ---------- + permutations : list + the mean nearest neighbor distance list. + Returns + ------- + smallest : float + largest : float + + """ + + return min(permutations),max(permutations) + + +def is_observed_distance_significant(smallest,largest,observed_distance): + """ + Returns True is the observed distance is significant. + + Parameters + ---------- + smallest : float + largest : float + + Returns + ------- + bool + + """ + + if observed_distance>=smallest and observed_distance<=largest: + return False + else: + return True + + + + +def manhattan_distance(a, b): + """ + Compute the Manhattan distance between two points + + Parameters + ---------- + a : tuple + A point in the form (x,y) + + b : tuple + A point in the form (x,y) + + Returns + ------- + distance : float + The Manhattan distance between the two points + """ + distance = abs(a[0] - b[0]) + abs(a[1] - b[1]) + return distance + + +def shift_point(point, x_shift, y_shift): + """ + Shift a point by some amount in the x and y directions + + Parameters + ---------- + point : tuple + in the form (x,y) + + x_shift : int or float + distance to shift in the x direction + + y_shift : int or float + distance to shift in the y direction + + Returns + ------- + new_x : int or float + shited x coordinate + + new_y : int or float + shifted y coordinate + + Note that the new_x new_y elements are returned as a tuple + + Example + ------- + >>> point = (0,0) + >>> shift_point(point, 1, 2) + (1,2) + """ + x = getx(point) + y = gety(point) + + x += x_shift + y += y_shift + + return x, y + + +def check_coincident(a, b): + """ + Check whether two points are coincident + Parameters + ---------- + a : tuple + A point in the form (x,y) + + b : tuple + A point in the form (x,y) + + Returns + ------- + equal : bool + Whether the points are equal + """ + return a == b + + +def check_in(point, point_list): + """ + Check whether point is in the point list + + Parameters + ---------- + point : tuple + In the form (x,y) + + point_list : list + in the form [point, point_1, point_2, ..., point_n] + """ + return point in point_list + + +def getx(point): + """ + A simple method to return the x coordinate of + an tuple in the form(x,y). We will look at + sequences in a coming lesson. + + Parameters + ---------- + point : tuple + in the form (x,y) + + Returns + ------- + : int or float + x coordinate + """ + return point[0] + + +def gety(point): + """ + A simple method to return the x coordinate of + an tuple in the form(x,y). We will look at + sequences in a coming lesson. + + Parameters + ---------- + point : tuple + in the form (x,y) + + Returns + ------- + : int or float + y coordinate + """ + return point[1]