Step-by-Step Guide to Regression with Python

Step-by-Step Guide to Regression with Python

Author: GURCHETAN SINGH

Translator: Zhang Yi

Proofreader: Ding Nanya

This article has 5800 words, and is recommended to read in 8 minutes. It starts with linear regression and polynomial regression, guiding you to implement spline regression using Python.

When I first started learning data science, the first algorithm I encountered was linear regression. Through applying this algorithm to various datasets, I summarized some of its advantages and disadvantages.

First, linear regression assumes a linear relationship between the independent and dependent variables, but this is rarely the case in reality. To improve this model, I tried polynomial regression, which indeed performed better (most of the time it improves). However, a new problem arose: when the dataset has too many variables, polynomial regression can easily lead to overfitting.

Step-by-Step Guide to Regression with Python

Moreover, the model I built was always too flexible; it might perform well on the test set but poorly on unseen data. Later, I came across another nonlinear method called spline regression, which combines linear/polynomial functions to fit the data.

In this article, I will introduce the basic concepts of linear regression and polynomial regression, then elaborate on more details about spline regression and its implementation in Python.

Note: To better understand the various concepts mentioned in this article, you need to have a foundational knowledge of linear regression and polynomial regression. Here are some related materials you can refer to:

https://www.analyticsvidhya.com/blog/2015/08/comprehensive-guide-regression/

Article Structure

  • Understanding the Data

  • A Brief Review of Linear Regression

  • Polynomial Regression: An Improvement on Linear Regression

  • Understanding Spline Regression and Its Implementation

    • Piecewise Step Functions

    • Basis Functions

    • Piecewise Polynomials

    • Constraints and Splines

    • Cubic Splines and Natural Cubic Splines

    • Determining the Number and Position of Knots

    • Comparing Spline Regression and Polynomial Regression

Understanding the Data

To better understand these concepts, we chose a wage prediction dataset for illustration. You can download it here:

https://drive.google.com/file/d/1QIHCTvHQIBpilzbNxGmbdEBEbmEkMd_K/view

This dataset is extracted from a recently popular book, Introduction to Statistical Learning (http://www-bcf.usc.edu/~gareth/ISL/ISLR%20Seventh%20Printing.pdf).

Our dataset includes information such as ID, year of birth, gender, marital status, race, education level, occupation, health status, health insurance, and wage records. To explain spline regression in detail, we will use age as the independent variable to predict wage (the dependent variable).

Let’s get started:

# Import necessary packages

import pandas as pd

import numpy as np

import statsmodels.api as sm

import matplotlib.pyplot as plt

%matplotlib inline

# Read in data

data = pd.read_csv(“Wage.csv”)

data.head()

We will get the following result:

Step-by-Step Guide to Regression with Python

Continuing:

data_x = data[‘age’]

data_y = data[‘wage’]

# Split the data into training and validation sets

from sklearn.model_selection import train_test_split

train_x, valid_x, train_y, valid_y = train_test_split(data_x, data_y, test_size=0.33, random_state = 1)

# Visualize the relationship between age and wage

import matplotlib.pyplot as plt

plt.scatter(train_x, train_y, facecolor=’None’, edgecolor=’k’, alpha=0.3)plt.show()

We will get such a plot:

Step-by-Step Guide to Regression with Python

Looking at the scatter plot above, what comes to mind? Does it represent a positive correlation, a negative correlation, or is there no relationship at all? Everyone can share their views in the comments below.

Introduction to Linear Regression

Linear regression is the simplest and most widely used statistical method in predictive modeling. It is a supervised learning method used to solve regression tasks.

This method establishes a linear relationship between the independent and dependent variables, which is why it is called linear regression. It is primarily a linear equation, as shown in the equation below. It can be understood that our features are a set of independent variables with coefficients.

Step-by-Step Guide to Regression with Python

In this equation, we consider Y as the dependent variable, X as the independent variable, and all β as coefficients. These coefficients are the weights corresponding to the features, indicating the importance of each feature. For example, if a predicted result heavily relies on one of the features (X1), it means that compared to all other features, the coefficient (i.e., weight) of X1 will be higher.

Next, let’s try to understand linear regression with only one feature. That is, only one independent variable. It is called simple linear regression. The corresponding equation is as follows:

Step-by-Step Guide to Regression with Python

As mentioned earlier, we will only use age as a feature to predict wage, so it is clear that we can apply simple linear regression on the training set and calculate the model’s error (RMSE) on the validation set.

from sklearn.linear_model import LinearRegression

# Fit the linear regression model

x = train_x.reshape(-1,1)

model = LinearRegression()

model.fit(x,train_y)

print(model.coef_)

print(model.intercept_)

-> array([0.72190831])

-> 80.65287740759283

# Make predictions on the validation set

valid_x = valid_x.reshape(-1,1)

pred = model.predict(valid_x)

# Visualization

# We will use 70 points between the minimum and maximum values of valid_x for plotting

xp = np.linspace(valid_x.min(),valid_x.max(),70)

xp = xp.reshape(-1,1)

pred_plot = model.predict(xp)

plt.scatter(valid_x, valid_y, facecolor=’None’, edgecolor=’k’, alpha=0.3)

plt.plot(xp, pred_plot)

plt.show()

The resulting plot is as follows:

Step-by-Step Guide to Regression with Python

Now let’s calculate the RMSE for the predicted results:

from sklearn.metrics import mean_squared_error

from math import sqrt

rms = sqrt(mean_squared_error(valid_y, pred))

print(rms)

-> 40.436

From the plot above, we can see that the linear regression model did not capture all the characteristics of the data, and for the wage prediction problem, this method did not perform well.

So the conclusion is that although linear models are relatively simple to describe and implement, and very easy to understand and apply, they have limited predictive power. This is because linear models assume that there is always a linear relationship between the independent and dependent variables. This assumption is weak; it is merely an approximation and can be very poor in some cases.

In the other methods mentioned below, we will temporarily set aside this linear assumption, but we cannot completely disregard it. We will expand on this simplest linear model to obtain polynomial regression, step functions, or more complex methods like spline regression, which will also be introduced below.

Improvements on Linear Regression: Polynomial Regression

Let’s take a look at a set of visualizations:

Step-by-Step Guide to Regression with Python

These graphs seem to have uncovered more connections between age and wage. They are nonlinear because the equations used to establish the age and wage model are nonlinear equations. This regression method using nonlinear functions is called polynomial regression.

Polynomial regression extends the simple linear model by adding additional predictor terms. Specifically, it raises each original predictor term to a power. For example, a cubic regression uses these three variables:

Step-by-Step Guide to Regression with Pythonas predictors. It provides a simple way to better fit the data with nonlinearity.

So how does this method replace linear models with nonlinear models to establish relationships between independent and dependent variables? The essence of this improvement is to use a polynomial equation to replace the original linear relationship.

Step-by-Step Guide to Regression with Python

However, as we increase the power, the curve starts to oscillate at high frequencies. This leads to an overly complex shape of the curve, ultimately causing overfitting.

# Generate weights for the regression function, set degree=2

weights = np.polyfit(train_x, train_y, 2)

print(weights)

-> array([ -0.05194765, 5.22868974, -10.03406116])

# Generate the model based on the given weights

model = np.poly1d(weights)

# Make predictions on the validation set

pred = model(valid_x)

# We only plot 70 points

xp = np.linspace(valid_x.min(),valid_x.max(),70)

pred_plot = model(xp)

plt.scatter(valid_x, valid_y, facecolor=’None’, edgecolor=’k’, alpha=0.3)

plt.plot(xp, pred_plot)

plt.show()

Step-by-Step Guide to Regression with Python

Similarly, we plot the graphs corresponding to different degree values:

Step-by-Step Guide to Regression with Python

Step-by-Step Guide to Regression with Python

Step-by-Step Guide to Regression with Python

Step-by-Step Guide to Regression with Python

Unfortunately, polynomial regression also has many issues; as the complexity of the equations increases, the number of features can grow to an uncontrollable level. Moreover, even on this simple one-dimensional dataset, polynomial regression can lead to overfitting.

In addition, there are other problems. For example, polynomial regression is essentially non-local. This means that changing the y-value of one point in the training set can affect the fitting of other data points far from this point. Therefore, to avoid using overly high-order polynomials across the entire dataset, we can use many different low-order polynomial functions as alternatives.

Spline Regression and Its Implementation

To overcome the drawbacks of polynomial regression, we can use another improved regression method. This method does not apply the model to the entire dataset but divides the dataset into multiple intervals, fitting a separate model for the data in each interval. This method is called spline regression.

Spline regression is one of the most important nonlinear regression methods. In polynomial regression, we create new features by applying different polynomial functions to existing features, which has a global effect on the dataset. To solve this problem, we can divide the data into different parts based on the distribution characteristics and fit linear or low-order polynomial functions on each part.

Step-by-Step Guide to Regression with Python

The points used for partitioning are called knots. We can use piecewise functions to model the data in each interval. There are many different piecewise functions that can be used to fit this data.

In the next section, we will introduce these functions in detail.

  • Piecewise Step Functions

Step functions are one of the most common piecewise functions. Their function value remains constant within a segment of intervals. We can apply different step functions to different data intervals to avoid affecting the structure of the entire dataset.

Here we will segment the values of X and fit a different constant for each part.

More specifically, we set the cut points C1, C2, … Ck. We construct K+1 new variables in the range of X.

Step-by-Step Guide to Regression with Python

In the above figure, I() is an indicator function that returns 1 if the condition is met, otherwise it returns 0. For example, when Ck ≤ X, the function value I(Ck ≤ X) is 1; otherwise, it equals 0. For any given value of X, only one of C1, C2, … Ck can be non-zero because X can only be assigned to one interval.

# Divide the data into four intervals

df_cut, bins = pd.cut(train_x, 4, retbins=True, right=True)

df_cut.value_counts(sort=False)

->(17.938, 33.5] 504

(33.5, 49.0] 941

(49.0, 64.5] 511

(64.5, 80.0] 54

Name: age, dtype: int64

df_steps = pd.concat([train_x, df_cut, train_y],

keys=[‘age’,’age_cuts’,’wage’], axis=1)

# Encode age as dummy variables

df_steps_dummies = pd.get_dummies(df_cut)

df_steps_dummies.head()

Step-by-Step Guide to Regression with Python

df_steps_dummies.columns = [‘17.938-33.5′,’33.5-49′,’49-64.5′,’64.5-80’]

# Fit a Generalized Linear Model

fit3 = sm.GLM(df_steps.wage, df_steps_dummies).fit()

# Also divide the validation set into four buckets

bin_mapping = np.digitize(valid_x, bins)

X_valid = pd.get_dummies(bin_mapping)

# Remove outliers

X_valid = pd.get_dummies(bin_mapping).drop([5], axis=1)

# Make predictions

pred2 = fit3.predict(X_valid)

# Calculate RMSE

from sklearn.metrics import mean_squared_error

from math import sqrt

rms = sqrt(mean_squared_error(valid_y, pred2))

print(rms)

->39.9

# Here we only plot the graph of 70 observation points

xp = np.linspace(valid_x.min(),valid_x.max()-1,70)

bin_mapping = np.digitize(xp, bins)

X_valid_2 = pd.get_dummies(bin_mapping)

pred2 = fit3.predict(X_valid_2)

# Visualization

fig, (ax1) = plt.subplots(1,1, figsize=(12,5))

fig.suptitle(‘Piecewise Constant’, fontsize=14)

# Plot the spline regression scatter plot

ax1.scatter(train_x, train_y, facecolor=’None’, edgecolor=’k’, alpha=0.3)

ax1.plot(xp, pred2, c=’b’)

ax1.set_xlabel(‘age’)

ax1.set_ylabel(‘wage’)

plt.show()

Step-by-Step Guide to Regression with Python

However, this piecewise method has obvious conceptual problems. The most apparent issue is that most of the problems we study will have a continuous trend that changes with the input. But this method cannot construct a continuous function for the predictor variable, so in most cases, when applying this method, we must first assume that there is no relationship between input and output.

For example, in the above chart, we can see that the fitted function for the first interval clearly did not capture the trend of wages increasing with age.

  • Basis Functions

To capture nonlinearity in the regression model, we need to transform part or all of the predictor terms. And to avoid treating each independent variable as linear, we want a more general family of transformations to apply to the predictor terms. It should have enough flexibility to fit a variety of curve shapes (when the model is appropriate), while being careful not to overfit.

This transformation that can combine to capture general data distributions is called basis functions. In this example, the basis functions are b1(x), b2(x), …, bk(x)

At this point, we are no longer fitting a linear model, but as shown below:

Step-by-Step Guide to Regression with Python

Next, let’s look at a commonly used basis function: piecewise polynomials.

  • Piecewise Polynomials

First, piecewise polynomials fit different low-order polynomials in different ranges of X, rather than fitting constants like step functions. Since we are using lower-order polynomials, we will not observe large oscillations in the curve.

For example, piecewise quadratic polynomials work by fitting quadratic regression equations:

Step-by-Step Guide to Regression with Python

The coefficients β0, β1, and β2 take different values in different intervals of X.

A piecewise cubic polynomial, when there is a knot at point C, will have the following form:

Step-by-Step Guide to Regression with Python

In other words, we are fitting two different cubic polynomials to the data: one applied to the data satisfying Xi < C, and the other applied to the part where Xi > C.

The coefficients for the first polynomial function are: β01, β11, β21, β31; the coefficients for the second are β02, β12, β22, β32. Each of these polynomial functions can be fitted using the least squares method.

Note: This polynomial function has 8 degrees of freedom, with each polynomial having 4 (since there are 4 variables).

The more knots used, the more flexible the resulting piecewise polynomial becomes, as we use different functions for each interval of X, and these functions are only related to the distribution of the data in that interval. Generally, if we set K different knots in the range of X, we will ultimately fit K+1 different cubic polynomials. Moreover, we can actually use any low-order polynomial to fit a segment of data. For example, we can change to piecewise linear functions, and in fact, the step functions used above are 0-order piecewise polynomials.

Now let’s look at some necessary conditions and constraints that should be followed when constructing piecewise polynomials.

  • Constraints and Splines

When using piecewise polynomials, we need to be very careful because there are many restrictions. Take a look at the figure below:

Step-by-Step Guide to Regression with Python

We might encounter a situation where the polynomials on either side of the knot are discontinuous at the knot. This should be avoided because polynomials should generate a unique output for each input.

The above figure clearly shows that at the first knot, there are two different values. Therefore, to avoid this situation, a constraint must be in place: the polynomials on either side of the knot must also be continuous at the knot.

Step-by-Step Guide to Regression with Python

After adding this constraint, we obtain a set of continuous polynomials. But is that enough? The answer is clearly no. Before continuing to read on, readers can first consider this question and see if we have missed anything.

Observing the above figure, we can see that at the knots, the curve is still not smooth. To achieve a smooth curve at the knots, we add another constraint: the first derivative of the two polynomials must be the same. One point to note is that each time we add a constraint to the piecewise cubic polynomial, it is equivalent to reducing one degree of freedom. This is because we reduce the complexity of the fitting of the piecewise polynomial. Therefore, in the above problem, we only used 10 degrees of freedom instead of 12.

Step-by-Step Guide to Regression with Python

By adding the constraint regarding the first derivative, we obtained the figures shown above. Because of the newly added constraint, its degrees of freedom decreased from 12 to 8. But even now, the curve looks much better, but there is still room for improvement. Now we will add a new constraint: the second derivative of the two polynomials at the knots must be equal.

Step-by-Step Guide to Regression with Python

This time, the result looks much better. It further reduces the degrees of freedom to 6. Polynomials that have m-1 continuous derivatives at knots are called splines. So in the figure above, we have actually established a cubic spline.

  • Cubic Splines and Natural Cubic Splines

Cubic splines are piecewise polynomials with a set of additional constraints (continuity, first derivative continuity, second derivative continuity). Typically, a cubic spline with K knots has 4+K degrees of freedom. Higher-order splines are rarely used (unless smoothness is of great interest).

from patsy import dmatrix

import statsmodels.api as sm

import statsmodels.formula.api as smf

# Generate a cubic spline with three knots (25,40,60)

transformed_x = dmatrix(“bs(train, knots=(25,40,60), degree=3, include_intercept=False)”, {“train”: train_x}, return_type=’dataframe’)

# Fit a Generalized Linear Model on the dataset

fit1 = sm.GLM(train_y, transformed_x).fit()

# Generate a cubic spline curve with four knots

transformed_x2 = dmatrix(“bs(train, knots=(25,40,50,65),degree =3, include_intercept=False)”, {“train”: train_x}, return_type=’dataframe’)

# Fit a Generalized Linear Model on the dataset

fit2 = sm.GLM(train_y, transformed_x2).fit()

# Make predictions on both splines

pred1 = fit1.predict(dmatrix(“bs(valid, knots=(25,40,60), include_intercept=False)”, {“valid”: valid_x}, return_type=’dataframe’))

pred2 = fit2.predict(dmatrix(“bs(valid, knots=(25,40,50,65),degree =3, include_intercept=False)”, {“valid”: valid_x}, return_type=’dataframe’))

# Calculate RMSE values

valuesrms1 = sqrt(mean_squared_error(valid_y, pred1))

print(rms1)

-> 39.4

rms2 = sqrt(mean_squared_error(valid_y, pred2))

print(rms2)

-> 39.3

# We will use 70 points for plotting

xp = np.linspace(valid_x.min(),valid_x.max(),70)

# Make some predictions

pred1 = fit1.predict(dmatrix(“bs(xp, knots=(25,40,60), include_intercept=False)”, {“xp”: xp}, return_type=’dataframe’))

pred2 = fit2.predict(dmatrix(“bs(xp, knots=(25,40,50,65),degree =3, include_intercept=False)”, {“xp”: xp}, return_type=’dataframe’))

# Plot the spline curves and error graphs

plt.scatter(data.age, data.wage, facecolor=’None’, edgecolor=’k’, alpha=0.1)

plt.plot(xp, pred1, label=’Specifying degree =3 with 3 knots’)

plt.plot(xp, pred2, color=’r’, label=’Specifying degree =3 with 4 knots’)

plt.legend()

plt.xlim(15,85)

plt.ylim(0,350)

plt.xlabel(‘age’)

plt.ylabel(‘wage’)

plt.show()

Step-by-Step Guide to Regression with Python

As is well known, polynomial fitting data often performs poorly near the boundaries. This is very dangerous. Splines have similar issues. Those polynomials that fit data beyond the boundary knots yield results that are more unexpected than the corresponding global polynomials in that area. To extend the smoothness of these curves beyond the knots at the boundaries, we will use a special type of spline called natural splines.

Natural cubic splines have an additional constraint: they require that the function is linear beyond the boundaries. This condition reduces the cubic and quadratic parts to 0, decreasing the degrees of freedom by 2 each time, with a total reduction of 4 degrees of freedom for both endpoints, ultimately reducing k+4 to k.

# Generate natural cubic splines

transformed_x3 = dmatrix(“cr(train,df = 3)”, {“train”: train_x}, return_type=’dataframe’)

fit3 = sm.GLM(train_y, transformed_x3).fit()

# Make predictions on the validation set

pred3 = fit3.predict(dmatrix(“cr(valid, df=3)”, {“valid”: valid_x}, return_type=’dataframe’))

# Calculate RMSE values

rms = sqrt(mean_squared_error(valid_y, pred3))

print(rms)

-> 39.44

# Select 70 points for plotting

xp = np.linspace(valid_x.min(),valid_x.max(),70)

pred3 = fit3.predict(dmatrix(“cr(xp, df=3)”, {“xp”: xp}, return_type=’dataframe’))

# Plot the spline curve

plt.scatter(data.age, data.wage, facecolor=’None’, edgecolor=’k’, alpha=0.1)

plt.plot(xp, pred3,color=’g’, label=’Natural spline’)

plt.legend()

plt.xlim(15,85)

plt.ylim(0,350)

plt.xlabel(‘age’)

plt.ylabel(‘wage’)

plt.show()

Step-by-Step Guide to Regression with Python

  • How to Determine the Number and Position of Knots

When fitting a spline curve, how should we choose the knots? One feasible method is to select areas with rapid changes, because in these places, the coefficients of the polynomial will change quickly. Therefore, we can place more knots in areas where we believe the function value changes rapidly, and fewer in more stable areas.

However, while this method is somewhat effective, in practice, knots are often selected in a uniform manner. One method is to specify the desired degrees of freedom, and then the software automatically places the corresponding number of knots at the uniform quantiles of the data.

Alternatively, another choice is to change the number of knots and repeatedly test which scheme yields a better curve.

Of course, there is also a more objective approach—cross-validation. If we use this method, we need to do the following:

  • Take away a portion of the data

  • Select a certain number of knots to fit the remaining data

  • Use the spline to predict the previously removed portion of the data

Repeat this process until all data has been removed once. Then calculate the overall RMSE for the cross-validation. This process can be repeated for different numbers of knots, and in the end, we choose the K value that minimizes the RMSE.

  • Comparing Spline Regression and Polynomial Regression

Generally, spline regression tends to perform better than polynomial regression. This is because polynomial regression must use very high-order terms to fit a more flexible model to the data. However, spline regression achieves this by increasing the number of knots while keeping the order unchanged.

Moreover, spline regression tends to produce more stable models. It allows us to add more knots where the function changes rapidly, and conversely, fewer knots in areas where the function changes smoothly. Polynomial models, if they require more flexibility, will sacrifice stability at the boundaries, while cubic natural splines balance flexibility and stability well.

Step-by-Step Guide to Regression with Python

Conclusion

In this article, we learned about spline regression and some of its advantages compared to linear regression and polynomial regression. There is another method of generating splines called smoothing splines. This method is similar to Ridge/Lasso regularization, where the penalty combines the loss function and the smoothing function. You can read more about it in the book Introduction to Statistical Learning. Or if you are interested, you can try these methods on a dataset with many variables to experience the differences firsthand.

Translator’s Note

Summary of all packages needed for the experiments in this article:

Step-by-Step Guide to Regression with Python

Original Title: Introduction to Regression Splines (with Python codes)

Original Link:https://www.analyticsvidhya.com/blog/2018/03/introduction-regression-splines-python-codes/

Translator’s Profile

Step-by-Step Guide to Regression with Python

Zhang Yi, a junior at Communication University of China, majoring in Digital Media Technology. Curious about data science, amazed by the new world it creates. Currently exploring and learning, hoping to be brave and passionate, learning the most interesting knowledge and making like-minded friends.

Recruitment Information for the Translation Team

Job Content: Requires a meticulous heart to translate selected foreign articles into fluent Chinese. If you are an international student in data science/statistics/computer-related fields, or working overseas in related fields, or confident in your language skills, you are welcome to join the translation team.

What You Can Get: Regular translation training to improve volunteers’ translation skills, increase awareness of cutting-edge data science, overseas friends can keep in touch with the development of technical applications in China, and the background of THU Data Team provides good development opportunities for volunteers.

Other Benefits: Data scientists from well-known companies, students from prestigious schools such as Peking University and Tsinghua University, and overseas students will become your partners in the translation team.

Click the “Read the Original” at the end of the article to join the Data Team~

Reprint Notice

If you need to reprint, please indicate the author and source prominently at the beginning of the article (originally from: Data Team ID: datapi), and place a prominent QR code of Data Team at the end of the article. For articles with original identification, please send [Article Name – Public Account Name and ID to be Authorized] to the contact email to apply for whitelist authorization and edit as required.

After publication, please provide the link feedback to the contact email (see below). Unauthorized reprints and adaptations will be pursued legally.

Step-by-Step Guide to Regression with Python

Click “Read the Original” to embrace the organization.

Leave a Comment