{ "cells": [ { "cell_type": "code", "execution_count": 229, "id": "d56bdde4-ef09-4c8a-8d23-f708cc5e23b5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.92203867, 0.93767056])" ] }, "execution_count": 229, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'''\n", "algorithm:\n", "\n", "1. randomly initialise k clusters\n", "2. for each datapoint in the dataset, compare the euclidean distance against all clusters\n", "3. assign that datapoint to the closest cluster\n", "\n", "1. update cluster locations\n", "\n", "'''\n", "\n", "import numpy as np\n", "from dataclasses import dataclass\n", "\n", "\n", "\"\"\"\n", "implements the euclidean distance.\n", "\"\"\"\n", "def k_dist(X: np.ndarray, Y: np.ndarray):\n", " return np.linalg.norm(X-Y)\n", "\n", "@dataclass\n", "class Centroid:\n", " location: np.ndarray\n", " vectors: np.ndarray # dimension ( None:\n", " self.k = k\n", " self.centroids = [Centroid(\n", " location = sum(X[i] for i in range(X.shape[0]))/(j+1),\n", " vectors = []\n", " ) for j in range(k)]\n", " self.X = X\n", "\n", " \"\"\"\n", " assign vectors to each centroid and then recompute the centroid location. occurs n iter times\n", " \"\"\"\n", " def fit(self, iters: int, viz: bool = False) -> None:\n", " for i in range(iters):\n", "\n", " # reset centroids for every iteration\n", " self.centroids = [Centroid(\n", " location = sum(self.X[i] for i in range(self.X.shape[0]))/(j+1),\n", " vectors = []\n", " ) for j in range(k)]\n", " \n", " for X_i in self.X:\n", " distances = []\n", " for C in self.centroids:\n", " distances.append(k_dist(X_i, C.location))\n", " # add the vector to the closest centroid:\n", " self.centroids[np.argmin(distances)].vectors.append(X_i)\n", "\n", " for C in self.centroids:\n", " if len(C.vectors) > 0:\n", " C.location = sum(C.vectors) / len(C.vectors)\n", " \n", " \"\"\"\n", " compares the given datapoint against all centroids and returns the class label of that centroid.\n", " \"\"\"\n", " def predict(self, X_i: np.ndarray) -> np.ndarray:\n", " return self.centroids[np.argmin(\n", " [k_dist(X_i, C.location) for C in self.centroids]\n", " )].location\n", "\n", "from sklearn.datasets import make_blobs\n", "X_train, y = make_blobs(n_samples=20, random_state=123, cluster_std=0.6, centers=[[1,1],[3,3]])\n", "kmeans = KMeans(k=20, X=X_train)\n", "kmeans.fit(5)\n", "kmeans.predict([1.2,1.2])" ] }, { "cell_type": "code", "execution_count": 231, "id": "e9c9ebee-48ed-4bf7-8fb4-6b01f9dbabcf", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "20" ] }, "execution_count": 231, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(kmeans.centroids)" ] }, { "cell_type": "code", "execution_count": 233, "id": "b84f20fb-cb95-4fdb-aee1-affdbecdc8f3", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "import matplotlib\n", "# colour for each centroid\n", "k = len(kmeans.centroids) \n", "from matplotlib import colormaps\n", "cmap = colormaps['hsv'].resampled(k)\n", "cluster_colours = cmap(range(k)) \n", "\n", "for i, C in enumerate(kmeans.centroids):\n", "\n", " # nothing was assigned to this centroid → skip it\n", " if not C.vectors: # empty list == False\n", " continue\n", " # turn [[x, y], [x, y], …] into an (N, 2) float array\n", " vecs = np.stack(C.vectors, axis=0) # safe because we know it’s non-empty\n", " xs, ys = vecs[:, 0], vecs[:, 1]\n", "\n", " # draw the points that belong to this centroid\n", " plt.scatter(xs, ys,\n", " color=cluster_colours[i],\n", " s=20, alpha=0.7, # tweak size / transparency at will\n", " label=f\"cluster {i}\")\n", "\n", " # (optional) also plot the centroid itself\n", " plt.scatter(*C.location, # or whatever attribute stores the mean\n", " marker='x', s=80, linewidths=2,\n", " color=cluster_colours[i])\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "22f0a904-4c23-442a-aeb7-b1aa186b34ff", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "metal", "language": "python", "name": "metal" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.11" } }, "nbformat": 4, "nbformat_minor": 5 }