Skip to content

Commit 3414fec

Browse files
committed
local_weighted_learning.py: fix mypy errors and more (TheAlgorithms#8073)
1 parent acb33c0 commit 3414fec

File tree

1 file changed

+112
-76
lines changed

1 file changed

+112
-76
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,55 @@
1+
"""
2+
Locally weighted linear regression, also called local regression, is a type of
3+
non-parametric linear regression that prioritizes data closest to a given
4+
prediction point. The algorithm estimates the vector of model coefficients β
5+
using weighted least squares regression:
6+
7+
β = (XᵀWX)⁻¹(XᵀWy),
8+
9+
where X is the design matrix, y is the response vector, and W is the diagonal
10+
weight matrix.
11+
12+
This implementation calculates wᵢ, the weight of the ith training sample, using
13+
the Gaussian weight:
14+
15+
wᵢ = exp(-‖xᵢ - x‖²/(2τ²)),
16+
17+
where xᵢ is the ith training sample, x is the prediction point, τ is the
18+
"bandwidth", and ‖x‖ is the Euclidean norm (also called the 2-norm or the L²
19+
norm). The bandwidth τ controls how quickly the weight of a training sample
20+
decreases as its distance from the prediction point increases. One can think of
21+
the Gaussian weight as a bell curve centered around the prediction point: a
22+
training sample is weighted lower if it's farther from the center, and τ
23+
controls the spread of the bell curve.
24+
25+
Other types of locally weighted regression such as locally estimated scatterplot
26+
smoothing (LOESS) typically use different weight functions.
27+
28+
References:
29+
- https://en.wikipedia.org/wiki/Local_regression
30+
- https://en.wikipedia.org/wiki/Weighted_least_squares
31+
- https://cs229.stanford.edu/notes2022fall/main_notes.pdf
32+
"""
33+
134
import matplotlib.pyplot as plt
235
import numpy as np
336

437

5-
def weighted_matrix(
6-
point: np.array, training_data_x: np.array, bandwidth: float
7-
) -> np.array:
38+
def weight_matrix(point: np.ndarray, x_train: np.ndarray, tau: float) -> np.ndarray:
839
"""
9-
Calculate the weight for every point in the data set.
10-
point --> the x value at which we want to make predictions
11-
>>> weighted_matrix(
40+
Calculate the weight of every point in the training data around a given
41+
prediction point
42+
43+
Args:
44+
point: x-value at which the prediction is being made
45+
x_train: ndarray of x-values for training
46+
tau: bandwidth value, controls how quickly the weight of training values
47+
decreases as the distance from the prediction point increases
48+
49+
Returns:
50+
m x m weight matrix around the prediction point, where m is the size of
51+
the training set
52+
>>> weight_matrix(
1253
... np.array([1., 1.]),
1354
... np.array([[16.99, 10.34], [21.01,23.68], [24.59,25.69]]),
1455
... 0.6
@@ -17,25 +58,30 @@ def weighted_matrix(
1758
[0.00000000e+000, 0.00000000e+000, 0.00000000e+000],
1859
[0.00000000e+000, 0.00000000e+000, 0.00000000e+000]])
1960
"""
20-
m, _ = np.shape(training_data_x) # m is the number of training samples
21-
weights = np.eye(m) # Initializing weights as identity matrix
22-
23-
# calculating weights for all training examples [x(i)'s]
61+
m = len(x_train) # Number of training samples
62+
weights = np.eye(m) # Initialize weights as identity matrix
2463
for j in range(m):
25-
diff = point - training_data_x[j]
26-
weights[j, j] = np.exp(diff @ diff.T / (-2.0 * bandwidth**2))
64+
diff = point - x_train[j]
65+
weights[j, j] = np.exp(diff @ diff.T / (-2.0 * tau**2))
66+
2767
return weights
2868

2969

3070
def local_weight(
31-
point: np.array,
32-
training_data_x: np.array,
33-
training_data_y: np.array,
34-
bandwidth: float,
35-
) -> np.array:
71+
point: np.ndarray, x_train: np.ndarray, y_train: np.ndarray, tau: float
72+
) -> np.ndarray:
3673
"""
37-
Calculate the local weights using the weight_matrix function on training data.
38-
Return the weighted matrix.
74+
Calculate the local weights at a given prediction point using the weight
75+
matrix for that point
76+
77+
Args:
78+
point: x-value at which the prediction is being made
79+
x_train: ndarray of x-values for training
80+
y_train: ndarray of y-values for training
81+
tau: bandwidth value, controls how quickly the weight of training values
82+
decreases as the distance from the prediction point increases
83+
Returns:
84+
ndarray of local weights
3985
>>> local_weight(
4086
... np.array([1., 1.]),
4187
... np.array([[16.99, 10.34], [21.01,23.68], [24.59,25.69]]),
@@ -45,97 +91,86 @@ def local_weight(
4591
array([[0.00873174],
4692
[0.08272556]])
4793
"""
48-
weight = weighted_matrix(point, training_data_x, bandwidth)
49-
w = np.linalg.inv(training_data_x.T @ (weight @ training_data_x)) @ (
50-
training_data_x.T @ weight @ training_data_y.T
94+
weight_mat = weight_matrix(point, x_train, tau)
95+
weight = np.linalg.inv(x_train.T @ weight_mat @ x_train) @ (
96+
x_train.T @ weight_mat @ y_train.T
5197
)
5298

53-
return w
99+
return weight
54100

55101

56102
def local_weight_regression(
57-
training_data_x: np.array, training_data_y: np.array, bandwidth: float
58-
) -> np.array:
103+
x_train: np.ndarray, y_train: np.ndarray, tau: float
104+
) -> np.ndarray:
59105
"""
60-
Calculate predictions for each data point on axis
106+
Calculate predictions for each point in the training data
107+
108+
Args:
109+
x_train: ndarray of x-values for training
110+
y_train: ndarray of y-values for training
111+
tau: bandwidth value, controls how quickly the weight of training values
112+
decreases as the distance from the prediction point increases
113+
114+
Returns:
115+
ndarray of predictions
61116
>>> local_weight_regression(
62117
... np.array([[16.99, 10.34], [21.01, 23.68], [24.59, 25.69]]),
63118
... np.array([[1.01, 1.66, 3.5]]),
64119
... 0.6
65120
... )
66121
array([1.07173261, 1.65970737, 3.50160179])
67122
"""
68-
m, _ = np.shape(training_data_x)
69-
ypred = np.zeros(m)
123+
y_pred = np.zeros(len(x_train)) # Initialize array of predictions
124+
for i, item in enumerate(x_train):
125+
y_pred[i] = item @ local_weight(item, x_train, y_train, tau)
70126

