{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"tags": [
"remove_input"
]
},
"outputs": [],
"source": [
"path_data = '../../data/'\n",
"\n",
"import numpy as np\n",
"import pandas as pd\n",
"import math\n",
"import scipy.stats as stats\n",
"\n",
"import matplotlib.pyplot as plt\n",
"from mpl_toolkits.mplot3d import Axes3D\n",
"plt.style.use('fivethirtyeight')\n",
"\n",
"import warnings\n",
"warnings.filterwarnings('ignore')"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"tags": [
"remove_input"
]
},
"outputs": [],
"source": [
"def standard_units(x):\n",
" return (x - np.mean(x))/np.std(x)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# Rows of Tables\n",
"Now that we have a qualitative understanding of nearest neighbor classification, it's time to implement our classifier.\n",
"\n",
"Until this chapter, we have worked mostly with single columns of tables. But now we have to see whether one *individual* is \"close\" to another. Data for individuals are contained in *rows* of tables.\n",
"\n",
"So let's start by taking a closer look at rows."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here is the original table `ckd` containing data on patients who were tested for chronic kidney disease."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
Age
\n",
"
Blood Pressure
\n",
"
Specific Gravity
\n",
"
Albumin
\n",
"
Sugar
\n",
"
Red Blood Cells
\n",
"
Pus Cell
\n",
"
Pus Cell clumps
\n",
"
Bacteria
\n",
"
Glucose
\n",
"
...
\n",
"
Packed Cell Volume
\n",
"
White Blood Cell Count
\n",
"
Red Blood Cell Count
\n",
"
Hypertension
\n",
"
Diabetes Mellitus
\n",
"
Coronary Artery Disease
\n",
"
Appetite
\n",
"
Pedal Edema
\n",
"
Anemia
\n",
"
Class
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
48
\n",
"
70
\n",
"
1.005
\n",
"
4
\n",
"
0
\n",
"
normal
\n",
"
abnormal
\n",
"
present
\n",
"
notpresent
\n",
"
117
\n",
"
...
\n",
"
32
\n",
"
6700
\n",
"
3.9
\n",
"
yes
\n",
"
no
\n",
"
no
\n",
"
poor
\n",
"
yes
\n",
"
yes
\n",
"
1
\n",
"
\n",
"
\n",
"
1
\n",
"
53
\n",
"
90
\n",
"
1.020
\n",
"
2
\n",
"
0
\n",
"
abnormal
\n",
"
abnormal
\n",
"
present
\n",
"
notpresent
\n",
"
70
\n",
"
...
\n",
"
29
\n",
"
12100
\n",
"
3.7
\n",
"
yes
\n",
"
yes
\n",
"
no
\n",
"
poor
\n",
"
no
\n",
"
yes
\n",
"
1
\n",
"
\n",
"
\n",
"
2
\n",
"
63
\n",
"
70
\n",
"
1.010
\n",
"
3
\n",
"
0
\n",
"
abnormal
\n",
"
abnormal
\n",
"
present
\n",
"
notpresent
\n",
"
380
\n",
"
...
\n",
"
32
\n",
"
4500
\n",
"
3.8
\n",
"
yes
\n",
"
yes
\n",
"
no
\n",
"
poor
\n",
"
yes
\n",
"
no
\n",
"
1
\n",
"
\n",
"
\n",
"
3
\n",
"
68
\n",
"
80
\n",
"
1.010
\n",
"
3
\n",
"
2
\n",
"
normal
\n",
"
abnormal
\n",
"
present
\n",
"
present
\n",
"
157
\n",
"
...
\n",
"
16
\n",
"
11000
\n",
"
2.6
\n",
"
yes
\n",
"
yes
\n",
"
yes
\n",
"
poor
\n",
"
yes
\n",
"
no
\n",
"
1
\n",
"
\n",
"
\n",
"
4
\n",
"
61
\n",
"
80
\n",
"
1.015
\n",
"
2
\n",
"
0
\n",
"
abnormal
\n",
"
abnormal
\n",
"
notpresent
\n",
"
notpresent
\n",
"
173
\n",
"
...
\n",
"
24
\n",
"
9200
\n",
"
3.2
\n",
"
yes
\n",
"
yes
\n",
"
yes
\n",
"
poor
\n",
"
yes
\n",
"
yes
\n",
"
1
\n",
"
\n",
" \n",
"
\n",
"
5 rows × 25 columns
\n",
"
"
],
"text/plain": [
" Age Blood Pressure Specific Gravity Albumin Sugar Red Blood Cells \\\n",
"0 48 70 1.005 4 0 normal \n",
"1 53 90 1.020 2 0 abnormal \n",
"2 63 70 1.010 3 0 abnormal \n",
"3 68 80 1.010 3 2 normal \n",
"4 61 80 1.015 2 0 abnormal \n",
"\n",
" Pus Cell Pus Cell clumps Bacteria Glucose ... Packed Cell Volume \\\n",
"0 abnormal present notpresent 117 ... 32 \n",
"1 abnormal present notpresent 70 ... 29 \n",
"2 abnormal present notpresent 380 ... 32 \n",
"3 abnormal present present 157 ... 16 \n",
"4 abnormal notpresent notpresent 173 ... 24 \n",
"\n",
" White Blood Cell Count Red Blood Cell Count Hypertension \\\n",
"0 6700 3.9 yes \n",
"1 12100 3.7 yes \n",
"2 4500 3.8 yes \n",
"3 11000 2.6 yes \n",
"4 9200 3.2 yes \n",
"\n",
" Diabetes Mellitus Coronary Artery Disease Appetite Pedal Edema Anemia \\\n",
"0 no no poor yes yes \n",
"1 yes no poor no yes \n",
"2 yes no poor yes no \n",
"3 yes yes poor yes no \n",
"4 yes yes poor yes yes \n",
"\n",
" Class \n",
"0 1 \n",
"1 1 \n",
"2 1 \n",
"3 1 \n",
"4 1 \n",
"\n",
"[5 rows x 25 columns]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ckd = pd.read_csv(path_data + 'ckd.csv').rename(columns={'Blood Glucose Random': 'Glucose'})\n",
"ckd.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The data corresponding to the first patient is in row 0 of the table, consistent with Python's indexing system. The Table method `row` accesses the row by taking the index of the row as its argument:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Age 48\n",
"Blood Pressure 70\n",
"Specific Gravity 1.005\n",
"Albumin 4\n",
"Sugar 0\n",
"Red Blood Cells normal\n",
"Pus Cell abnormal\n",
"Pus Cell clumps present\n",
"Bacteria notpresent\n",
"Glucose 117\n",
"Blood Urea 56\n",
"Serum Creatinine 3.8\n",
"Sodium 111\n",
"Potassium 2.5\n",
"Hemoglobin 11.2\n",
"Packed Cell Volume 32\n",
"White Blood Cell Count 6700\n",
"Red Blood Cell Count 3.9\n",
"Hypertension yes\n",
"Diabetes Mellitus no\n",
"Coronary Artery Disease no\n",
"Appetite poor\n",
"Pedal Edema yes\n",
"Anemia yes\n",
"Class 1\n",
"Name: 0, dtype: object"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ckd.iloc[0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Or, as an array"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([48, 70, 1.005, 4, 0, 'normal', 'abnormal', 'present', 'notpresent',\n",
" 117, 56, 3.8, 111, 2.5, 11.2, 32, 6700, 3.9, 'yes', 'no', 'no',\n",
" 'poor', 'yes', 'yes', 1], dtype=object)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.array(ckd.loc[0])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Rows have their very own data type: they are *row objects*. Notice how when using the pandas `df.loc[row]` the display shows not only the values in the row but also the labels of the corresponding columns.\n",
"\n",
"Rows are in general **not arrays**, as their elements can be of different types. For example, some of the elements of the row above are strings (like `'abnormal'`) and some are numerical.\n",
"\n",
"However, rows share some characteristics with arrays. You can use `.iloc[row:column]` to access a particular element of a row. For example, to access the Albumin level of Patient 0, we can look at the labels in the printout of the row above to find that it's the column 'Albumin' is column index 3 i.e. `.iloc[row_number,3:`."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"4"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ckd.iloc[0,3]"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"numpy.int64"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(ckd['Class'].iloc[0])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Converting Rows to Arrays (When Possible)\n",
"Rows whose elements are all numerical (or all strings) can be converted to arrays. Converting a row to an array gives us access to arithmetic operations and other nice NumPy functions, so it is often useful.\n",
"\n",
"Recall that in the previous section we tried to classify the patients as 'CKD' or 'not CKD', based on two attributes `Hemoglobin` and `Glucose`, both measured in standard units. "
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
Hemoglobin
\n",
"
Glucose
\n",
"
Class
\n",
"
Color
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
-0.865744
\n",
"
-0.221549
\n",
"
1
\n",
"
darkblue
\n",
"
\n",
"
\n",
"
1
\n",
"
-1.457446
\n",
"
-0.947597
\n",
"
1
\n",
"
darkblue
\n",
"
\n",
"
\n",
"
2
\n",
"
-1.004968
\n",
"
3.841231
\n",
"
1
\n",
"
darkblue
\n",
"
\n",
"
\n",
"
3
\n",
"
-2.814879
\n",
"
0.396364
\n",
"
1
\n",
"
darkblue
\n",
"
\n",
"
\n",
"
4
\n",
"
-2.083954
\n",
"
0.643529
\n",
"
1
\n",
"
darkblue
\n",
"
\n",
"
\n",
"
...
\n",
"
...
\n",
"
...
\n",
"
...
\n",
"
...
\n",
"
\n",
"
\n",
"
153
\n",
"
0.700526
\n",
"
0.133751
\n",
"
0
\n",
"
gold
\n",
"
\n",
"
\n",
"
154
\n",
"
0.978974
\n",
"
-0.870358
\n",
"
0
\n",
"
gold
\n",
"
\n",
"
\n",
"
155
\n",
"
0.735332
\n",
"
-0.484162
\n",
"
0
\n",
"
gold
\n",
"
\n",
"
\n",
"
156
\n",
"
0.178436
\n",
"
-0.267893
\n",
"
0
\n",
"
gold
\n",
"
\n",
"
\n",
"
157
\n",
"
0.735332
\n",
"
-0.005280
\n",
"
0
\n",
"
gold
\n",
"
\n",
" \n",
"
\n",
"
158 rows × 4 columns
\n",
"
"
],
"text/plain": [
" Hemoglobin Glucose Class Color\n",
"0 -0.865744 -0.221549 1 darkblue\n",
"1 -1.457446 -0.947597 1 darkblue\n",
"2 -1.004968 3.841231 1 darkblue\n",
"3 -2.814879 0.396364 1 darkblue\n",
"4 -2.083954 0.643529 1 darkblue\n",
".. ... ... ... ...\n",
"153 0.700526 0.133751 0 gold\n",
"154 0.978974 -0.870358 0 gold\n",
"155 0.735332 -0.484162 0 gold\n",
"156 0.178436 -0.267893 0 gold\n",
"157 0.735332 -0.005280 0 gold\n",
"\n",
"[158 rows x 4 columns]"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ckd = pd.DataFrame(\n",
" {'Hemoglobin':standard_units(ckd['Hemoglobin']),\n",
" 'Glucose':standard_units(ckd['Glucose']), \n",
" 'Class':ckd['Class']}\n",
")\n",
"\n",
"color_table = pd.DataFrame(\n",
" {'Class':np.array([1, 0]),\n",
" 'Color':np.array(['darkblue', 'gold'])}\n",
")\n",
"\n",
"ckd = pd.merge(ckd, color_table, on='Class')\n",
"\n",
"ckd"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here is a scatter plot of the two attributes, along with a red point corresponding to Alice, a new patient. Her value of hemoglobin is 0 (that is, at the average) and glucose 1.1 (that is, 1.1 SDs above average)."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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dnY2YmJipC5DEq767q763gDf3VXf3R5u3bqFFi1aSF0GuTBTvwfZokZEREQkUwxqRERERDLFoEZEREQkUwxqRERERDLFoEZEREQkUwxqRERERDLFoEZEREQkUwxqRERERDLFoEZEREQkUwxqRERELi41NdXktknOQKVSYffu3SbPFxYWQqVS1bthvBwxqBERETm5a9euYdGiRejRowfuvvtudOrUCRMmTMCXX34pdWnUSO5SF0BEREQNp1ar8eCDD8LPzw/Lli1D165dodPpcPjwYcyfPx9nzpxxSB1VVVXw8PBwyLNcCVvUiIiInNiCBQsgCAIOHjyIcePGISYmBh06dMDMmTNx9OhRAMDly5cxZcoUhIaGIjQ0FFOnTsXVq1fN3vfvf/87evbsiYCAAPTs2RNbt241OK9SqbBx40ZMnToVwcHBWL58eYPqX7t2LWJiYhASEoJZs2Zh1apV6Natm/68TqfDmjVr0KVLF9x9993o378/9u3bZ/aep06dwuDBg9GmTRsMHDgQWVlZDapNDhjUiIiIbEShUcP7ZhJ8r8fD+2YSFBq1XZ938+ZNfPXVV0hKSoKfn5/ReZVKBUEQMGXKFBQUFCA9PR179uxBXl4epkyZAkEQRO+7Z88eLFy4EHPmzMHx48cxe/ZsPP/888jIyDC4bvXq1RgxYgSOHTuGGTNmAABCQkLM/jNhwgT95//1r39h9erVWLp0KQ4fPowOHTpg/fr1Bs9477338Le//Q0vv/wyjh07hlGjRuGxxx7DTz/9JFp7WVkZJk6ciMjISBw8eBAvv/wyli5datX3Kifs+iQiIrIBhUYN3xsJcNPmVB+4A7jdyUJZqzQI7hF2eebFixchCALat29v8ppDhw7hzJkzOH36NCIiquvYtGkTevbsicOHD2PIkCFGn1m3bh0mTZqEmTNnAgDatWuHH374AW+//TZGjhypv27cuHF4/PHHDT5b34B9Ly8v/b+///77ePTRR/X3mD9/Po4cOYLz588b1DJv3jwkJiYCAF588UUcO3YM69atwwcffGB0/x07dqCqqgrvvvsu/Pz80LlzZzz//POYNWuW2brkymla1FauXAmVSmXwj7nfmERERI7kVZLyR0j7nZs2B14lKXZ7pqkWsdrOnTuHoKAgfUgDgMjISAQFBeHs2bMmP9OnTx+DY/369TO6vmfPnkaf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QE5OMQDg++9vISurAGlpIxnWiIiaEKcOavv27ZO6BCJJpKRk6UNajZyc6ha2jRuHSVQVERHZmtOto0ZEQG5uuejxvDzx40RE5JwY1IicUFCQj+jxwEDx40RE5JwY1IicUHJyLKKiDMeiRUX5Izk5VqKKiIjIHhjUiJxQRIQ/0tJGIjExGr16qZCYGM2JBERETZBTTyYgcmUREf7YuHEYV6gnImrC2KJGREREJFMMakREREQyxaBGREREJFMMakREREQyxaBGREREJFMMakREREQyxaBGREREJFMMakREREQyxaBGREREJFMMakREREQyxaBGREREJFMMakREREQyxaBGREREJFMMakREREQyxaBGREREJFMMakREREQyxaBGREREJFMMakREREQyxaBGREREJFMMakREREQyxaBGREREJFMMakREREQyxaBGREREJFMMakREREQyxaBGREREJFMMakREREQyxaBGREREJFNOE9Q2btyI/v37IywsDGFhYfjTn/6EL774QuqyiIiIiOzGaYJacHAwXnnlFRw+fBgHDx7EoEGDMGXKFJw5c0bq0oiIiIjswl3qAiw1atQog5+XLl2KDz/8ECdPnkTXrl0lqoqIiIjIfpwmqNWm1WqRlpaGsrIy3HfffVKXQ0RERGQXThXU/vOf/2DEiBGoqKiAr68vPv74Y3Tp0kXqsoiIiIjsQlFUVCRIXYSlqqqqcOXKFdy6dQvp6enYunUr9u7di86dO4ten52d7eAKiYjIXmJiYqQugcjhnCqo1TV27FiEhYVh3bp1UpciG9nZ2S77HzNXfXdXfW+A7+6q707kSpxm1qcYnU6HqqoqqcsgIiIisgunGaP28ssvY8SIEQgJCUFpaSl27tyJo0ePYvv27VKXRkRERGQXThPU8vPzMXPmTFy7dg3+/v7o0qULdu7cieHDh0tdGhEREZFdOE1Qe++996QugYiIiMihnHqMGhEREVFTxqBGREREJFMMakREREQyxaBGREREJFMMakREREQyxaBGREREJFMNXp5Dq9Xi1q1b8Pf3h7u706zyQeQU1OpipKRkITe3HEFBPkhOjkVEhL/UZRERkYNZ3aJ26tQpJCQkIDg4GDExMfj2228BAIWFhZg4cSIOHz5s8yKJXIlaXYyEhAzs2HEBR4/mYseOC0hIyIBaXSx1aURE5GBWBbXv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vx8isi1FUVGR+JQhB0hJScEbb7xh9po9e/Zg4MCB+p9Pnz6NoUOHWjSZIDs72+x5Ilu6evU23n8/BwUFVQgI8MDs2VEICfFu8HVkmofiKkI83kczRQHuCAG4WjUbVUKI1GXJRlP9fmJiYqQugcjhJA1qhYWFKCwsNHtNaGgofHz+6BayJqi5ouzsbJf9j5mrvrurvjfAd3fVdydyJZKOUWvdujVat24tZQlEREREsuU0kwny8/ORn5+P8+fPAwDOnTuHW7duISwsDC1btpS4OiIiIiLbc5rJBJs3b8agQYOQlJQEAJg4cSIGDRqE/fv3S1wZERERkX04TYvakiVLsGTJEqnLICIiInIYp2lRIyIiInI1DGpEREREMsWgRkRERCRTDGpEREREMsWgRkRERCRTDGpEREREMsWgRkRERCRTDGpEREREMsWgRkRERCRTDGpEREREMsWgRkRERCRTDGpEREREMsWgRkRERCRTDGpEREREMsWgRkRERCRTDGpEREREMsWgRkRERCRTDGpEREREMsWgRkRERCRTDGpEREREMsWgRkRERCRTDGpEREREMsWgRkRERCRTDGpEREREMsWgRkRERCRTDGpEREREMsWgRkRERCRTDGpEREREMsWgRkRERCRTDGpEREREMsWgRkRERCRTDGpEREREMsWgRkRERCRTThHUbt68iYULF6J3794IDAxEly5dMH/+fNy4cUPq0oiIiIjsximCWm5uLnJzc/HKK6/g2LFj2LBhA44dO4annnpK6tKIiIiI7MZd6gIs0blzZ3z88cf6n9u2bYvly5dj0qRJKC4uhr+/v4TVEREREdmHU7SoiSkpKYGnpyd8fHykLkVWYmJipC5BMq767q763gDfnYiaPqcMakVFRXj11Vfx+OOPw93dKRoFiYiIiKwmaVBLSUmBSqUy+8+RI0cMPlNWVobJkycjKCgIy5cvl6hyIiIiIvtTFBUVCVI9vLCwEIWFhWavCQ0N1XdvlpaWIjExEQCwY8cO+Pn52b1GIiIiIqlIGtSsUVJSgsTERAiCgJ07d6J58+ZSl0RERERkV04xwKukpAQPP/wwSkpKkJqaivLycpSXlwMAWrZsCQ8PD4krJCIiIrI9p5hM8MMPP+DkyZM4e/YsevXqhQ4dOuj/OXHiRL2fFwQB48ePh0qlwu7dux1QsfT+/Oc/o0ePHggMDER0dDQmT56Mc+fOSV2W3bny4shbtmxBfHw8wsPDoVKpoFarpS7JbjZt2oR77rkHbdq0weDBg3Hs2DGpS3KIb7/9Fo888gg6deoElUqF1NRUqUtyiLVr12Lo0KEICwtDdHQ0Jk2ahP/+979Sl0XkEE4R1AYOHIiioiLRfwYOHFjv59etWwc3NzcHVCofPXv2xPr163HixAn861//giAISEhIwJ07d6Quza5ceXHk8vJyDBs2DIsXL5a6FLvatWsXFi9ejOeffx7ffPMN7rvvPiQmJuLy5ctSl2Z3ZWVl6Ny5M1atWgVvb2+py3GYo0eP4qmnnsIXX3yB9PR0uLu7IyEhATdv3pS6NCK7c5oxag11+vRpTJ06FYcOHUJMTAy2bt2KsWPHSl2Ww505cwYDBgzAyZMnXW79pS+//BKTJk2CWq12icWRT58+jaFDh+LHH39ERESE1OXY3PDhw9GlSxe88847+mP33nsvxo4di2XLlklYmWOFhIRgzZo1mDJlitSlOFxpaSnCw8ORmpqKkSNHSl0OkV05RYtaQ5WUlOCpp57CW2+9hYCAAKnLkUxZWRlSU1MRGhqK8PBwqctxOC6O3HRUVVXhhx9+wLBhwwyODxs2zKJhENQ0lJaWQqfTQaVSSV0Kkd016aA2f/58DB8+HCNGjJC6FEls2rQJISEhCAkJwVdffYX09HR4enpKXZZDcXHkpqWwsBBardboL14BAQG4du2aRFWRoy1evBjdunXDfffdJ3UpRHbndEHN0kVy//nPf+LMmTNYsWKF1CXbjLULBCcmJuKbb77Bvn37EB0djSeeeEI/W9bZuOriyA15b1egUCgMfhYEwegYNU0vvPACMjMz8Y9//MPlxh6Ta3K6JoY5c+Zg4sSJZq8JDQ3FJ598grNnzyIkJMTg3LRp03Dffffh888/t2eZdmHpu9do0aIFWrRogejoaPTu3RuRkZFIT0/HI488Yu9Sbc7ad6+9OPK2bdvg5eVl1/rsxdr3bupat24NNzc3o9az69evu/TwBlexZMkS7Nq1C3v27EFkZKTU5RA5hNMFtdatW6N169b1Xrd06VI888wzBsf69++PFStWYNSoUfYqz64sfXcxgiBAEARUVVXZuCrHsObd6y6O7Mw7WDTm17wp8vDwQI8ePXDw4EEkJCTojx88eBBjxoyRrjCyu0WLFmHXrl3Yu3cv2rdvL3U5RA7jdEHNUsHBwQgODjY6Hhoa2uT/Jnbx4kWkp6djyJAhaN26NX777Te89dZb8PDwQFxcnNTl2ZUrL46cn5+P/Px8nD9/HgBw7tw53Lp1C2FhYWjZsqXE1dnO3LlzMWvWLPTq1Qt9+vTB5s2bkZeXh2nTpkldmt2Vlpbi4sWLAACdTocrV67gp59+QsuWLREWFiZxdfazYMECbNu2DR9//DFUKhXy8/MBAL6+vk79FzEiSzT55TlqU6lULrE8x5UrV/Dss8/ihx9+wK1bt3D33Xejf//+WLhwYZP/m+iRI0cwevRo0XN79uyxaN09Z7Vy5UqsXr3a6Pi7777b5JZw2LRpE95++23k5+ejU6dOeO2113D//fdLXZbdmfr9PXnyZLz33nsSVOQYpmZ3Llq0CEuWLHFsMUQO5lJBjYiIiMiZON2sTyIiIiJXwaBGREREJFMMakREREQyxaBGREREJFMMakREREQyxaBGREREJFMMaiQL3bp1w/jx4+u9Tq1WQ6VSITU11QFVWWflypUm13uytyNHjki676fYr0tqaipUKhXUarUkNRERNQUMatQo6enpUKlU2Llzp9G50aNHmz0XEREBQWj8Mn7Hjx/HypUrUVRU1Oh71VV3A/SgoCDExsYiJSUFxcXFNn+eo5SUlOCNN97A4MGDERYWhjZt2iA2NhYLFy7EhQsXWJMJpaWlWLlypWSBmIhcT5PdQooco1+/fgCqw9KECRP0xzUaDb7//nu4u7ubPDdw4EAoFAqrnhceHo68vDw0a9ZMfywzMxOrV6/Go48+apcWrUGDBulX9i8vL8eJEyfwxhtv4NSpU9i1a5fNn2dvly5dwrhx46BWqzFmzBg8+uij8Pb2xrlz57Br1y5s2bIFBQUFLl+TmLKyMv3uD015lwsikg8GNWqUgIAAREdH4/jx4wbHf/zxR5SXl2PixIkmz/Xt29fq5ykUCnh5eTWqZmtFR0dj0qRJ+p+nTZsGnU6H7du34+bNm061j6ZGo8HUqVORl5eH9PR0DBgwwOD80qVLsXz5cpeviYhILtj1SY3Wr18/nD171qDrMTMzE0FBQZg0aZLouZrP1fX999/jwQcfRGBgILp06YL169cbnK87FmrlypV45ZVXAADdu3fXd1HW7po6ePAg4uPjERoaiuDgYMTHx+PEiRONeuc2bdoAANzd6/+7zkcffYT+/fujTZs2aNeuHWbNmoXc3Fyj644fP47Ro0cjJCQEoaGhSEhIQFZWltF12dnZePjhhxEUFIT27dtj6dKluHPnjkV1p6en48yZM5g/f75RIAIALy8vvPbaawbHLly4gOnTpyM6Olq/b+zHH39s0fPsVZMl35WpMYNi4/lGjRqF3r1748KFCxg/fjyCg4MRExODV155BTqdDkD1770OHToAAFavXq3/vTZnzpzGfgVERCYxqFGj9e3bFzqdDt99953+WGZmJvr06YPevXsDgNE5Ly8v9OzZ0+A+arUajzzyCPr06YNXX30VkZGReOGFF3DgwAGTzx49ejQefvhhAMBrr72GDRs2YMOGDfr/oe7cuRPjx4+Hm5sbXnzxRbz44ou4ceMGxowZIxqCxFRUVKCwsBCFhYW4fPkyPvvsM3z00UeIj49H8+bNzX72rbfewp///GeoVCosX74ckydPxu7duxEXF2cQXr/99luMHTsWly9fxoIFC/Dcc8/h/PnzGDVqlEGdBQUFGDVqFE6ePIm5c+fi2WefxeHDh/HSSy9Z9C4ZGRkAgEceecSi68+dO4fhw4fjxx9/xNy5c7Fy5UqEhYVh3rx5RiG6oaytydLvylrFxcUYO3YsoqKikJKSgt69e+Ott97CRx99BAC466678PrrrwMA4uPj9b/Xpk2b1uBnEhHVh12f1Gg1LWOZmZkYMWIEAODEiRN47rnn4O/vj44dOxqd69mzJzw9PQ3uc/78eaSlpWHIkCEAgKlTp6Jr167YunUrhg0bJvrsrl27olu3bti1axdGjRqFiIgI/bmysjIsWLAAkyZNwnvvvac/Pm3aNPTt2xfLly9Henp6ve/36aef4tNPPzU4NmLECHzwwQdmP1dYWIhVq1ZhwIABSEtL07e+9e3bF1OmTMG6deuQnJwMAHjxxRfh6+uLr776CnfddRcAYPLkybjvvvuQnJyMzz//HADwf//3f7h27Rr279+P/v37AwCefPJJ0ZYoMefOnYO/vz/CwsIsun7x4sVo06YNDh48CB8fHwDAU089hWnTpmHlypV44okn4Ovra9G9bFWTpd+VtfLz8/HOO+/g8ccfBwBMnz4dAwYMwNatW/Hkk0/C19cXY8aMwcKFC9GlSxeD7nAiInthixo1WnR0NNq0aaMfi3bhwgVcu3ZNPwatb9++RudqQkbd+9SENADw9PREbGwsLl261KC6Dh48iKKiIkycOFHfIlZYWIjbt29jyJAhOH78uEVdhnFxcUhLS0NaWho++eQTPP/88zhy5AhmzpxpdtbqoUOHUFlZiaefftqgi3TUqFGIiYnBF198AaA6IPzwww+YPHmyPngAQHBwMCZMmIATJ07oW9+++OILdO/e3eD78/HxwZNPPmnRd1JSUlJvK2CNoqIiHDp0CAkJCbh9+7bBd/jAAw+gpKQEp0+ftuhetqrJmu/KWl5eXvpJIzXuv//+Bv/+IyKyBbaokU306dMHX375JaqqqpCZmQkfHx9069ZNf+6TTz7RnwMgOpFArEVFpVLhP//5T4NqqlnSYdy4cSavuXXrlsH/8MUEBwcbBMiHHnoIrVq1wosvvojPP/8cI0eOFP3cr7/+CgBo37690bn27dvj6NGj9V7XoUMHCIKAy5cvQ6VS4fLlyxg9erTRde3atTP7DjWaN29ucfC4cOECBEHA6tWr9TMd67p+/bpF97JVTdZ8V9YKDg6Gm5ubwTGVSoWbN29afS8iIlthUCOb6Nu3L9LT03H69GlkZmaiV69e+lakPn36oKKiQn9OqVTivvvuM7pH3f9J1mjoWms1g8DXr1+P4OBg0Wv8/f0bdO9BgwYBAI4dO2YyqJlj6TuJXSe2pIml9+vQoQN++uknXLlyBaGhoWavrfn+nn76aX23dV2dO3e26Lm2qsmcut+BqaVfat6rLlO//4iIpMSgRjZR0xWXmZmJzMxMJCQk6M9FRkYiMDBQf65Lly5o0aKFzZ5t6n/IUVFRAKoHgdduEbMFjUYDoHocnCnh4eEAgF9++cWoxSs7O1t/vvZ1dWVnZ0OhUOhbG8PCwkSvs3RB2JEjR2LHjh345z//iQULFpi9NjIyEkD1zFZbf38Nrcma76qmVa2oqMigha0xOyVYu+4fEVFjcYwa2US3bt3g5+eHffv2ITs726hrs0+fPibPNVbNIPe6Y5OGDx+OFi1a4I033kBlZaXR5xrTbffll18CqJ7MYMqQIUPg6emJ999/H1qtVn88IyMD2dnZiIuLA1C91EePHj3wz3/+E4WFhfrrcnNzsWPHDvTp00cfNEaMGIEff/wRx44d019XXl6OLVu2WFT3mDFj0LVrV6xdu9ZofTsAqKysxIsvvgigeo28QYMGYcuWLbhy5YrRtbbo9rS2Jmu+q7Zt2wKAwTIcGo0Gf//73xtcq6nfa0RE9sIWNbIJNzc3xMbG4tChQ1AqlYiNjTU436dPH7zwwgsAIDqRoDFqlvlYsWIFxo8fDw8PDwwaNAgBAQF4++238dRTT2HAgAFITExEmzZtcPXqVRw5cgS+vr6i21vVdeHCBWzbtg1A9VIdWVlZ+OSTT9C2bVuzM/9at26NxYsX45VXXsHYsWMxevRoXL16FR988AHCw8Mxb948/bWvvvoqEhIS8MADD+CJJ56AIAj48MMPcefOHaxYsUJ/3bPPPovt27fjkUcewcyZM9GqVSv885//1AeI+ri7u+Pjjz9GQkIC4uPjMWbMGPTv3x+enp44f/48du3ahWvXruHVV18FAKxduxZxcXG4//778cQTTyA6OhqFhYX48ccfceDAAVy+fNmi59qyJku/q2HDhiE8PBx//vOfkZ2dDS8vL+zcubNR25b5+fkhJiYGu3btQrt27dCqVStEREQY/X4nIrIVBjWymX79+uHQoUPo1KmTUddm7VY0W7eo9e7dG8nJydiyZQvmzp0LnU6HPXv2ICAgAAkJCQgKCsLatWuxfv163L59W7+HZM0yDPX55ptv8M033wCoDqSBgYF47LHH9MtEmPPcc8+hdevWeP/997F06VL4+flh7NixWLZsmUF33P3334/du3fjtddew5o1a6BQKBAbG4u///3v+rXoAODuu+/Gvn37sGjRIqxbtw7NmzfHpEmTMGzYMP16cvWJjIzEN998gw0bNiA9PR1ffvklNBoNwsLCMGrUKIMFXNu1a4dDhw5hzZo12LFjB65fv47WrVujQ4cOBqGosaypydLvyt3dHampqVi4cCFWrVqFVq1aYerUqRgwYIBB17y13n33XSxZsgTJycmorKzE5MmTGdSIyG4URUVFjd8Vm4iIiIhsjmPUiIiIiGSKQY2IiIhIphjUiIiIiGSKQY2IiIhIphjUiIiIiGSKQY2IiIhIphjUiIiIiGSKQY2IiIhIphjUiIiIiGSKQY2IiIhIpv4fHw40NqbCOsUAAAAASUVORK5CYII=\n",
"text/plain": [
"
"
],
"text/plain": [
" Hemoglobin Glucose\n",
"0 -0.865744 -0.221549\n",
"1 -1.457446 -0.947597\n",
"2 -1.004968 3.841231\n",
"3 -2.814879 0.396364\n",
"4 -2.083954 0.643529\n",
".. ... ...\n",
"153 0.700526 0.133751\n",
"154 0.978974 -0.870358\n",
"155 0.735332 -0.484162\n",
"156 0.178436 -0.267893\n",
"157 0.735332 -0.005280\n",
"\n",
"[158 rows x 2 columns]"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ckd_attributes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Each row consists of the coordinates of one point in our training sample. **Because the rows now consist only of numerical values**, it is possible to convert them to arrays. For this, we use the function `np.array`, which converts any kind of sequential object, like a row, to an array. (Our old friend `make_array` is for *creating* arrays, not for *converting* other kinds of sequences to arrays.)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Hemoglobin -2.814879\n",
"Glucose 0.396364\n",
"Name: 3, dtype: float64"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ckd_attributes.iloc[3]"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([-2.81487943, 0.39636401])"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.array(ckd_attributes.iloc[3])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This is very handy because we can now use array operations on the data in each row."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Distance Between Points When There are Two Attributes\n",
"The main calculation we need to do is to find the distance between Alice's point and any other point. For this, the first thing we need is a way to compute the distance between any pair of points. \n",
"\n",
"How do we do this? In 2-dimensional space, it's pretty easy. If we have a point at coordinates $(x_0,y_0)$ and another at $(x_1,y_1)$, the distance between them is\n",
"\n",
"$$\n",
"D = \\sqrt{(x_0-x_1)^2 + (y_0-y_1)^2}\n",
"$$\n",
"\n",
"(Where did this come from? It comes from the Pythogorean theorem: we have a right triangle with side lengths $x_0-x_1$ and $y_0-y_1$, and we want to find the length of the hypotenuse.)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the next section we'll see that this formula has a straightforward extension when there are more than two attributes. For now, let's use the formula and array operations to find the distance between Alice and the patient in Row 3."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(array([0. , 1.1]), array([-2.81487943, 0.39636401]))"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"patient3 = np.array(ckd_attributes.iloc[3])\n",
"alice, patient3"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2.9014909651272616"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"distance = np.sqrt(np.sum((alice - patient3)**2))\n",
"distance"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We're going to need the distance between Alice and a bunch of points, so let's write a function called `distance` that computes the distance between any pair of points. The function will take two arrays, each containing the $(x, y)$ coordinates of a point. (Remember, those are really the Hemoglobin and Glucose levels of a patient.)"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"def distance(point1, point2):\n",
" \"\"\"Returns the Euclidean distance between point1 and point2.\n",
" \n",
" Each argument is an array containing the coordinates of a point.\"\"\"\n",
" return np.sqrt(np.sum((point1 - point2)**2))"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2.9014909651272616"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"distance(alice, patient3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We have begun to build our classifier: the `distance` function is the first building block. Now let's work on the next piece."
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"### Using `apply` on an Entire Row\n",
"Recall that if you want to apply a function to each element of a column of a table, one way to do that is by the call `table_name.apply(function_name, column_label)`. This evaluates to an array consisting of the values of the function when we call it on each element of the column. So each entry of the array is based on the corresponding row of the table.\n",
"\n",
"If you use `apply` without specifying a column label, then the entire row is passed to the function. Let's see how this works on a very small table `t` containing the information about the first five patients in the training sample."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
Hemoglobin
\n",
"
Glucose
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
-0.865744
\n",
"
-0.221549
\n",
"
\n",
"
\n",
"
1
\n",
"
-1.457446
\n",
"
-0.947597
\n",
"
\n",
"
\n",
"
2
\n",
"
-1.004968
\n",
"
3.841231
\n",
"
\n",
"
\n",
"
3
\n",
"
-2.814879
\n",
"
0.396364
\n",
"
\n",
"
\n",
"
4
\n",
"
-2.083954
\n",
"
0.643529
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Hemoglobin Glucose\n",
"0 -0.865744 -0.221549\n",
"1 -1.457446 -0.947597\n",
"2 -1.004968 3.841231\n",
"3 -2.814879 0.396364\n",
"4 -2.083954 0.643529"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"t = ckd_attributes.take(np.arange(5))\n",
"t"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Just as an example, suppose that for each patient we want to know how unusual their most unusual attribute is. Concretely, if a patient's hemoglobin level is further from the average than her glucose level, we want to know how far it is from the average. If her glucose level is further from the average than her hemoglobin level, we want to know how far that is from the average instead.\n",
"\n",
"That's the same as taking the maximum of the absolute values of the two quantities. To do this for a particular row, we can convert the row to an array and use array operations."
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"def max_abs(row):\n",
" return np.max(np.abs(np.array(row)))"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.8657439478031919"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"max_abs(t.iloc[0])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And now we can apply `max_abs` to each row of the table `t`:"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0 0.865744\n",
"1 1.457446\n",
"2 3.841231\n",
"3 2.814879\n",
"4 2.083954\n",
"dtype: float64"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"t.apply(max_abs, axis=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This way of using `apply` will help us create the next building block of our classifier."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Alice's $k$ Nearest Neighbors\n",
"If we want to classify Alice using a k-nearest neighbor classifier, we have to identify her $k$ nearest neighbors. What are the steps in this process? Suppose $k = 5$. Then the steps are:\n",
"- **Step 1.** Find the distance between Alice and each point in the training sample.\n",
"- **Step 2.** Sort the data table in increasing order of the distances.\n",
"- **Step 3.** Take the top 5 rows of the sorted table.\n",
"\n",
"Steps 2 and 3 seem straightforward, provided we have the distances. So let's focus on Step 1.\n",
"\n",
"Here's Alice:"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([0. , 1.1])"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"alice"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"What we need is a function that finds the distance between Alice and another point whose coordinates are contained in a row. The function `distance` returns the distance between any two points whose coordinates are in arrays. We can use that to define `distance_from_alice`, which takes a row as its argument and returns the distance between that row and Alice."
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [],
"source": [
"def distance_from_alice(row):\n",
" \"\"\"Returns distance between Alice and a row of the attributes table\"\"\"\n",
" return distance(alice, np.array([row]))"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2.9014909651272616"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"distance_from_alice(ckd_attributes.iloc[3])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we can `apply` the function `distance_from_alice` to each row of `ckd_attributes`, and augment the table `ckd` with the distances. Step 1 is complete!"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [],
"source": [
"distances = ckd_attributes.apply(distance_from_alice, axis=1)\n",
"ckd_with_distances = ckd.copy()\n",
"ckd_with_distances['Distance from Alice'] = distances"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"