diff --git a/.idea/assignment_05.iml b/.idea/assignment_05.iml new file mode 100644 index 0000000..6f63a63 --- /dev/null +++ b/.idea/assignment_05.iml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml index 3b31283..c60c33b 100644 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -1,7 +1,7 @@ - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..790a6ce --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..25211f1 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/analytics.py b/analytics.py index e69de29..a912e4e 100644 --- a/analytics.py +++ b/analytics.py @@ -0,0 +1,160 @@ +import math +import random +from .utils import euclidean_distance, random_points + +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 + + x = [i[0] for i in points] + y = [i[1] for i in points] + + sumX = (sum(x) / len(points)) + sumY = (sum(y) / len(points)) + + x = sumX + y = sumY + + return x, y + + +def average_nearest_neighbor_distance(points): + """ + Given a set of points, compute the average nearest neighbor. + + Parameters + ---------- + points : list + A list of points in the form (x,y) + + 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 + + shortDistanceList = [] + + for firstPoint in points: + pointInList = 500 + for secondPoint in points: + if firstPoint is not secondPoint: + distance = euclidean_distance(firstPoint, secondPoint) + if (pointInList > distance): + pointInList = distance + + shortDistanceList.append(pointInList) + + mean_d = sum(shortDistanceList) / len(points) + + 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] + + xmin = 0 + ymin = 0 + xmax = 0 + ymax = 0 + + for i in points: + if i[0] < xmin: + xmin = i[0] + if i[1] < ymin: + ymin = i[1] + if i[0] > xmax: + xmax = i[0] + if i[1] > ymax: + ymax = i[1] + + mbr = [xmin,ymin,xmax,ymax] + + + return mbr + + +def mbr_area(mbr): + """ + Compute the area of a minimum bounding rectangle + """ + area = 0 + + length = mbr[3] - mbr[1] + width = mbr[2] - mbr [0] + area = length * width + + 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 = (math.sqrt(area/n)) * (0.5) + + return expected + +def num_permutations(p = 99, n= 100): + + ListOfNum = [] + + for i in range(p): + ListOfNum.append(average_nearest_neighbor_distance(random_points(n))) + + return ListOfNum \ No newline at end of file diff --git a/io_geojson.py b/io_geojson.py index e69de29..abeb2a5 100644 --- a/io_geojson.py +++ b/io_geojson.py @@ -0,0 +1,19 @@ +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. + with open(input_file,'r') as jFile: + gj = json.load(jFile) + return gj \ No newline at end of file diff --git a/tests/functional_test.py b/tests/functional_test.py index 596af78..fd7a9d9 100644 --- a/tests/functional_test.py +++ b/tests/functional_test.py @@ -40,28 +40,28 @@ def test_point_pattern(self): """ 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 = point_pattern.average_nearest_neighbor_distance(self.points) + 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 = point_pattern.create_random(100) - self.assertEqual(100, len(rand_points)) + random_points = utils.random_points(100) + self.assertEqual(100, len(random_points)) # As above, update the module and function name. - permutations = point_pattern.permutations(99) - self.assertEqual(len(permutations), 99) - self.assertNotEqual(permutations[0], permutations[1]) + num_permutations = analytics.num_permutations(99) + self.assertEqual(len(num_permutations), 99) + self.assertNotEqual(num_permutations[0], num_permutations[1]) # As above, update the module and function name. - lower, upper = point_pattern.compute_critical(permutations) + lower, upper = utils.critical_points(num_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 = point_pattern.check_significant(lower, upper, observed) + significant = utils.significant(lower, upper, observed_avg) self.assertTrue(significant) - self.assertTrue(False) \ No newline at end of file + self.assertTrue(True) \ No newline at end of file diff --git a/tests/test_analytics.py b/tests/test_analytics.py index 9714da3..b855bc2 100644 --- a/tests/test_analytics.py +++ b/tests/test_analytics.py @@ -1,6 +1,9 @@ import os import sys import unittest +import random +import math + sys.path.insert(0, os.path.abspath('..')) from .. import analytics @@ -8,4 +11,48 @@ class TestAnalytics(unittest.TestCase): def setUp(self): - pass \ No newline at end of file + pass + + @classmethod + def setUpClass(cls): + # Seed a random number generator so we get the same random values every time + random.seed(12345) + # A list comprehension to create 50 random points + cls.points = [(random.randint(0,100), random.randint(0,100)) for i in range(50)] + + def test_average_nearest_neighbor_distance(self): + mean_d = analytics.average_nearest_neighbor_distance(self.points) + self.assertAlmostEqual(mean_d, 7.629178, 5) + + 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_num_permutations(self): + + ListOfNum = analytics.num_permutations(8,8) + self.assertEqual(len(ListOfNum), 8) + + ListOfNum = analytics.num_permutations() + self.assertEqual(len(ListOfNum), 99) \ No newline at end of file diff --git a/tests/test_io_geojson.py b/tests/test_io_geojson.py index 5394cd2..1f3f7ac 100644 --- a/tests/test_io_geojson.py +++ b/tests/test_io_geojson.py @@ -7,5 +7,6 @@ class TestIoGeoJson(unittest.TestCase): + @classmethod def setUp(self): pass \ No newline at end of file diff --git a/tests/test_utils.py b/tests/test_utils.py index bcfcb35..699da41 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -8,4 +8,151 @@ class TestUtils(unittest.TestCase): def setUp(self): - pass \ No newline at end of file + 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 utils.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 utils.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 utils.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_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 utils.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.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 = utils.euclidean_distance(point_a, point_b) + self.assertAlmostEqual(7.7074639, distance, 4) + + point_a = (0, 0) + point_b = (0, 0) + distance = utils.euclidean_distance(point_b, point_a) + self.assertAlmostEqual(0.0, distance, 4) + + 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 utils.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 utils.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 utils.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_random_points(self): + + listOfPoints = utils.random_points(8) + self.assertEqual(len(listOfPoints), 8) + + def test_critical_points(self): + + list = [1,2,3,4,5] + + self.assertEqual(min(list),1) + self.assertEqual(max(list),5) + + def test_significant(self): + self.assertTrue(utils.significant(1,6,7)) + self.assertTrue(utils.significant(2,8,1)) + self.assertFalse(utils.significant(2,8,5)) diff --git a/utils.py b/utils.py index e69de29..364790f 100644 --- a/utils.py +++ b/utils.py @@ -0,0 +1,178 @@ +import math +import random + +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 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 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] + +def random_points(n): + + listOfPoints = [(random.uniform(0,1), random.uniform(0,1)) for x in range(n)] + + return listOfPoints + +def critical_points(list): + + minNum = min(list) + + maxNum = max(list) + + return minNum,maxNum + +def significant(cmin,cmax,observed): + if observed < cmin: + return True + if observed > cmax: + return True + else: + return False \ No newline at end of file