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))

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 [5]:
%%timeit -n 10 -r 1
s = 0
for i in range(d):
    s = s + (x[i] - y[i])**2
dist1 = np.sqrt(s)
1.88 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 10 loops each)
In [6]:
%%timeit -n 10 -r 1
dist2 = np.linalg.norm(x - y)
70.2 µs ± 0 ns per loop (mean ± std. dev. of 1 run, 10 loops each)
In [7]:
# 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[7]:
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 [8]:
#Plot the meshgrid
fig, ax = subplots(figsize = (6, 6))
ax.scatter(df['x'], df['y'], s=2)
Out[8]:
<matplotlib.collections.PathCollection at 0x10c0f5700>
No description has been provided for this image
In [9]:
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[9]:
<matplotlib.collections.PathCollection at 0x10c075b20>
No description has been provided for this image
In [10]:
fig, ax = subplots(figsize = (5, 5))
scatter = ax.scatter(data['WBCC_in_std'], data['Glucose_in_std'], c = data['Class'], alpha = 0.5)
ax.set_xlabel('WBCC')
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")
Out[10]:
<matplotlib.legend.Legend at 0x10c0902e0>
No description has been provided for this image
In [11]:
x = np.linspace(-2, 6, 200)
y = np.linspace(-2, 6, 200)

X, Y = np.meshgrid(x, y)
result = np.stack([X.ravel(), Y.ravel()], axis=1)
df = pd.DataFrame(result)
df.columns = ('x', 'y')
df
Out[11]:
x y
0 -2.000000 -2.0
1 -1.959799 -2.0
2 -1.919598 -2.0
3 -1.879397 -2.0
4 -1.839196 -2.0
... ... ...
39995 5.839196 6.0
39996 5.879397 6.0
39997 5.919598 6.0
39998 5.959799 6.0
39999 6.000000 6.0

40000 rows × 2 columns

