From 4fd9083563f59531ab601acfd6de2794e7c5069a Mon Sep 17 00:00:00 2001 From: David Date: Wed, 28 Oct 2020 17:18:23 +0000 Subject: [PATCH] Added python version --- Solutions/solutions.zip | Bin 1632 -> 2730 bytes kmeans.py | 42 +++++++++++++++++++++++++++++++++ pyinclude/draw.py | 50 ++++++++++++++++++++++++++++++++++++++++ pyinclude/point.py | 41 ++++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+) create mode 100644 kmeans.py create mode 100644 pyinclude/draw.py create mode 100644 pyinclude/point.py diff --git a/Solutions/solutions.zip b/Solutions/solutions.zip index 17ac756e6a9cf2e828507b79094d527465955e43..5f891ab01c5c0cb57678a1081eab2b99320be886 100644 GIT binary patch delta 2692 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 len(centroids) ) + for p in points[:len(centroids)]: + p.randomise( RANGE ) + + for i in range( len(centroids), len(points) ): + points[i].randomise( RANGE, points[i%len(centroids)] ) + + # set random starting positions for the centroids + for c in centroids: + c.randomise( RANGE ) + print( c ) + + # clustering iterations + for i in range( maxIterations ): + # kmeans + + # display centroid positions + print( f"Iteration {i}" ) + for c in centroids: + print( c ) + + draw( points, centroids, RANGE, RANGE, f"{i:0{int(math.log(maxIterations,10)+1)}d}_clusters.bmp" ) + diff --git a/pyinclude/draw.py b/pyinclude/draw.py new file mode 100644 index 0000000..1374876 --- /dev/null +++ b/pyinclude/draw.py @@ -0,0 +1,50 @@ +from PIL import Image, ImageColor, ImageDraw + +__colormap = ( + (255, 0, 0), (255, 31, 0), (255, 63, 0), (255, 95, 0), (255, 127, 0), + (255, 159, 0), (255, 191, 0), (255, 223, 0), (255, 255, 0), (223, 255, 0), + (191, 255, 0), (159, 255, 0), (127, 255, 0), ( 95, 255, 0), ( 63, 255, 0), + ( 31, 255, 0), ( 0, 255, 0), ( 0, 255, 31), ( 0, 255, 63), ( 0, 255, 95), + ( 0, 255, 127), ( 0, 255, 159), ( 0, 255, 191), ( 0, 255, 223), ( 0, 255, 255), + ( 0, 223, 255), ( 0, 191, 255), ( 0, 159, 255), ( 0, 127, 255), ( 0, 95, 255), + ( 0, 63, 255), ( 0, 31, 255), ( 0, 0, 255), ( 31, 0, 255), ( 63, 0, 255), + ( 95, 0, 255), (127, 0, 255), (159, 0, 255), (191, 0, 255), (223, 0, 255), + (255, 0, 255), (255, 0, 223), (255, 0, 191), (255, 0, 159), (255, 0, 127), + (255, 0, 95), (255, 0, 63), (255, 0, 31), (255, 255, 255), ( 0, 0, 0) +) + +def draw( points, centroids, width=0, height=0, filename="temp.bmp" ): + _width = 0 + _height = 0 + clusters = 0 + + # find width, heigth based on max x, y of points, centroids + for p in points + centroids: + if p.cluster > clusters: clusters = p.cluster + if p.x > _width: _width = p.x + if p.y > _height: _height = p.y + + clusters += 1 + _width += 1 + _height += 1 + + # use out values if user hasn't specified any dimensions + if width==0 or height==0: + width = _width + height = _height + + # create the image + image = Image.new( "RGB", (width,height), (255,255,255) ) + draw = ImageDraw.Draw( image ) + + colors = [ __colormap[i] for i in range( 0, len(__colormap), int(len(__colormap)/clusters)) ] + + # draw the points + for p in points: + draw.ellipse( (p.x-2, p.y-2, p.x+2, p.y+2), fill=colors[p.cluster] ) + + # draw the clusters + for c in centroids: + draw.ellipse( (c.x-5, c.y-5, c.x+5, c.y+5), outline=colors[c.cluster] ) + + image.save( filename ) diff --git a/pyinclude/point.py b/pyinclude/point.py new file mode 100644 index 0000000..0c58598 --- /dev/null +++ b/pyinclude/point.py @@ -0,0 +1,41 @@ +import random + +class Coord: + def __init__( self, x=0, y=0 ): + self.x = int(x) + self.y = int(y) + + def __eq__( self, other ): + return self.x == other.x and self.y == other.y + + def randomise( self, range, coord=None ): + if coord is None: + self.x = random.randint( 0, range ) + self.y = random.randint( 0, range ) + return + + x = random.gauss( coord.x, range/10 ) + y = random.gauss( coord.y, range/10 ) + + if x < 0 : x = 0 + if y < 0 : y = 0 + if x > range : x = range + if y > range : y = range + + self.x = int(x) + self.y = int(y) + +class Point( Coord ): + def __init__( self, x=0, y=0, cluster=0 ): + Coord.__init__( self, x, y ) + self.cluster = cluster + +class Centroid( Coord ): + __counter = 0 + + def __init__( self, x=0, y=0 ): + self.cluster = Centroid.__counter + Centroid.__counter += 1 + + def __repr__( self ): + return f"Centroid[{self.cluster}] @ ({self.x},{self.y})" \ No newline at end of file