from tkinter import * from tkinter.messagebox import showinfo from R2Graph import * import math import numpy as np from sklearn.cluster import KMeans, DBSCAN SCALEX = 40. SCALEY = SCALEX STEPX = 5./SCALEX # 5 pixels STEPY = 5./SCALEX blueColor = "#0000FF" redColor = "#BB0000" # Cluster numbers undefined = 255 noise = (-1) # Cluster colors clusterColors = ( "#EE0000", "#00AA00", "#0044FF", "#888800", "#008888", "#880088", "#BB4400", "#224488" ) numClusterColors = len(clusterColors) undefinedColor = "#444444" noiseColor = "#888888" pointRadius = 0.15 # Default values k_default = 3 eps_default = 1.25 minPts_default = 4 def pointColor(clusterNumber): if clusterNumber == undefined: return undefinedColor elif clusterNumber == noise: return noiseColor else: return clusterColors[clusterNumber % numClusterColors] def main(): points = [] clusterNumbers = [] corePoints = [] objectIDs = [] scaleX = SCALEX; scaleY = SCALEY root = Tk() root.title("Clustering") root.geometry("900x600") panel = Frame(root) panel2 = Frame(root) clusterButton = Button(panel, text="Cluster") resetButton = Button(panel, text="Reset") clearButton = Button(panel, text="Clear") drawArea = Canvas(root, bg="white") panel.pack(side=TOP, fill=X) panel2.pack(side=TOP, fill=X) clusterButton.pack(side=LEFT, padx=4, pady=4) resetButton.pack(side=LEFT, padx=4, pady=4) clearButton.pack(side=LEFT, padx=4, pady=4) drawArea.pack(side=TOP, fill=BOTH, expand=True, padx=4, pady=4) clusterMethodIdx = IntVar() # Control variable for the group of radio buttons clusterMethodIdx.set(value = 0) kMeansRadio = Radiobutton( panel, text = "K-Means", variable=clusterMethodIdx, value=0, fg = blueColor ) dbscanRadio = Radiobutton( panel, text = "DBSCAN", variable=clusterMethodIdx, value=1, fg = redColor ) kMeansRadio.pack(side=LEFT, padx=4, pady=4) dbscanRadio.pack(side=LEFT, padx=4, pady=4) kMeansLabel = Label(panel2, text="K-Means:", fg = blueColor) kLabel = Label(panel2, text=" K (num. centers):", fg = blueColor) kText = StringVar(value = str(k_default)) kEntry = Entry( panel2, bg="white", textvariable=kText, fg=blueColor, width = 5 ) kMeansLabel.pack(side=LEFT, padx=4, pady=4) kLabel.pack(side=LEFT, padx=4, pady=4) kEntry.pack(side=LEFT, padx=4, pady=4) dbscanLabel = Label(panel2, text=" DBSCAN:", fg = redColor) epsLabel = Label(panel2, text=" eps:", fg = redColor) epsText = StringVar(value = str(eps_default)) epsEntry = Entry( panel2, bg="white", textvariable=epsText, fg=redColor, width = 5 ) minPtsLabel = Label(panel2, text="minPts:", fg = redColor) minPtsText = StringVar(value = str(minPts_default)) minPtsEntry = Entry( panel2, bg="white", textvariable=minPtsText, fg=redColor, width = 5 ) kMeansLabel.pack(side=LEFT, padx=4, pady=4) kLabel.pack(side=LEFT, padx=4, pady=4) kEntry.pack(side=LEFT, padx=4, pady=4) dbscanLabel.pack(side=LEFT, padx=4, pady=4) epsLabel.pack(side=LEFT, padx=4, pady=4) epsEntry.pack(side=LEFT, padx=4, pady=4) minPtsLabel.pack(side=LEFT, padx=4, pady=4) minPtsEntry.pack(side=LEFT, padx=4, pady=4) def map(t): w = drawArea.winfo_width() h = drawArea.winfo_height() centerX = w/2. centerY = h/2. x = centerX + t.x*scaleX y = centerY - t.y*scaleY return (x, y) def invmap(p): w = drawArea.winfo_width() h = drawArea.winfo_height() centerX = w/2. centerY = h/2. x = (p[0] - centerX)/scaleX y = (centerY - p[1])/scaleY return R2Point(x, y) def xMin(): w = drawArea.winfo_width() return (-(w/scaleX)/2.) def xMax(): return (-xMin()) def yMin(): w = drawArea.winfo_height() return (-(w/scaleY)/2.) def yMax(): return (-yMin()) def drawGrid(): ix0 = int(xMin()) ix1 = int(xMax()) x = ix0 while x <= ix1: if x != 0: p0 = map(R2Point(x, yMin())) p1 = map(R2Point(x, yMax())) drawArea.create_line(p0, p1, fill="lightGray", width=1) x += 1 iy0 = int(yMin()) iy1 = int(yMax()) y = iy0 while y <= iy1: if y != 0: p0 = map(R2Point(xMin(), y)) p1 = map(R2Point(xMax(), y)) drawArea.create_line(p0, p1, fill="lightGray", width=1) y += 1 # Draw x-axis drawArea.create_line( map(R2Point(xMin(), 0.)), map(R2Point(xMax(), 0.)), fill="black", width=2 ) # Draw y-axis drawArea.create_line( map(R2Point(0., yMin())), map(R2Point(0., yMax())), fill="black", width=2 ) def onMouseRelease(e): # print("Mouse release event:", e) p = (e.x, e.y) t = invmap(p) points.append(t) clusterNumbers.append(undefined) corePoints.append(False) drawPoint(t, undefined) def drawPoint(t, clusterNumber=undefined, corePoint=False): vx = R2Vector(pointRadius, 0.) vy = R2Vector(0., pointRadius) color = pointColor(clusterNumber) if corePoint: outlineColor = "black" else: outlineColor = color circleID = drawArea.create_oval( map(t - vx - vy), map(t + vx + vy), fill=color, outline=outlineColor, width=2 ) objectIDs.append(circleID) def drawPoints(): for i in range(len(points)): drawPoint(points[i], clusterNumbers[i], corePoints[i]) def onCluster(): clusterMethod = clusterMethodIdx.get() x = np.array( [[p[0], p[1]] for p in points] ) if clusterMethod == 0: print("Using K-Means clustering.") try: k = int(kText.get()) except ValueError: k = k_default kText.set(value = str(k)) if k <= 0: print("K must be > 0") k = 1 kText.set(value = "1") print("k =", k) clf_kmeans = KMeans(n_clusters = k, n_init = 10) clf_kmeans.fit(x) y_predicted = clf_kmeans.predict(x) for i in range(len(x)): clusterNumbers[i] = y_predicted[i] corePoints[i] = False # No core points in K-Means clustering clearPicture() drawPoints() else: print("Using DBSCAN clustering.") showinfo(message="Not implemented yet...") # ... to do ... def onReset(): for i in range(len(clusterNumbers)): clusterNumbers[i] = undefined corePoints[i] = False clearPicture() drawPoints() def clearPicture(): for i in objectIDs: drawArea.delete(i) objectIDs.clear() def onClear(): clearPicture() points.clear() clusterNumbers.clear() corePoints.clear() def onConfigure(e): drawArea.delete("all") drawGrid() drawPoints() clusterButton.configure(command = onCluster) resetButton.configure(command = onReset) clearButton.configure(command = onClear) drawArea.bind("", onMouseRelease) drawArea.bind("", onMouseRelease) drawArea.bind("", onMouseRelease) drawArea.bind("", onConfigure) drawGrid() root.mainloop() if __name__ == "__main__": main()