Predictors and Transformations

When we create regression models, we need to check the validity of the model using diagnostic plots, since some models may make assumptions about the underlying data such as linearity. But, what do we do if the diagnostic plot shows our model does not meet such assumptions?

If non-linearity is an issue, we can transform predictor variables to improve the compatibility of our data with the assumptions.

Creating Nonlinear Predictors

The lm() function can accomodate for non-linear transformations of the predictors. For instance, given a predictor X, we can create a predictor X^2 using the function I(X^2).

The function I() is needed since the ^ symbol has a special meaning in a formula object; wrapping as we do allows the standard usage in R, which is to raise X to the power of 2.

We’ll now perform a regression of medv onto lstat and lstat^2:

lm.fit_quadratic <- lm(medv ~ lstat + I(lstat^2), data = Boston)
summary(lm.fit_quadratic)

Call:
lm(formula = medv ~ lstat + I(lstat^2), data = Boston)

Residuals:
     Min       1Q   Median       3Q      Max 
-15.2834  -3.8313  -0.5295   2.3095  25.4148 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 42.862007   0.872084   49.15   <2e-16 ***
lstat       -2.332821   0.123803  -18.84   <2e-16 ***
I(lstat^2)   0.043547   0.003745   11.63   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 5.524 on 503 degrees of freedom
Multiple R-squared:  0.6407,    Adjusted R-squared:  0.6393 
F-statistic: 448.5 on 2 and 503 DF,  p-value: < 2.2e-16

The near zero p-value associated with lstat^2 suggests that the transformation leads to an improved.

Evaluating the non-linear model fit

To further evaluate the extent to which the quadratic fit (lstat^2) is superior to the linear fit (lstat), we’ll use the anova() function.

lm.fit <- lm(medv ~ lstat, data = Boston)
anova(lm.fit, lm.fit_quadratic)
Res.Df RSS Df Sum of Sq F Pr(>F)
504 19472.38 NA NA NA NA
503 15347.24 1 4125.138 135.1998 0

Here, model 1 (first row) represents the linear submodel containing only one predictor, lstat, while model 2 (second row) corresponds to the larger quadratic model that has two predictors, lstat and lstat^2.

The anova() function performs a hypothesis test comparing the two models. The null hypothesis predicts that the two models fit the data equally well, while the alternative hypothesis suggests that model 2 is superior.

H_0: RSS_{M1} = RSS_{M2}

H_a: RSS_{M1} < RSS_{M2}

In the ANOVA output above, the F-statistic is 135 and the associated p-value is virtually zero. This provides very clear evidence that the model containing the predictors lstat and lstat^2 is far superior to the model that only contains the predictor lstat.

This is not surprising, since earlier we saw evidence for non-linearity n the relationship between medv and lstat.

If we examine the diagnostic plots for the model with lstat^2, we will see little discernable pattern in the residuals:

par(mfrow = c(2,2))
plot(lm.fit_quadratic)

Model = lm(medv ~ lstat + I(lstat^2))

Model = lm(medv ~ lstat + I(lstat^2))

For reference, this is the Residuals vs Fitted Values plot of the original model, where lstat is not squared:

Model = lm(medv ~ lstat), for Residuals vs Fitted Values plot comparison.

Model = lm(medv ~ lstat), for Residuals vs Fitted Values plot comparison.

We see that in the model with the transformed predictor, the distribution of residuals appears more evenly-spread with less of a discernable pattern, compared to the original. This is an improvement because it indicates there is less non-linearity in our new model!

More Polynomial Predictors

In order to create a cubic fit, we can include a predictor of the form I(X^3). However, this approach can start to get cumbersome for higher-order polynomials. A better approach involves using the poly() function to create the polynomial within lm(). For example, the following command produces a fifth-order polynomial fit:

lm.fit5 <- lm(medv ~ poly(lstat, 5),data=Boston)
summary(lm.fit5)

Call:
lm(formula = medv ~ poly(lstat, 5), data = Boston)

Residuals:
     Min       1Q   Median       3Q      Max 
-13.5433  -3.1039  -0.7052   2.0844  27.1153 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)       22.5328     0.2318  97.197  < 2e-16 ***
poly(lstat, 5)1 -152.4595     5.2148 -29.236  < 2e-16 ***
poly(lstat, 5)2   64.2272     5.2148  12.316  < 2e-16 ***
poly(lstat, 5)3  -27.0511     5.2148  -5.187 3.10e-07 ***
poly(lstat, 5)4   25.4517     5.2148   4.881 1.42e-06 ***
poly(lstat, 5)5  -19.2524     5.2148  -3.692 0.000247 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 5.215 on 500 degrees of freedom
Multiple R-squared:  0.6817,    Adjusted R-squared:  0.6785 
F-statistic: 214.2 on 5 and 500 DF,  p-value: < 2.2e-16

This output suggests that including additional polynomial terms, up to fifth order, leads to an improvement in the model fit!

However, further investigation of the data reveals that no polynomial terms beyond fifth order have significant p-values in a regression fit.

By default, the poly() function orthogonalizes the predictors: this means that the features output by this function are not simply a sequence of powers of the argument. However, a linear model applied to the output of the poly() function will have the same fitted values as a linear model applied to the raw polynomials (although the coefficient estimates, standard errors, and p-values will differ). In order to obtain the raw polynomials from the poly() function, the argument raw = TRUE must be used.

Log Predictors

Of course, we are in no way restricted to using polynomial transformations of the predictors. Here we try a log transformation:

summary(lm(medv ~ log(rm), data = Boston))

Call:
lm(formula = medv ~ log(rm), data = Boston)

Residuals:
    Min      1Q  Median      3Q     Max 
-19.487  -2.875  -0.104   2.837  39.816 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -76.488      5.028  -15.21   <2e-16 ***
log(rm)       54.055      2.739   19.73   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 6.915 on 504 degrees of freedom
Multiple R-squared:  0.4358,    Adjusted R-squared:  0.4347 
F-statistic: 389.3 on 1 and 504 DF,  p-value: < 2.2e-16

Additional transformations for predictors include, but are not limited to:

  • The reciprocal: 1/X
  • Square root: \sqrt{X}

Qualitative Predictors

So far, we’ve looked at numerical data in our analysis, but what about categorical data? In the Carseats dataset, which is part of the ISLR2 library, we will attempt to predict Sales (child carseat sales) based on a number of predictors.

head(Carseats)
Sales CompPrice Income Advertising Population Price ShelveLoc Age Education Urban US
9.50 138 73 11 276 120 Bad 42 17 Yes Yes
11.22 111 48 16 260 83 Good 65 10 Yes Yes
10.06 113 35 10 269 80 Medium 59 12 Yes Yes
7.40 117 100 4 466 97 Medium 55 14 Yes Yes
4.15 141 64 3 340 128 Bad 38 13 Yes No
10.81 124 113 13 501 72 Bad 78 16 No Yes

The Carseats dataset includes qualitative predictors such as ShelveLoc, an indicator of the quality of the shelving location. The predictor ShelveLoc is a categorical factor that takes on three possible values: Good, Medium, and Bad.

Dummy variables and contrasts()

When given a qualitative variable such as ShelveLoc, R generates dummy variables automatically. Below we fit a multiple regression model that includes some interaction terms.

lm.fit <- lm(Sales ~ . + Income:Advertising + Price:Age, data = Carseats)
summary(lm.fit)

Call:
lm(formula = Sales ~ . + Income:Advertising + Price:Age, data = Carseats)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.9208 -0.7503  0.0177  0.6754  3.3413 

Coefficients:
                     Estimate Std. Error t value Pr(>|t|)    
(Intercept)         6.5755654  1.0087470   6.519 2.22e-10 ***
CompPrice           0.0929371  0.0041183  22.567  < 2e-16 ***
Income              0.0108940  0.0026044   4.183 3.57e-05 ***
Advertising         0.0702462  0.0226091   3.107 0.002030 ** 
Population          0.0001592  0.0003679   0.433 0.665330    
Price              -0.1008064  0.0074399 -13.549  < 2e-16 ***
ShelveLocGood       4.8486762  0.1528378  31.724  < 2e-16 ***
ShelveLocMedium     1.9532620  0.1257682  15.531  < 2e-16 ***
Age                -0.0579466  0.0159506  -3.633 0.000318 ***
Education          -0.0208525  0.0196131  -1.063 0.288361    
UrbanYes            0.1401597  0.1124019   1.247 0.213171    
USYes              -0.1575571  0.1489234  -1.058 0.290729    
Income:Advertising  0.0007510  0.0002784   2.698 0.007290 ** 
Price:Age           0.0001068  0.0001333   0.801 0.423812    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 1.011 on 386 degrees of freedom
Multiple R-squared:  0.8761,    Adjusted R-squared:  0.8719 
F-statistic:   210 on 13 and 386 DF,  p-value: < 2.2e-16

The contrasts() function returns the coding that R uses for the dummy variables.

Use the R command ?contrasts to learn about other contrasts, and how to set them.

attach(Carseats)
contrasts(ShelveLoc)
       Good Medium
Bad       0      0
Good      1      0
Medium    0      1

As seen above:

  • R has created a ShelveLocGood dummy variable that takes on a value of 1 if the shelving location is good and 0 otherwise.
  • It has also created a ShelveLocMedium dummy variable that equals 1 if the shelving location is medium, and 0 otherwise.
  • A bad shelving location corresponds to a zero for each of the dummy variables.

The fact that the coefficient for ShelveLocGood in the regression output is positive indicates that a good shelving location is associated with high sales (relative to a bad location). ShelveLocMedium also has a positive coefficient, albeit smaller, indicating that a medium shelving location is also associated with higher sales than a bad shelving location, but lower than a good shelving location.