Browse Source

Default arguments don't do what you expect...

tb-init-ui-render
Taylor Bockman 5 years ago
parent
commit
24667348fd
  1. 73
      clusterview/algorithms.py
  2. 30
      tests/test_algorithms.py

73
clusterview/algorithms.py

@ -8,7 +8,7 @@ class CentroidGrouping:
can change). This allows us to do better than just dumping the grouping
into a dictionary with a long tuple pointing at an array.
"""
def __init__(self, centroid, points=[]):
def __init__(self, centroid, points=None):
if not isinstance(centroid, Point):
ValueError("Centroid must be a Point.")
@ -16,6 +16,10 @@ class CentroidGrouping:
ValueError("Points must be in a list.")
self.__centroid = centroid
if points is None:
self.__points = []
else:
self.__points = points
@property
@ -32,15 +36,20 @@ class CentroidGrouping:
@param point The point.
"""
if not isinstance(point, Point):
raise ValueError("Point must be of type Point.")
self.__points.append(point)
def __repr__(self):
s = f"CENTROID: {self.__centroid}\n"
s += f"POINTS: {self.__points}"
return s
def __eq__(self, other):
return (self.centroid == other.centroid and
self.points == other.points)
return (self.__centroid == other.centroid and
self.__points == other.points)
class Algorithms:
@ -49,72 +58,48 @@ class Algorithms:
geometry algorithms.
"""
# Since all algorithms rely on a set of centroids it is stored here
# statically.
__centroids = []
@classmethod
def clear_centroids(cls):
cls.__centroids = []
@classmethod
def centroids(cls):
return cls.__centroids
@classmethod
def set_centroids(cls, centroids):
for c in centroids:
if not isinstance(c, Point):
raise ValueError("Centroids must be of type Point.")
cls.__centroids.append(c)
@classmethod
def euclidean_grouping(cls, point_set):
@staticmethod
def euclidean_grouping(centroids, point_set):
"""
Given a point set that EXCLUDES the centroids specified
it returns a map from centroid to array of points, where the array
of points contains the points with the smallest euclidean distance
from that point.
@param cls The class calling the method.
@param point_set The set of points from the UI.
@param centroids The centroids to use.
@param point_set The set of points from the UI excluding centroids.
"""
if not isinstance(point_set, PointSet):
raise ValueError("Euclidean grouping can only be calculated on " +
"PointSet types.")
if not cls.__centroids:
if not isinstance(centroids, list):
raise ValueError("Centroids must be of type list.")
if not centroids:
raise ValueError("No centroids specified.")
groups = []
for centroid in cls.__centroids:
for centroid in centroids:
groups.append(CentroidGrouping(centroid))
for point in point_set.points:
nearest_distance = float("inf")
nearest_centroid = None
nearest_group = None
for centroid in cls.__centroids:
current_distance = Math.euclidean_distance(centroid, point)
for current_group in groups:
current_distance = (
Math.euclidean_distance(current_group.centroid, point))
if current_distance < nearest_distance:
nearest_centroid = centroid
nearest_group = current_group
nearest_distance = current_distance
if nearest_centroid is None:
if nearest_group is None:
raise ValueError("Failed to find centroid nearest " +
f"to point {point}")
# We successfully found the nearest centroid to the point
# and we can add it to the list.
# TODO: Can CentroidGrouping be made hashable?
# This is relatively slow for large numbers of groups. If
# CentroidGrouping can be made hashable then this becomes O(1).
for group in groups:
if nearest_centroid == group.centroid:
group.add_point(point)
break
nearest_group.add_point(point)
return groups

30
tests/test_algorithms.py

@ -5,19 +5,9 @@ from clusterview.colors import Color
from clusterview.points import Point, PointSet
@pytest.fixture(autouse=True, scope="function")
def teardown():
"""
Teardown function for after each test. The current pytest best practice
is to run a setup routine, yield, and then run your teardown routine.
"""
yield
Algorithms.clear_centroids()
def test_empty_centroids():
with pytest.raises(ValueError):
Algorithms.euclidean_grouping(None)
Algorithms.euclidean_grouping([], None)
def test_wrong_point_set():
@ -27,13 +17,22 @@ def test_wrong_point_set():
centroids = [centroid_g1, centroid_g2, centroid_g3]
Algorithms.set_centroids(centroids)
with pytest.raises(ValueError):
Algorithms.euclidean_grouping(centroids, None)
def test_empty_point_set():
centroid_g1 = Point(101, 81, Color.ORANGE, 8, 800, 600)
centroid_g2 = Point(357, 222, Color.RED, 8, 800, 600)
centroid_g3 = Point(728, 47, Color.PURPLE, 8, 800, 600)
centroids = [centroid_g1, centroid_g2, centroid_g3]
with pytest.raises(ValueError):
Algorithms.euclidean_grouping(None)
Algorithms.euclidean_grouping(centroids, [])
def test_euclidean_distance():
def test_euclidean_grouping():
centroid_g1 = Point(101, 81, Color.ORANGE, 8, 800, 600)
centroid_g2 = Point(357, 222, Color.RED, 8, 800, 600)
centroid_g3 = Point(728, 47, Color.PURPLE, 8, 800, 600)
@ -77,8 +76,7 @@ def test_euclidean_distance():
expected = [centroid_grouping_1, centroid_grouping_2, centroid_grouping_3]
Algorithms.set_centroids(centroids)
actual = Algorithms.euclidean_grouping(point_set)
actual = Algorithms.euclidean_grouping(centroids, point_set)
assert len(actual) == len(expected)

Loading…
Cancel
Save