Tshepo Chris

Comparing Parametric Methods: Advanced Approach to Regression Algorithms

Simple linear regression is a model used to determine the influence of a dependent variable on an independent variable. One can use this model to predict a continuous variable. Regression is based on a formula for a straight-line relationship between two or more variables. The closer the observations are to the line, the better the fit.

In this example, we compared several parametric methods (or linear regression methods), namely:


• Ordinary Least Squared Regression (which estimates a relationship between independent variable and dependent variable by minimizing the sum of the squares in the difference between the actual and predicted values).
• Ridge Regression (which allows for regularization of coefficients).
• RidgeCV Regression (which is a ridge regression with built-in cross-validation).
• Lasso Regression (which allows for regularization of coefficients using few parameters).
• Extreme Gradient Boost Regression (which allows gradient boosting of decision trees)
• Random Forest Regression (which uses multiple decision trees and Bootstrap Aggregation)

We determined whether changes in employees’ work experience influences changes in salary. Thereafter, we used employees’ work experience to predict salary. The hypothesis was follows:-

Null hypothesis: There is no significant difference between an employee’s years of work experience and salary.

Alternative hypothesis: There is a significant difference between an employee’s years of work experience and salary.

Import Python Libraries

Underneath, key Python libraries were imported. These libraries gallantly helped us create arrays and profile tables, compute graphs and make accurate predictions.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
sns.set("talk","ticks",font_scale=1,font="sans-serif",color_codes=True)
import scipy.stats as stats
import statsmodels.api as sm
from sklearn import metrics
from sklearn.linear_model import LinearRegression, Lasso, Ridge, RidgeCV
from xgboost import XGBRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import cross_val_score, GridSearchCV, train_test_split
from sklearn.ensemble import RandomForestRegressor
import warnings
warnings.filterwarnings("ignore")
from pylab import rcParams
plt.rcParams["figure.figsize"] = [10,10]
df = pd.read_csv(r"C:\Users\Tshepo\Desktop\MLAgortihms\Datasets\Salary_Data.csv")
df.head()

Load data

We called the pd.read_csv() function to load obtained csv file into a pandas data frame. See underneath table to examine the data structure.

df = pd.read_csv(r"C:\Users\Tshepo\Desktop\MLAgortihms\Datasets\Salary_Data.csv")
df.head()

Detect missing values

The visible presence of missing values result in measurement errors that negatively impact conclusions. To studiously avoid such as a dilemma, data was carefully examined to unanimously determine whether there were any missing values. See underneath figure for a visual representation of the possible presence of missing values. 

There were no missing values detected in the data.

Create x and y array

The code underneath allowed us to create vectors of x and y using the NumPy library. 

x = np.array(df["YearsExperience"])
y = np.array(df["Salary"])

Above we created x and y arrays.

x – independent variable (YearsExperience)

y – dependent variable (Salary)

Reshape x and y array

Given that most scikit-lean libraries require a 1D array of the dependent variable to be shaped as a 2D array. The code underneath was used to reshape the created vectors.

x = x.reshape(-1,1)
y = y.reshape(-1,1)

Split training and test data

The data was split into training data and test data by properly calling the train_test_split() function. The training data was used to fit the data. Meanwhile, the test data was conveniently used to validate the simple linear regression model. 80% of the data was adequately trained and 20% of the data was for testing purposes.

x_train, x_test, y_train, y_test = train_test_split(x,y,test_size=0.2,random_state=0)
x_train, x_val, y_train, y_val = train_test_split(x_train,y_train,test_size=0.2,random_state=0)

Normalize data

We called the StandardScaler() function to normalize the data. This function transformed the data in such a way that the mean value was 0 and standard deviation was 1.

scaler = StandardScaler()
x_train = scaler.fit_transform(x_train)
x_test = scaler.transform(x_test)

Training data

Underneath we finalized different models, namely Ordinary Least Squared, Ridge, RidgeCV, Extreme Gradient Boost and Random Forest Regression.

