{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Iris flower dataset\n", "\n", "The [iris flower dataset](https://en.wikipedia.org/wiki/Iris_flower_data_set) is a common dataset used in machine learning.\n", "\n", "It has been created Ronald Fisher in 1936. It contains the petal length, petal width, sepal length and sepal width of 150 iris flowers from 3 different species.\n", "\n", "Dataset has been downloaded from [Kaggle](https://www.kaggle.com/uciml/iris).\n", "\n", "To go through this example, you need to install AutoClassWrapper:\n", "```bash\n", "$ python3 -m pip install autoclasswrapper\n", "```\n", "\n", "[AutoClass C](https://ti.arc.nasa.gov/tech/rse/synthesis-projects-applications/autoclass/autoclass-c/) also needs to be installed locally and available in path.\n", "\n", "Here is a quick solution for a Linux Bash shell:\n", "```bash\n", "wget https://ti.arc.nasa.gov/m/project/autoclass/autoclass-c-3-3-6.tar.gz\n", "tar zxvf autoclass-c-3-3-6.tar.gz\n", "rm -f autoclass-c-3-3-6.tar.gz\n", "export PATH=$PATH:$(pwd)/autoclass-c\n", "\n", "# if you use a 64-bit operating system,\n", "# you also need to install the standard 32-bit C libraries:\n", "# sudo apt-get install -y libc6-i386\n", "```" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Python: 3.7.1 | packaged by conda-forge | (default, Feb 26 2019, 04:48:14) \n", "[GCC 7.3.0]\n", "matplotlib: 3.0.3\n", "numpy: 1.16.2\n", "pandas: 0.24.1\n", "AutoClassWrapper: 1.4.1\n" ] } ], "source": [ "from pathlib import Path\n", "import sys\n", "import time\n", "\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "from matplotlib.lines import Line2D\n", "import numpy as np\n", "import pandas as pd\n", "\n", "%matplotlib inline\n", "\n", "print(\"Python:\", sys.version)\n", "print(\"matplotlib:\", matplotlib.__version__)\n", "print(\"numpy:\", np.__version__)\n", "print(\"pandas:\", pd.__version__)\n", "\n", "import autoclasswrapper as wrapper\n", "print(\"AutoClassWrapper:\", wrapper.__version__)\n", "\n", "version = sys.version_info \n", "if not ((version.major >= 3) and (version.minor >= 6)):\n", " sys.exit(\"Need Python>=3.6\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Dataset preparation" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
SepalLengthCmSepalWidthCmPetalLengthCmPetalWidthCmSpecies
Id
15.13.51.40.2Iris-setosa
24.93.01.40.2Iris-setosa
34.73.21.30.2Iris-setosa
44.63.11.50.2Iris-setosa
55.03.61.40.2Iris-setosa
\n", "
" ], "text/plain": [ " SepalLengthCm SepalWidthCm PetalLengthCm PetalWidthCm Species\n", "Id \n", "1 5.1 3.5 1.4 0.2 Iris-setosa\n", "2 4.9 3.0 1.4 0.2 Iris-setosa\n", "3 4.7 3.2 1.3 0.2 Iris-setosa\n", "4 4.6 3.1 1.5 0.2 Iris-setosa\n", "5 5.0 3.6 1.4 0.2 Iris-setosa" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = pd.read_csv(\"iris.csv\", index_col=\"Id\")\n", "df.head()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
countuniquetopfreqmeanstdmin25%50%75%max
SepalLengthCm150NaNNaNNaN5.843330.8280664.35.15.86.47.9
SepalWidthCm150NaNNaNNaN3.0540.43359422.833.34.4
PetalLengthCm150NaNNaNNaN3.758671.7644211.64.355.16.9
PetalWidthCm150NaNNaNNaN1.198670.7631610.10.31.31.82.5
Species1503Iris-versicolor50NaNNaNNaNNaNNaNNaNNaN
\n", "
" ], "text/plain": [ " count unique top freq mean std min 25% \\\n", "SepalLengthCm 150 NaN NaN NaN 5.84333 0.828066 4.3 5.1 \n", "SepalWidthCm 150 NaN NaN NaN 3.054 0.433594 2 2.8 \n", "PetalLengthCm 150 NaN NaN NaN 3.75867 1.76442 1 1.6 \n", "PetalWidthCm 150 NaN NaN NaN 1.19867 0.763161 0.1 0.3 \n", "Species 150 3 Iris-versicolor 50 NaN NaN NaN NaN \n", "\n", " 50% 75% max \n", "SepalLengthCm 5.8 6.4 7.9 \n", "SepalWidthCm 3 3.3 4.4 \n", "PetalLengthCm 4.35 5.1 6.9 \n", "PetalWidthCm 1.3 1.8 2.5 \n", "Species NaN NaN NaN " ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.describe(include='all').T" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Add discrete values\n", "\n", "Apart iris species, data in this dataset are numerical values only.\n", "\n", "To demonstrate the ability of AutoClass C to handle discrete values, we will convert `PetalWidthCm` column to discrete categorical values." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "def categorize(value):\n", " if value <= 0.75:\n", " return \"small\"\n", " elif 0.75 < value <= 1.75:\n", " return \"medium\"\n", " elif 1.75 < value:\n", " return \"large\"" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
SepalLengthCmSepalWidthCmPetalLengthCmPetalWidthCmSpeciesPetalWidthCat
Id
15.13.51.40.2Iris-setosasmall
24.93.01.40.2Iris-setosasmall
34.73.21.30.2Iris-setosasmall
44.63.11.50.2Iris-setosasmall
55.03.61.40.2Iris-setosasmall
\n", "
" ], "text/plain": [ " SepalLengthCm SepalWidthCm PetalLengthCm PetalWidthCm Species \\\n", "Id \n", "1 5.1 3.5 1.4 0.2 Iris-setosa \n", "2 4.9 3.0 1.4 0.2 Iris-setosa \n", "3 4.7 3.2 1.3 0.2 Iris-setosa \n", "4 4.6 3.1 1.5 0.2 Iris-setosa \n", "5 5.0 3.6 1.4 0.2 Iris-setosa \n", "\n", " PetalWidthCat \n", "Id \n", "1 small \n", "2 small \n", "3 small \n", "4 small \n", "5 small " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df[\"PetalWidthCat\"] = df[\"PetalWidthCm\"].apply(categorize)\n", "df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Add missing values\n", "\n", "To demonstrate the ability of AutoClass C to handle missing values, we will delete some values." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
SepalLengthCmSepalWidthCmPetalLengthCmPetalWidthCmSpeciesPetalWidthCat
Id
1NaN3.51.40.2Iris-setosasmall
24.9NaN1.40.2Iris-setosasmall
34.73.2NaN0.2Iris-setosasmall
44.63.11.50.2Iris-setosasmall
55.03.61.40.2Iris-setosasmall
\n", "
" ], "text/plain": [ " SepalLengthCm SepalWidthCm PetalLengthCm PetalWidthCm Species \\\n", "Id \n", "1 NaN 3.5 1.4 0.2 Iris-setosa \n", "2 4.9 NaN 1.4 0.2 Iris-setosa \n", "3 4.7 3.2 NaN 0.2 Iris-setosa \n", "4 4.6 3.1 1.5 0.2 Iris-setosa \n", "5 5.0 3.6 1.4 0.2 Iris-setosa \n", "\n", " PetalWidthCat \n", "Id \n", "1 small \n", "2 small \n", "3 small \n", "4 small \n", "5 small " ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.loc[1, \"SepalLengthCm\"] = np.nan\n", "df.loc[2, \"SepalWidthCm\"] = np.nan\n", "df.loc[3, \"PetalLengthCm\"] = np.nan\n", "df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Save dataset in two different files. One with real values and the other one with discrete values (column `PetalWidthCat`).\n", "Missing values must encoded with nothing." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Id\tSepalLengthCm\tSepalWidthCm\tPetalLengthCm\n", "1\t\t3.5\t1.4\n", "2\t4.9\t\t1.4\n", "3\t4.7\t3.2\t\n", "4\t4.6\t3.1\t1.5\n", "5\t5.0\t3.6\t1.4\n", "6\t5.4\t3.9\t1.7\n", "7\t4.6\t3.4\t1.4\n", "8\t5.0\t3.4\t1.5\n", "9\t4.4\t2.9\t1.4\n" ] } ], "source": [ "df.drop([\"Species\", \"PetalWidthCm\", \"PetalWidthCat\"], axis=1).to_csv(\"iris_real.tsv\", sep=\"\\t\", header=True)\n", "!head iris_real.tsv" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Id\tPetalWidthCat\n", "1\tsmall\n", "2\tsmall\n", "3\tsmall\n", "4\tsmall\n", "5\tsmall\n", "6\tsmall\n", "7\tsmall\n", "8\tsmall\n", "9\tsmall\n" ] } ], "source": [ "df[\"PetalWidthCat\"].to_csv(\"iris_discrete.tsv\", sep=\"\\t\", header=True)\n", "!head iris_discrete.tsv" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 1 - prepare input files" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2019-07-07 19:07:58 INFO Reading data file 'iris_real.tsv' as 'real scalar' with error 0.01\n", "2019-07-07 19:07:58 INFO Detected encoding: ascii\n", "2019-07-07 19:07:59 INFO Found 150 rows and 4 columns\n", "2019-07-07 19:07:59 DEBUG Checking column names\n", "2019-07-07 19:07:59 DEBUG Index name 'Id'\n", "2019-07-07 19:07:59 DEBUG Column name 'SepalLengthCm'\n", "2019-07-07 19:07:59 DEBUG Column name 'SepalWidthCm'\n", "2019-07-07 19:07:59 DEBUG Column name 'PetalLengthCm'\n", "2019-07-07 19:07:59 INFO Checking data format\n", "2019-07-07 19:07:59 INFO Column 'SepalLengthCm'\n", "2019-07-07 19:07:59 INFO count 149.000000\n", "2019-07-07 19:07:59 INFO mean 5.848322\n", "2019-07-07 19:07:59 INFO std 0.828594\n", "2019-07-07 19:07:59 INFO min 4.300000\n", "2019-07-07 19:07:59 INFO 50% 5.800000\n", "2019-07-07 19:07:59 INFO max 7.900000\n", "2019-07-07 19:07:59 INFO ---\n", "2019-07-07 19:07:59 INFO Column 'SepalWidthCm'\n", "2019-07-07 19:07:59 INFO count 149.000000\n", "2019-07-07 19:07:59 INFO mean 3.054362\n", "2019-07-07 19:07:59 INFO std 0.435034\n", "2019-07-07 19:07:59 INFO min 2.000000\n", "2019-07-07 19:07:59 INFO 50% 3.000000\n", "2019-07-07 19:07:59 INFO max 4.400000\n", "2019-07-07 19:07:59 INFO ---\n", "2019-07-07 19:07:59 INFO Column 'PetalLengthCm'\n", "2019-07-07 19:07:59 INFO count 149.000000\n", "2019-07-07 19:07:59 INFO mean 3.775168\n", "2019-07-07 19:07:59 INFO std 1.758720\n", "2019-07-07 19:07:59 INFO min 1.000000\n", "2019-07-07 19:07:59 INFO 50% 4.400000\n", "2019-07-07 19:07:59 INFO max 6.900000\n", "2019-07-07 19:07:59 INFO ---\n", "2019-07-07 19:07:59 INFO Reading data file 'iris_discrete.tsv' as 'discrete'\n", "2019-07-07 19:07:59 INFO Detected encoding: ascii\n", "2019-07-07 19:07:59 INFO Found 150 rows and 2 columns\n", "2019-07-07 19:07:59 DEBUG Checking column names\n", "2019-07-07 19:07:59 DEBUG Index name 'Id'\n", "2019-07-07 19:07:59 DEBUG Column name 'PetalWidthCat'\n", "2019-07-07 19:07:59 INFO Checking data format\n", "2019-07-07 19:07:59 INFO Column 'PetalWidthCat': 3 different values\n", "2019-07-07 19:07:59 INFO Preparing input data\n", "2019-07-07 19:07:59 INFO Final dataframe has 150 lines and 5 columns\n", "2019-07-07 19:07:59 INFO Searching for missing values\n", "2019-07-07 19:07:59 WARNING Missing values found in column: SepalLengthCm\n", "2019-07-07 19:07:59 WARNING Missing values found in column: SepalWidthCm\n", "2019-07-07 19:07:59 WARNING Missing values found in column: PetalLengthCm\n", "2019-07-07 19:07:59 INFO Writing autoclass.db2 file\n", "2019-07-07 19:07:59 INFO If any, missing values will be encoded as '?'\n", "2019-07-07 19:07:59 DEBUG Writing autoclass.tsv file [for later use]\n", "2019-07-07 19:07:59 INFO Writing .hd2 file\n", "2019-07-07 19:07:59 INFO Writing .model file\n", "2019-07-07 19:07:59 INFO Writing .s-params file\n", "2019-07-07 19:07:59 INFO Writing .r-params file\n" ] } ], "source": [ "# Create object to prepare dataset.\n", "clust = wrapper.Input()\n", "\n", "# Load datasets from tsv files.\n", "clust.add_input_data(\"iris_real.tsv\", \"real scalar\")\n", "clust.add_input_data(\"iris_discrete.tsv\", \"discrete\")\n", "\n", "# Prepare input data:\n", "# - create a final dataframe\n", "# - merge datasets if multiple inputs\n", "clust.prepare_input_data()\n", "\n", "# Create files needed by AutoClass.\n", "clust.create_db2_file()\n", "clust.create_hd2_file()\n", "clust.create_model_file()\n", "# We wanted reproducible results to ease documentation.\n", "# But bear in mind, that this parameter is not advised by authors of AutoClass C in production run.\n", "# Use clust.create_sparams_file() instead.\n", "clust.create_sparams_file(reproducible_run=True)\n", "clust.create_rparams_file()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 2 - prepare run script & run autoclass" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2019-07-07 19:08:02 INFO AutoClass C executable found in /home/pierre/.soft/bin/autoclass\n", "2019-07-07 19:08:02 INFO Writing run file\n", "2019-07-07 19:08:02 INFO AutoClass C executable found in /home/pierre/.soft/bin/autoclass\n", "2019-07-07 19:08:02 INFO AutoClass C version: AUTOCLASS C (version 3.3.6unx)\n", "2019-07-07 19:08:02 INFO Running clustering...\n" ] } ], "source": [ "# Clean previous status file and results if a classification has already been performed.\n", "!rm -f autoclass-run-* *.results-bin\n", "\n", "# Search autoclass in path.\n", "wrapper.search_autoclass_in_path()\n", "\n", "# Create object to run AutoClass.\n", "run = wrapper.Run()\n", "\n", "# Prepare run script.\n", "run.create_run_file()\n", "\n", "# Run AutoClass.\n", "run.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 3 - parse and format results" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2019-07-07 19:08:05 INFO Extracting autoclass results\n", "2019-07-07 19:08:05 INFO Found 150 cases classified in 4 classes\n", "2019-07-07 19:08:05 INFO Aggregating input data\n", "2019-07-07 19:08:05 INFO Writing classes + probabilities .tsv file\n", "2019-07-07 19:08:05 INFO Writing .cdt file\n", "2019-07-07 19:08:05 INFO Writing .cdt file (with probabilities)\n", "2019-07-07 19:08:05 INFO Writing class statistics\n", "2019-07-07 19:08:05 INFO Writing dendrogram\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "timer = 0\n", "step = 2\n", "while not Path(\"autoclass-run-success\").exists():\n", " timer += step\n", " sys.stdout.write(\"\\r\")\n", " sys.stdout.write(f\"Time: {timer} sec.\")\n", " sys.stdout.flush()\n", " time.sleep(step)\n", "\n", "results = wrapper.Output()\n", "results.extract_results()\n", "results.aggregate_input_data()\n", "results.write_cdt()\n", "results.write_cdt(with_proba=True)\n", "results.write_class_stats()\n", "results.write_dendrogram()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For comparison, add class number to original dataset." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
SepalLengthCmSepalWidthCmPetalLengthCmPetalWidthCmSpeciesPetalWidthCatmain-class
Id
1NaN3.51.40.2Iris-setosasmall1
24.9NaN1.40.2Iris-setosasmall1
34.73.2NaN0.2Iris-setosasmall1
44.63.11.50.2Iris-setosasmall1
55.03.61.40.2Iris-setosasmall1
\n", "
" ], "text/plain": [ " SepalLengthCm SepalWidthCm PetalLengthCm PetalWidthCm Species \\\n", "Id \n", "1 NaN 3.5 1.4 0.2 Iris-setosa \n", "2 4.9 NaN 1.4 0.2 Iris-setosa \n", "3 4.7 3.2 NaN 0.2 Iris-setosa \n", "4 4.6 3.1 1.5 0.2 Iris-setosa \n", "5 5.0 3.6 1.4 0.2 Iris-setosa \n", "\n", " PetalWidthCat main-class \n", "Id \n", "1 small 1 \n", "2 small 1 \n", "3 small 1 \n", "4 small 1 \n", "5 small 1 " ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df_class = pd.read_csv(\"autoclass_out.tsv\", sep=\"\\t\", index_col=\"Id\")\n", "df = pd.concat([df, df_class[\"main-class\"]], axis=1, join=\"outer\")\n", "df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Compute class distribution for iris species" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
main-class1234
Species
Iris-setosa50000
Iris-versicolor023027
Iris-virginica016322
\n", "
" ], "text/plain": [ "main-class 1 2 3 4\n", "Species \n", "Iris-setosa 50 0 0 0\n", "Iris-versicolor 0 23 0 27\n", "Iris-virginica 0 16 32 2" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.pivot_table(df, index=[\"Species\"], columns=[\"main-class\"], values=[], aggfunc=len, fill_value=0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The setosa species is found only in cluster 1. Note that missing values did not interfere in the classification of the 3 first flowers as setosa.\n", "\n", "The versicolor species is found in cluster 2 and 4.\n", "\n", "The virginica species is found mainly in cluster 3 but also in cluster 2 and 4.\n", "\n", "AutoClass-C determines automatically what is the optimal number of classes. It's always a good idea to analyse the final results to check if some cluster can be merged (for instance cluster 2 and 4).\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.7.1" } }, "nbformat": 4, "nbformat_minor": 2 }