2918 lines
150 KiB
Plaintext
2918 lines
150 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 1,
|
||
"metadata": {
|
||
"id": "Q6ifg03dKPR4"
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"############################\n",
|
||
"# BLOCK 1: IMPORTS\n",
|
||
"############################\n",
|
||
"\n",
|
||
"# libraries!\n",
|
||
"import numpy as np # numpy is Python's \"array\" library\n",
|
||
"import pandas as pd # Pandas is Python's \"data\" library (\"dataframe\" == spreadsheet)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": []
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 2,
|
||
"metadata": {
|
||
"id": "0ghtg7ecRQ50"
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"#############################\n",
|
||
"# BLOCK 2: VARIABLES LISTING\n",
|
||
"#############################\n",
|
||
"\n",
|
||
"# for reference, as you work throughout, come back and list all the variable names\n",
|
||
"# here along with what that variable holds\n",
|
||
"\n",
|
||
"# Variable: Contents\n",
|
||
"# -------------------\n",
|
||
"# [FILL IN VARS BELOW]\n",
|
||
"#"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"metadata": {
|
||
"colab": {
|
||
"base_uri": "https://localhost:8080/",
|
||
"height": 424
|
||
},
|
||
"id": "DjCaxH3BKJ_O",
|
||
"outputId": "ab33e45d-d332-412e-db2a-88cc4c9528f4"
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"####################################\n",
|
||
"#from sklearn.datasets import load_iris\n",
|
||
"#data = load_iris()\n",
|
||
"#df = pd.DataFrame(data.data, columns=data.feature_names)\n",
|
||
"#df"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 4,
|
||
"metadata": {
|
||
"id": "ANJhDRNPKPR5"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"iris.csv: file read into a pandas DataFrame.\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/html": [
|
||
"<div>\n",
|
||
"<style scoped>\n",
|
||
" .dataframe tbody tr th:only-of-type {\n",
|
||
" vertical-align: middle;\n",
|
||
" }\n",
|
||
"\n",
|
||
" .dataframe tbody tr th {\n",
|
||
" vertical-align: top;\n",
|
||
" }\n",
|
||
"\n",
|
||
" .dataframe thead th {\n",
|
||
" text-align: right;\n",
|
||
" }\n",
|
||
"</style>\n",
|
||
"<table border=\"1\" class=\"dataframe\">\n",
|
||
" <thead>\n",
|
||
" <tr style=\"text-align: right;\">\n",
|
||
" <th></th>\n",
|
||
" <th>sepallen</th>\n",
|
||
" <th>sepalwid</th>\n",
|
||
" <th>petallen</th>\n",
|
||
" <th>petalwid</th>\n",
|
||
" <th>irisname</th>\n",
|
||
" <th>junk</th>\n",
|
||
" </tr>\n",
|
||
" </thead>\n",
|
||
" <tbody>\n",
|
||
" <tr>\n",
|
||
" <th>0</th>\n",
|
||
" <td>NaN</td>\n",
|
||
" <td>3.5</td>\n",
|
||
" <td>1.4</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" <td>remove_me</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>1</th>\n",
|
||
" <td>4.9</td>\n",
|
||
" <td>3.0</td>\n",
|
||
" <td>1.4</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" <td>remove_me</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>2</th>\n",
|
||
" <td>4.7</td>\n",
|
||
" <td>3.2</td>\n",
|
||
" <td>1.3</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" <td>remove_me</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>3</th>\n",
|
||
" <td>4.6</td>\n",
|
||
" <td>3.1</td>\n",
|
||
" <td>1.5</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" <td>remove_me</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>4</th>\n",
|
||
" <td>5.0</td>\n",
|
||
" <td>3.6</td>\n",
|
||
" <td>1.4</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" <td>remove_me</td>\n",
|
||
" </tr>\n",
|
||
" </tbody>\n",
|
||
"</table>\n",
|
||
"</div>"
|
||
],
|
||
"text/plain": [
|
||
" sepallen sepalwid petallen petalwid irisname junk\n",
|
||
"0 NaN 3.5 1.4 0.2 setosa remove_me\n",
|
||
"1 4.9 3.0 1.4 0.2 setosa remove_me\n",
|
||
"2 4.7 3.2 1.3 0.2 setosa remove_me\n",
|
||
"3 4.6 3.1 1.5 0.2 setosa remove_me\n",
|
||
"4 5.0 3.6 1.4 0.2 setosa remove_me"
|
||
]
|
||
},
|
||
"execution_count": 4,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"#############################\n",
|
||
"# BLOCK 3: READING DATA\n",
|
||
"#############################\n",
|
||
"\n",
|
||
"# let's read in our flower data...\n",
|
||
"#\n",
|
||
"filename = 'iris.csv'\n",
|
||
"df = pd.read_csv(filename, header=0) # encoding=\"latin1\" et al.\n",
|
||
"print(f\"{filename}: file read into a pandas DataFrame.\")\n",
|
||
"df.head()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 5,
|
||
"metadata": {
|
||
"colab": {
|
||
"base_uri": "https://localhost:8080/",
|
||
"height": 361
|
||
},
|
||
"id": "8v3oVZYTKPR6",
|
||
"outputId": "4110ac58-5c52-4cbf-a83f-16d9a7189ccb"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/html": [
|
||
"<div>\n",
|
||
"<style scoped>\n",
|
||
" .dataframe tbody tr th:only-of-type {\n",
|
||
" vertical-align: middle;\n",
|
||
" }\n",
|
||
"\n",
|
||
" .dataframe tbody tr th {\n",
|
||
" vertical-align: top;\n",
|
||
" }\n",
|
||
"\n",
|
||
" .dataframe thead th {\n",
|
||
" text-align: right;\n",
|
||
" }\n",
|
||
"</style>\n",
|
||
"<table border=\"1\" class=\"dataframe\">\n",
|
||
" <thead>\n",
|
||
" <tr style=\"text-align: right;\">\n",
|
||
" <th></th>\n",
|
||
" <th>sepallen</th>\n",
|
||
" <th>sepalwid</th>\n",
|
||
" <th>petallen</th>\n",
|
||
" <th>petalwid</th>\n",
|
||
" <th>irisname</th>\n",
|
||
" <th>junk</th>\n",
|
||
" </tr>\n",
|
||
" </thead>\n",
|
||
" <tbody>\n",
|
||
" <tr>\n",
|
||
" <th>0</th>\n",
|
||
" <td>NaN</td>\n",
|
||
" <td>3.5</td>\n",
|
||
" <td>1.4</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" <td>remove_me</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>1</th>\n",
|
||
" <td>4.9</td>\n",
|
||
" <td>3.0</td>\n",
|
||
" <td>1.4</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" <td>remove_me</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>2</th>\n",
|
||
" <td>4.7</td>\n",
|
||
" <td>3.2</td>\n",
|
||
" <td>1.3</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" <td>remove_me</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>3</th>\n",
|
||
" <td>4.6</td>\n",
|
||
" <td>3.1</td>\n",
|
||
" <td>1.5</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" <td>remove_me</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>...</th>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>146</th>\n",
|
||
" <td>6.3</td>\n",
|
||
" <td>2.5</td>\n",
|
||
" <td>5.0</td>\n",
|
||
" <td>1.9</td>\n",
|
||
" <td>virginica</td>\n",
|
||
" <td>remove_me</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>147</th>\n",
|
||
" <td>6.5</td>\n",
|
||
" <td>3.0</td>\n",
|
||
" <td>5.2</td>\n",
|
||
" <td>2.0</td>\n",
|
||
" <td>virginica</td>\n",
|
||
" <td>remove_me</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>148</th>\n",
|
||
" <td>6.2</td>\n",
|
||
" <td>3.4</td>\n",
|
||
" <td>5.4</td>\n",
|
||
" <td>2.3</td>\n",
|
||
" <td>alieniris</td>\n",
|
||
" <td>remove_me</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>149</th>\n",
|
||
" <td>5.9</td>\n",
|
||
" <td>3.0</td>\n",
|
||
" <td>5.1</td>\n",
|
||
" <td>1.8</td>\n",
|
||
" <td>alieniris</td>\n",
|
||
" <td>remove_me</td>\n",
|
||
" </tr>\n",
|
||
" </tbody>\n",
|
||
"</table>\n",
|
||
"<p>150 rows × 6 columns</p>\n",
|
||
"</div>"
|
||
],
|
||
"text/plain": [
|
||
" sepallen sepalwid petallen petalwid irisname junk\n",
|
||
"0 NaN 3.5 1.4 0.2 setosa remove_me\n",
|
||
"1 4.9 3.0 1.4 0.2 setosa remove_me\n",
|
||
"2 4.7 3.2 1.3 0.2 setosa remove_me\n",
|
||
"3 4.6 3.1 1.5 0.2 setosa remove_me\n",
|
||
".. ... ... ... ... ... ...\n",
|
||
"146 6.3 2.5 5.0 1.9 virginica remove_me\n",
|
||
"147 6.5 3.0 5.2 2.0 virginica remove_me\n",
|
||
"148 6.2 3.4 5.4 2.3 alieniris remove_me\n",
|
||
"149 5.9 3.0 5.1 1.8 alieniris remove_me\n",
|
||
"\n",
|
||
"[150 rows x 6 columns]"
|
||
]
|
||
},
|
||
"execution_count": 5,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"#############################\n",
|
||
"# BLOCK 4: SETTING OPTIONS\n",
|
||
"#############################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# a dataframe is a \"spreadsheet in Python\"\n",
|
||
"# (this one seems to have an extra column!)\n",
|
||
"#\n",
|
||
"pd.set_option('display.max_rows', 8) # None for no limit; default: 10\n",
|
||
"pd.set_option('display.min_rows', 8) # None for no limit; default: 10\n",
|
||
"# let's view it!\n",
|
||
"df"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"metadata": {
|
||
"colab": {
|
||
"base_uri": "https://localhost:8080/"
|
||
},
|
||
"id": "aksGQcJ8KPR6",
|
||
"outputId": "4cbdedf5-cabe-4263-c06c-26bbb8f509b1"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"<class 'pandas.DataFrame'>\n",
|
||
"RangeIndex: 150 entries, 0 to 149\n",
|
||
"Data columns (total 6 columns):\n",
|
||
" # Column Non-Null Count Dtype \n",
|
||
"--- ------ -------------- ----- \n",
|
||
" 0 sepallen 149 non-null float64\n",
|
||
" 1 sepalwid 150 non-null float64\n",
|
||
" 2 petallen 150 non-null float64\n",
|
||
" 3 petalwid 149 non-null float64\n",
|
||
" 4 irisname 149 non-null str \n",
|
||
" 5 junk 150 non-null str \n",
|
||
"dtypes: float64(4), str(2)\n",
|
||
"memory usage: 7.2 KB\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"##############################\n",
|
||
"# BLOCK 5: USING DF'S .info()\n",
|
||
"##############################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# let's look at our pandas DataFrame's info (Aargh: that extra column!)\n",
|
||
"#\n",
|
||
"df.info()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 7,
|
||
"metadata": {
|
||
"colab": {
|
||
"base_uri": "https://localhost:8080/"
|
||
},
|
||
"id": "ADWpUqeKKPR7",
|
||
"outputId": "76ac8d2f-0d98-4721-e0f2-b79b48736b00"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"<class 'pandas.DataFrame'>\n",
|
||
"RangeIndex: 150 entries, 0 to 149\n",
|
||
"Data columns (total 5 columns):\n",
|
||
" # Column Non-Null Count Dtype \n",
|
||
"--- ------ -------------- ----- \n",
|
||
" 0 sepallen 149 non-null float64\n",
|
||
" 1 sepalwid 150 non-null float64\n",
|
||
" 2 petallen 150 non-null float64\n",
|
||
" 3 petalwid 149 non-null float64\n",
|
||
" 4 irisname 149 non-null str \n",
|
||
"dtypes: float64(4), str(1)\n",
|
||
"memory usage: 6.0 KB\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"#############################\n",
|
||
"# BLOCK 6: CLEANING DATA\n",
|
||
"#############################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# let's drop that last column (dropping is usually by _name_):\n",
|
||
"#\n",
|
||
"# if you want a list of the column names use df.columns\n",
|
||
"#col5name = df.columns[5] # get column name at index 5\n",
|
||
"\n",
|
||
"df_clean = df.drop(columns=['junk']) # drop by name is typical, but what else is possible?\n",
|
||
"df_clean.info() # Is the bad last column gone?"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 8,
|
||
"metadata": {
|
||
"colab": {
|
||
"base_uri": "https://localhost:8080/"
|
||
},
|
||
"id": "9RAydRsiKPR7",
|
||
"outputId": "18ec37ec-3a95-4ef9-c111-847ac8f6ecec"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"FEATURES: Index(['sepallen', 'sepalwid', 'petallen', 'petalwid', 'irisname'], dtype='str')\n",
|
||
"\n",
|
||
"First feature: sepallen\n",
|
||
"\n",
|
||
"feature_name_to_index: {'sepallen': 0, 'sepalwid': 1, 'petallen': 2, 'petalwid': 3, 'irisname': 4}\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"##############################\n",
|
||
"# BLOCK 7: FEATURE NAMES DICT\n",
|
||
"##############################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# let's keep our column names in variables, for reference;\n",
|
||
"# in machine learning contexts, these are referred to as \"features\"\n",
|
||
"#\n",
|
||
"features = df_clean.columns # \"list\" of columns\n",
|
||
"print(f\"FEATURES: {features}\\n\")\n",
|
||
" # It's a \"pandas\" list, called an Index\n",
|
||
" # use it just as a Python list of strings:\n",
|
||
"print(f\"First feature: {features[0]}\\n\")\n",
|
||
"\n",
|
||
"# let's create a dictionary to look up any column index by name\n",
|
||
"feature_name_to_index = {}\n",
|
||
"for i, name in enumerate(features):\n",
|
||
" feature_name_to_index[name] = i # using the name (as key), assign the value (i)\n",
|
||
"print(f\"feature_name_to_index: {feature_name_to_index}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 9,
|
||
"metadata": {
|
||
"colab": {
|
||
"base_uri": "https://localhost:8080/",
|
||
"height": 1000
|
||
},
|
||
"id": "PWkPTOGnKPR8",
|
||
"outputId": "f6a46e8f-0169-4646-864f-1b3ed749f238"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"<class 'pandas.DataFrame'>\n",
|
||
"RangeIndex: 150 entries, 0 to 149\n",
|
||
"Data columns (total 5 columns):\n",
|
||
" # Column Non-Null Count Dtype \n",
|
||
"--- ------ -------------- ----- \n",
|
||
" 0 sepallen 149 non-null float64\n",
|
||
" 1 sepalwid 150 non-null float64\n",
|
||
" 2 petallen 150 non-null float64\n",
|
||
" 3 petalwid 149 non-null float64\n",
|
||
" 4 irisname 149 non-null str \n",
|
||
"dtypes: float64(4), str(1)\n",
|
||
"memory usage: 6.0 KB\n",
|
||
"======================================================================\n",
|
||
" sepallen sepalwid petallen petalwid irisname\n",
|
||
"0 NaN 3.5 1.4 0.2 setosa\n",
|
||
"1 4.9 3.0 1.4 0.2 setosa\n",
|
||
"2 4.7 3.2 1.3 0.2 setosa\n",
|
||
"3 4.6 3.1 1.5 0.2 setosa\n",
|
||
".. ... ... ... ... ...\n",
|
||
"146 6.3 2.5 5.0 1.9 virginica\n",
|
||
"147 6.5 3.0 5.2 2.0 virginica\n",
|
||
"148 6.2 3.4 5.4 2.3 alieniris\n",
|
||
"149 5.9 3.0 5.1 1.8 alieniris\n",
|
||
"\n",
|
||
"[150 rows x 5 columns]\n",
|
||
"======================================================================\n",
|
||
"Irisname entries: <StringArray>\n",
|
||
"['setosa', nan, 'versicolor', 'virginica', 'alieniris']\n",
|
||
"Length: 5, dtype: str\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"##############################\n",
|
||
"# BLOCK 8: INSPECTING DATA\n",
|
||
"##############################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# let's look at our cleaned-up dataframe...\n",
|
||
"#\n",
|
||
"df_clean.info()\n",
|
||
"print('=' * 70)\n",
|
||
"\n",
|
||
"#\n",
|
||
"# Notice that the non-null count is _different_ for irisname!\n",
|
||
"# Why? Show a table and inspect...\n",
|
||
"print(df_clean)\n",
|
||
"print('=' * 70)\n",
|
||
"\n",
|
||
"# or more to the point, grab that column and inspect:\n",
|
||
"print(f\"Irisname entries: {df_clean['irisname'].unique()}\")\n",
|
||
"\n",
|
||
"# also note how the last two rows in the df have problems, among others...\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 10,
|
||
"metadata": {
|
||
"colab": {
|
||
"base_uri": "https://localhost:8080/"
|
||
},
|
||
"id": "4B2An3iML1ay",
|
||
"outputId": "5492db65-acd8-4c11-f669-742a79b6cb62"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"Index(['sepallen', 'sepalwid', 'petallen', 'petalwid', 'irisname', 'junk'], dtype='str')"
|
||
]
|
||
},
|
||
"execution_count": 10,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"df.columns"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 11,
|
||
"metadata": {
|
||
"id": "xM22NWdqKPR-",
|
||
"scrolled": false
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"<class 'pandas.DataFrame'>\n",
|
||
"Index: 147 entries, 1 to 149\n",
|
||
"Data columns (total 5 columns):\n",
|
||
" # Column Non-Null Count Dtype \n",
|
||
"--- ------ -------------- ----- \n",
|
||
" 0 sepallen 147 non-null float64\n",
|
||
" 1 sepalwid 147 non-null float64\n",
|
||
" 2 petallen 147 non-null float64\n",
|
||
" 3 petalwid 147 non-null float64\n",
|
||
" 4 irisname 147 non-null str \n",
|
||
"dtypes: float64(4), str(1)\n",
|
||
"memory usage: 6.9 KB\n",
|
||
"======================================================================\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/html": [
|
||
"<div>\n",
|
||
"<style scoped>\n",
|
||
" .dataframe tbody tr th:only-of-type {\n",
|
||
" vertical-align: middle;\n",
|
||
" }\n",
|
||
"\n",
|
||
" .dataframe tbody tr th {\n",
|
||
" vertical-align: top;\n",
|
||
" }\n",
|
||
"\n",
|
||
" .dataframe thead th {\n",
|
||
" text-align: right;\n",
|
||
" }\n",
|
||
"</style>\n",
|
||
"<table border=\"1\" class=\"dataframe\">\n",
|
||
" <thead>\n",
|
||
" <tr style=\"text-align: right;\">\n",
|
||
" <th></th>\n",
|
||
" <th>sepallen</th>\n",
|
||
" <th>sepalwid</th>\n",
|
||
" <th>petallen</th>\n",
|
||
" <th>petalwid</th>\n",
|
||
" <th>irisname</th>\n",
|
||
" </tr>\n",
|
||
" </thead>\n",
|
||
" <tbody>\n",
|
||
" <tr>\n",
|
||
" <th>1</th>\n",
|
||
" <td>4.9</td>\n",
|
||
" <td>3.0</td>\n",
|
||
" <td>1.4</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>2</th>\n",
|
||
" <td>4.7</td>\n",
|
||
" <td>3.2</td>\n",
|
||
" <td>1.3</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>3</th>\n",
|
||
" <td>4.6</td>\n",
|
||
" <td>3.1</td>\n",
|
||
" <td>1.5</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>4</th>\n",
|
||
" <td>5.0</td>\n",
|
||
" <td>3.6</td>\n",
|
||
" <td>1.4</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>...</th>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>146</th>\n",
|
||
" <td>6.3</td>\n",
|
||
" <td>2.5</td>\n",
|
||
" <td>5.0</td>\n",
|
||
" <td>1.9</td>\n",
|
||
" <td>virginica</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>147</th>\n",
|
||
" <td>6.5</td>\n",
|
||
" <td>3.0</td>\n",
|
||
" <td>5.2</td>\n",
|
||
" <td>2.0</td>\n",
|
||
" <td>virginica</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>148</th>\n",
|
||
" <td>6.2</td>\n",
|
||
" <td>3.4</td>\n",
|
||
" <td>5.4</td>\n",
|
||
" <td>2.3</td>\n",
|
||
" <td>alieniris</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>149</th>\n",
|
||
" <td>5.9</td>\n",
|
||
" <td>3.0</td>\n",
|
||
" <td>5.1</td>\n",
|
||
" <td>1.8</td>\n",
|
||
" <td>alieniris</td>\n",
|
||
" </tr>\n",
|
||
" </tbody>\n",
|
||
"</table>\n",
|
||
"<p>147 rows × 5 columns</p>\n",
|
||
"</div>"
|
||
],
|
||
"text/plain": [
|
||
" sepallen sepalwid petallen petalwid irisname\n",
|
||
"1 4.9 3.0 1.4 0.2 setosa\n",
|
||
"2 4.7 3.2 1.3 0.2 setosa\n",
|
||
"3 4.6 3.1 1.5 0.2 setosa\n",
|
||
"4 5.0 3.6 1.4 0.2 setosa\n",
|
||
".. ... ... ... ... ...\n",
|
||
"146 6.3 2.5 5.0 1.9 virginica\n",
|
||
"147 6.5 3.0 5.2 2.0 virginica\n",
|
||
"148 6.2 3.4 5.4 2.3 alieniris\n",
|
||
"149 5.9 3.0 5.1 1.8 alieniris\n",
|
||
"\n",
|
||
"[147 rows x 5 columns]"
|
||
]
|
||
},
|
||
"execution_count": 11,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"##############################\n",
|
||
"# BLOCK 9: USING DF'S dropna\n",
|
||
"##############################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# typically, after dropping columns that we don't want,\n",
|
||
"# we drop rows with missing data (other approaches are possible, too)\n",
|
||
"#\n",
|
||
"df_clean = df_clean.dropna() # this removes all rows with nan items\n",
|
||
"df_clean.info()\n",
|
||
"print('=' * 70)\n",
|
||
"df_clean\n",
|
||
"\n",
|
||
"#\n",
|
||
"# notice that _all_ of the rows now have 144 non-null items\n",
|
||
"# also, the first and last rows (among others) aren't valid data...\n",
|
||
"# we'll handle that next"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 12,
|
||
"metadata": {
|
||
"id": "h0spkxGzKPR-"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Irisname entries: <StringArray>\n",
|
||
"['setosa', 'versicolor', 'virginica', 'alieniris']\n",
|
||
"Length: 4, dtype: str\n",
|
||
"1 False\n",
|
||
"2 False\n",
|
||
"3 False\n",
|
||
"4 False\n",
|
||
" ... \n",
|
||
"146 False\n",
|
||
"147 False\n",
|
||
"148 True\n",
|
||
"149 True\n",
|
||
"Name: irisname, Length: 147, dtype: bool\n",
|
||
"1 True\n",
|
||
"2 True\n",
|
||
"3 True\n",
|
||
"4 True\n",
|
||
" ... \n",
|
||
"146 True\n",
|
||
"147 True\n",
|
||
"148 False\n",
|
||
"149 False\n",
|
||
"Name: irisname, Length: 147, dtype: bool\n",
|
||
"(145, 5)\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/html": [
|
||
"<div>\n",
|
||
"<style scoped>\n",
|
||
" .dataframe tbody tr th:only-of-type {\n",
|
||
" vertical-align: middle;\n",
|
||
" }\n",
|
||
"\n",
|
||
" .dataframe tbody tr th {\n",
|
||
" vertical-align: top;\n",
|
||
" }\n",
|
||
"\n",
|
||
" .dataframe thead th {\n",
|
||
" text-align: right;\n",
|
||
" }\n",
|
||
"</style>\n",
|
||
"<table border=\"1\" class=\"dataframe\">\n",
|
||
" <thead>\n",
|
||
" <tr style=\"text-align: right;\">\n",
|
||
" <th></th>\n",
|
||
" <th>sepallen</th>\n",
|
||
" <th>sepalwid</th>\n",
|
||
" <th>petallen</th>\n",
|
||
" <th>petalwid</th>\n",
|
||
" <th>irisname</th>\n",
|
||
" </tr>\n",
|
||
" </thead>\n",
|
||
" <tbody>\n",
|
||
" <tr>\n",
|
||
" <th>1</th>\n",
|
||
" <td>4.9</td>\n",
|
||
" <td>3.0</td>\n",
|
||
" <td>1.4</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>2</th>\n",
|
||
" <td>4.7</td>\n",
|
||
" <td>3.2</td>\n",
|
||
" <td>1.3</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>3</th>\n",
|
||
" <td>4.6</td>\n",
|
||
" <td>3.1</td>\n",
|
||
" <td>1.5</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>4</th>\n",
|
||
" <td>5.0</td>\n",
|
||
" <td>3.6</td>\n",
|
||
" <td>1.4</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>setosa</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>...</th>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>144</th>\n",
|
||
" <td>6.7</td>\n",
|
||
" <td>3.3</td>\n",
|
||
" <td>5.7</td>\n",
|
||
" <td>2.5</td>\n",
|
||
" <td>virginica</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>145</th>\n",
|
||
" <td>6.7</td>\n",
|
||
" <td>3.0</td>\n",
|
||
" <td>5.2</td>\n",
|
||
" <td>2.3</td>\n",
|
||
" <td>virginica</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>146</th>\n",
|
||
" <td>6.3</td>\n",
|
||
" <td>2.5</td>\n",
|
||
" <td>5.0</td>\n",
|
||
" <td>1.9</td>\n",
|
||
" <td>virginica</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>147</th>\n",
|
||
" <td>6.5</td>\n",
|
||
" <td>3.0</td>\n",
|
||
" <td>5.2</td>\n",
|
||
" <td>2.0</td>\n",
|
||
" <td>virginica</td>\n",
|
||
" </tr>\n",
|
||
" </tbody>\n",
|
||
"</table>\n",
|
||
"<p>145 rows × 5 columns</p>\n",
|
||
"</div>"
|
||
],
|
||
"text/plain": [
|
||
" sepallen sepalwid petallen petalwid irisname\n",
|
||
"1 4.9 3.0 1.4 0.2 setosa\n",
|
||
"2 4.7 3.2 1.3 0.2 setosa\n",
|
||
"3 4.6 3.1 1.5 0.2 setosa\n",
|
||
"4 5.0 3.6 1.4 0.2 setosa\n",
|
||
".. ... ... ... ... ...\n",
|
||
"144 6.7 3.3 5.7 2.5 virginica\n",
|
||
"145 6.7 3.0 5.2 2.3 virginica\n",
|
||
"146 6.3 2.5 5.0 1.9 virginica\n",
|
||
"147 6.5 3.0 5.2 2.0 virginica\n",
|
||
"\n",
|
||
"[145 rows x 5 columns]"
|
||
]
|
||
},
|
||
"execution_count": 12,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"################################\n",
|
||
"# BLOCK 10: REMOVING BOGUS DATA\n",
|
||
"################################\n",
|
||
"\n",
|
||
"# or more to the point, grab that column and inspect:\n",
|
||
"print(f\"Irisname entries: {df_clean['irisname'].unique()}\")\n",
|
||
"print(df_clean['irisname'] == 'alieniris') # what does this show?\n",
|
||
"print(df_clean['irisname'] != 'alieniris') # what does this show?\n",
|
||
"\n",
|
||
"# define a final version of the DataFrame by pulling out the\n",
|
||
"# bad alieniris data (remember that you can pass a boolean\n",
|
||
"# Series to select data that you want)\n",
|
||
"df_final = df_clean[df_clean['irisname'] != 'alieniris'].copy()\n",
|
||
"# ^^^^ YOU NEED TO WRITE CODE HERE...\n",
|
||
"\n",
|
||
"print(df_final.shape)\n",
|
||
"df_final"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 13,
|
||
"metadata": {
|
||
"id": "EywtvRyMKPR-"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"{'setosa': 0, 'versicolor': 1, 'virginica': 2}\n",
|
||
"<class 'dict'>\n",
|
||
"setosa maps to 0\n",
|
||
"versicolor maps to 1\n",
|
||
"virginica maps to 2\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"##########################################\n",
|
||
"# BLOCK 11: CONVERT SPECIES NAME TO INDEX\n",
|
||
"##########################################\n",
|
||
"\n",
|
||
"# all of scikit-learn's ML routines need numbers, not strings\n",
|
||
"# ... even for categories/classifications (like species!)\n",
|
||
"# so, we will convert the flower-species to numbers:\n",
|
||
"\n",
|
||
"species_names = df_final['irisname'].unique()\n",
|
||
"species_name_to_index = { species_names[i]:i for i in range(len(species_names))}\n",
|
||
"print(species_name_to_index)\n",
|
||
"print(type(species_name_to_index))\n",
|
||
"\n",
|
||
"def convertSpecies(species_name: str) -> int:\n",
|
||
" ''' return the species index (a unique integer/category) '''\n",
|
||
" #print(f\"converting {species_name}...\")\n",
|
||
" return species_name_to_index[species_name]\n",
|
||
"\n",
|
||
"# Let's try it out...\n",
|
||
"for name in species_names:\n",
|
||
" print(f\"{name} maps to {convertSpecies(name)}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {
|
||
"id": "NuKqemiiKPR_"
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"##########################################\n",
|
||
"# BLOCK 12: USING DF'S .apply\n",
|
||
"##########################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# we can \"apply\" our new convertSpecies function to a whole column\n",
|
||
"#\n",
|
||
"# (The following will issue a \"SettingWithCopyWarning\" here...)\n",
|
||
"# see https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
|
||
"#\n",
|
||
"# >>> ADD CODE HERE TO KEEP THE WARNING FROM HAPPENING <<<\n",
|
||
"\n",
|
||
"df_final.loc[:, 'irisname'] = df_final['irisname'].apply(convertSpecies)\n",
|
||
"#df_final.loc[:, 'irisname'] = df_final['irisname'].apply(convertSpecies)\n",
|
||
"# Don't run this twice! Why?! What's \"KeyError: 0\"?\n",
|
||
"# (of course, you can always go back and re-establish definitions of df_final)\n",
|
||
"\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 15,
|
||
"metadata": {
|
||
"id": "tWoVKJBGKPR_"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/html": [
|
||
"<div>\n",
|
||
"<style scoped>\n",
|
||
" .dataframe tbody tr th:only-of-type {\n",
|
||
" vertical-align: middle;\n",
|
||
" }\n",
|
||
"\n",
|
||
" .dataframe tbody tr th {\n",
|
||
" vertical-align: top;\n",
|
||
" }\n",
|
||
"\n",
|
||
" .dataframe thead th {\n",
|
||
" text-align: right;\n",
|
||
" }\n",
|
||
"</style>\n",
|
||
"<table border=\"1\" class=\"dataframe\">\n",
|
||
" <thead>\n",
|
||
" <tr style=\"text-align: right;\">\n",
|
||
" <th></th>\n",
|
||
" <th>sepallen</th>\n",
|
||
" <th>sepalwid</th>\n",
|
||
" <th>petallen</th>\n",
|
||
" <th>petalwid</th>\n",
|
||
" <th>irisname</th>\n",
|
||
" </tr>\n",
|
||
" </thead>\n",
|
||
" <tbody>\n",
|
||
" <tr>\n",
|
||
" <th>1</th>\n",
|
||
" <td>4.9</td>\n",
|
||
" <td>3.0</td>\n",
|
||
" <td>1.4</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>0</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>2</th>\n",
|
||
" <td>4.7</td>\n",
|
||
" <td>3.2</td>\n",
|
||
" <td>1.3</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>0</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>3</th>\n",
|
||
" <td>4.6</td>\n",
|
||
" <td>3.1</td>\n",
|
||
" <td>1.5</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>0</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>4</th>\n",
|
||
" <td>5.0</td>\n",
|
||
" <td>3.6</td>\n",
|
||
" <td>1.4</td>\n",
|
||
" <td>0.2</td>\n",
|
||
" <td>0</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>...</th>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>...</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>144</th>\n",
|
||
" <td>6.7</td>\n",
|
||
" <td>3.3</td>\n",
|
||
" <td>5.7</td>\n",
|
||
" <td>2.5</td>\n",
|
||
" <td>2</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>145</th>\n",
|
||
" <td>6.7</td>\n",
|
||
" <td>3.0</td>\n",
|
||
" <td>5.2</td>\n",
|
||
" <td>2.3</td>\n",
|
||
" <td>2</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>146</th>\n",
|
||
" <td>6.3</td>\n",
|
||
" <td>2.5</td>\n",
|
||
" <td>5.0</td>\n",
|
||
" <td>1.9</td>\n",
|
||
" <td>2</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>147</th>\n",
|
||
" <td>6.5</td>\n",
|
||
" <td>3.0</td>\n",
|
||
" <td>5.2</td>\n",
|
||
" <td>2.0</td>\n",
|
||
" <td>2</td>\n",
|
||
" </tr>\n",
|
||
" </tbody>\n",
|
||
"</table>\n",
|
||
"<p>145 rows × 5 columns</p>\n",
|
||
"</div>"
|
||
],
|
||
"text/plain": [
|
||
" sepallen sepalwid petallen petalwid irisname\n",
|
||
"1 4.9 3.0 1.4 0.2 0\n",
|
||
"2 4.7 3.2 1.3 0.2 0\n",
|
||
"3 4.6 3.1 1.5 0.2 0\n",
|
||
"4 5.0 3.6 1.4 0.2 0\n",
|
||
".. ... ... ... ... ...\n",
|
||
"144 6.7 3.3 5.7 2.5 2\n",
|
||
"145 6.7 3.0 5.2 2.3 2\n",
|
||
"146 6.3 2.5 5.0 1.9 2\n",
|
||
"147 6.5 3.0 5.2 2.0 2\n",
|
||
"\n",
|
||
"[145 rows x 5 columns]"
|
||
]
|
||
},
|
||
"execution_count": 15,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"##########################################\n",
|
||
"# BLOCK 13: CONFIRMING FINAL DATAFRAME\n",
|
||
"##########################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# let's see it! (this is safe to run many times...)\n",
|
||
"#\n",
|
||
"df_final # print(df_final.tostring()) # for _all_ rows..."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 16,
|
||
"metadata": {
|
||
"id": "qxpBs7NnKPR_"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"type of A: <class 'numpy.ndarray'>\n",
|
||
" sepallen sepalwid petallen petalwid irisname\n",
|
||
"1 4.9 3.0 1.4 0.2 0\n",
|
||
"2 4.7 3.2 1.3 0.2 0\n",
|
||
"3 4.6 3.1 1.5 0.2 0\n",
|
||
"4 5.0 3.6 1.4 0.2 0\n",
|
||
"5 5.4 3.9 1.7 0.4 0\n",
|
||
"A[0] = [4.9 3. 1.4 0.2 0. ]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"##########################################\n",
|
||
"# BLOCK 14: CONVERTING TO NUMPY FORMAT\n",
|
||
"##########################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# let's convert our dataframe to a numpy array, named A\n",
|
||
"# Our ML library, scikit-learn operates entirely on numpy arrays.\n",
|
||
"#\n",
|
||
"#A = df_final.values\n",
|
||
"A = df_final.to_numpy() # better -- self-documenting!\n",
|
||
"\n",
|
||
"print(f\"type of A: {type(A)}\")\n",
|
||
"print(df_final.head())\n",
|
||
"print(f\"A[0] = {A[0]}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 31,
|
||
"metadata": {
|
||
"id": "bblbBuIiKPSA"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"array([[4.9, 3. , 1.4, 0.2, 0. ],\n",
|
||
" [4.7, 3.2, 1.3, 0.2, 0. ],\n",
|
||
" [4.6, 3.1, 1.5, 0.2, 0. ],\n",
|
||
" [5. , 3.6, 1.4, 0.2, 0. ],\n",
|
||
" [5.4, 3.9, 1.7, 0.4, 0. ],\n",
|
||
" [4.6, 3.4, 1.4, 0.3, 0. ],\n",
|
||
" [4.4, 2.9, 1.4, 0.2, 0. ],\n",
|
||
" [4.9, 3.1, 1.5, 0.1, 0. ],\n",
|
||
" [5.4, 3.7, 1.5, 0.2, 0. ],\n",
|
||
" [4.8, 3.4, 1.6, 0.2, 0. ],\n",
|
||
" [4.8, 3. , 1.4, 0.1, 0. ],\n",
|
||
" [4.3, 3. , 1.1, 0.1, 0. ],\n",
|
||
" [5.8, 4. , 1.2, 0.2, 0. ],\n",
|
||
" [5.7, 4.4, 1.5, 0.4, 0. ],\n",
|
||
" [5.4, 3.9, 1.3, 0.4, 0. ],\n",
|
||
" [5.1, 3.5, 1.4, 0.3, 0. ],\n",
|
||
" [5.7, 3.8, 1.7, 0.3, 0. ],\n",
|
||
" [5.1, 3.8, 1.5, 0.3, 0. ],\n",
|
||
" [5.4, 3.4, 1.7, 0.2, 0. ],\n",
|
||
" [5.1, 3.7, 1.5, 0.4, 0. ],\n",
|
||
" [4.6, 3.6, 1. , 0.2, 0. ],\n",
|
||
" [5.1, 3.3, 1.7, 0.5, 0. ],\n",
|
||
" [4.8, 3.4, 1.9, 0.2, 0. ],\n",
|
||
" [5. , 3.4, 1.6, 0.4, 0. ],\n",
|
||
" [5.2, 3.5, 1.5, 0.2, 0. ],\n",
|
||
" [5.2, 3.4, 1.4, 0.2, 0. ],\n",
|
||
" [4.7, 3.2, 1.6, 0.2, 0. ],\n",
|
||
" [4.8, 3.1, 1.6, 0.2, 0. ],\n",
|
||
" [5.4, 3.4, 1.5, 0.4, 0. ],\n",
|
||
" [5.2, 4.1, 1.5, 0.1, 0. ],\n",
|
||
" [5.5, 4.2, 1.4, 0.2, 0. ],\n",
|
||
" [4.9, 3.1, 1.5, 0.2, 0. ],\n",
|
||
" [5. , 3.2, 1.2, 0.2, 0. ],\n",
|
||
" [5.5, 3.5, 1.3, 0.2, 0. ],\n",
|
||
" [4.9, 3.6, 1.4, 0.1, 0. ],\n",
|
||
" [4.4, 3. , 1.3, 0.2, 0. ],\n",
|
||
" [5.1, 3.4, 1.5, 0.2, 0. ],\n",
|
||
" [5. , 3.5, 1.3, 0.3, 0. ],\n",
|
||
" [4.5, 2.3, 1.3, 0.3, 0. ],\n",
|
||
" [4.4, 3.2, 1.3, 0.2, 0. ],\n",
|
||
" [5. , 3.5, 1.6, 0.6, 0. ],\n",
|
||
" [5.1, 3.8, 1.9, 0.4, 0. ],\n",
|
||
" [4.8, 3. , 1.4, 0.3, 0. ],\n",
|
||
" [5.1, 3.8, 1.6, 0.2, 0. ],\n",
|
||
" [4.6, 3.2, 1.4, 0.2, 0. ],\n",
|
||
" [5.3, 3.7, 1.5, 0.2, 0. ],\n",
|
||
" [5. , 3.3, 1.4, 0.2, 0. ],\n",
|
||
" [7. , 3.2, 4.7, 1.4, 1. ],\n",
|
||
" [6.4, 3.2, 4.5, 1.5, 1. ],\n",
|
||
" [6.9, 3.1, 4.9, 1.5, 1. ],\n",
|
||
" [5.5, 2.3, 4. , 1.3, 1. ],\n",
|
||
" [6.5, 2.8, 4.6, 1.5, 1. ],\n",
|
||
" [5.7, 2.8, 4.5, 1.3, 1. ],\n",
|
||
" [6.3, 3.3, 4.7, 1.6, 1. ],\n",
|
||
" [4.9, 2.4, 3.3, 1. , 1. ],\n",
|
||
" [6.6, 2.9, 4.6, 1.3, 1. ],\n",
|
||
" [5.2, 2.7, 3.9, 1.4, 1. ],\n",
|
||
" [5. , 2. , 3.5, 1. , 1. ],\n",
|
||
" [5.9, 3. , 4.2, 1.5, 1. ],\n",
|
||
" [6. , 2.2, 4. , 1. , 1. ],\n",
|
||
" [6.1, 2.9, 4.7, 1.4, 1. ],\n",
|
||
" [5.6, 2.9, 3.6, 1.3, 1. ],\n",
|
||
" [6.7, 3.1, 4.4, 1.4, 1. ],\n",
|
||
" [5.6, 3. , 4.5, 1.5, 1. ],\n",
|
||
" [5.8, 2.7, 4.1, 1. , 1. ],\n",
|
||
" [6.2, 2.2, 4.5, 1.5, 1. ],\n",
|
||
" [5.6, 2.5, 3.9, 1.1, 1. ],\n",
|
||
" [5.9, 3.2, 4.8, 1.8, 1. ],\n",
|
||
" [6.1, 2.8, 4. , 1.3, 1. ],\n",
|
||
" [6.3, 2.5, 4.9, 1.5, 1. ],\n",
|
||
" [6.1, 2.8, 4.7, 1.2, 1. ],\n",
|
||
" [6.4, 2.9, 4.3, 1.3, 1. ],\n",
|
||
" [6.6, 3. , 4.4, 1.4, 1. ],\n",
|
||
" [6.8, 2.8, 4.8, 1.4, 1. ],\n",
|
||
" [6.7, 3. , 5. , 1.7, 1. ],\n",
|
||
" [6. , 2.9, 4.5, 1.5, 1. ],\n",
|
||
" [5.7, 2.6, 3.5, 1. , 1. ],\n",
|
||
" [5.5, 2.4, 3.8, 1.1, 1. ],\n",
|
||
" [5.5, 2.4, 3.7, 1. , 1. ],\n",
|
||
" [5.8, 2.7, 3.9, 1.2, 1. ],\n",
|
||
" [6. , 2.7, 5.1, 1.6, 1. ],\n",
|
||
" [5.4, 3. , 4.5, 1.5, 1. ],\n",
|
||
" [6. , 3.4, 4.5, 1.6, 1. ],\n",
|
||
" [6.7, 3.1, 4.7, 1.5, 1. ],\n",
|
||
" [6.3, 2.3, 4.4, 1.3, 1. ],\n",
|
||
" [5.6, 3. , 4.1, 1.3, 1. ],\n",
|
||
" [5.5, 2.5, 4. , 1.3, 1. ],\n",
|
||
" [5.5, 2.6, 4.4, 1.2, 1. ],\n",
|
||
" [6.1, 3. , 4.6, 1.4, 1. ],\n",
|
||
" [5.8, 2.6, 4. , 1.2, 1. ],\n",
|
||
" [5. , 2.3, 3.3, 1. , 1. ],\n",
|
||
" [5.6, 2.7, 4.2, 1.3, 1. ],\n",
|
||
" [5.7, 3. , 4.2, 1.2, 1. ],\n",
|
||
" [5.7, 2.9, 4.2, 1.3, 1. ],\n",
|
||
" [6.2, 2.9, 4.3, 1.3, 1. ],\n",
|
||
" [5.1, 2.5, 3. , 1.1, 1. ],\n",
|
||
" [5.7, 2.8, 4.1, 1.3, 1. ],\n",
|
||
" [6.3, 3.3, 6. , 2.5, 2. ],\n",
|
||
" [5.8, 2.7, 5.1, 1.9, 2. ],\n",
|
||
" [7.1, 3. , 5.9, 2.1, 2. ],\n",
|
||
" [6.3, 2.9, 5.6, 1.8, 2. ],\n",
|
||
" [6.5, 3. , 5.8, 2.2, 2. ],\n",
|
||
" [7.6, 3. , 6.6, 2.1, 2. ],\n",
|
||
" [4.9, 2.5, 4.5, 1.7, 2. ],\n",
|
||
" [7.3, 2.9, 6.3, 1.8, 2. ],\n",
|
||
" [6.7, 2.5, 5.8, 1.8, 2. ],\n",
|
||
" [7.2, 3.6, 6.1, 2.5, 2. ],\n",
|
||
" [6.5, 3.2, 5.1, 2. , 2. ],\n",
|
||
" [6.4, 2.7, 5.3, 1.9, 2. ],\n",
|
||
" [6.8, 3. , 5.5, 2.1, 2. ],\n",
|
||
" [5.7, 2.5, 5. , 2. , 2. ],\n",
|
||
" [5.8, 2.8, 5.1, 2.4, 2. ],\n",
|
||
" [6.4, 3.2, 5.3, 2.3, 2. ],\n",
|
||
" [6.5, 3. , 5.5, 1.8, 2. ],\n",
|
||
" [7.7, 3.8, 6.7, 2.2, 2. ],\n",
|
||
" [7.7, 2.6, 6.9, 2.3, 2. ],\n",
|
||
" [6. , 2.2, 5. , 1.5, 2. ],\n",
|
||
" [6.9, 3.2, 5.7, 2.3, 2. ],\n",
|
||
" [5.6, 2.8, 4.9, 2. , 2. ],\n",
|
||
" [7.7, 2.8, 6.7, 2. , 2. ],\n",
|
||
" [6.3, 2.7, 4.9, 1.8, 2. ],\n",
|
||
" [6.7, 3.3, 5.7, 2.1, 2. ],\n",
|
||
" [7.2, 3.2, 6. , 1.8, 2. ],\n",
|
||
" [6.2, 2.8, 4.8, 1.8, 2. ],\n",
|
||
" [6.1, 3. , 4.9, 1.8, 2. ],\n",
|
||
" [6.4, 2.8, 5.6, 2.1, 2. ],\n",
|
||
" [7.2, 3. , 5.8, 1.6, 2. ],\n",
|
||
" [7.4, 2.8, 6.1, 1.9, 2. ],\n",
|
||
" [7.9, 3.8, 6.4, 2. , 2. ],\n",
|
||
" [6.4, 2.8, 5.6, 2.2, 2. ],\n",
|
||
" [6.3, 2.8, 5.1, 1.5, 2. ],\n",
|
||
" [6.1, 2.6, 5.6, 1.4, 2. ],\n",
|
||
" [7.7, 3. , 6.1, 2.3, 2. ],\n",
|
||
" [6.3, 3.4, 5.6, 2.4, 2. ],\n",
|
||
" [6.4, 3.1, 5.5, 1.8, 2. ],\n",
|
||
" [6. , 3. , 4.8, 1.8, 2. ],\n",
|
||
" [6.9, 3.1, 5.4, 2.1, 2. ],\n",
|
||
" [6.7, 3.1, 5.6, 2.4, 2. ],\n",
|
||
" [6.9, 3.1, 5.1, 2.3, 2. ],\n",
|
||
" [5.8, 2.7, 5.1, 1.9, 2. ],\n",
|
||
" [6.8, 3.2, 5.9, 2.3, 2. ],\n",
|
||
" [6.7, 3.3, 5.7, 2.5, 2. ],\n",
|
||
" [6.7, 3. , 5.2, 2.3, 2. ],\n",
|
||
" [6.3, 2.5, 5. , 1.9, 2. ],\n",
|
||
" [6.5, 3. , 5.2, 2. , 2. ]])"
|
||
]
|
||
},
|
||
"execution_count": 31,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"##################################################\n",
|
||
"# BLOCK 15: CONVERTING NUMPY ARRAY TO ALL FLOATS\n",
|
||
"##################################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# let's convert to make sure it's all floating-point, so we can multiply and divide\n",
|
||
"#\n",
|
||
"A = A.astype('float64') # so many: www.tutorialspoint.com/numpy/numpy_data_types.htm\n",
|
||
"A"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 17,
|
||
"metadata": {
|
||
"id": "wbns1qpJKPSA"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"\n",
|
||
"The dataset has 145 rows and 5 cols\n",
|
||
"[[4.9 3. 1.4 0.2 0. ]\n",
|
||
" [4.7 3.2 1.3 0.2 0. ]\n",
|
||
" [4.6 3.1 1.5 0.2 0. ]\n",
|
||
" [5. 3.6 1.4 0.2 0. ]\n",
|
||
" [5.4 3.9 1.7 0.4 0. ]\n",
|
||
" [4.6 3.4 1.4 0.3 0. ]\n",
|
||
" [4.4 2.9 1.4 0.2 0. ]\n",
|
||
" [4.9 3.1 1.5 0.1 0. ]\n",
|
||
" [5.4 3.7 1.5 0.2 0. ]\n",
|
||
" [4.8 3.4 1.6 0.2 0. ]\n",
|
||
" [4.8 3. 1.4 0.1 0. ]\n",
|
||
" [4.3 3. 1.1 0.1 0. ]\n",
|
||
" [5.8 4. 1.2 0.2 0. ]\n",
|
||
" [5.7 4.4 1.5 0.4 0. ]\n",
|
||
" [5.4 3.9 1.3 0.4 0. ]\n",
|
||
" [5.1 3.5 1.4 0.3 0. ]\n",
|
||
" [5.7 3.8 1.7 0.3 0. ]\n",
|
||
" [5.1 3.8 1.5 0.3 0. ]\n",
|
||
" [5.4 3.4 1.7 0.2 0. ]\n",
|
||
" [5.1 3.7 1.5 0.4 0. ]\n",
|
||
" [4.6 3.6 1. 0.2 0. ]\n",
|
||
" [5.1 3.3 1.7 0.5 0. ]\n",
|
||
" [4.8 3.4 1.9 0.2 0. ]\n",
|
||
" [5. 3.4 1.6 0.4 0. ]\n",
|
||
" [5.2 3.5 1.5 0.2 0. ]\n",
|
||
" [5.2 3.4 1.4 0.2 0. ]\n",
|
||
" [4.7 3.2 1.6 0.2 0. ]\n",
|
||
" [4.8 3.1 1.6 0.2 0. ]\n",
|
||
" [5.4 3.4 1.5 0.4 0. ]\n",
|
||
" [5.2 4.1 1.5 0.1 0. ]\n",
|
||
" [5.5 4.2 1.4 0.2 0. ]\n",
|
||
" [4.9 3.1 1.5 0.2 0. ]\n",
|
||
" [5. 3.2 1.2 0.2 0. ]\n",
|
||
" [5.5 3.5 1.3 0.2 0. ]\n",
|
||
" [4.9 3.6 1.4 0.1 0. ]\n",
|
||
" [4.4 3. 1.3 0.2 0. ]\n",
|
||
" [5.1 3.4 1.5 0.2 0. ]\n",
|
||
" [5. 3.5 1.3 0.3 0. ]\n",
|
||
" [4.5 2.3 1.3 0.3 0. ]\n",
|
||
" [4.4 3.2 1.3 0.2 0. ]\n",
|
||
" [5. 3.5 1.6 0.6 0. ]\n",
|
||
" [5.1 3.8 1.9 0.4 0. ]\n",
|
||
" [4.8 3. 1.4 0.3 0. ]\n",
|
||
" [5.1 3.8 1.6 0.2 0. ]\n",
|
||
" [4.6 3.2 1.4 0.2 0. ]\n",
|
||
" [5.3 3.7 1.5 0.2 0. ]\n",
|
||
" [5. 3.3 1.4 0.2 0. ]\n",
|
||
" [7. 3.2 4.7 1.4 1. ]\n",
|
||
" [6.4 3.2 4.5 1.5 1. ]\n",
|
||
" [6.9 3.1 4.9 1.5 1. ]\n",
|
||
" [5.5 2.3 4. 1.3 1. ]\n",
|
||
" [6.5 2.8 4.6 1.5 1. ]\n",
|
||
" [5.7 2.8 4.5 1.3 1. ]\n",
|
||
" [6.3 3.3 4.7 1.6 1. ]\n",
|
||
" [4.9 2.4 3.3 1. 1. ]\n",
|
||
" [6.6 2.9 4.6 1.3 1. ]\n",
|
||
" [5.2 2.7 3.9 1.4 1. ]\n",
|
||
" [5. 2. 3.5 1. 1. ]\n",
|
||
" [5.9 3. 4.2 1.5 1. ]\n",
|
||
" [6. 2.2 4. 1. 1. ]\n",
|
||
" [6.1 2.9 4.7 1.4 1. ]\n",
|
||
" [5.6 2.9 3.6 1.3 1. ]\n",
|
||
" [6.7 3.1 4.4 1.4 1. ]\n",
|
||
" [5.6 3. 4.5 1.5 1. ]\n",
|
||
" [5.8 2.7 4.1 1. 1. ]\n",
|
||
" [6.2 2.2 4.5 1.5 1. ]\n",
|
||
" [5.6 2.5 3.9 1.1 1. ]\n",
|
||
" [5.9 3.2 4.8 1.8 1. ]\n",
|
||
" [6.1 2.8 4. 1.3 1. ]\n",
|
||
" [6.3 2.5 4.9 1.5 1. ]\n",
|
||
" [6.1 2.8 4.7 1.2 1. ]\n",
|
||
" [6.4 2.9 4.3 1.3 1. ]\n",
|
||
" [6.6 3. 4.4 1.4 1. ]\n",
|
||
" [6.8 2.8 4.8 1.4 1. ]\n",
|
||
" [6.7 3. 5. 1.7 1. ]\n",
|
||
" [6. 2.9 4.5 1.5 1. ]\n",
|
||
" [5.7 2.6 3.5 1. 1. ]\n",
|
||
" [5.5 2.4 3.8 1.1 1. ]\n",
|
||
" [5.5 2.4 3.7 1. 1. ]\n",
|
||
" [5.8 2.7 3.9 1.2 1. ]\n",
|
||
" [6. 2.7 5.1 1.6 1. ]\n",
|
||
" [5.4 3. 4.5 1.5 1. ]\n",
|
||
" [6. 3.4 4.5 1.6 1. ]\n",
|
||
" [6.7 3.1 4.7 1.5 1. ]\n",
|
||
" [6.3 2.3 4.4 1.3 1. ]\n",
|
||
" [5.6 3. 4.1 1.3 1. ]\n",
|
||
" [5.5 2.5 4. 1.3 1. ]\n",
|
||
" [5.5 2.6 4.4 1.2 1. ]\n",
|
||
" [6.1 3. 4.6 1.4 1. ]\n",
|
||
" [5.8 2.6 4. 1.2 1. ]\n",
|
||
" [5. 2.3 3.3 1. 1. ]\n",
|
||
" [5.6 2.7 4.2 1.3 1. ]\n",
|
||
" [5.7 3. 4.2 1.2 1. ]\n",
|
||
" [5.7 2.9 4.2 1.3 1. ]\n",
|
||
" [6.2 2.9 4.3 1.3 1. ]\n",
|
||
" [5.1 2.5 3. 1.1 1. ]\n",
|
||
" [5.7 2.8 4.1 1.3 1. ]\n",
|
||
" [6.3 3.3 6. 2.5 2. ]\n",
|
||
" [5.8 2.7 5.1 1.9 2. ]\n",
|
||
" [7.1 3. 5.9 2.1 2. ]\n",
|
||
" [6.3 2.9 5.6 1.8 2. ]\n",
|
||
" [6.5 3. 5.8 2.2 2. ]\n",
|
||
" [7.6 3. 6.6 2.1 2. ]\n",
|
||
" [4.9 2.5 4.5 1.7 2. ]\n",
|
||
" [7.3 2.9 6.3 1.8 2. ]\n",
|
||
" [6.7 2.5 5.8 1.8 2. ]\n",
|
||
" [7.2 3.6 6.1 2.5 2. ]\n",
|
||
" [6.5 3.2 5.1 2. 2. ]\n",
|
||
" [6.4 2.7 5.3 1.9 2. ]\n",
|
||
" [6.8 3. 5.5 2.1 2. ]\n",
|
||
" [5.7 2.5 5. 2. 2. ]\n",
|
||
" [5.8 2.8 5.1 2.4 2. ]\n",
|
||
" [6.4 3.2 5.3 2.3 2. ]\n",
|
||
" [6.5 3. 5.5 1.8 2. ]\n",
|
||
" [7.7 3.8 6.7 2.2 2. ]\n",
|
||
" [7.7 2.6 6.9 2.3 2. ]\n",
|
||
" [6. 2.2 5. 1.5 2. ]\n",
|
||
" [6.9 3.2 5.7 2.3 2. ]\n",
|
||
" [5.6 2.8 4.9 2. 2. ]\n",
|
||
" [7.7 2.8 6.7 2. 2. ]\n",
|
||
" [6.3 2.7 4.9 1.8 2. ]\n",
|
||
" [6.7 3.3 5.7 2.1 2. ]\n",
|
||
" [7.2 3.2 6. 1.8 2. ]\n",
|
||
" [6.2 2.8 4.8 1.8 2. ]\n",
|
||
" [6.1 3. 4.9 1.8 2. ]\n",
|
||
" [6.4 2.8 5.6 2.1 2. ]\n",
|
||
" [7.2 3. 5.8 1.6 2. ]\n",
|
||
" [7.4 2.8 6.1 1.9 2. ]\n",
|
||
" [7.9 3.8 6.4 2. 2. ]\n",
|
||
" [6.4 2.8 5.6 2.2 2. ]\n",
|
||
" [6.3 2.8 5.1 1.5 2. ]\n",
|
||
" [6.1 2.6 5.6 1.4 2. ]\n",
|
||
" [7.7 3. 6.1 2.3 2. ]\n",
|
||
" [6.3 3.4 5.6 2.4 2. ]\n",
|
||
" [6.4 3.1 5.5 1.8 2. ]\n",
|
||
" [6. 3. 4.8 1.8 2. ]\n",
|
||
" [6.9 3.1 5.4 2.1 2. ]\n",
|
||
" [6.7 3.1 5.6 2.4 2. ]\n",
|
||
" [6.9 3.1 5.1 2.3 2. ]\n",
|
||
" [5.8 2.7 5.1 1.9 2. ]\n",
|
||
" [6.8 3.2 5.9 2.3 2. ]\n",
|
||
" [6.7 3.3 5.7 2.5 2. ]\n",
|
||
" [6.7 3. 5.2 2.3 2. ]\n",
|
||
" [6.3 2.5 5. 1.9 2. ]\n",
|
||
" [6.5 3. 5.2 2. 2. ]]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"##########################################\n",
|
||
"# BLOCK 16: USING NUMPY'S .shape\n",
|
||
"##########################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# nice to have num_rows and num_cols variables handy...\n",
|
||
"#\n",
|
||
"num_rows, num_cols = A.shape\n",
|
||
"print(f\"\\nThe dataset has {num_rows} rows and {num_cols} cols\")\n",
|
||
"print(A)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 18,
|
||
"metadata": {
|
||
"id": "ixiZhsuqKPSA",
|
||
"scrolled": true
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"flower #132 data is [7.7 3. 6.1 2.3 2. ]\n",
|
||
" Its sepallen is 7.7\n",
|
||
" Its sepalwid is 3.0\n",
|
||
" Its petallen is 6.1\n",
|
||
" Its petalwid is 2.3\n",
|
||
" Its irisname is virginica (2)\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"##########################################\n",
|
||
"# BLOCK 17: PRINTING FLOWER INFO\n",
|
||
"##########################################\n",
|
||
"\n",
|
||
"# let's use all of our previously-defined variables, to reinforce names...\n",
|
||
"\n",
|
||
"# choose a row index (particular flower) arbitrarily:\n",
|
||
"flower = 132\n",
|
||
"print(f\"flower #{flower} data is {A[flower]}\")\n",
|
||
"\n",
|
||
"for i in range(len(features)):\n",
|
||
" col_name = features[i]\n",
|
||
" if col_name != 'irisname':\n",
|
||
" print(f\" Its {col_name} is {A[flower][i]}\")\n",
|
||
" else:\n",
|
||
" species_num = int(A[flower][i])\n",
|
||
" species_name = species_names[species_num]\n",
|
||
" print(f\" Its {col_name} is {species_name} ({species_num})\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 19,
|
||
"metadata": {
|
||
"id": "izGld2tSKPSA"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"I predict versicolor (1) from features [4.6, 3.6, 3.0, 1.2]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"##########################################\n",
|
||
"# BLOCK 18: WRITING OUR OWN 1-NN FUNCTION\n",
|
||
"##########################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# We don't have to use scikit-learn to implement NN!\n",
|
||
"#\n",
|
||
"\n",
|
||
"#\n",
|
||
"# data-driven predictive model (1-nearest-neighbor)\n",
|
||
"#\n",
|
||
"\n",
|
||
"# Python functions are first-class objects!\n",
|
||
"dist = np.linalg.norm # built in to numpy... but what does norm do?\n",
|
||
"\n",
|
||
"num_rows, num_cols = A.shape # data size\n",
|
||
"\n",
|
||
"def predictiveModel( features: list[float] ) -> str:\n",
|
||
" \"\"\" input: a list of four features\n",
|
||
" [ sepallen, sepalwid, petallen, petalwid ]\n",
|
||
" output: the predicted species of iris, from\n",
|
||
" setosa (0), versicolor (1), virginica (2)\n",
|
||
" \"\"\"\n",
|
||
" our_features = np.asarray(features) # make a numpy array\n",
|
||
"\n",
|
||
" closest_flower = A[0]\n",
|
||
" closest_features = A[0,0:4]\n",
|
||
" closest_distance = dist(our_features - closest_features)\n",
|
||
"\n",
|
||
" for i in range(1, num_rows, 1):\n",
|
||
" current_flower = A[i]\n",
|
||
" current_features = A[i,0:4]\n",
|
||
" current_distance = dist(our_features - current_features)\n",
|
||
"\n",
|
||
" if current_distance < closest_distance:\n",
|
||
" closest_distance = current_distance # remember closest!\n",
|
||
" closest_flower = current_flower\n",
|
||
"\n",
|
||
" # done comparing with every flower in the dataset\n",
|
||
" predicted_species = int(round(closest_flower[4])) # what type is closest_flower?\n",
|
||
" name = species_names[predicted_species]\n",
|
||
" return f\"{name} ({predicted_species})\"\n",
|
||
"\n",
|
||
"#\n",
|
||
"# Try it!\n",
|
||
"#\n",
|
||
"# features = eval(input(\"Enter new features: \"))\n",
|
||
"#\n",
|
||
"features = [ 4.6, 3.6, 3.0, 1.2 ]\n",
|
||
"result = predictiveModel( features )\n",
|
||
"print(f\"I predict {result} from features {features}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 20,
|
||
"metadata": {
|
||
"id": "9nhDlGbRKPSB"
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"##########################################\n",
|
||
"# BLOCK 19: COMMENTS ON SCIKIT-LEARN kNN\n",
|
||
"##########################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# but, we don't have to write our own ... because\n",
|
||
"#\n",
|
||
"# we want knn for any k (not just k=1 as above)\n",
|
||
"# we want an already-debugged algorithm!\n",
|
||
"# we want to ask iris-related questions instead of implementation ones...\n",
|
||
"#"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 21,
|
||
"metadata": {
|
||
"id": "64kSAiCvKPSB"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"+++ Start of data definitions +++\n",
|
||
"\n",
|
||
"X_all (just features) is \n",
|
||
" [[4.9 3. 1.4 0.2]\n",
|
||
" [4.7 3.2 1.3 0.2]\n",
|
||
" [4.6 3.1 1.5 0.2]\n",
|
||
" [5. 3.6 1.4 0.2]\n",
|
||
" [5.4 3.9 1.7 0.4]\n",
|
||
" [4.6 3.4 1.4 0.3]\n",
|
||
" [4.4 2.9 1.4 0.2]\n",
|
||
" [4.9 3.1 1.5 0.1]\n",
|
||
" [5.4 3.7 1.5 0.2]\n",
|
||
" [4.8 3.4 1.6 0.2]\n",
|
||
" [4.8 3. 1.4 0.1]\n",
|
||
" [4.3 3. 1.1 0.1]\n",
|
||
" [5.8 4. 1.2 0.2]\n",
|
||
" [5.7 4.4 1.5 0.4]\n",
|
||
" [5.4 3.9 1.3 0.4]\n",
|
||
" [5.1 3.5 1.4 0.3]\n",
|
||
" [5.7 3.8 1.7 0.3]\n",
|
||
" [5.1 3.8 1.5 0.3]\n",
|
||
" [5.4 3.4 1.7 0.2]\n",
|
||
" [5.1 3.7 1.5 0.4]\n",
|
||
" [4.6 3.6 1. 0.2]\n",
|
||
" [5.1 3.3 1.7 0.5]\n",
|
||
" [4.8 3.4 1.9 0.2]\n",
|
||
" [5. 3.4 1.6 0.4]\n",
|
||
" [5.2 3.5 1.5 0.2]\n",
|
||
" [5.2 3.4 1.4 0.2]\n",
|
||
" [4.7 3.2 1.6 0.2]\n",
|
||
" [4.8 3.1 1.6 0.2]\n",
|
||
" [5.4 3.4 1.5 0.4]\n",
|
||
" [5.2 4.1 1.5 0.1]\n",
|
||
" [5.5 4.2 1.4 0.2]\n",
|
||
" [4.9 3.1 1.5 0.2]\n",
|
||
" [5. 3.2 1.2 0.2]\n",
|
||
" [5.5 3.5 1.3 0.2]\n",
|
||
" [4.9 3.6 1.4 0.1]\n",
|
||
" [4.4 3. 1.3 0.2]\n",
|
||
" [5.1 3.4 1.5 0.2]\n",
|
||
" [5. 3.5 1.3 0.3]\n",
|
||
" [4.5 2.3 1.3 0.3]\n",
|
||
" [4.4 3.2 1.3 0.2]\n",
|
||
" [5. 3.5 1.6 0.6]\n",
|
||
" [5.1 3.8 1.9 0.4]\n",
|
||
" [4.8 3. 1.4 0.3]\n",
|
||
" [5.1 3.8 1.6 0.2]\n",
|
||
" [4.6 3.2 1.4 0.2]\n",
|
||
" [5.3 3.7 1.5 0.2]\n",
|
||
" [5. 3.3 1.4 0.2]\n",
|
||
" [7. 3.2 4.7 1.4]\n",
|
||
" [6.4 3.2 4.5 1.5]\n",
|
||
" [6.9 3.1 4.9 1.5]\n",
|
||
" [5.5 2.3 4. 1.3]\n",
|
||
" [6.5 2.8 4.6 1.5]\n",
|
||
" [5.7 2.8 4.5 1.3]\n",
|
||
" [6.3 3.3 4.7 1.6]\n",
|
||
" [4.9 2.4 3.3 1. ]\n",
|
||
" [6.6 2.9 4.6 1.3]\n",
|
||
" [5.2 2.7 3.9 1.4]\n",
|
||
" [5. 2. 3.5 1. ]\n",
|
||
" [5.9 3. 4.2 1.5]\n",
|
||
" [6. 2.2 4. 1. ]\n",
|
||
" [6.1 2.9 4.7 1.4]\n",
|
||
" [5.6 2.9 3.6 1.3]\n",
|
||
" [6.7 3.1 4.4 1.4]\n",
|
||
" [5.6 3. 4.5 1.5]\n",
|
||
" [5.8 2.7 4.1 1. ]\n",
|
||
" [6.2 2.2 4.5 1.5]\n",
|
||
" [5.6 2.5 3.9 1.1]\n",
|
||
" [5.9 3.2 4.8 1.8]\n",
|
||
" [6.1 2.8 4. 1.3]\n",
|
||
" [6.3 2.5 4.9 1.5]\n",
|
||
" [6.1 2.8 4.7 1.2]\n",
|
||
" [6.4 2.9 4.3 1.3]\n",
|
||
" [6.6 3. 4.4 1.4]\n",
|
||
" [6.8 2.8 4.8 1.4]\n",
|
||
" [6.7 3. 5. 1.7]\n",
|
||
" [6. 2.9 4.5 1.5]\n",
|
||
" [5.7 2.6 3.5 1. ]\n",
|
||
" [5.5 2.4 3.8 1.1]\n",
|
||
" [5.5 2.4 3.7 1. ]\n",
|
||
" [5.8 2.7 3.9 1.2]\n",
|
||
" [6. 2.7 5.1 1.6]\n",
|
||
" [5.4 3. 4.5 1.5]\n",
|
||
" [6. 3.4 4.5 1.6]\n",
|
||
" [6.7 3.1 4.7 1.5]\n",
|
||
" [6.3 2.3 4.4 1.3]\n",
|
||
" [5.6 3. 4.1 1.3]\n",
|
||
" [5.5 2.5 4. 1.3]\n",
|
||
" [5.5 2.6 4.4 1.2]\n",
|
||
" [6.1 3. 4.6 1.4]\n",
|
||
" [5.8 2.6 4. 1.2]\n",
|
||
" [5. 2.3 3.3 1. ]\n",
|
||
" [5.6 2.7 4.2 1.3]\n",
|
||
" [5.7 3. 4.2 1.2]\n",
|
||
" [5.7 2.9 4.2 1.3]\n",
|
||
" [6.2 2.9 4.3 1.3]\n",
|
||
" [5.1 2.5 3. 1.1]\n",
|
||
" [5.7 2.8 4.1 1.3]\n",
|
||
" [6.3 3.3 6. 2.5]\n",
|
||
" [5.8 2.7 5.1 1.9]\n",
|
||
" [7.1 3. 5.9 2.1]\n",
|
||
" [6.3 2.9 5.6 1.8]\n",
|
||
" [6.5 3. 5.8 2.2]\n",
|
||
" [7.6 3. 6.6 2.1]\n",
|
||
" [4.9 2.5 4.5 1.7]\n",
|
||
" [7.3 2.9 6.3 1.8]\n",
|
||
" [6.7 2.5 5.8 1.8]\n",
|
||
" [7.2 3.6 6.1 2.5]\n",
|
||
" [6.5 3.2 5.1 2. ]\n",
|
||
" [6.4 2.7 5.3 1.9]\n",
|
||
" [6.8 3. 5.5 2.1]\n",
|
||
" [5.7 2.5 5. 2. ]\n",
|
||
" [5.8 2.8 5.1 2.4]\n",
|
||
" [6.4 3.2 5.3 2.3]\n",
|
||
" [6.5 3. 5.5 1.8]\n",
|
||
" [7.7 3.8 6.7 2.2]\n",
|
||
" [7.7 2.6 6.9 2.3]\n",
|
||
" [6. 2.2 5. 1.5]\n",
|
||
" [6.9 3.2 5.7 2.3]\n",
|
||
" [5.6 2.8 4.9 2. ]\n",
|
||
" [7.7 2.8 6.7 2. ]\n",
|
||
" [6.3 2.7 4.9 1.8]\n",
|
||
" [6.7 3.3 5.7 2.1]\n",
|
||
" [7.2 3.2 6. 1.8]\n",
|
||
" [6.2 2.8 4.8 1.8]\n",
|
||
" [6.1 3. 4.9 1.8]\n",
|
||
" [6.4 2.8 5.6 2.1]\n",
|
||
" [7.2 3. 5.8 1.6]\n",
|
||
" [7.4 2.8 6.1 1.9]\n",
|
||
" [7.9 3.8 6.4 2. ]\n",
|
||
" [6.4 2.8 5.6 2.2]\n",
|
||
" [6.3 2.8 5.1 1.5]\n",
|
||
" [6.1 2.6 5.6 1.4]\n",
|
||
" [7.7 3. 6.1 2.3]\n",
|
||
" [6.3 3.4 5.6 2.4]\n",
|
||
" [6.4 3.1 5.5 1.8]\n",
|
||
" [6. 3. 4.8 1.8]\n",
|
||
" [6.9 3.1 5.4 2.1]\n",
|
||
" [6.7 3.1 5.6 2.4]\n",
|
||
" [6.9 3.1 5.1 2.3]\n",
|
||
" [5.8 2.7 5.1 1.9]\n",
|
||
" [6.8 3.2 5.9 2.3]\n",
|
||
" [6.7 3.3 5.7 2.5]\n",
|
||
" [6.7 3. 5.2 2.3]\n",
|
||
" [6.3 2.5 5. 1.9]\n",
|
||
" [6.5 3. 5.2 2. ]]\n",
|
||
"y_all (just labels) is \n",
|
||
" [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.\n",
|
||
" 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.\n",
|
||
" 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
|
||
" 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
|
||
" 1. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2.\n",
|
||
" 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2.\n",
|
||
" 2.]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"################################################\n",
|
||
"# BLOCK 20: DEFINIING FEATURES & LABELS FOR kNN\n",
|
||
"################################################\n",
|
||
"\n",
|
||
"print(\"+++ Start of data definitions +++\\n\")\n",
|
||
"\n",
|
||
"X_all = A[:,0:4] # X (features) ... is all rows, columns 0, 1, 2, 3\n",
|
||
"y_all = A[:,4] # y (labels) ... is all rows, column 4 only\n",
|
||
" # (look back at slide 20)\n",
|
||
"print(f\"X_all (just features) is \\n {X_all}\")\n",
|
||
"print(f\"y_all (just labels) is \\n {y_all}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 22,
|
||
"metadata": {
|
||
"id": "2ChEk2CDKPSC"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Weighting sepallen by 1.0\n",
|
||
"Weighting sepalwid by 1.0\n",
|
||
"Weighting petallen by 1.0\n",
|
||
"Weighting petalwid by 1.0\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"################################################\n",
|
||
"# BLOCK 21: REWEIGHTING FEATURES\n",
|
||
"################################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# we can re-weight different features here...\n",
|
||
"#\n",
|
||
"\n",
|
||
"col_weights = { # could be called feature weight...\n",
|
||
" 'sepallen':1.0,\n",
|
||
" 'sepalwid':1.0,\n",
|
||
" 'petallen':1.0,\n",
|
||
" 'petalwid':1.0,\n",
|
||
"}\n",
|
||
"\n",
|
||
"for col_name in col_weights:\n",
|
||
" i = feature_name_to_index[col_name] # get the column index, i, of the column name\n",
|
||
" weight = col_weights[col_name] # from the dictionary above\n",
|
||
" print(f\"Weighting {col_name} by {weight}\")\n",
|
||
" # weighting == \"multiplying\"\n",
|
||
" X_all[:,i] *= weight # multiply by the weight to give this column (\"feature\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 23,
|
||
"metadata": {
|
||
"id": "sTDi2mFnKPSC"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"[[6. 2.9 4.5 1.5]\n",
|
||
" [6.2 2.9 4.3 1.3]\n",
|
||
" [6.4 2.9 4.3 1.3]\n",
|
||
" [6.3 2.3 4.4 1.3]\n",
|
||
" [6.2 2.8 4.8 1.8]\n",
|
||
" [4.9 3.1 1.5 0.1]\n",
|
||
" [6.6 3. 4.4 1.4]\n",
|
||
" [7.2 3.2 6. 1.8]\n",
|
||
" [6.7 3.3 5.7 2.5]\n",
|
||
" [6.8 3.2 5.9 2.3]\n",
|
||
" [5.1 3.4 1.5 0.2]\n",
|
||
" [4.8 3.4 1.6 0.2]\n",
|
||
" [4.9 3.1 1.5 0.2]\n",
|
||
" [4.9 3. 1.4 0.2]\n",
|
||
" [5.7 4.4 1.5 0.4]\n",
|
||
" [6.7 3.1 4.7 1.5]\n",
|
||
" [4.5 2.3 1.3 0.3]\n",
|
||
" [5.2 3.5 1.5 0.2]\n",
|
||
" [5.4 3. 4.5 1.5]\n",
|
||
" [5.1 3.8 1.9 0.4]\n",
|
||
" [6.7 3.3 5.7 2.1]\n",
|
||
" [6.1 3. 4.9 1.8]\n",
|
||
" [6.6 2.9 4.6 1.3]\n",
|
||
" [6.5 3.2 5.1 2. ]\n",
|
||
" [7.4 2.8 6.1 1.9]\n",
|
||
" [5.4 3.4 1.7 0.2]\n",
|
||
" [5.8 2.7 5.1 1.9]\n",
|
||
" [5.7 2.8 4.5 1.3]\n",
|
||
" [6.9 3.1 5.4 2.1]\n",
|
||
" [6.4 3.2 5.3 2.3]\n",
|
||
" [6.5 3. 5.8 2.2]\n",
|
||
" [5. 2. 3.5 1. ]\n",
|
||
" [5.1 3.8 1.6 0.2]\n",
|
||
" [5.6 3. 4.1 1.3]\n",
|
||
" [7.7 3. 6.1 2.3]\n",
|
||
" [6.1 2.6 5.6 1.4]\n",
|
||
" [4.6 3.2 1.4 0.2]\n",
|
||
" [7.2 3.6 6.1 2.5]\n",
|
||
" [4.6 3.4 1.4 0.3]\n",
|
||
" [4.8 3.1 1.6 0.2]\n",
|
||
" [7.7 2.6 6.9 2.3]\n",
|
||
" [5.6 2.5 3.9 1.1]\n",
|
||
" [5.5 2.4 3.8 1.1]\n",
|
||
" [6.5 3. 5.2 2. ]\n",
|
||
" [6.7 3. 5.2 2.3]\n",
|
||
" [6.1 3. 4.6 1.4]\n",
|
||
" [5.6 3. 4.5 1.5]\n",
|
||
" [6.3 2.5 4.9 1.5]\n",
|
||
" [5.5 2.4 3.7 1. ]\n",
|
||
" [5.8 2.6 4. 1.2]\n",
|
||
" [6.3 3.3 6. 2.5]\n",
|
||
" [6. 2.2 4. 1. ]\n",
|
||
" [5.7 2.9 4.2 1.3]\n",
|
||
" [5.4 3.7 1.5 0.2]\n",
|
||
" [4.4 3. 1.3 0.2]\n",
|
||
" [4.6 3.1 1.5 0.2]\n",
|
||
" [5. 3.5 1.3 0.3]\n",
|
||
" [7.7 2.8 6.7 2. ]\n",
|
||
" [5.9 3.2 4.8 1.8]\n",
|
||
" [5.2 3.4 1.4 0.2]\n",
|
||
" [4.4 2.9 1.4 0.2]\n",
|
||
" [5.7 2.6 3.5 1. ]\n",
|
||
" [4.9 2.4 3.3 1. ]\n",
|
||
" [7.2 3. 5.8 1.6]\n",
|
||
" [5. 3.4 1.6 0.4]\n",
|
||
" [5.1 2.5 3. 1.1]\n",
|
||
" [5.4 3.9 1.3 0.4]\n",
|
||
" [6.7 2.5 5.8 1.8]\n",
|
||
" [5.6 2.7 4.2 1.3]\n",
|
||
" [6.5 3. 5.5 1.8]\n",
|
||
" [6.2 2.2 4.5 1.5]\n",
|
||
" [6.4 3.1 5.5 1.8]\n",
|
||
" [6. 3. 4.8 1.8]\n",
|
||
" [5. 2.3 3.3 1. ]\n",
|
||
" [6.3 2.7 4.9 1.8]\n",
|
||
" [6.5 2.8 4.6 1.5]\n",
|
||
" [6.9 3.1 5.1 2.3]\n",
|
||
" [5.8 2.7 3.9 1.2]\n",
|
||
" [4.7 3.2 1.6 0.2]\n",
|
||
" [5.1 3.7 1.5 0.4]\n",
|
||
" [4.9 3.6 1.4 0.1]\n",
|
||
" [5.7 2.8 4.1 1.3]\n",
|
||
" [5. 3.6 1.4 0.2]\n",
|
||
" [6.1 2.9 4.7 1.4]\n",
|
||
" [5.8 4. 1.2 0.2]\n",
|
||
" [5. 3.5 1.6 0.6]\n",
|
||
" [6. 3.4 4.5 1.6]\n",
|
||
" [6.9 3.1 4.9 1.5]\n",
|
||
" [6.3 2.5 5. 1.9]\n",
|
||
" [6.3 3.3 4.7 1.6]\n",
|
||
" [4.8 3. 1.4 0.3]\n",
|
||
" [6.4 3.2 4.5 1.5]\n",
|
||
" [5.5 2.5 4. 1.3]\n",
|
||
" [4.8 3. 1.4 0.1]\n",
|
||
" [6.1 2.8 4. 1.3]\n",
|
||
" [5.2 2.7 3.9 1.4]\n",
|
||
" [5.1 3.5 1.4 0.3]\n",
|
||
" [5.1 3.3 1.7 0.5]\n",
|
||
" [6.9 3.2 5.7 2.3]\n",
|
||
" [5.6 2.8 4.9 2. ]\n",
|
||
" [4.4 3.2 1.3 0.2]\n",
|
||
" [6.8 3. 5.5 2.1]\n",
|
||
" [5.5 4.2 1.4 0.2]\n",
|
||
" [5.8 2.7 5.1 1.9]\n",
|
||
" [4.8 3.4 1.9 0.2]\n",
|
||
" [6.3 3.4 5.6 2.4]\n",
|
||
" [5.6 2.9 3.6 1.3]\n",
|
||
" [5.4 3.9 1.7 0.4]\n",
|
||
" [6.7 3. 5. 1.7]\n",
|
||
" [4.3 3. 1.1 0.1]\n",
|
||
" [5.5 3.5 1.3 0.2]\n",
|
||
" [7.7 3.8 6.7 2.2]\n",
|
||
" [7.3 2.9 6.3 1.8]\n",
|
||
" [5.4 3.4 1.5 0.4]\n",
|
||
" [5.7 2.5 5. 2. ]\n",
|
||
" [5.1 3.8 1.5 0.3]\n",
|
||
" [5.7 3.8 1.7 0.3]\n",
|
||
" [6.4 2.7 5.3 1.9]\n",
|
||
" [7.1 3. 5.9 2.1]\n",
|
||
" [6. 2.7 5.1 1.6]\n",
|
||
" [5.3 3.7 1.5 0.2]\n",
|
||
" [5.8 2.8 5.1 2.4]\n",
|
||
" [5. 3.3 1.4 0.2]\n",
|
||
" [5. 3.2 1.2 0.2]\n",
|
||
" [5.2 4.1 1.5 0.1]\n",
|
||
" [6.4 2.8 5.6 2.2]\n",
|
||
" [4.9 2.5 4.5 1.7]\n",
|
||
" [6.1 2.8 4.7 1.2]\n",
|
||
" [5.5 2.6 4.4 1.2]\n",
|
||
" [5.5 2.3 4. 1.3]\n",
|
||
" [6.7 3.1 5.6 2.4]\n",
|
||
" [4.6 3.6 1. 0.2]\n",
|
||
" [7.9 3.8 6.4 2. ]\n",
|
||
" [7. 3.2 4.7 1.4]\n",
|
||
" [6.3 2.8 5.1 1.5]\n",
|
||
" [6.4 2.8 5.6 2.1]\n",
|
||
" [6.8 2.8 4.8 1.4]\n",
|
||
" [7.6 3. 6.6 2.1]\n",
|
||
" [5.9 3. 4.2 1.5]\n",
|
||
" [5.8 2.7 4.1 1. ]\n",
|
||
" [6.3 2.9 5.6 1.8]\n",
|
||
" [6.7 3.1 4.4 1.4]\n",
|
||
" [5.7 3. 4.2 1.2]\n",
|
||
" [4.7 3.2 1.3 0.2]\n",
|
||
" [6. 2.2 5. 1.5]]\n",
|
||
"[1. 1. 1. 1. 2. 0. 1. 2. 2. 2. 0. 0. 0. 0. 0. 1. 0. 0. 1. 0. 2. 2. 1. 2.\n",
|
||
" 2. 0. 2. 1. 2. 2. 2. 1. 0. 1. 2. 2. 0. 2. 0. 0. 2. 1. 1. 2. 2. 1. 1. 1.\n",
|
||
" 1. 1. 2. 1. 1. 0. 0. 0. 0. 2. 1. 0. 0. 1. 1. 2. 0. 1. 0. 2. 1. 2. 1. 2.\n",
|
||
" 2. 1. 2. 1. 2. 1. 0. 0. 0. 1. 0. 1. 0. 0. 1. 1. 2. 1. 0. 1. 1. 0. 1. 1.\n",
|
||
" 0. 0. 2. 2. 0. 2. 0. 2. 0. 2. 1. 0. 1. 0. 0. 2. 2. 0. 2. 0. 0. 2. 2. 1.\n",
|
||
" 0. 2. 0. 0. 0. 2. 2. 1. 1. 1. 2. 0. 2. 1. 2. 2. 1. 2. 1. 1. 2. 1. 1. 0.\n",
|
||
" 2.]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"################################################\n",
|
||
"# BLOCK 22: PERMUTING THE DATA\n",
|
||
"################################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"#\n",
|
||
"# we scramble the data, to give a different TRAIN/TEST split each time...\n",
|
||
"#\n",
|
||
"indices = np.random.permutation(len(y_all)) # indices is a permutation-list\n",
|
||
"\n",
|
||
"# we scramble both X and y, necessarily with the same permutation\n",
|
||
"X_labeled = X_all[indices] # we apply the _same_ permutation to each!\n",
|
||
"y_labeled = y_all[indices] # again...\n",
|
||
"print(X_labeled) # note that X_labeled and y_labeled are permuted identically\n",
|
||
"print(y_labeled)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 24,
|
||
"metadata": {
|
||
"id": "_pD7IZR1KPSC"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"total rows: 145; training with 116 rows; testing with 29 rows\n",
|
||
"\t(sanity check: 116 + 29 = 145)\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"################################################\n",
|
||
"# BLOCK 23: DEFINING TRAIN VS TEST SETS\n",
|
||
"################################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# We next separate into test data and training data ...\n",
|
||
"# + We will train on the training data...\n",
|
||
"# + We will _not_ look at the testing data when building the model\n",
|
||
"#\n",
|
||
"# Then, afterward, we will test on the testing data -- and see how well we do!\n",
|
||
"#\n",
|
||
"\n",
|
||
"#\n",
|
||
"# a common convention: train on 80%, test on 20% Let's define the TEST_PERCENT\n",
|
||
"#\n",
|
||
"num_rows = X_labeled.shape[0] # the number of labeled rows\n",
|
||
"test_percent = 0.20\n",
|
||
"test_size = int(test_percent * num_rows) # no harm in rounding down\n",
|
||
"\n",
|
||
"X_test = X_labeled[:test_size] # first section are for testing\n",
|
||
"y_test = y_labeled[:test_size]\n",
|
||
"\n",
|
||
"X_train = X_labeled[test_size:] # all the rest are for training\n",
|
||
"y_train = y_labeled[test_size:]\n",
|
||
"\n",
|
||
"num_train_rows = len(y_train)\n",
|
||
"num_test_rows = len(y_test)\n",
|
||
"print(f\"total rows: {num_rows}; training with {num_train_rows} rows; testing with {num_test_rows} rows\" )\n",
|
||
"print(f\"\\t(sanity check: {num_train_rows} + {num_test_rows} = {num_train_rows + num_test_rows})\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 25,
|
||
"metadata": {
|
||
"id": "jvJMYC3eKPSD"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Created and trained a knn classifier with k = 84\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"#######################################################\n",
|
||
"# BLOCK 24: FIRST ATTEMPT TO BUILD & TRAIN A kNN MODEL\n",
|
||
"#######################################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# +++ This is the \"Model-building and Model-training Cell\"\n",
|
||
"#\n",
|
||
"# Create a kNN model and train it!\n",
|
||
"#\n",
|
||
"from sklearn.neighbors import KNeighborsClassifier\n",
|
||
"\n",
|
||
"k = 84 # we don't know what k to use, so we guess for now! (this will _not_ be a good value)\n",
|
||
"knn_model = KNeighborsClassifier(n_neighbors = k) # here, k is the \"k\" in kNN\n",
|
||
"\n",
|
||
"# we train the model (it's one line!)\n",
|
||
"knn_model.fit(X_train, y_train) # yay! trained!\n",
|
||
"print(\"Created and trained a knn classifier with k =\", k)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 26,
|
||
"metadata": {
|
||
"id": "d3cB0xryKPSD"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Predicted labels: [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.\n",
|
||
" 1. 1. 1. 1. 1.]\n",
|
||
"Actual labels : [1. 1. 1. 1. 2. 0. 1. 2. 2. 2. 0. 0. 0. 0. 0. 1. 0. 0. 1. 0. 2. 2. 1. 2.\n",
|
||
" 2. 0. 2. 1. 2.]\n",
|
||
"\n",
|
||
"Results on test set: 9 correct out of 29 total.\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"################################################\n",
|
||
"# BLOCK 25: TEST THE kNN MODEL\n",
|
||
"################################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# +++ This is the \"Model-testing Cell\"\n",
|
||
"#\n",
|
||
"# Now, let's see how well we did on our \"held-out data\" (the testing data)\n",
|
||
"#\n",
|
||
"\n",
|
||
"# We run our test set!\n",
|
||
"predicted_labels = knn_model.predict(X_test)\n",
|
||
"actual_labels = y_test\n",
|
||
"\n",
|
||
"# Let's print them so we can compare...\n",
|
||
"print(\"Predicted labels:\", predicted_labels)\n",
|
||
"print(\"Actual labels :\", actual_labels)\n",
|
||
"\n",
|
||
"# And, some overall results\n",
|
||
"num_correct = sum(predicted_labels == actual_labels)\n",
|
||
"total = len(actual_labels)\n",
|
||
"print(f\"\\nResults on test set: {num_correct} correct out of {total} total.\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 27,
|
||
"metadata": {
|
||
"id": "liXBT5-IKPSD"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"row 0 : versicolor versicolor \n",
|
||
"row 1 : versicolor versicolor \n",
|
||
"row 2 : versicolor versicolor \n",
|
||
"row 3 : versicolor versicolor \n",
|
||
"row 4 : versicolor virginica incorrect\n",
|
||
"row 5 : versicolor setosa incorrect\n",
|
||
"row 6 : versicolor versicolor \n",
|
||
"row 7 : versicolor virginica incorrect\n",
|
||
"row 8 : versicolor virginica incorrect\n",
|
||
"row 9 : versicolor virginica incorrect\n",
|
||
"row 10 : versicolor setosa incorrect\n",
|
||
"row 11 : versicolor setosa incorrect\n",
|
||
"row 12 : versicolor setosa incorrect\n",
|
||
"row 13 : versicolor setosa incorrect\n",
|
||
"row 14 : versicolor setosa incorrect\n",
|
||
"row 15 : versicolor versicolor \n",
|
||
"row 16 : versicolor setosa incorrect\n",
|
||
"row 17 : versicolor setosa incorrect\n",
|
||
"row 18 : versicolor versicolor \n",
|
||
"row 19 : versicolor setosa incorrect\n",
|
||
"row 20 : versicolor virginica incorrect\n",
|
||
"row 21 : versicolor virginica incorrect\n",
|
||
"row 22 : versicolor versicolor \n",
|
||
"row 23 : versicolor virginica incorrect\n",
|
||
"row 24 : versicolor virginica incorrect\n",
|
||
"row 25 : versicolor setosa incorrect\n",
|
||
"row 26 : versicolor virginica incorrect\n",
|
||
"row 27 : versicolor versicolor \n",
|
||
"row 28 : versicolor virginica incorrect\n",
|
||
"\n",
|
||
"Correct: 9 out of 29\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"9"
|
||
]
|
||
},
|
||
"execution_count": 27,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"################################################\n",
|
||
"# BLOCK 26: PRETTY-PRINT PREDICTED LABELS\n",
|
||
"################################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# Let's print these more helpfully, in a vertical table\n",
|
||
"#\n",
|
||
"\n",
|
||
"def compareLabels(predicted_labels: np.ndarray, actual_labels: np.ndarray) -> int:\n",
|
||
" ''' a more neatly formatted comparison, returning the number correct '''\n",
|
||
" num_labels = len(predicted_labels)\n",
|
||
" num_correct = 0\n",
|
||
"\n",
|
||
" for i in range(num_labels):\n",
|
||
" predicted = int(round(predicted_labels[i])) # round-to-int protects from float imprecision\n",
|
||
" actual = int(round(actual_labels[i]))\n",
|
||
" result = \"incorrect\"\n",
|
||
" if predicted == actual: # if they match,\n",
|
||
" result = \"\" # no longer incorrect\n",
|
||
" num_correct += 1 # and we count a match!\n",
|
||
"\n",
|
||
" # note the justification formatting:\n",
|
||
" # :>3d right justifies integers (d) to width 3\n",
|
||
" # :<12s left justifies strings (s) to width 12\n",
|
||
" print(f\"row {i:>3d} : \", end = \"\")\n",
|
||
" print(f\"{species_names[predicted]:>12s} \", end = \"\")\n",
|
||
" print(f\"{species_names[actual]:<12s} {result}\")\n",
|
||
"\n",
|
||
" print()\n",
|
||
" print(f\"Correct: {num_correct} out of {num_labels}\")\n",
|
||
" return num_correct\n",
|
||
"\n",
|
||
"#\n",
|
||
"# let's try it out!\n",
|
||
"#\n",
|
||
"\n",
|
||
"compareLabels(predicted_labels,actual_labels)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 28,
|
||
"metadata": {
|
||
"id": "h_ZigXq2KPSE"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"predicted_species = [1.]\n",
|
||
"I predict versicolor (1) from features [6.7, 3.3, 5.7, 2.1]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"################################################\n",
|
||
"# BLOCK 27: USE THE FIRST-ATTEMPT kNN MODEL\n",
|
||
"################################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# Ok! We have our knn model, we could just use it...\n",
|
||
"#\n",
|
||
"\n",
|
||
"#\n",
|
||
"# data-driven predictive model (k-nearest-neighbor), using scikit-learn\n",
|
||
"#\n",
|
||
"\n",
|
||
"def predictiveModel( features: list[float] ) -> str:\n",
|
||
" ''' input: a list of four features\n",
|
||
" [ sepallen, sepalwid, petallen, petalwid ]\n",
|
||
" output: the predicted species of iris, from\n",
|
||
" setosa (0), versicolor (1), virginica (2)\n",
|
||
" '''\n",
|
||
" our_features = np.asarray([features]) # extra brackets needed\n",
|
||
" predicted_species = knn_model.predict(our_features)\n",
|
||
" print(f\"predicted_species = {predicted_species}\")\n",
|
||
"\n",
|
||
" predicted_species = int(round(predicted_species[0])) # unpack one element\n",
|
||
" name = species_names[predicted_species]\n",
|
||
" return f\"{name} ({predicted_species})\"\n",
|
||
"\n",
|
||
"#\n",
|
||
"# Try it!\n",
|
||
"#\n",
|
||
"# features = eval(input(\"Enter new features: \"))\n",
|
||
"#\n",
|
||
"features = [6.7,3.3,5.7,2.1] # [5.8,2.7,4.1,1.0] [4.6,3.6,3.0,2.2] [6.7,3.3,5.7,2.1]\n",
|
||
"result = predictiveModel( features )\n",
|
||
"print(f\"I predict {result} from features {features}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 29,
|
||
"metadata": {
|
||
"id": "kPByHBBnKPSE"
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"################################################\n",
|
||
"# BLOCK 28: COMMENTS ON CHOICE OF BEST k\n",
|
||
"################################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# Except, we didn't really explore whether this was the BEST model we could build!\n",
|
||
"#\n",
|
||
"#\n",
|
||
"# We used k = 84 (a neighborhood size of 84 flowers)\n",
|
||
"# In a dataset of only 140ish flowers, with three species, this seems like a bad idea!\n",
|
||
"#\n",
|
||
"# Perhaps we should try ALL the neighborhood sizes in their own TRAIN/TEST split\n",
|
||
"# and see which neighborhood size works the best, for irises, at least...\n",
|
||
"#"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 30,
|
||
"metadata": {
|
||
"id": "ZUmotr7hKPSE"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"k: 1 cv accuracy: 0.9482\n",
|
||
"k: 2 cv accuracy: 0.9225\n",
|
||
"k: 3 cv accuracy: 0.9482\n",
|
||
"k: 4 cv accuracy: 0.9308\n",
|
||
"k: 5 cv accuracy: 0.9395\n",
|
||
"k: 6 cv accuracy: 0.9308\n",
|
||
"k: 7 cv accuracy: 0.9482\n",
|
||
"k: 8 cv accuracy: 0.9478\n",
|
||
"k: 9 cv accuracy: 0.9478\n",
|
||
"k: 10 cv accuracy: 0.9391\n",
|
||
"k: 11 cv accuracy: 0.9478\n",
|
||
"k: 12 cv accuracy: 0.9478\n",
|
||
"k: 13 cv accuracy: 0.9478\n",
|
||
"k: 14 cv accuracy: 0.9478\n",
|
||
"k: 15 cv accuracy: 0.9482\n",
|
||
"k: 16 cv accuracy: 0.9304\n",
|
||
"k: 17 cv accuracy: 0.9391\n",
|
||
"k: 18 cv accuracy: 0.9304\n",
|
||
"k: 19 cv accuracy: 0.9217\n",
|
||
"k: 20 cv accuracy: 0.9304\n",
|
||
"k: 21 cv accuracy: 0.9130\n",
|
||
"k: 22 cv accuracy: 0.9304\n",
|
||
"k: 23 cv accuracy: 0.9304\n",
|
||
"k: 24 cv accuracy: 0.9304\n",
|
||
"k: 25 cv accuracy: 0.9478\n",
|
||
"k: 26 cv accuracy: 0.9391\n",
|
||
"k: 27 cv accuracy: 0.9478\n",
|
||
"k: 28 cv accuracy: 0.9565\n",
|
||
"k: 29 cv accuracy: 0.9304\n",
|
||
"k: 30 cv accuracy: 0.9217\n",
|
||
"k: 31 cv accuracy: 0.9304\n",
|
||
"k: 32 cv accuracy: 0.9217\n",
|
||
"k: 33 cv accuracy: 0.9217\n",
|
||
"k: 34 cv accuracy: 0.9217\n",
|
||
"k: 35 cv accuracy: 0.9304\n",
|
||
"k: 36 cv accuracy: 0.9217\n",
|
||
"k: 37 cv accuracy: 0.9304\n",
|
||
"k: 38 cv accuracy: 0.9217\n",
|
||
"k: 39 cv accuracy: 0.9217\n",
|
||
"k: 40 cv accuracy: 0.9304\n",
|
||
"k: 41 cv accuracy: 0.9304\n",
|
||
"k: 42 cv accuracy: 0.9130\n",
|
||
"k: 43 cv accuracy: 0.9043\n",
|
||
"k: 44 cv accuracy: 0.9130\n",
|
||
"k: 45 cv accuracy: 0.8957\n",
|
||
"k: 46 cv accuracy: 0.8870\n",
|
||
"k: 47 cv accuracy: 0.8957\n",
|
||
"k: 48 cv accuracy: 0.8957\n",
|
||
"k: 49 cv accuracy: 0.8957\n",
|
||
"k: 50 cv accuracy: 0.9043\n",
|
||
"k: 51 cv accuracy: 0.9043\n",
|
||
"k: 52 cv accuracy: 0.8703\n",
|
||
"k: 53 cv accuracy: 0.8873\n",
|
||
"k: 54 cv accuracy: 0.8964\n",
|
||
"k: 55 cv accuracy: 0.9047\n",
|
||
"k: 56 cv accuracy: 0.8964\n",
|
||
"k: 57 cv accuracy: 0.8877\n",
|
||
"k: 58 cv accuracy: 0.8877\n",
|
||
"k: 59 cv accuracy: 0.8873\n",
|
||
"k: 60 cv accuracy: 0.7333\n",
|
||
"k: 61 cv accuracy: 0.7076\n",
|
||
"k: 62 cv accuracy: 0.5518\n",
|
||
"k: 63 cv accuracy: 0.4565\n",
|
||
"k: 64 cv accuracy: 0.4304\n",
|
||
"k: 65 cv accuracy: 0.3967\n",
|
||
"k: 66 cv accuracy: 0.3623\n",
|
||
"k: 67 cv accuracy: 0.3536\n",
|
||
"k: 68 cv accuracy: 0.3449\n",
|
||
"k: 69 cv accuracy: 0.3536\n",
|
||
"k: 70 cv accuracy: 0.3536\n",
|
||
"k: 71 cv accuracy: 0.3536\n",
|
||
"k: 72 cv accuracy: 0.3536\n",
|
||
"k: 73 cv accuracy: 0.3536\n",
|
||
"k: 74 cv accuracy: 0.3536\n",
|
||
"k: 75 cv accuracy: 0.3536\n",
|
||
"k: 76 cv accuracy: 0.3536\n",
|
||
"k: 77 cv accuracy: 0.3536\n",
|
||
"k: 78 cv accuracy: 0.3536\n",
|
||
"k: 79 cv accuracy: 0.3536\n",
|
||
"k: 80 cv accuracy: 0.3536\n",
|
||
"k: 81 cv accuracy: 0.3536\n",
|
||
"k: 82 cv accuracy: 0.3536\n",
|
||
"k: 83 cv accuracy: 0.3536\n",
|
||
"k: 84 cv accuracy: 0.3536\n",
|
||
"best_k = 28 yields the highest average cv accuracy.\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA04AAAIjCAYAAAA0vUuxAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAeltJREFUeJzt3Xd4VNXaxuFn0ntICEkIhN5bgiC9KgoWEEUBRUGwHQGliB49qNhjOSCIIOonliMIUgRsKNIERJDeewk9hEAS0jOzvz9IRmICyYSEmWR+93XNdciePXveCRtPnqy13mUyDMMQAAAAAOCKXOxdAAAAAAA4OoITAAAAABSC4AQAAAAAhSA4AQAAAEAhCE4AAAAAUAiCEwAAAAAUguAEAAAAAIUgOAEAAABAIQhOAAAAAFAIghMAXAevvPKKTCaT4uPj7V0KyihHuoeOHDkik8mkL774wt6lAMB1Q3ACAAfy8MMPy2QyqVmzZjIMI9/zJpNJw4cPt36d+wOsyWTSvHnz8p1fnB+2p06dKpPJpNatW1/DJ4Gj6NWrl3x8fJScnHzFcwYMGCAPDw+dO3fuutYGAGUJwQkAHND27ds1f/58m17z2muvFRi2bDVjxgzVqFFD69ev14EDB675erCvAQMGKC0tTd99912Bz6empmrhwoXq0aOHKlaseN3rA4CyguAEAA7G29tb9erVsykIRUdHa9u2bVf84bioDh8+rD/++EMTJkxQpUqVNGPGjGu6XmlKSUmxdwllQq9eveTv76+ZM2cW+PzChQuVkpKiAQMGXPfaAKAsITgBgJ0cPXpUderUUZMmTXTmzBnrcRcXF7344os2BaH+/fvbHLYKMmPGDAUFBemOO+7Qvffee8XgdOHCBY0aNUo1atSQp6enqlatqoEDB+aZEpienq5XXnlF9erVk5eXlypXrqx77rlHBw8elCStWLFCJpNJK1asyHPtgtbPPPzww/Lz89PBgwd1++23y9/f3/qD/qpVq3TfffepWrVq8vT0VGRkpEaNGqW0tLR8de/Zs0d9+/ZVpUqV5O3trfr162vs2LGSpOXLl8tkMhX4PZ85c6ZMJpPWrl1b4Pdjw4YNMplM+vLLL/M998svv8hkMumHH36QJCUnJ2vkyJHW711oaKhuueUWbdq06Qp/K1d2pXvoct7e3rrnnnu0dOlSxcXFFfjZ/P391atXLyUkJGjMmDFq2rSp/Pz8FBAQoNtuu01bt24ttJYuXbqoS5cu+Y4//PDDqlGjRp5jFotFEydOVOPGjeXl5aWwsDA98cQTOn/+vE2fHwCuJ4ITANjBwYMH1alTJ/n7+2vFihUKCwvL8/wDDzygunXrFjkIubq66sUXX9TWrVuvadRpxowZuueee+Th4aH7779f+/fv119//ZXnnIsXL6pjx46aPHmybr31Vk2aNEn/+te/tGfPHh0/flySZDabdeedd+rVV19VixYtNH78eI0YMUKJiYnasWNHsWrLzs5W9+7dFRoaqv/+97/q06ePJGnOnDlKTU3Vk08+qcmTJ6t79+6aPHmyBg4cmOf127ZtU+vWrbVs2TI99thjmjRpknr37q3vv/9eyvnBPzIyssCwOGPGDNWuXVtt27YtsLaWLVuqVq1a+vbbb/M9N3v2bAUFBal79+6SpH/961/66KOP1KdPH02dOlVjxoyRt7e3du/ebdP3o7B76HIDBgxQdnZ2vvoSEhL0yy+/6O6775a3t7cOHTqkBQsW6M4779SECRP07LPPavv27ercubNOnjxpU31X88QTT+jZZ59V+/btNWnSJA0ePFgzZsxQ9+7dlZWVVWLvAwAlygAAlLpx48YZkoyzZ88au3fvNiIiIowbb7zRSEhIyHPeoEGDDF9fX8MwDOPLL780JBnz58+3Pi/JGDZsmPXrw4cPG5KM9957z8jOzjbq1q1rREVFGRaLJd/7FmbDhg2GJGPJkiWGYRiGxWIxqlataowYMSLPeS+//HK+unLlvu/06dMNScaECROueM7y5csNScby5cvzPJ/7mT7//PM83xdJxvPPP5/veqmpqfmOxcTEGCaTyTh69Kj1WKdOnQx/f/88xy6vxzAM44UXXjA8PT2NCxcuWI/FxcUZbm5uxrhx4/K9z+VeeOEFw93dPc/faUZGhlGhQgVjyJAh1mOBgYF5/g6Lqqj3UEGys7ONypUrG23bts1zfNq0aYYk45dffjEMwzDS09MNs9mc55zDhw8bnp6exmuvvZbn2D//jjp37mx07tw533sPGjTIqF69uvXrVatWGZKMGTNm5Dlv8eLFBR4HAEfBiBMAXEc7duxQ586dVaNGDf32228KCgq64rkDBgwo9qjTggULbK5txowZCgsLU9euXaWcDn79+vXTrFmzZDabrefNmzdPUVFRuvvuu/Ndw2QyWc8JCQnRU089dcVziuPJJ5/Md8zb29v655SUFMXHx6tdu3YyDEObN2+WJJ09e1a///67hgwZomrVql2xnoEDByojI0Nz5861Hps9e7ays7P14IMPXrW2fv36KSsrK09Tj19//VUXLlxQv379rMcqVKigdevWFXsEx5Z7KJerq6v69++vtWvX6siRI9bjM2fOVFhYmG6++WZJkqenp1xcLv1oYDabde7cOfn5+al+/frFmkpYkDlz5igwMFC33HKL4uPjrY8WLVrIz89Py5cvL5H3AYCSRnACgOuoZ8+e8vf31y+//KKAgICrnpsbhLZs2VLkIDRgwADVqVPH5rVOZrNZs2bNUteuXXX48GEdOHBABw4cUOvWrXXmzBktXbrUeu7BgwfVpEmTq17v4MGDql+/vtzc3IpcQ2Hc3NxUtWrVfMdjY2P18MMPKzg4WH5+fqpUqZI6d+4sSUpMTJQkHTp0SJIKrbtBgwa68cYb80zXmzFjhtq0aaM6depc9bVRUVFq0KCBZs+ebT02e/ZshYSE6KabbrIee/fdd7Vjxw5FRkaqVatWeuWVV6z1FYUt99DlcteE5TaJOH78uFatWqX+/fvL1dVVyll79P7776tu3bry9PRUSEiIKlWqpG3btlm/l9dq//79SkxMVGhoqCpVqpTncfHixQLXYQGAIyA4AcB11KdPHx08eLDI3epsDUKXh62FCxcWua5ly5bp1KlTmjVrlurWrWt99O3bV8oJDyXtSiNPl49uXe7y0ZDLz73lllv0448/6t///rcWLFigJUuWWBtLWCwWm+saOHCgVq5cqePHj+vgwYP6888/Cx1tytWvXz8tX75c8fHxysjI0KJFi9SnT588AbJv3746dOiQJk+erIiICL333ntq3Lixfv755yK9h633UK4WLVqoQYMG+uabbyRJ33zzjQzDyNNN76233tLo0aPVqVMnff311/rll1+0ZMkSNW7cuNDvZVH/Pi0Wi0JDQ7VkyZICH6+99ppNnwsArpeS+1UgAKBQ7733ntzc3DR06FD5+/vrgQceuOr5uUHo4YcfLnIQevDBB/XGG2/o1VdfVa9evYr0mhkzZig0NFRTpkzJ99z8+fP13Xffadq0afL29lbt2rULbfBQu3ZtrVu3TllZWXJ3dy/wnNwpZhcuXMhz/OjRo0Wq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DRw4UH5+fqpcubLGjx+f770yMjI0ZswYValSRb6+vmrdurVWrFghSUpPT1fjxo31+OOPW88/ePCg/P39NX36dOuxnj17asOGDTp48OBVPxcA4NoRnAAAxTZz5kzdf//9mjFjhgYMGJDveRcXF91///2aOXNmnuMzZsxQ+/btVb16det5H3zwgXbu3Kkvv/xSy5Yt03PPPXdNtcXExOirr77StGnTtHPnTo0aNUoPPvigVq5cecXXrFq1Si1btiz02gcPHtSCBQv0ww8/6IcfftDKlSv19ttvW59/9tlntXLlSi1cuFC//vqrVqxYoU2bNuW5xvDhw7V27VrNmjVL27Zt03333acePXpo//798vLy0owZM/Tll19q4cKFMpvNevDBB3XLLbdoyJAh1mtUq1ZNYWFhWrVqVbG/TwCAonGzdwEAgLJpypQpGjt2rL7//nt17tz5iucNGDBA48ePV2xsrKpVqyaLxaJZs2bpxRdftJ5zedOEGjVq6I033tC//vUvTZ06tVi1ZWRk6K233tJvv/2mtm3bSpJq1aql1atX6+OPP75ivUePHi1ScLJYLPriiy+s66AeeughLV26VG+++aYuXryozz77TF9//bVuvvlmSdKXX36pqlWrWl8fGxurzz//XLGxsYqIiJAkjRkzRosXL9bnn3+ut956S9HR0XrjjTf06KOPqn///jp69Kh++OGHfLVERERYR+4AAKWH4AQAsNncuXMVFxenNWvW6MYbb7zqudHR0WrYsKFmzpyp559/XitXrlRcXJzuu+8+6zm//fabYmJitGfPHiUlJSk7O1vp6elKTU2Vj4+PzfUdOHBAqampuuWWW/Icz8zMVPPmza/4urS0NHl5eRV6/Ro1alhDkyRVrlxZcXFxUs5oVGZmplq3bm19Pjg4WPXr17d+vX37dpnNZtWrVy/PdTMyMlSxYkXr188884wWLFigDz/8UD///HOe53J5e3srNTW10JoBANeG4AQAsFnz5s21adMmTZ8+XS1btpTJZLrq+QMGDLAGp5kzZ6pHjx7WEHDkyBHdeeedevLJJ/Xmm28qODhYq1ev1iOPPKLMzMwCg5OLi4sMw8hzLCsry/rn3PVRP/74Y571SpLk6el5xTpDQkJ0/vz5Qj+/u7t7nq9NJpNNbcEvXrwoV1dXbdy4Ua6urnme8/Pzs/45Li5O+/btk6urq/bv368ePXrku1ZCQoIqVapU5PcGABQPa5wAADarXbu2li9froULF+qpp54q9PwHHnhAO3bs0MaNGzV37tw866E2btwoi8Wi8ePHq02bNqpXr55Onjx51etVqlRJp06dynNsy5Yt1j83atRInp6eio2NVZ06dfI8IiMjr3jd5s2ba9euXYV+nqupXbu23N3dtW7dOuux8+fP52lp3rx5c5nNZsXFxeWrLzw83HrekCFD1LRpU3355Zf697//rd27d+d5r/T0dB08ePCqo2gAgJLBiBMAoFjq1aun5cuXq0uXLnJzc9PEiROveG6NGjXUrl07PfLIIzKbzerVq5f1uTp16igrK0uTJ09Wz549tWbNGk2bNu2q733TTTfpvffe01dffaW2bdvq66+/1o4dO6wBwt/fX2PGjNGoUaNksVjUoUMHJSYmas2aNQoICNCgQYMKvG737t316KOPymw25xsJKio/Pz898sgjevbZZ1WxYkWFhoZq7NixcnH5+3eV9erV04ABAzRw4ECNHz9ezZs319mzZ7V06VI1a9ZMd9xxh6ZMmaK1a9dq27ZtioyM1I8//qgBAwbozz//lIeHhyTpzz//lKenp3UdFwCg9DDiBAAotvr162vZsmX65ptv9Mwzz1z13AEDBmjr1q26++675e3tbT0eFRWlCRMm6J133lGTJk00Y8YMxcTEXPVa3bt310svvaTnnntON954o5KTkzVw4MA857z++ut66aWXFBMTo4YNG6pHjx768ccfVbNmzSte97bbbpObm5t+++23In8PCvLee++pY8eO6tmzp7p166YOHTqoRYsWec75/PPPNXDgQD3zzDOqX7++evfurb/++kvVqlXTnj179Oyzz2rq1KnWEbKpU6cqPj5eL730kvUa33zzjQYMGFCsdWAAANuYjH9OEgcAwIlNmTJFixYt0i+//GLvUq4qPj5e9evX14YNG64aBgEAJYOpegAAXOaJJ57QhQsXlJycnKdznqM5cuSIpk6dSmgCgOuEEScAAAAAKARrnAAAAACgEAQnAAAAACgEwQkAAAAACkFwAgAAAIBCEJwAAAAAoBAEJwAAAAAoBMEJAAAAAApBcAIAAACAQhCcAAAAAKAQ/w9lVreHBQJq8AAAAABJRU5ErkJggg==",
|
||
"text/plain": [
|
||
"<Figure size 1000x600 with 1 Axes>"
|
||
]
|
||
},
|
||
"metadata": {},
|
||
"output_type": "display_data"
|
||
}
|
||
],
|
||
"source": [
|
||
"################################################\n",
|
||
"# BLOCK 29: USING CROSS VALIDATION\n",
|
||
"################################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# to do this, we use \"cross validation\"\n",
|
||
"# (see slide 45)\n",
|
||
"#\n",
|
||
"\n",
|
||
"import seaborn as sns\n",
|
||
"import matplotlib.pyplot as plt\n",
|
||
"from sklearn.model_selection import cross_val_score\n",
|
||
"\n",
|
||
"#\n",
|
||
"# cross-validation splits the training set into two pieces:\n",
|
||
"# + model-building and model-validation. We'll use \"build\" and \"validate\"\n",
|
||
"#\n",
|
||
"\n",
|
||
"max_k = 85\n",
|
||
"all_accuracies = []\n",
|
||
"best_k = 1\n",
|
||
"best_accuracy = -1\n",
|
||
"\n",
|
||
"for k in range(1, max_k, 1):\n",
|
||
" knn_cv_model = KNeighborsClassifier(n_neighbors=k)\n",
|
||
" cv_scores = cross_val_score(knn_cv_model, X_train, y_train, cv=5)\n",
|
||
" this_cv_accuracy = cv_scores.mean()\n",
|
||
" print(f\"k: {k:2d} cv accuracy: {this_cv_accuracy:7.4f}\")\n",
|
||
" all_accuracies.append(this_cv_accuracy)\n",
|
||
" if this_cv_accuracy > best_accuracy:\n",
|
||
" best_accuracy = this_cv_accuracy\n",
|
||
" best_k = k\n",
|
||
"# assign best value of k to best_k\n",
|
||
"#best_k = k # *** AT THE MOMENT THIS IS INCORRECT ***\n",
|
||
"# you'll need to modify the loop above to find and remember the real best_k\n",
|
||
"\n",
|
||
"print(f\"best_k = {best_k} yields the highest average cv accuracy.\") # print the best one\n",
|
||
"\n",
|
||
"plt.figure(figsize=(10, 6))\n",
|
||
"sns.lineplot(x=range(1,len(all_accuracies)+1), y=all_accuracies)\n",
|
||
"plt.xlabel(\"k value (index)\")\n",
|
||
"plt.ylabel(\"Cross-validated accuracy\")\n",
|
||
"plt.title(\"kNN Accuracy vs k Value\")\n",
|
||
"plt.show()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 31,
|
||
"metadata": {
|
||
"id": "pJW6c8XdKPSF"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Created + trained a knn classifier, now tuned with a (best) k of 28\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"################################################\n",
|
||
"# BLOCK 30: RE-BUILD & RE-TRAIN USING BEST k\n",
|
||
"################################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# Now, we re-create and re-run the \"Model-building and -training Cell\"\n",
|
||
"#\n",
|
||
"# Now, using best_k instead of the original, randomly-guessed value How does it do?!\n",
|
||
"#\n",
|
||
"from sklearn.neighbors import KNeighborsClassifier\n",
|
||
"knn_model_tuned = KNeighborsClassifier(n_neighbors = best_k) # here, we use the best_k\n",
|
||
"\n",
|
||
"# we train the model (one line!)\n",
|
||
"knn_model_tuned.fit(X_train, y_train) # yay! trained!\n",
|
||
"print(f\"Created + trained a knn classifier, now tuned with a (best) k of {best_k}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 32,
|
||
"metadata": {
|
||
"id": "9h5-1TukKPSF"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Predicted labels: [1. 1. 1. 1. 1. 0. 1. 2. 2. 2. 0. 0. 0. 0. 0. 1. 0. 0. 1. 0. 2. 2. 1. 2.\n",
|
||
" 2. 0. 2. 1. 2.]\n",
|
||
"Actual labels: [1. 1. 1. 1. 2. 0. 1. 2. 2. 2. 0. 0. 0. 0. 0. 1. 0. 0. 1. 0. 2. 2. 1. 2.\n",
|
||
" 2. 0. 2. 1. 2.]\n",
|
||
"\n",
|
||
"row 0 : versicolor versicolor \n",
|
||
"row 1 : versicolor versicolor \n",
|
||
"row 2 : versicolor versicolor \n",
|
||
"row 3 : versicolor versicolor \n",
|
||
"row 4 : versicolor virginica incorrect\n",
|
||
"row 5 : setosa setosa \n",
|
||
"row 6 : versicolor versicolor \n",
|
||
"row 7 : virginica virginica \n",
|
||
"row 8 : virginica virginica \n",
|
||
"row 9 : virginica virginica \n",
|
||
"row 10 : setosa setosa \n",
|
||
"row 11 : setosa setosa \n",
|
||
"row 12 : setosa setosa \n",
|
||
"row 13 : setosa setosa \n",
|
||
"row 14 : setosa setosa \n",
|
||
"row 15 : versicolor versicolor \n",
|
||
"row 16 : setosa setosa \n",
|
||
"row 17 : setosa setosa \n",
|
||
"row 18 : versicolor versicolor \n",
|
||
"row 19 : setosa setosa \n",
|
||
"row 20 : virginica virginica \n",
|
||
"row 21 : virginica virginica \n",
|
||
"row 22 : versicolor versicolor \n",
|
||
"row 23 : virginica virginica \n",
|
||
"row 24 : virginica virginica \n",
|
||
"row 25 : setosa setosa \n",
|
||
"row 26 : virginica virginica \n",
|
||
"row 27 : versicolor versicolor \n",
|
||
"row 28 : virginica virginica \n",
|
||
"\n",
|
||
"Correct: 28 out of 29\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"28"
|
||
]
|
||
},
|
||
"execution_count": 32,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"################################################\n",
|
||
"# BLOCK 31: RE-TEST THE BEST-k MODEL\n",
|
||
"################################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# Re-create and re-run the \"Model-testing Cell\" How does it do with best_k?!\n",
|
||
"#\n",
|
||
"predicted_labels = knn_model_tuned.predict(X_test)\n",
|
||
"actual_labels = y_test\n",
|
||
"\n",
|
||
"# Let's print them so we can compare...\n",
|
||
"print(\"Predicted labels:\", predicted_labels)\n",
|
||
"print(\"Actual labels:\", actual_labels)\n",
|
||
"print()\n",
|
||
"# and, we'll print our nicer table...\n",
|
||
"compareLabels(predicted_labels,actual_labels)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 33,
|
||
"metadata": {
|
||
"id": "8dIjchI0RQ57"
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"################################################\n",
|
||
"# BLOCK 32: TRYING DIFFERENT PERMUTATIONS\n",
|
||
"################################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# Before moving on, go back and choose a new permutation to give\n",
|
||
"# new testing and training sets, and go back through the steps\n",
|
||
"# for identifying the \"best\" k (starting at Block 22 and re-doing\n",
|
||
"# up through Block 31).\n",
|
||
"#\n",
|
||
"# Repeat this several times.\n",
|
||
"# Do you get the same value for k each time? Why or why not?\n",
|
||
"#"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 34,
|
||
"metadata": {
|
||
"id": "h1fhLBYSKPSF"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Created + trained a 'final' knn classifier, with a (best) k of 28\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"####################################################\n",
|
||
"# BLOCK 33: REBUILD & TRAIN USING BEST k & ALL DATA\n",
|
||
"####################################################\n",
|
||
"\n",
|
||
"# Ok! Now we have tuned knn to use the \"best\" value of k...\n",
|
||
"#\n",
|
||
"# And, we should really use ALL available data to train our final predictive model\n",
|
||
"# (not split into test and train as before)\n",
|
||
"#\n",
|
||
"\n",
|
||
"knn_model_final = KNeighborsClassifier(n_neighbors=best_k) # here, we use the best_k\n",
|
||
"knn_model_final.fit(X_all, y_all) # yay! trained!\n",
|
||
"print(f\"Created + trained a 'final' knn classifier, with a (best) k of {best_k}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 35,
|
||
"metadata": {
|
||
"id": "AsC4oa2qKPSG"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"I predict virginica (2) from features [6.7, 3.3, 5.7, 2.1]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"####################################################\n",
|
||
"# BLOCK 34: TRYING FINAL MODEL \"IN THE WILD\"\n",
|
||
"####################################################\n",
|
||
"\n",
|
||
"#\n",
|
||
"# final predictive model (k-nearest-neighbor), with tuned k + ALL data incorporated\n",
|
||
"#\n",
|
||
"\n",
|
||
"def predictiveModel( features: list[float] ) -> None:\n",
|
||
" ''' input: a list of four features\n",
|
||
" [ sepallen, sepalwid, petallen, petalwid ]\n",
|
||
" output: the predicted species of iris, from\n",
|
||
" setosa (0), versicolor (1), virginica (2)\n",
|
||
" '''\n",
|
||
" our_features = np.asarray([features]) # extra brackets needed\n",
|
||
" predicted_species = knn_model_final.predict(our_features)\n",
|
||
"\n",
|
||
" predicted_species = int(round(predicted_species[0])) # unpack one element\n",
|
||
" name = species_names[predicted_species]\n",
|
||
" return f\"{name} ({predicted_species})\"\n",
|
||
"\n",
|
||
"#\n",
|
||
"# Try it on several!\n",
|
||
"#\n",
|
||
"# features = eval(input(\"Enter new features: \"))\n",
|
||
"#\n",
|
||
"features = [6.7,3.3,5.7,2.1] # [5.8,2.7,4.1,1.0] [4.6,3.6,3.0,2.2] [6.7,3.3,5.7,2.1]\n",
|
||
"result = predictiveModel( features )\n",
|
||
"print(f\"I predict {result} from features {features}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 36,
|
||
"metadata": {
|
||
"id": "UHNILKXiRQ58"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"<StringArray>\n",
|
||
"['setosa', 'versicolor', 'virginica']\n",
|
||
"Length: 3, dtype: str\n",
|
||
"======================================================================\n",
|
||
" sepallen sepalwid petallen petalwid irisname\n",
|
||
"1 4.9 3.0 1.4 0.2 0\n",
|
||
"2 4.7 3.2 1.3 0.2 0\n",
|
||
"3 4.6 3.1 1.5 0.2 0\n",
|
||
"4 5.0 3.6 1.4 0.2 0\n",
|
||
".. ... ... ... ... ...\n",
|
||
"144 6.7 3.3 5.7 2.5 2\n",
|
||
"145 6.7 3.0 5.2 2.3 2\n",
|
||
"146 6.3 2.5 5.0 1.9 2\n",
|
||
"147 6.5 3.0 5.2 2.0 2\n",
|
||
"\n",
|
||
"[145 rows x 5 columns]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"################################################################\n",
|
||
"# BLOCK 35a: HOW DOES THE MODEL PERFORM ON MEAN OF EACH FLOWER?\n",
|
||
"################################################################\n",
|
||
"\n",
|
||
"# let's recall the species names and their order in the list\n",
|
||
"print(species_names)\n",
|
||
"print('=' * 70)\n",
|
||
"\n",
|
||
"# and let's recall what the final DataFrame looks like, noting that\n",
|
||
"# the irisname was converted to the corresponding index in species_names\n",
|
||
"print(df_final)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 37,
|
||
"metadata": {
|
||
"id": "MlnIrLPWRQ58"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
" sepallen sepalwid petallen petalwid irisname\n",
|
||
"50 7.0 3.2 4.7 1.4 1\n",
|
||
"51 6.4 3.2 4.5 1.5 1\n",
|
||
"52 6.9 3.1 4.9 1.5 1\n",
|
||
"53 5.5 2.3 4.0 1.3 1\n",
|
||
".. ... ... ... ... ...\n",
|
||
"96 5.7 2.9 4.2 1.3 1\n",
|
||
"97 6.2 2.9 4.3 1.3 1\n",
|
||
"98 5.1 2.5 3.0 1.1 1\n",
|
||
"99 5.7 2.8 4.1 1.3 1\n",
|
||
"\n",
|
||
"[50 rows x 5 columns]\n",
|
||
"======================================================================\n",
|
||
"sepallen 5.936\n",
|
||
"sepalwid 2.770\n",
|
||
"petallen 4.260\n",
|
||
"petalwid 1.326\n",
|
||
"irisname 1.000\n",
|
||
"dtype: float64\n",
|
||
"======================================================================\n",
|
||
"[5.936 2.77 4.26 1.326]\n",
|
||
"Mean versicolor features [5.936 2.77 4.26 1.326] -> versicolor (1)\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"################################################################\n",
|
||
"# BLOCK 35b: HOW DOES THE MODEL PERFORM ON MEAN OF EACH FLOWER?\n",
|
||
"################################################################\n",
|
||
"\n",
|
||
"# let's grab the data from df_final corresponding to versicolor\n",
|
||
"versicolor_data = df_final[ df_final['irisname'] == convertSpecies('versicolor') ]\n",
|
||
"print(versicolor_data)\n",
|
||
"print('=' * 70)\n",
|
||
"\n",
|
||
"# let's print the mean of each column\n",
|
||
"print(versicolor_data.mean())\n",
|
||
"print('=' * 70)\n",
|
||
"\n",
|
||
"# and let's grab the features means only (don't need the label) for versicolor\n",
|
||
"versicolor_features_means = np.asarray(versicolor_data.mean()[:-1])\n",
|
||
"print(versicolor_features_means)\n",
|
||
"\n",
|
||
"# now run the predictive model (see Block 34 above) and print a corresponding\n",
|
||
"# message\n",
|
||
"\n",
|
||
"# >>> YOU ADD CODE HERE\n",
|
||
"result = predictiveModel(versicolor_features_means)\n",
|
||
"print(f\"Mean versicolor features {versicolor_features_means} -> {result}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 38,
|
||
"metadata": {
|
||
"id": "twR2L9IRRQ58"
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Mean setosa features [5.00425532 3.43617021 1.45957447 0.24893617] -> setosa (0)\n",
|
||
"Mean versicolor features [5.936 2.77 4.26 1.326] -> versicolor (1)\n",
|
||
"Mean virginica features [6.61041667 2.96458333 5.56458333 2.025 ] -> virginica (2)\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"################################################################\n",
|
||
"# BLOCK 35c: HOW DOES THE MODEL PERFORM ON MEAN OF EACH FLOWER?\n",
|
||
"################################################################\n",
|
||
"\n",
|
||
"# Automate the process used in Block 35b, across all species:\n",
|
||
"#\n",
|
||
"# Loop across species names, and for each species,\n",
|
||
"# (a) pull that corresponding data from df_final\n",
|
||
"# (b) compute the means of the features, storing as a numpy array\n",
|
||
"# (c) call the predictive model using those features\n",
|
||
"# (d) print a corresponding message\n",
|
||
"\n",
|
||
"# >> YOU ADD CODE HERE -- YOU SHOULD NEED NO MORE THAN 5 LINES OF CODE\n",
|
||
"for name in species_names:\n",
|
||
" species_data = df_final[df_final['irisname'] == convertSpecies(name)]\n",
|
||
" species_features_means = np.asarray(species_data.mean()[:-1])\n",
|
||
" result = predictiveModel(species_features_means)\n",
|
||
" print(f\"Mean {name} features {species_features_means} -> {result}\")"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"colab": {
|
||
"provenance": []
|
||
},
|
||
"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.14.3"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 0
|
||
}
|