{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "**Chapter 9 – Unsupervised Learning**\n", "\n", "_This notebook contains all the sample code in chapter 9._" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", " \n", "
 \n", " Run in Google Colab\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, let's import a few common modules, ensure MatplotLib plots figures inline and prepare a function to save the figures. We also check that Python 3.5 or later is installed (although Python 2.x may work, it is deprecated so we strongly recommend you use Python 3 instead), as well as Scikit-Learn ≥0.20." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Python ≥3.5 is required\n", "import sys\n", "assert sys.version_info >= (3, 5)\n", "\n", "# Scikit-Learn ≥0.20 is required\n", "import sklearn\n", "assert sklearn.__version__ >= \"0.20\"\n", "\n", "# Common imports\n", "import numpy as np\n", "import os\n", "\n", "# to make this notebook's output stable across runs\n", "np.random.seed(42)\n", "\n", "# To plot pretty figures\n", "%matplotlib inline\n", "import matplotlib as mpl\n", "import matplotlib.pyplot as plt\n", "mpl.rc('axes', labelsize=14)\n", "mpl.rc('xtick', labelsize=12)\n", "mpl.rc('ytick', labelsize=12)\n", "\n", "# Where to save the figures\n", "PROJECT_ROOT_DIR = \".\"\n", "CHAPTER_ID = \"unsupervised_learning\"\n", "IMAGES_PATH = os.path.join(PROJECT_ROOT_DIR, \"images\", CHAPTER_ID)\n", "os.makedirs(IMAGES_PATH, exist_ok=True)\n", "\n", "def save_fig(fig_id, tight_layout=True, fig_extension=\"png\", resolution=300):\n", " path = os.path.join(IMAGES_PATH, fig_id + \".\" + fig_extension)\n", " print(\"Saving figure\", fig_id)\n", " if tight_layout:\n", " plt.tight_layout()\n", " plt.savefig(path, format=fig_extension, dpi=resolution)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Clustering" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Introduction – Classification _vs_ Clustering" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from sklearn.datasets import load_iris" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array(['setosa', 'versicolor', 'virginica'], dtype='" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plt.figure(figsize=(9, 3.5))\n", "\n", "plt.subplot(121)\n", "plt.plot(X[y==0, 2], X[y==0, 3], \"yo\", label=\"Iris setosa\")\n", "plt.plot(X[y==1, 2], X[y==1, 3], \"bs\", label=\"Iris versicolor\")\n", "plt.plot(X[y==2, 2], X[y==2, 3], \"g^\", label=\"Iris virginica\")\n", "plt.xlabel(\"Petal length\", fontsize=14)\n", "plt.ylabel(\"Petal width\", fontsize=14)\n", "plt.legend(fontsize=12)\n", "\n", "plt.subplot(122)\n", "plt.scatter(X[:, 2], X[:, 3], c=\"k\", marker=\".\")\n", "plt.xlabel(\"Petal length\", fontsize=14)\n", "plt.tick_params(labelleft=False)\n", "\n", "save_fig(\"classification_vs_clustering_plot\")\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A Gaussian mixture model (explained below) can actually separate these clusters pretty well (using all 4 features: petal length & width, and sepal length & width)." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "from sklearn.mixture import GaussianMixture" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "y_pred = GaussianMixture(n_components=3, random_state=42).fit(X).predict(X)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's map each cluster to a class. Instead of hard coding the mapping (as is done in the book, for simplicity), we will pick the most common class for each cluster (using the scipy.stats.mode() function):" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{1: 0, 2: 1, 0: 2}" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from scipy import stats\n", "\n", "mapping = {}\n", "for class_id in np.unique(y):\n", " mode, _ = stats.mode(y_pred[y==class_id])\n", " mapping[mode[0]] = class_id\n", "\n", "mapping" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "y_pred = np.array([mapping[cluster_id] for cluster_id in y_pred])" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYkAAAENCAYAAAD6/JlzAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAsDklEQVR4nO3de3xV9Znv8c+TEAkkQFRCKMilzICiHi8ltVorOmMLY3u81qkXaGunHqzYGSqnlXqh3vBS7WHOdMRWZsBLoRedCvZoddQqOipeYgE72EIFCZVLCAoBBJKQPOePtQM7yd7J2tn3ne/79dov9/7t31rr2Ynkt9dav+f3mLsjIiISS1G2AxARkdylQUJEROLSICEiInFpkBARkbg0SIiISFx9sh1AKg0ePNhHjx6d7TBERPLK22+/vd3dK2O9V1CDxOjRo6mpqcl2GCIiecXMauO9p8tNIiISlwYJERGJS4OEiIjEpUFCRETiKqgb113ZtWsX27Zto7m5Oduh9DolJSUMGTKEgQMHZjsUEUlQxgYJM+sL3A98HjgCeA+4wd2fjtH3CmABsC+q+X+6+7KeHHvXrl3U1dUxfPhw+vXrh5n1ZDfSA+7Ovn372LRpE4AGCkmrLbu3cOmvL+VXF/+KoeVDU7JtMvssBJm83NQH+AtwJjAImA08amaj4/Rf7u7lUY9lPT3wtm3bGD58OP3799cAkWFmRv/+/Rk+fDjbtm3LdjhS4G5/+XZe2fgKt790e8q2TWafhSBjg4S7f+zut7j7BndvdfcngfeBCek+dnNzM/369Uv3YaQL/fr106U+Sastu7fw4MoHafVWHlz5IFv3bE1622T2WSiyduPazKqAccDqOF1ONrPtZrbWzGabWcxLY2Y2zcxqzKymvr6+q+MlH7T0mH7+km63v3w7rd4KQIu3JPTNP962yeyzUGRlkDCzEmAx8LC7/ylGl5eB44EhwJeBy4DvxdqXu89392p3r66sjJlVLiIFru0bf1NLEwBNLU2hv/nH23bV1lU93mchyfggYWZFwM+AJuDbsfq4+3p3fz9yWeoPwG3AxRkMM6fccsstTJ06NdthiOSs6G/8bcJ+84+37ZTHp/R4n4Uko4OEBdccFgBVwJfdPexFagcK+nrFz3/+c6qrqykvL+cTn/gE55xzDq+88krK9r9hwwbMjAMHDqRsnwDTpk3j6KOPpqioiIceeiil+xYJa/kHyw9+42/T1NLEax+81uNt1+1Y1+N9FpJM50n8BBgPfN7d98XrZGbnAL939zozO4ZgJtRjGYqxnbq6xaxffyONjRvp23ckY8bcQVXVlJQeY+7cudx999389Kc/ZfLkyRx22GE888wzPPHEE3zuc59L6bF66sCBA/Tp0/l/lxNPPJFLLrmEWbNmZSEqkcCKq1ZkZdveIGNnEmY2CrgKOAnYamZ7Io8pZjYy8nxkpPvZwDtm9jHwW+Bx4M5Mxdqmrm4xa9ZMo7GxFnAaG2tZs2YadXWLU3aMhoYGfvCDHzBv3jwuuugiysrKKCkp4dxzz+Xee+/t1H/ZsmUcddRR7dpGjx7N888/D8Cbb75JdXU1AwcOpKqqipkzZwIwceJEACoqKigvL2f58uUALFy4kPHjx3P44YczefJkamsPLQZpZsybN4+xY8cyduzYmPFfc801nH322ZSWlib/w5CCsGX3Fs586My0X7tfuWUlFXdX8E7dO2k9Tm+XySmwte5u7l7aIf9hsbtvjDzfGOn7XXevcvcydx/j7j9I4NJUyqxffyOtrXvbtbW27mX9+htTdozly5ezf/9+LrzwwpTsb8aMGcyYMYNdu3axbt06vvKVrwDw8ssvA7Bz50727NnDaaedxtKlS7nzzjt5/PHHqa+v54wzzuCyyy5rt7+lS5fyxhtv8O6776YkPil8mcormLpkKg2NDVz+68vTepzeTms3daGxcWNC7T3x4YcfMnjw4JiXcnqipKSE9957j+3bt1NeXs6pp54at+8DDzzA9ddfz/jx4+nTpw833HADK1eubHc2cf3113PEEUcoz0RCyVRewcotK1ldH8yeX12/WmcTaaRBogt9+45MqL0njjzySLZv356yG8oLFixg7dq1HHPMMXz605/mySefjNu3traWGTNmUFFRQUVFBUcccQTufnAJDYARI0akJC7pHTKVVzB1SfvZfjqbSB8NEl0YM+YOior6t2srKurPmDF3pOwYp512GqWlpSxdujRU/7KyMvbuPXQJrKWlhegkwrFjx/KLX/yCbdu2MWvWLC6++GI+/vjjmMlsI0aM4IEHHmDnzp0HH/v27eOzn/3swT5KgpOwkslVSET0WUQbnU2kjwaJLlRVTeHoo+fTt+8owOjbdxRHHz0/pbObBg0axG233cY111zD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\n", "text/plain": [ "