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 100755 index 0000000..ae0ecbd --- /dev/null +++ b/analytics.py @@ -0,0 +1,176 @@ +from point import Point +import math +import statistics +import random + +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 + """ + total = len(points) + y = 0 + x = 0 + for point in points: + x += point[0] + y += point[1] + + x = x/total + y = y/total + return x, y + +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] + """ + x_list = [] + y_list = [] + + for p in points: + x_list.append(p[0]) + y_list.append(p[1]) + + mbr = [0,0,0,0] + mbr[0] = min(x_list) + mbr[1] = min(y_list) + mbr[2] = max(x_list) + mbr[3] = max(y_list) + + return mbr + + +def mbr_area(mbr): + """ + Compute the area of a minimum bounding rectangle + """ + width = mbr[2] - mbr[0] + length = mbr[3] - mbr[1] + area = width * length + + 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.5 * (math.sqrt( area / n )) + return expected + + +""" +Below are the functions that you created last week. +Your syntax might have been different (which is awesome), +but the functionality is identical. No need to touch +these unless you are interested in another way of solving +the assignment +""" + +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 create_random_marked_points(n, marks = None): + point_list = [] + rand = random.Random() + for i in range(n): + rand_x = round(rand.uniform(0,1),2) + rand_y = round(rand.uniform(0,1),2) + if marks is None: + point_list.append(Point(rand_x, rand_y)) + else: + rand_mark = random.choice(marks) + point_list.append(Point(rand_x, rand_y, rand_mark)) + return point_list + +def euclidean_distance(a, b): + distance = math.sqrt((a.x - b.x)**2 + (a.y - b.y)**2) + return distance + +def average_nearest_neighbor_distance(points, mark = None): + new_points = [] + if mark is None: + new_points = points + else: + for point in points: + if point.mark is mark: + new_points.append(point) + + dists = [] + for num1, point in enumerate(new_points): + dists.append(None) + for num2, point2 in enumerate(new_points): + if num1 is not num2: + new_dist = euclidean_distance(point, point2) + if dists[num1] == None: + dists[num1] = new_dist + elif dists[num1] > new_dist: + dists[num1] = new_dist + + return sum(dists)/len(points) + +def permutations(p=99, n=100, marks=None): + neighbor_perms = [] + for i in range(p): + neighbor_perms.append(average_nearest_neighbor_distance(create_random_marked_points(n), + marks)) + return neighbor_perms + +def compute_critical(perms): + return max(perms), min(perms) + +def check_significant(lower, upper, observed): + return(lower <= observed or observed <= upper) diff --git a/io_geojson.py b/io_geojson.py new file mode 100644 index 0000000..c62228e --- /dev/null +++ b/io_geojson.py @@ -0,0 +1,78 @@ +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. + + with open(input_file, 'r') as f: + gj = json.load(f) + 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 + """ + max_population = 0 + city = None + features_list = gj.get('features') + x = 0 + + for f in features_list: + if f['properties']['pop_max'] > max_population: + max_population = f['properties']['pop_max'] + city = f['properties']['name'] + + 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! + """ + #find the largest city west of the Mississippi River + + largest_western_city = None + features_list = gj.get('features') + for f in features_list: + if f['properties']['longitude'] < -95.202: + largest_western_city = f['properties']['longitude'] + + + return largest_western_city diff --git a/point.py b/point.py new file mode 100644 index 0000000..0ff68d9 --- /dev/null +++ b/point.py @@ -0,0 +1,111 @@ +import analytics +import math +import random +import random +import numpy as np + + +class Point: + + def __init__(self, x = 0, y = 0, mark = ''): + self.x = x + self.y = y + self.mark = mark + + def __add__(self, other): + return Point(self.x + other.x, self.y + other.y) + + def __eq__(self, other): + return self.x == other.x and self.y == other.y + + def __radd__(self, other): + return Point(self.x + other, self.y + other) + + def check_coincident(self, b): + + if b.x == self.x and b.y == self.y: + return True + + def shift_point(self, x_shift, y_shift): + + self.x += x_shift + self.y += y_shift + +class PointPattern(object): + + def __init__(self): + self.points = [] + self.marks = [] + self.length = len(self.points) + + def __len__(self): + count = 0 + for item in self.points: + count += 1 + return count + + def add_point(self, point): + self.points.append(point) + + def remove_point(self, index): + del(self.points[index]) + + def average_nearest_neighor(self, mark=None): + return analytics.average_nearest_neighbor_distance(self.points, + mark) + + def count_coincident(self): + counted = [] + count = 0 + for index, point in enumerate(self.points): + for index2, point2 in enumerate(self.points): + if index != index2: + if point.check_coincident(point2) is True: + count += 1 + return count + + def list_marks(self): + marks = [] + for point in self.points: + if point.mark not in marks and point.mark is not None: + marks.append(point.mark) + return marks + + def points_by_mark(self, mark): + + points_to_return = [] + for point in self.points: + if point.mark == mark: + points_to_return.append(point) + return points_to_return + + def generate_random_points(self, n = None, marks = None): + + if n is None: + n = len(self.points) + point_list = analytics.create_random_marked_points(n, marks) + return point_list + + def generate_realizations(p = 99, marks = None): + neighbor_perms = [] + for i in range(p): + neighbor_perms.append( + analytics.average_nearest_neighbor( + generate_random_points())) + return neighbor_perms + + def get_critical(neighbor_perms): + return max(perms), min(perms) + + def comupte_g(self, nsteps): + ds = np.linspace(0, 1, nsteps) + dist_counts = [] + for i, d in enumerate(ds): + min_dist = None + for n in range(nsteps): + if n != i: + if min_dist is None or min_dist > d: + min_dist = d + dist_counts.append(min_dist) + return sum(dist_counts)/nsteps + 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..d2096a4 --- /dev/null +++ b/tests/functional_test.py @@ -0,0 +1,73 @@ +import random +import unittest + +from .. import point + + +class TestFunctionalPointPattern(unittest.TestCase): + + def setUp(self): + random.seed(12345) + i = 0 + self.points = [] + marks = ['ying', 'yang', 'black', 'white'] + rand_marks = [] + for mark in range(100): + rand_marks.append(random.choice(marks)) + while i < 100: + seed = point.Point(round(random.random(),2), + round(random.random(),2), rand_marks[i]) + self.points.append(seed) + 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.x + x_offset, 2), round(seed.y + + y_offset,2), random.choice(marks)) + self.points.append(pt) + i += 1 + if i == 100: + break + if i == 100: + break + + 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 = point.average_nearest_neighbor_distance(self.points) + self.assertAlmostEqual(0.02779598180193161, 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 + marks = ['ying', 'yang', 'black', 'white'] + rand_points = point.create_random_marked_points(100, marks) + self.assertEqual(100, len(rand_points)) + + # As above, update the module and function name. + permutations = point.permutations(99) + self.assertEqual(len(permutations), 99) + self.assertNotEqual(permutations[0], permutations[1]) + + # As above, update the module and function name. + lower, upper = point.compute_critical(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.check_significant(lower, upper, observed_avg) + self.assertTrue(significant) + + self.assertTrue(True) diff --git a/tests/point_test.py b/tests/point_test.py new file mode 100644 index 0000000..a43d9f7 --- /dev/null +++ b/tests/point_test.py @@ -0,0 +1,124 @@ +from .. import point +from .. import analytics +from .. import utils + + +import unittest +import random + +class Test_Point(unittest.TestCase): + + def test_points(self): + + """set up random x and y values for point instantiation""" + random.seed(12345) + rand_tup = (random.randint(0,10),random.randint(0,10)) + x_val = rand_tup[0] + y_val = rand_tup[1] + new_tup = (random.randint(0,10),random.randint(0,10)) + x_new = new_tup[0] + y_new = new_tup[1] + + """check that point is created properly""" + rand_point = point.Point(x_val, y_val, "random_mark") #(6,0,"random_mark") + self.assertEqual(rand_point.x, 6) + self.assertEqual(rand_point.y, 0) + + """check for whether the points are checking coincidence properly""" + new_point = point.Point(x_new, y_new, "different_mark") #(4,5,"different_mark") + self.assertTrue(rand_point.check_coincident(rand_point)) + self.assertFalse(rand_point.check_coincident(new_point)) + + """check whether points can be shifted""" + rand_point.shift_point(2,2) + self.assertEqual(rand_point.x, 8) + self.assertEqual(rand_point.y, 2) + + def test_marks(self): + + random.seed(12345) + marks = ['ying', 'yang', 'black', 'white'] + marked_points =[] + for i in range(20): + marked_points.append(point.Point(0,0, random.choice(marks))) + + ying_count = 0 + yang_count = 0 + white_count = 0 + black_count = 0 + for i in marked_points: + if i.mark == 'ying': + ying_count += 1 + if i.mark == 'yang': + yang_count += 1 + if i.mark == 'black': + black_count += 1 + else: + white_count += 1 + self.assertEqual(ying_count, 3) + self.assertEqual(yang_count, 7) + self.assertEqual(black_count, 6) + self.assertEqual(white_count, 14) + + def test_nearest_neighbor(self): + random.seed(12345) + marks = ['ying', 'yang', 'black', 'white'] + point_list = point.create_random_marked_points(20, marks) + + points_with_mark = point.average_nearest_neighbor_distance(point_list, + marks) + points_without_mark = point.average_nearest_neighbor_distance(point_list) + + self.assertNotEqual(points_with_mark, 0.2, 5) + self.assertAlmostEqual(points_without_mark, 0.11982627009007044, 1) + + + def test_magic_methods(self): + """check whether add and eq magic methods work""" + new_point = point.Point(2,4) + point_to_add = point.Point(3,6) + added = new_point + point_to_add + #This asserts that the add method worked as well as eq method + self.assertEqual(added, point.Point(5,10)) + + """check whether reverse adding works on random points""" + point_list = point.create_random_marked_points(20) + self.assertTrue(type(random.choice(point_list) + + random.choice(point_list)), point.Point) + +class Point_Pattern_Test(unittest.TestCase): + + def setUp(self): + self.point_pattern = point.PointPattern() + self.point_pattern.add_point(point.Point(3, 6, 'ying')) + self.point_pattern.add_point(point.Point(9, 6, 'yang')) + self.point_pattern.add_point(point.Point(3, 9, 'black')) + self.point_pattern.add_point(point.Point(9, 6)) + + def test_remove_point(self): + length_after_removal = len(self.point_pattern) - 1 + self.point_pattern.remove_point(0) + self.assertEqual(length_after_removal, len(self.point_pattern)) + + def test_coincident(self): + self.assertEqual(self.point_pattern.count_coincident(), 2) + + def test_list_marks(self): + self.assertEqual(len(self.point_pattern.list_marks()), 4) + + def test_points_by_mark(self): + self.assertEqual(len(self.point_pattern.points_by_mark('yang')), 1) + + def test_point_generation(self): + self.assertEqual(len(self.point_pattern.generate_random_points(10)), 10) + self.assertEqual(len(self.point_pattern.generate_random_points()), 4) + + def test_g(self): + self.assertEqual(self.point_pattern.comupte_g(100), 0.5) + + + + + + + diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..541c803 --- /dev/null +++ b/utils.py @@ -0,0 +1,114 @@ + +import random + +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.x + + +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.y + +