diff --git a/iris.csv b/iris.csv new file mode 100644 index 0000000..1bf42f2 --- /dev/null +++ b/iris.csv @@ -0,0 +1,151 @@ +Id,SepalLengthCm,SepalWidthCm,PetalLengthCm,PetalWidthCm,Species +1,5.1,3.5,1.4,0.2,Iris-setosa +2,4.9,3.0,1.4,0.2,Iris-setosa +3,4.7,3.2,1.3,0.2,Iris-setosa +4,4.6,3.1,1.5,0.2,Iris-setosa +5,5.0,3.6,1.4,0.2,Iris-setosa +6,5.4,3.9,1.7,0.4,Iris-setosa +7,4.6,3.4,1.4,0.3,Iris-setosa +8,5.0,3.4,1.5,0.2,Iris-setosa +9,4.4,2.9,1.4,0.2,Iris-setosa +10,4.9,3.1,1.5,0.1,Iris-setosa +11,5.4,3.7,1.5,0.2,Iris-setosa +12,4.8,3.4,1.6,0.2,Iris-setosa +13,4.8,3.0,1.4,0.1,Iris-setosa +14,4.3,3.0,1.1,0.1,Iris-setosa +15,5.8,4.0,1.2,0.2,Iris-setosa +16,5.7,4.4,1.5,0.4,Iris-setosa +17,5.4,3.9,1.3,0.4,Iris-setosa +18,5.1,3.5,1.4,0.3,Iris-setosa +19,5.7,3.8,1.7,0.3,Iris-setosa +20,5.1,3.8,1.5,0.3,Iris-setosa +21,5.4,3.4,1.7,0.2,Iris-setosa +22,5.1,3.7,1.5,0.4,Iris-setosa +23,4.6,3.6,1.0,0.2,Iris-setosa +24,5.1,3.3,1.7,0.5,Iris-setosa +25,4.8,3.4,1.9,0.2,Iris-setosa +26,5.0,3.0,1.6,0.2,Iris-setosa +27,5.0,3.4,1.6,0.4,Iris-setosa +28,5.2,3.5,1.5,0.2,Iris-setosa +29,5.2,3.4,1.4,0.2,Iris-setosa +30,4.7,3.2,1.6,0.2,Iris-setosa +31,4.8,3.1,1.6,0.2,Iris-setosa +32,5.4,3.4,1.5,0.4,Iris-setosa +33,5.2,4.1,1.5,0.1,Iris-setosa +34,5.5,4.2,1.4,0.2,Iris-setosa +35,4.9,3.1,1.5,0.1,Iris-setosa +36,5.0,3.2,1.2,0.2,Iris-setosa +37,5.5,3.5,1.3,0.2,Iris-setosa +38,4.9,3.1,1.5,0.1,Iris-setosa +39,4.4,3.0,1.3,0.2,Iris-setosa +40,5.1,3.4,1.5,0.2,Iris-setosa +41,5.0,3.5,1.3,0.3,Iris-setosa +42,4.5,2.3,1.3,0.3,Iris-setosa +43,4.4,3.2,1.3,0.2,Iris-setosa +44,5.0,3.5,1.6,0.6,Iris-setosa +45,5.1,3.8,1.9,0.4,Iris-setosa +46,4.8,3.0,1.4,0.3,Iris-setosa +47,5.1,3.8,1.6,0.2,Iris-setosa +48,4.6,3.2,1.4,0.2,Iris-setosa +49,5.3,3.7,1.5,0.2,Iris-setosa +50,5.0,3.3,1.4,0.2,Iris-setosa +51,7.0,3.2,4.7,1.4,Iris-versicolor +52,6.4,3.2,4.5,1.5,Iris-versicolor +53,6.9,3.1,4.9,1.5,Iris-versicolor +54,5.5,2.3,4.0,1.3,Iris-versicolor +55,6.5,2.8,4.6,1.5,Iris-versicolor +56,5.7,2.8,4.5,1.3,Iris-versicolor +57,6.3,3.3,4.7,1.6,Iris-versicolor +58,4.9,2.4,3.3,1.0,Iris-versicolor +59,6.6,2.9,4.6,1.3,Iris-versicolor +60,5.2,2.7,3.9,1.4,Iris-versicolor +61,5.0,2.0,3.5,1.0,Iris-versicolor +62,5.9,3.0,4.2,1.5,Iris-versicolor +63,6.0,2.2,4.0,1.0,Iris-versicolor +64,6.1,2.9,4.7,1.4,Iris-versicolor +65,5.6,2.9,3.6,1.3,Iris-versicolor +66,6.7,3.1,4.4,1.4,Iris-versicolor +67,5.6,3.0,4.5,1.5,Iris-versicolor +68,5.8,2.7,4.1,1.0,Iris-versicolor +69,6.2,2.2,4.5,1.5,Iris-versicolor +70,5.6,2.5,3.9,1.1,Iris-versicolor +71,5.9,3.2,4.8,1.8,Iris-versicolor +72,6.1,2.8,4.0,1.3,Iris-versicolor +73,6.3,2.5,4.9,1.5,Iris-versicolor +74,6.1,2.8,4.7,1.2,Iris-versicolor +75,6.4,2.9,4.3,1.3,Iris-versicolor +76,6.6,3.0,4.4,1.4,Iris-versicolor +77,6.8,2.8,4.8,1.4,Iris-versicolor +78,6.7,3.0,5.0,1.7,Iris-versicolor +79,6.0,2.9,4.5,1.5,Iris-versicolor +80,5.7,2.6,3.5,1.0,Iris-versicolor +81,5.5,2.4,3.8,1.1,Iris-versicolor +82,5.5,2.4,3.7,1.0,Iris-versicolor +83,5.8,2.7,3.9,1.2,Iris-versicolor +84,6.0,2.7,5.1,1.6,Iris-versicolor +85,5.4,3.0,4.5,1.5,Iris-versicolor +86,6.0,3.4,4.5,1.6,Iris-versicolor +87,6.7,3.1,4.7,1.5,Iris-versicolor +88,6.3,2.3,4.4,1.3,Iris-versicolor +89,5.6,3.0,4.1,1.3,Iris-versicolor +90,5.5,2.5,4.0,1.3,Iris-versicolor +91,5.5,2.6,4.4,1.2,Iris-versicolor +92,6.1,3.0,4.6,1.4,Iris-versicolor +93,5.8,2.6,4.0,1.2,Iris-versicolor +94,5.0,2.3,3.3,1.0,Iris-versicolor +95,5.6,2.7,4.2,1.3,Iris-versicolor +96,5.7,3.0,4.2,1.2,Iris-versicolor +97,5.7,2.9,4.2,1.3,Iris-versicolor +98,6.2,2.9,4.3,1.3,Iris-versicolor +99,5.1,2.5,3.0,1.1,Iris-versicolor +100,5.7,2.8,4.1,1.3,Iris-versicolor +101,6.3,3.3,6.0,2.5,Iris-virginica +102,5.8,2.7,5.1,1.9,Iris-virginica +103,7.1,3.0,5.9,2.1,Iris-virginica +104,6.3,2.9,5.6,1.8,Iris-virginica +105,6.5,3.0,5.8,2.2,Iris-virginica +106,7.6,3.0,6.6,2.1,Iris-virginica +107,4.9,2.5,4.5,1.7,Iris-virginica +108,7.3,2.9,6.3,1.8,Iris-virginica +109,6.7,2.5,5.8,1.8,Iris-virginica +110,7.2,3.6,6.1,2.5,Iris-virginica +111,6.5,3.2,5.1,2.0,Iris-virginica +112,6.4,2.7,5.3,1.9,Iris-virginica +113,6.8,3.0,5.5,2.1,Iris-virginica +114,5.7,2.5,5.0,2.0,Iris-virginica +115,5.8,2.8,5.1,2.4,Iris-virginica +116,6.4,3.2,5.3,2.3,Iris-virginica +117,6.5,3.0,5.5,1.8,Iris-virginica +118,7.7,3.8,6.7,2.2,Iris-virginica +119,7.7,2.6,6.9,2.3,Iris-virginica +120,6.0,2.2,5.0,1.5,Iris-virginica +121,6.9,3.2,5.7,2.3,Iris-virginica +122,5.6,2.8,4.9,2.0,Iris-virginica +123,7.7,2.8,6.7,2.0,Iris-virginica +124,6.3,2.7,4.9,1.8,Iris-virginica +125,6.7,3.3,5.7,2.1,Iris-virginica +126,7.2,3.2,6.0,1.8,Iris-virginica +127,6.2,2.8,4.8,1.8,Iris-virginica +128,6.1,3.0,4.9,1.8,Iris-virginica +129,6.4,2.8,5.6,2.1,Iris-virginica +130,7.2,3.0,5.8,1.6,Iris-virginica +131,7.4,2.8,6.1,1.9,Iris-virginica +132,7.9,3.8,6.4,2.0,Iris-virginica +133,6.4,2.8,5.6,2.2,Iris-virginica +134,6.3,2.8,5.1,1.5,Iris-virginica +135,6.1,2.6,5.6,1.4,Iris-virginica +136,7.7,3.0,6.1,2.3,Iris-virginica +137,6.3,3.4,5.6,2.4,Iris-virginica +138,6.4,3.1,5.5,1.8,Iris-virginica +139,6.0,3.0,4.8,1.8,Iris-virginica +140,6.9,3.1,5.4,2.1,Iris-virginica +141,6.7,3.1,5.6,2.4,Iris-virginica +142,6.9,3.1,5.1,2.3,Iris-virginica +143,5.8,2.7,5.1,1.9,Iris-virginica +144,6.8,3.2,5.9,2.3,Iris-virginica +145,6.7,3.3,5.7,2.5,Iris-virginica +146,6.7,3.0,5.2,2.3,Iris-virginica +147,6.3,2.5,5.0,1.9,Iris-virginica +148,6.5,3.0,5.2,2.0,Iris-virginica +149,6.2,3.4,5.4,2.3,Iris-virginica +150,5.9,3.0,5.1,1.8,Iris-virginica diff --git a/kmedoids.ipynb b/kmedoids.ipynb new file mode 100644 index 0000000..a05a43c --- /dev/null +++ b/kmedoids.ipynb @@ -0,0 +1,240 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e1e5a2b7", + "metadata": {}, + "source": [ + "# Analiza skupień metodą k-medoids (PAM) " + ] + }, + { + "cell_type": "markdown", + "id": "80d5deaf", + "metadata": {}, + "source": [ + "### Co to jest klasteryzacja? " + ] + }, + { + "cell_type": "markdown", + "id": "4040df16", + "metadata": {}, + "source": [ + "Analiza skupień lub klasteryzacja to zadanie polegające na grupowaniu zbioru obiektów w taki sposób, aby obiekty w tej samej grupie lub klastrze były do siebie bardziej podobne niż obiekty w innych grupach lub klastrach. Sama analiza skupień nie jest jednym konkretnym algorytmem, lecz ogólnym zadaniem do rozwiązania. Można je zrealizować za pomocą różnych algorytmów (algorytm k-średnich, algorytm k-medoid), które różnią się znacznie w rozumieniu tego, czym jest klaster i jak skutecznie je znaleźć. Popularne pojęcia klastrów obejmują grupy o małych odległościach między elementami klastra. Klastrowanie można zatem sformułować jako wieloprzedmiotowy problem optymalizacyjny. Wybór odpowiedniego algorytmu grupowania i ustawień parametrów zależy od indywidualnego zbioru danych i przeznaczenia wyników. Analiza skupień jako taka nie jest zadaniem automatycznym, lecz iteracyjnym procesem odkrywania wiedzy lub interaktywnej optymalizacji wieloprzedmiotowej, który wymaga prób i błędów. Często konieczne jest modyfikowanie wstępnego przetwarzania danych i parametrów modelu, aż do uzyskania pożądanych właściwości." + ] + }, + { + "cell_type": "markdown", + "id": "3dc57d21", + "metadata": {}, + "source": [ + "W naszym projekcie przedstawimy metodę k-medoid i porównamy ją z metodą k-średnich." + ] + }, + { + "cell_type": "markdown", + "id": "f7c684c9", + "metadata": {}, + "source": [ + "### Algorytm k-medoid" + ] + }, + { + "cell_type": "markdown", + "id": "af45d7c7", + "metadata": {}, + "source": [ + "1. Inicjalizacja: wybierz k losowych punktów spośród n punktów danych jako medoidy.\n", + "2. Przyporządkuj każdy punkt danych do najbliższego medoidu, używając dowolnych popularnych metod metryki odległości.\n", + "3. Podczas gdy koszt maleje:\n", + " Dla każdej medoidy m, dla każdego punktu danych o, który nie jest medoidą: \n", + " i. Zamień punkty m i o, przyporządkuj każdy punkt danych do najbliższej medoidy, ponownie oblicz koszt. \n", + " ii. Jeśli całkowity koszt jest większy niż w poprzednim kroku, cofnij zamianę." + ] + }, + { + "cell_type": "markdown", + "id": "d8f6dd1e", + "metadata": {}, + "source": [ + "**Rozwiązanie**: Implementacja algorytmu k-medoid w Pythonie. Do wykonania algorytmu k-medoidy potrzebne jest wstępne przetworzenie danych. W naszym rozwiązaniu przeprowadziliśmy wstępne przetwarzanie danych w celu zaimplementowania algorytmu k-medoid. Dodatkowo oceniliśmy jaka jest jakość naszego grupowania. Posłużyliśmy się tzw. sylwetką (ang. silhouette) $s(x_i)$ obliczaną dla każdego obiektu $x_i$. Najpierw dla $x_i$ znajduje się jego średnią odległość $a(x_i)$ od pozostałych obiektów grupy, do której został przydzielony, a następnie wybiera się minimalną wartość $b(x_i)$ spośród obliczonych odległości od $x_i$ do każdej spośród pozostałych grup osobno. Odległość $x_i$ od danej grupy oblicza się jako średnią odległość od $x_i$ do wszystkich elementów tej grupy. Obie wielkości zestawia się we wzorze: " + ] + }, + { + "cell_type": "markdown", + "id": "ab825d40", + "metadata": {}, + "source": [ + "

