I am unsure of how to use Decorators properly; I have drawn reference from Real Python and Try-Except for Multiple Methods. I am coding up a Linear Regression class, and I realised that you need to call fit
before you can do predict, or other methods that my class have. But it is cumbersome to define each and every method to raise error when the self._fitted
flag is False
. So I turned to decorators, I am unsure if I am using correctly, because it does behave the way I want it to, however it neglects any other forms of errors like ValueError etc. Asking for advice here.
import functools
from sklearn.exceptions import NotFittedError
def NotFitted(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except:
raise NotFittedError
return wrapper
class LinearRegression:
def __init__(self, fit_intercept: bool = True):
self.coef_ = None
self.intercept_ = None
self.fit_intercept = fit_intercept
# a flag to turn to true once we called fit on the data
self._fitted = False
def check_shape(self, X: np.array, y: np.array):
# if X is 1D array, then it is simple linear regression, reshape to 2D
# [1,2,3] -> [[1],[2],[3]] to fit the data
if X is not None and len(X.shape) == 1:
X = X.reshape(-1, 1)
# self._features = X
# self.intercept_ = y
return X, y
def fit(self, X: np.array = None, y: np.array = None):
X, y = self.check_shape(X, y)
n_samples, n_features = X.shape[0], X.shape[1]
if self.fit_intercept:
X = np.c_[np.ones(n_samples), X]
XtX = np.dot(X.T, X)
XtX_inv = np.linalg.inv(XtX)
XtX_inv_Xt = np.dot(XtX_inv, X.T)
_optimal_betas = np.dot(XtX_inv_Xt, y)
# set attributes from None to the optimal ones
self.coef_ = _optimal_betas[1:]
self.intercept_ = _optimal_betas[0]
self._fitted = True
return self
@NotFitted
def predict(self, X: np.array):
"""
after calling .fit, you can continue to .predict to get model prediction
"""
# if self._fitted is False:
# raise NotFittedError
if self.fit_intercept:
y_hat = self.intercept_ + np.dot(X, self.coef_)
else:
y_hat = self.intercept_
return y_hat