71-
for i, item in enumerate(training_data_x):
72-
ypred[i] = item @ local_weight(
73-
item, training_data_x, training_data_y, bandwidth
74-
)
75-
76-
return ypred
127+
return y_pred
77128

78129

79130
def load_data(
80-
dataset_name: str, cola_name: str, colb_name: str
81-
) -> tuple[np.array, np.array, np.array, np.array]:
131+
dataset_name: str, x_name: str, y_name: str
132+
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
82133
"""
83134
Load data from seaborn and split it into x and y points
135+
>>> pass # No doctests, function is for demo purposes only
84136
"""
85137
import seaborn as sns
86138

87139
data = sns.load_dataset(dataset_name)
88-
col_a = np.array(data[cola_name]) # total_bill
89-
col_b = np.array(data[colb_name]) # tip
90-
91-
mcol_a = col_a.copy()
92-
mcol_b = col_b.copy()
93-
94-
one = np.ones(np.shape(mcol_b)[0], dtype=int)
140+
x_data = np.array(data[x_name])
141+
y_data = np.array(data[y_name])
95142

96-
# pairing elements of one and mcol_a
97-
training_data_x = np.column_stack((one, mcol_a))
143+
one = np.ones(len(y_data))
98144

99-
return training_data_x, mcol_b, col_a, col_b
145+
# pairing elements of one and x_data
146+
x_train = np.column_stack((one, x_data))
100147

101-
102-
def get_preds(training_data_x: np.array, mcol_b: np.array, tau: float) -> np.array:
103-
"""
104-
Get predictions with minimum error for each training data
105-
>>> get_preds(
106-
... np.array([[16.99, 10.34], [21.01, 23.68], [24.59, 25.69]]),
107-
... np.array([[1.01, 1.66, 3.5]]),
108-
... 0.6
109-
... )
110-
array([1.07173261, 1.65970737, 3.50160179])
111-
"""
112-
ypred = local_weight_regression(training_data_x, mcol_b, tau)
113-
return ypred
148+
return x_train, x_data, y_data
114149

115150

116151
def plot_preds(
117-
training_data_x: np.array,
118-
predictions: np.array,
119-
col_x: np.array,
120-
col_y: np.array,
121-
cola_name: str,
122-
colb_name: str,
123-
) -> plt.plot:
152+
x_train: np.ndarray,
153+
preds: np.ndarray,
154+
x_data: np.ndarray,
155+
y_data: np.ndarray,
156+
x_name: str,
157+
y_name: str,
158+
) -> None:
124159
"""
125160
Plot predictions and display the graph
161+
>>> pass # No doctests, function is for demo purposes only
126162
"""
127-
xsort = training_data_x.copy()
128-
xsort.sort(axis=0)
129-
plt.scatter(col_x, col_y, color="blue")
163+
x_train_sorted = np.sort(x_train, axis=0)
164+
plt.scatter(x_data, y_data, color="blue")
130165
plt.plot(
131-
xsort[:, 1],
132-
predictions[training_data_x[:, 1].argsort(0)],
166+
x_train_sorted[:, 1],
167+
preds[x_train[:, 1].argsort(0)],
133168
color="yellow",
134169
linewidth=5,
135170
)
136171
plt.title("Local Weighted Regression")
137-
plt.xlabel(cola_name)
138-
plt.ylabel(colb_name)
172+
plt.xlabel(x_name)
173+
plt.ylabel(y_name)
139174
plt.show()
140175

141176

@@ -144,6 +179,7 @@ def plot_preds(
144179

145180
doctest.testmod()
146181

147-
training_data_x, mcol_b, col_a, col_b = load_data("tips", "total_bill", "tip")
148-
predictions = get_preds(training_data_x, mcol_b, 0.5)
149-
plot_preds(training_data_x, predictions, col_a, col_b, "total_bill", "tip")
182+
# Demo with a dataset from the seaborn module
183+
training_data_x, total_bill, tip = load_data("tips", "total_bill", "tip")
184+
predictions = local_weight_regression(training_data_x, tip, 5)
185+
plot_preds(training_data_x, predictions, total_bill, tip, "total_bill", "tip")

0 commit comments

Comments
 (0)