$s(x_i) = \\frac{b(x_i)-a(x_i)}{max(a(x_i),b(x_i))}$

\n" + ] + }, + { + "cell_type": "markdown", + "id": "f6d344cf", + "metadata": {}, + "source": [ + "otrzymując wartość sylwetki dla danego obiektu $x_i$. Ma ona prostą interpretację: obiekty, dla których wskaźnik jest bliski 1, zostały trafnie zgrupowane, pozostałe (o wartości ok. 0 i ujemnej) prawdopodobnie trafiły do złych grup." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "73cffc81", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sylwetka (ang. Silhouette) dla algorytmu k-medoid dla k = 2 ---------- 0.6114567207221335\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "from sklearn import preprocessing\n", + "import random\n", + "import math\n", + "import copy\n", + "from sklearn.preprocessing import MinMaxScaler\n", + "from sklearn.metrics import silhouette_samples, silhouette_score\n", + "\n", + "\n", + "class TrainModel:\n", + " def __init__(self, data, k_value):\n", + " self.data = data\n", + " scaler = MinMaxScaler()\n", + " self.data = scaler.fit_transform(self.data)\n", + " self.k_value = k_value\n", + " self.kmedoids(self.data)\n", + "\n", + " def get_random_medoids(self, data):\n", + " points = random.sample(range(0, len(data)), self.k_value)\n", + " medoids = []\n", + " for i in range(self.k_value):\n", + " medoids.append(data[i])\n", + " return medoids\n", + "\n", + " def get_closest_medoids(self, sample_point, medoids):\n", + " min_distance = float('inf')\n", + " closest_medoid = None\n", + " for i in range(len(medoids)):\n", + " distance = self.calculateDistance(sample_point, medoids[i])\n", + " if distance < min_distance:\n", + " min_distance = distance\n", + " closest_medoid = i\n", + " return closest_medoid\n", + "\n", + " def get_clusters(self, data_points, medoids):\n", + " clusters = [[] for _ in range(self.k_value)]\n", + " for i in range(len(data_points)):\n", + " x = self.get_closest_medoids(data_points[i], medoids)\n", + " clusters[x].append(data_points[i])\n", + " return clusters\n", + "\n", + " def calculate_cost(self, data_points, clusters, medoids):\n", + " cost = 0\n", + " for i in range(len(clusters)):\n", + " for j in range(len(clusters[i])):\n", + " cost += self.calculateDistance(medoids[i], clusters[i][j])\n", + " return cost\n", + "\n", + " def get_non_medoids(self, data_points, medoids):\n", + " non_medoids = []\n", + " for sample in data_points:\n", + " flag = False\n", + " for m in medoids:\n", + " if (sample == m).all():\n", + " flag = True\n", + " if flag == False:\n", + " non_medoids.append(sample)\n", + " return non_medoids\n", + "\n", + " def get_clusters_label(self, data_points, clusters):\n", + " labels = []\n", + " for i in range(len(data_points)):\n", + " labels.append(0)\n", + " for i in range(len(clusters)):\n", + " cluster = clusters[i]\n", + " for j in range(len(cluster)):\n", + " for k in range(len(data_points)):\n", + " if (cluster[j] == data_points[k]).all():\n", + " labels[k] = i\n", + " break\n", + " return labels\n", + "\n", + " def kmedoids(self, data):\n", + " medoids = self.get_random_medoids(data)\n", + " clusters = self.get_clusters(data, medoids)\n", + " initial_cost = self.calculate_cost(data, clusters, medoids)\n", + " while True:\n", + " best_medoids = medoids\n", + " lowest_cost = initial_cost\n", + " for i in range(len(medoids)):\n", + " non_medoids = self.get_non_medoids(data, medoids)\n", + " for j in range(len(non_medoids)):\n", + " new_medoids = medoids.copy()\n", + " for k in range(len(new_medoids)):\n", + " if (new_medoids[k] == medoids[i]).all():\n", + " new_medoids[k] = non_medoids[j]\n", + " new_clusters = self.get_clusters(data, new_medoids)\n", + " new_cost = self.calculate_cost(data, new_clusters, new_medoids)\n", + " if new_cost < lowest_cost:\n", + " lowest_cost = new_cost\n", + " best_medoids = new_medoids\n", + " if lowest_cost < initial_cost:\n", + " initial_cost = lowest_cost\n", + " medoids = best_medoids\n", + " else:\n", + " break\n", + " final_clusters = self.get_clusters(data, medoids)\n", + " cluster_labels = self.get_clusters_label(data, final_clusters)\n", + " silhouette_avg = silhouette_score(data, cluster_labels)\n", + "\n", + " print('Sylwetka (ang. Silhouette) dla algorytmu k-medoid dla k =', self.k_value, 10*'-', silhouette_avg)\n", + "\n", + " def calculateDistance(self, x, y):\n", + " return np.linalg.norm(x-y)\n", + "\n", + "\n", + "dataset = pd.read_csv('iris.csv')\n", + "dataset = dataset.iloc[:,:-1]\n", + "dataset = dataset.iloc[: , 1:]\n", + "dataset = dataset.values\n", + "model = TrainModel(dataset, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a8a592f", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.8.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}