lm = LinearRegression().fit(x_train,y_train)
ridge = Ridge().fit(x_train,y_train)
ridgecv = RidgeCV().fit(x_train,y_train)
lasso = Lasso().fit(x_train,y_train)
xgb = XGBRegressor().fit(x_train,y_train)
rfr = RandomForestRegressor().fit(x_train,y_train)

Cross validation

def get_val_score(model):
    scores = cross_val_score(model, x_train, y_train, scoring="r2")
    print("CV mean: ", np.mean(scores))
    print("CV std: ", np.std(scores))
    print("\n")
get_val_score(lm)

Output

CV mean: 0.8799423889246292

CV std: 0.03213821058156567

get_val_score(lasso)

CV mean: 0.8799429197435654

CV std: 0.032139173602634706

get_val_score(ridge)

Output

CV mean: 0.8735101932639928

CV std: 0.041566258797297057

get_val_score(ridgecv)

Output

CV mean: 0.880009188026896

CV std: 0.03238720294360753

get_val_score(xgb)

Output

CV mean: 0.7548196430599866

CV std: 0.21279942011143785

get_val_score(rfr)

Output

CV mean: 0.8992202034295529

CV std: 0.0444344358414017

Hyper-parameter tuning (Ordinary Least Squared)

param_gridOLS = {'fit_intercept':[True,False], 'normalize':[True,False], 'copy_X':[True, False]}
grid_modelOLS  = GridSearchCV(estimator=lm, param_grid=param_gridOLS, n_jobs=-1)
grid_modelOLS.fit(x_train,y_train)

Output

GridSearchCV(cv=’warn’, error_score=’raise-deprecating’, estimator=LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False), iid=’warn’, n_jobs=-1, param_grid={‘copy_X’: [True, False], ‘fit_intercept’: [True, False], ‘normalize’: [True, False]}, pre_dispatch=’2*n_jobs’, refit=True, return_train_score=False, scoring=None, verbose=0)

print("Best score", grid_modelOLS.best_score_, "Best parameters", grid_modelOLS.best_params_)

Output

Best score 0.8792397989835684 Best parameters {'copy_X': True, 'fit_intercept': True, 'normalize': True}

Hyper-parameter tuning (Ridge)

alpharidge = [0.0001,0.001,0.01,0.1,1,10,100,1000]
param_gridridge = dict(alpha=alpha1)
grid_modelridge  = GridSearchCV(estimator=ridge, param_grid=param_gridridge, n_jobs=-1)
grid_modelridge .fit(x_train,y_train)

Output

GridSearchCV(cv=’warn’, error_score=’raise-deprecating’, estimator=Ridge(alpha=1.0, copy_X=True, fit_intercept=True, max_iter=None, normalize=False, random_state=None, solver=’auto’, tol=0.001), iid=’warn’, n_jobs=-1, param_grid={‘alpha’: [0.0001, 0.001, 0.01, 0.1, 1, 10, 100, 1000]}, pre_dispatch=’2*n_jobs’, refit=True, return_train_score=False, scoring=None, verbose=0)

print("Best score", grid_modelridge.best_score_, "Best parameters", grid_modelridge.best_params_)

Output

Best score 0.8792397321262045 Best parameters {'alpha': 0.0001}

Hyper-parameter tuning (lasso)

alphalasso = [0.0001,0.001,0.01,0.1,1,10,100,1000]
param_gridlasso = dict(alpha=alpha)
grid_modellasso = GridSearchCV(estimator=lasso, param_grid=param_gridlasso, n_jobs=-1)
grid_modellasso.fit(x_train,y_train)

Output

GridSearchCV(cv=’warn’, error_score=’raise-deprecating’, estimator=Lasso(alpha=0.0001, copy_X=True, fit_intercept=True, max_iter=1000, normalize=False, positive=False, precompute=False, random_state=None, selection=’cyclic’, tol=0.0001, warm_start=False), iid=’warn’, n_jobs=-1, param_grid={‘alpha’: [0.0001, 0.001, 0.01, 0.1, 1, 10, 100, 1000]}, pre_dispatch=’2*n_jobs’, refit=True, return_train_score=False, scoring=None, verbose=0)

print("Best score", grid_modellasso.best_score_, "Best parameters", grid_modellasso.best_params_)
Best score 0.8792397989069052 Best parameters {'alpha': 0.0001}

Hyper-parameter tuning (Random Forest Regression)

param_gridrfr = {"n_estimators": [2,4,6,8,10,12,14], "max_depth": [2,4,6,8], "min_samples_leaf": [1,2,3,4,5]}
grid_modelrfr = GridSearchCV(estimator=rfr, param_grid=param_gridrfr, n_jobs=-1)
grid_modelrfr.fit(x_train,y_train)

Output

GridSearchCV(cv=’warn’, error_score=’raise-deprecating’, estimator=RandomForestRegressor(bootstrap=True, criterion=’mse’, max_depth=None, max_features=’auto’, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=None, oob_score=False, random_state=None, verbose=0, warm_start=False), iid=’warn’, n_jobs=-1, param_grid={‘max_depth’: [2, 4, 6, 8], ‘min_samples_leaf’: [1, 2, 3, 4, 5], ‘n_estimators’: [2, 4, 6, 8, 10, 12, 14]}, pre_dispatch=’2*n_jobs’, refit=True, return_train_score=False, scoring=None, verbose=0)

print("Best score", grid_modelrfr.best_score_, "Best parameters", grid_modelrfr.best_params_)

Output

Best score 0.9138512380928171 Best parameters {'max_depth': 4, 'min_samples_leaf': 1, 'n_estimators': 14}

Hyper-parameter tuning (Extreme Gradient Boost Regression)

param_gridxgb = {'nthread':[2,4,6,8,10], 'objective':['reg:linear'],'learning_rate': [0.0001,0.001,0.01,0.1,0.2,0.4,0.6,0.8], 'max_depth': [2,4,6,8],'min_child_weight': [2,4,6,8],'silent': [1],'subsample': [0.7],'colsample_bytree': [0.0001,0.001,0.01,0.1,0.2,0.4,0.6,0.8],'n_estimators': [2,4,6,8,10,12,14]}
grid_modelxgb = GridSearchCV(estimator=xgb, param_grid=param_gridxgb, n_jobs=-1)
grid_modelxgb.fit(x_train,y_train)

GridSearchCV(cv=’warn’, error_score=’raise-deprecating’, estimator=XGBRegressor(base_score=0.5, booster=’gbtree’, colsample_bylevel=1, colsample_bynode=1, colsample_bytree=1, gamma=0, importance_type=’gain’, learning_rate=0.1, max_delta_step=0, max_depth=3, min_child_weight=1, missing=None, n_estimators=100, n_jobs=1, nthread=None, objective=’reg:linear’, random_sta… param_grid={‘colsample_bytree’: [0.0001, 0.001, 0.01, 0.1, 0.2, 0.4, 0.6, 0.8], ‘learning_rate’: [0.0001, 0.001, 0.01, 0.1, 0.2, 0.4, 0.6, 0.8], ‘max_depth’: [2, 4, 6, 8], ‘min_child_weight’: [2, 4, 6, 8], ‘n_estimators’: [2, 4, 6, 8, 10, 12, 14], ‘nthread’: [2, 4, 6, 8, 10], ‘objective’: [‘reg:linear’], ‘silent’: [1], ‘subsample’: [0.7]}, pre_dispatch=’2*n_jobs’, refit=True, return_train_score=False, scoring=None, verbose=0)

print("Best score", grid_modelxgb.best_score_, "Best parameters", grid_modelxgb.best_params_)

Output

Best score 0.8837578939122723 Best parameters {‘colsample_bytree’: 0.0001, ‘learning_rate’: 0.4, ‘max_depth’: 2, ‘min_child_weight’: 2, ‘n_estimators’: 14, ‘nthread’: 2, ‘objective’: ‘reg:linear’, ‘silent’: 1, ‘subsample’: 0.7}

Configuring hyper-parameters for all regression models

lm = LinearRegression(copy_X= True, fit_intercept=True, normalize=True).fit(x_train,y_train)
ridge = Ridge(alpha=0.0001).fit(x_train,y_train)
ridgecv = RidgeCV().fit(x_train,y_train)
lasso = Lasso(alpha=0.0001).fit(x_train,y_train)
xgb = XGBRegressor(colsample_bytree = 0.0001, learning_rate=0.4, max_depth=2, min_child_weight=2, n_estimators=14, nthread=2, objective="reg:linear", silent=1, subsample=0.7).fit(x_train,y_train)
rfr = RandomForestRegressor(max_depth=4, min_samples_leaf=1, n_estimators=14).fit(x_train,y_train)

Predictions

We passed on the model using the predict() function to make predictions. Thereafter, we created a data frame consisting of predicted values.

y_predlm = lm.predict(x_test)
y_predlm = pd.DataFrame(y_predlm, columns = ["Ordinary Least Squared"])
y_predridge = ridge.predict(x_test)
y_predridge = pd.DataFrame(y_predridge, columns = ["Ridge"])
y_predridgecv = ridgecv.predict(x_test)
y_predridgecv = pd.DataFrame(y_predridgecv, columns = ["RidgeCV"])
y_predlasso = lasso.predict(x_test)
y_predlasso = pd.DataFrame(y_predlasso, columns = ["Lasso"])
y_predxgb = xgb.predict(x_test)
y_predxgb = pd.DataFrame(y_predxgb, columns = ["Extreme Gradient Boost"])
y_predrfr = rfr.predict(x_test)
y_predrfr= pd.DataFrame(y_predrfr, columns = ["Random Forest"])
prediction1 = pd.concat([y_predlm,y_predridge],axis=1)
prediction2 = pd.concat([y_predridgecv,y_predlasso],axis=1)
prediction3 = pd.concat([prediction1,prediction2],axis=1)
prediction4 = pd.concat([y_predxgb,y_predrfr],axis=1)
prediction5 = pd.concat([prediction3,prediction4],axis=1)
prediction5
Ordinary Least Squared Ridge RidgeCV Lasso Extreme Gradient Boost Random Forest
038773.91688338774.10520438961.25322338773.91704243122.15234446893.619048
1125228.013663125227.746965124962.710753125228.013438109790.335938111207.928571
264317.17275064317.22663464370.77476664317.17279556123.13671957209.486565
362352.30691462352.37113962416.19618662352.30696856123.13671957209.486565
4117368.550319117368.324987117144.396432117368.550129109790.335938111207.928571
5109509.086976109508.903009109326.082111109509.086821109790.335938109360.928571

Actual

Underneath are actual values, which can be compared with predicated values above.

y_test = pd.DataFrame(y_test, columns = ["Actual"])
y_test
Actual
037731.0
1122391.0
257081.0
363218.0
4116969.0
5109431.0
y_test.mean()

Actual 84470.166667

dtype: float64

MAElm = metrics.mean_absolute_error(y_test,y_predlm)
MAEridge = metrics.mean_absolute_error(y_test,y_predridge)
MAEridgecv = metrics.mean_absolute_error(y_test,y_predridgecv)
MAElasso = metrics.mean_absolute_error(y_test,y_predlasso)
MAExgb = metrics.mean_absolute_error(y_test,y_predxgb)
MAErfr = metrics.mean_absolute_error(y_test,y_predrfr)
MAEmodel = [[MAElm,MAEridge,MAEridgecv,MAElasso,MAExgb,MAErfr]]
MAEmodeldata = pd.DataFrame(MAEmodel, columns = ("Ordinary Least Squared", "Ridge", "RidgeCV", "Lasso", "Extreme Gradient Boost", "Random Forest"), index=["MAE"]).transpose()
MSElm = metrics.mean_squared_error(y_test,y_predlm)
MSEridge = metrics.mean_squared_error(y_test,y_predridge)
MSEridgecv = metrics.mean_squared_error(y_test,y_predridgecv)
MSElasso = metrics.mean_squared_error(y_test,y_predlasso)
MSExgb = metrics.mean_squared_error(y_test,y_predxgb)
MSErfr = metrics.mean_squared_error(y_test,y_predrfr)
MSEmodel = [[MSElm,MSEridge,MSEridgecv,MSElasso,MSExgb,MSErfr]]
MSEmodeldata = pd.DataFrame(MSEmodel, columns = ("Ordinary Least Squared", "Ridge", "RidgeCV", "Lasso", "Extreme Gradient Boost", "Random Forest"), index=["MSE"]).transpose()
RMSElm = np.sqrt(MSElm)
RMSEridge = np.sqrt(MSEridge)
RMSEridgecv = np.sqrt(MSEridgecv)
RMSElasso = np.sqrt(MSElasso)
RMSExgb = np.sqrt(MSExgb)
RMSErfr = np.sqrt(MSErfr)
RMSEmodel = [[RMSElm,RMSEridge,RMSEridgecv,RMSElasso,RMSExgb,RMSErfr]]
RMSEmodeldata = pd.DataFrame(RMSEmodel, columns = ("Ordinary Least Squared", "Ridge", "RidgeCV", "Lasso", "Extreme Gradient Boost", "Random Forest"), index=["RMSE"]).transpose()
R2lm = metrics.r2_score(y_test,y_predlm)
R2ridge = metrics.r2_score(y_test,y_predridge)
R2ridgecv = metrics.r2_score(y_test,y_predridgecv)
R2lasso = metrics.r2_score(y_test,y_predlasso)
R2xgb = metrics.r2_score(y_test,y_predxgb)
R2rfr = metrics.r2_score(y_test,y_predrfr)
R2model = [[R2lm,R2ridge,R2ridgecv,R2lasso,R2xgb,R2rfr]]
R2modeldata = pd.DataFrame(R2model, columns = ("Ordinary Least Squared", "Ridge", "RidgeCV", "Lasso", "Extreme Gradient Boost", "Random Forest"), index=["R2"]).transpose()
modelevaluation1 = pd.concat([MAEmodeldata, MSEmodeldata],axis=1)
modelevaluation2 = pd.concat([RMSEmodeldata, R2modeldata],axis=1)
modelevaluation3 = pd.concat([modelevaluation1, modelevaluation2],axis=1)
modelevaluation3

MAE MSE RMSE R2
Ordinary Least Squared2076.5722801.040228e+073225.2565990.990403
Ridge2076.4892771.040217e+073225.2395350.990403
RidgeCV2028.9761461.032545e+073213.3237300.990474
Lasso2076.5722091.040228e+073225.2565840.990403
Extreme Gradient Boost5597.0904954.845970e+076961.2999920.955293
Random Forest5385.6388894.638805e+076810.8769740.957204

Conclusion

As seen on the above, RidgeCV is the best regression model for the following reasons:-

  • There is a low average magnitude of errors in predictions without considering their direction (at 2028.976146).
  • There is a low squares errors or estimated values and predicted values (at 1.032545e+07).
  • 99.04% of the variation in data is explained by the model.

This comes as no surprise. RidgeCV is built-in with generalized cross-validation, which is a form of efficient Leave-One-Out cross-validation.

Given the above, we will proceed with our analysis using the RidgeCV Regression.

Test data

Underneath we plotted our test data to gain insight on predicted salary.

plt.scatter(x_test,y_predridgecv,alpha=0.8)
plt.plot(x_test,y_predridgecv,color="red",alpha=0.8)
plt.title("YearsExperience vs Salary - Test data")
plt.xlabel("YearsExperience")
plt.ylabel("Salary")
plt.show()

Observation are tightly aligned to the straight line.

Training data

plt.scatter(x_train,y_train,alpha=0.8)
plt.plot(x_test,y_predridgecv,color="red",alpha=0.8)
plt.title("YearsExperience vs Salary - Training data")
plt.xlabel("YearsExperience")
plt.ylabel("Salary")
plt.show()

Above we plotted the training data and found that observations are close to straight line – not as tight as the test data. The test data might have exaggerated some points.

Actual values vs Predicted values

Underneath we compared predicted values against actual values.

plt.scatter(y_test,y_predridgecv,alpha=0.8)
plt.axhline(color="red",alpha=0.8)
plt.title("Actual Salary vs Predicted Salary")
plt.xlabel("Actual Salary")
plt.ylabel("Predicted Salary")
plt.show()

The above plot shows that the difference between actual values and predicted values was not large.