In [12]:
fig, ax = subplots(figsize = (10, 10))
scatter = ax.scatter(data['WBCC_in_std'], data['Glucose_in_std'], c = data['Class'], alpha=0.5)
ax.set_xlabel('WBCC')
ax.set_ylabel('Glucose')
Y = cdist(df, data[['WBCC_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')
ax.scatter(df2['x'], df2['y'], s=2, c=df2['Class'], alpha = 0.2);
No description has been provided for this image
In [13]:
k = 8
fig, ax = subplots(figsize = (10, 10))
scatter = ax.scatter(data['WBCC_in_std'], data['Glucose_in_std'], c = data['Class'], alpha=0.5)
Y = cdist(df, data[['WBCC_in_std', 'Glucose_in_std']])
X = np.argsort(Y, axis=1)
X = np.argsort(X, axis=1)
X[X < k] = 0
X[X >= k] = 1
X = 1 - X
T = np.array(data['Class'])
Z = np.matmul(X, T)
Z[Z < k//2] = 0
Z[Z >= k//2] = 1
ax.scatter(df['x'], df['y'], s=2, c=Z, alpha = 0.2);
No description has been provided for this image
In [14]:
# Nearest neighbor classification
ckd = pd.read_csv('ckd.csv')
ckd = ckd.rename(columns = {'Blood Glucose Random' : 'Glucose'})
ckd = ckd.rename(columns = {'White Blood Cell Count' : 'WBCC'})
ckd = ckd[['Glucose', 'Hemoglobin', 'WBCC', 'Class']]
indices = np.random.permutation(ckd.shape[0])
split_idx = int(len(indices) * 0.5) # 50% training
train_idx, test_idx = indices[:split_idx], indices[split_idx:]
ckd_train, ckd_test = ckd.iloc[train_idx, :], ckd.iloc[test_idx, :]
ckd_train = ckd_train.reset_index(drop=True)
ckd_test = ckd_test.reset_index(drop=True)

ckd_train = ckd_train.assign(Glucose_in_std = (ckd_train['Glucose']-ckd_train['Glucose'].mean())/ckd_train['Glucose'].std())
ckd_train = ckd_train.assign(WBCC_in_std = (ckd_train['WBCC']-ckd_train['WBCC'].mean())/ckd_train['WBCC'].std())

ckd_test = ckd_test.assign(Glucose_in_std = (ckd_test['Glucose']-ckd_train['Glucose'].mean())/ckd_train['Glucose'].std())
ckd_test = ckd_test.assign(WBCC_in_std = (ckd_test['WBCC']-ckd_train['WBCC'].mean())/ckd_train['WBCC'].std())
ckd_test
Out[14]:
Glucose Hemoglobin WBCC Class Glucose_in_std WBCC_in_std
0 94 15.5 9500 0 -0.660883 0.246393
1 99 13.0 8300 0 -0.588208 -0.099652
2 127 15.1 11000 0 -0.181228 0.678949
3 83 16.2 9300 0 -0.820768 0.188719
4 130 15.9 7800 0 -0.137623 -0.243838
... ... ... ... ... ... ...
74 93 16.6 7100 0 -0.675418 -0.445697
75 88 13.3 7000 0 -0.748093 -0.474534
76 100 16.9 6700 0 -0.573673 -0.561046
77 130 13.5 6700 0 -0.137623 -0.561046
78 124 14.9 7000 0 -0.224833 -0.474534

79 rows × 6 columns

In [15]:
k = 7
fig, ax = subplots(figsize = (8, 8))
scatter = ax.scatter(ckd_train['WBCC_in_std'], ckd_train['Glucose_in_std'], c = ckd_train['Class'], alpha=0.5)
Y = cdist(df, ckd_train[['WBCC_in_std', 'Glucose_in_std']])
X = np.argsort(Y, axis=1)
X = np.argsort(X, axis=1)
X[X < k] = 0
X[X >= k] = 1
X = 1 - X
T = np.array(ckd_train['Class'])
Z = np.matmul(X, T)
Z[Z < k//2] = 0
Z[Z >= k//2] = 1
ax.scatter(df['x'], df['y'], s=2, c=Z, alpha = 0.2);
No description has been provided for this image
In [16]:
df = ckd_test[['WBCC_in_std', 'Glucose_in_std']]

k = 7
fig, ax = subplots(figsize = (8, 8))
scatter = ax.scatter(ckd_train['WBCC_in_std'], ckd_train['Glucose_in_std'], s=10, c = ckd_train['Class'], alpha=0.5)
Y = cdist(df, ckd_train[['WBCC_in_std', 'Glucose_in_std']])
X = np.argsort(Y, axis=1)
X = np.argsort(X, axis=1)
X[X < k] = 0
X[X >= k] = 1
X = 1 - X
T = np.array(ckd_train['Class'])
Z = np.matmul(X, T)
Z[Z < k//2] = 0
Z[Z >= k//2] = 1
ax.scatter(df['WBCC_in_std'], df['Glucose_in_std'], s=100, c=Z, alpha = 0.9);
No description has been provided for this image
In [17]:
#Accuracy of the KNN classifier on the test set
ckd_test = ckd_test.assign(predicted_label = Z)
(ckd_test[ckd_test['Class'] == ckd_test['predicted_label']].shape[0]*100)/ckd_test.shape[0]
Out[17]:
91.13924050632912

Multiple Linear Regression¶

In [ ]:
house = pd.read_csv('house.csv')
house.head(3)
Out[ ]:
Order PID MS SubClass MS Zoning Lot Frontage Lot Area Street Alley Lot Shape Land Contour ... Pool Area Pool QC Fence Misc Feature Misc Val Mo Sold Yr Sold Sale Type Sale Condition SalePrice
0 1 526301100 20 RL 141.0 31770 Pave NaN IR1 Lvl ... 0 NaN NaN NaN 0 5 2010 WD Normal 215000
1 2 526350040 20 RH 80.0 11622 Pave NaN Reg Lvl ... 0 NaN MnPrv NaN 0 6 2010 WD Normal 105000
2 3 526351010 20 RL 81.0 14267 Pave NaN IR1 Lvl ... 0 NaN NaN Gar2 12500 6 2010 WD Normal 172000

3 rows × 82 columns

In [19]:
house = house[house['Bldg Type'] == '1Fam']
house = house[house['Sale Condition'] == 'Normal']
sales = house[['SalePrice', '1st Flr SF', '2nd Flr SF', 'Total Bsmt SF', 
               'Garage Area', 'Wood Deck SF', 'Open Porch SF', 'Lot Area', 
               'Year Built', 'Yr Sold']]
sales.head(3)
Out[19]:
SalePrice 1st Flr SF 2nd Flr SF Total Bsmt SF Garage Area Wood Deck SF Open Porch SF Lot Area Year Built Yr Sold
0 215000 1656 0 1080.0 528.0 210 62 31770 1960 2010
1 105000 896 0 882.0 730.0 140 0 11622 1961 2010
2 172000 1329 0 1329.0 312.0 393 36 14267 1958 2010
In [20]:
sales['1st Flr SF'].corr(sales['SalePrice'])
Out[20]:
np.float64(0.6424662541030226)
In [21]:
sales['2nd Flr SF'].corr(sales['SalePrice'])
Out[21]:
np.float64(0.3575218942800825)
In [22]:
(sales['1st Flr SF'] + sales['2nd Flr SF']).corr(sales['SalePrice'])
Out[22]:
np.float64(0.7821920556134878)
In [23]:
sales.corr()
Out[23]:
SalePrice 1st Flr SF 2nd Flr SF Total Bsmt SF Garage Area Wood Deck SF Open Porch SF Lot Area Year Built Yr Sold
SalePrice 1.000000 0.642466 0.357522 0.652979 0.638594 0.352699 0.336909 0.290823 0.565165 0.025949
1st Flr SF 0.642466 1.000000 -0.230561 0.765052 0.464566 0.247685 0.220888 0.303834 0.316339 0.026647
2nd Flr SF 0.357522 -0.230561 1.000000 -0.182213 0.172852 0.097926 0.227684 0.025948 0.079553 -0.020525
Total Bsmt SF 0.652979 0.765052 -0.182213 1.000000 0.456592 0.237535 0.204110 0.208933 0.425617 0.047675
Garage Area 0.638594 0.464566 0.172852 0.456592 1.000000 0.237945 0.240943 0.183717 0.487135 0.029293
Wood Deck SF 0.352699 0.247685 0.097926 0.237535 0.237945 1.000000 0.055236 0.127837 0.254986 0.019196
Open Porch SF 0.336909 0.220888 0.227684 0.204110 0.240943 0.055236 1.000000 0.095189 0.190891 -0.031687
Lot Area 0.290823 0.303834 0.025948 0.208933 0.183717 0.127837 0.095189 1.000000 0.083281 -0.008188
Year Built 0.565165 0.316339 0.079553 0.425617 0.487135 0.254986 0.190891 0.083281 1.000000 0.046000
Yr Sold 0.025949 0.026647 -0.020525 0.047675 0.029293 0.019196 -0.031687 -0.008188 0.046000 1.000000
In [ ]: