In [1]:
import pandas as pd
import numpy as np
from matplotlib.pyplot import subplots
# We build the nearest neighbor classifier, which is the simplest classification alorithm
# We will work with the data on Chronic Kidney Disease (CKD)
ckd = pd.read_csv('ckd.csv')
ckd.head(3)
Out[1]:
| Age | Blood Pressure | Specific Gravity | Albumin | Sugar | Red Blood Cells | Pus Cell | Pus Cell clumps | Bacteria | Blood Glucose Random | ... | Packed Cell Volume | White Blood Cell Count | Red Blood Cell Count | Hypertension | Diabetes Mellitus | Coronary Artery Disease | Appetite | Pedal Edema | Anemia | Class | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 48 | 70 | 1.005 | 4 | 0 | normal | abnormal | present | notpresent | 117 | ... | 32 | 6700 | 3.9 | yes | no | no | poor | yes | yes | 1 |
| 1 | 53 | 90 | 1.020 | 2 | 0 | abnormal | abnormal | present | notpresent | 70 | ... | 29 | 12100 | 3.7 | yes | yes | no | poor | no | yes | 1 |
| 2 | 63 | 70 | 1.010 | 3 | 0 | abnormal | abnormal | present | notpresent | 380 | ... | 32 | 4500 | 3.8 | yes | yes | no | poor | yes | no | 1 |
3 rows × 25 columns
In [2]:
# Rename the columns so that it becomes easier to handle later
ckd = ckd.rename(columns = {'Blood Glucose Random': 'Glucose'})
ckd = ckd.rename(columns = {'White Blood Cell Count': 'WBCC'})
data = ckd[['Glucose', 'Hemoglobin', 'WBCC', 'Class']]
# Add columns for readings in standard units
data = data.assign(Glucose_in_std = (data['Glucose'] - data['Glucose'].mean())/data['Glucose'].std())
data = data.assign(WBCC_in_std = (data['WBCC'] - data['WBCC'].mean())/data['WBCC'].std())
data = data.assign(Hemoglobin_in_std = (data['Hemoglobin'] - data['Hemoglobin'].mean())/data['Hemoglobin'].std())
data.head(3)
Out[2]:
| Glucose | Hemoglobin | WBCC | Class | Glucose_in_std | WBCC_in_std | Hemoglobin_in_std | |
|---|---|---|---|---|---|---|---|
| 0 | 117 | 11.2 | 6700 | 1 | -0.220847 | -0.567962 | -0.863000 |
| 1 | 70 | 9.5 | 12100 | 1 | -0.944594 | 1.158999 | -1.452826 |
| 2 | 380 | 10.8 | 4500 | 1 | 3.829056 | -1.271539 | -1.001783 |
In [3]:
# Let us see if we can make a prediction using the Glucose and Hemoglobin readings only
# First, we do a scatter plot where we color every data point with the class (ckd/no ckd)
# to see if there is a pattern that points to the success of the nearest neighbor classifier
fig, ax = subplots(figsize = (5, 5))
scatter = ax.scatter(data['Hemoglobin_in_std'], data['Glucose_in_std'], c = data['Class'], alpha = 0.5)
ax.set_xlabel('Hemoglobin')
ax.set_ylabel('Glucose')
handles, labels = scatter.legend_elements()
# You can define custom labels here if "1", "2", etc. are not descriptive enough
custom_labels = ["No CKD", "CKD"]
# Add the legend using the generated handles and custom labels
ax.legend(handles = handles, labels=custom_labels, loc="upper right", title="Classes");
In [4]:
rng = np.random.default_rng()
d = 10000 #dimension
x = np.array(rng.normal(0, 1, d))
y = np.array(rng.normal(0, 1, d))
Out[4]:
array([ 0.69932398, -1.07588608, 0.38289209, ..., -0.13803762,
-1.39723815, -0.01613155])
The Euclidean distance between $(x_1, x_2, ..., x_d)$ and $(y_1, y_2, ..., y_d)$ is given by $\sqrt{\sum_i (x_i - y_i)^2}$
In [8]:
%%timeit -n 10 -r 1
s = 0
for i in range(d):
s = s + (x[i] - y[i])**2
dist1 = np.sqrt(s)
3.62 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 10 loops each)
In [10]:
%%timeit -n 10 -r 1
dist2 = np.linalg.norm(x - y)
146 µs ± 0 ns per loop (mean ± std. dev. of 1 run, 10 loops each)
In [30]:
# Create a meshgrid
x = np.linspace(-4, 3, 100)
y = np.linspace(-1, 6, 100)
X, Y = np.meshgrid(x, y)
stacked = np.stack((X.ravel(), Y.ravel()), axis = 1)
df = pd.DataFrame(stacked)
df.columns = ('x', 'y')
df
Out[30]:
| x | y | |
|---|---|---|
| 0 | -4.000000 | -1.0 |
| 1 | -3.929293 | -1.0 |
| 2 | -3.858586 | -1.0 |
| 3 | -3.787879 | -1.0 |
| 4 | -3.717172 | -1.0 |
| ... | ... | ... |
| 9995 | 2.717172 | 6.0 |
| 9996 | 2.787879 | 6.0 |
| 9997 | 2.858586 | 6.0 |
| 9998 | 2.929293 | 6.0 |
| 9999 | 3.000000 | 6.0 |
10000 rows × 2 columns
In [34]:
#Plot the meshgrid
fig, ax = subplots(figsize = (6, 6))
ax.scatter(df['x'], df['y'], s=2)
Out[34]:
<matplotlib.collections.PathCollection at 0x10b89ee20>
In [32]:
from scipy.spatial.distance import cdist
fig, ax = subplots(figsize = (10, 10))
scatter = ax.scatter(data['Hemoglobin_in_std'], data['Glucose_in_std'], c = data['Class'], alpha = 0.5)
ax.set_xlabel('Hemoglobin')
ax.set_ylabel('Glucose')
Y = cdist(df, data[['Hemoglobin_in_std', 'Glucose_in_std']])
X = np.argmin(Y, axis = 1)
df1 = df.assign(index=X)
df2 = df1.merge(data.reset_index(), left_on='index', right_on='index')
# Plotting the meshgrid points along with the class of their nearest neighbors in the training set will give
# an approximation of the decision boundary
ax.scatter(df2['x'], df2['y'], s=2, c=df2['Class'], alpha=0.5)
Out[32]:
<matplotlib.collections.PathCollection at 0x11e4bc7f0>
In [ ]: