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");
No description has been provided for this image
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>
No description has been provided for this image
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>
No description has been provided for this image
In [ ]: