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))
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>
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>
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>
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);
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);
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);
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);
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 [ ]: