/
README.Rmd
258 lines (166 loc) · 14.9 KB
/
README.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
---
output:
md_document:
variant: markdown_github
bibliography: README.bib
csl: inst/bib_style.csl
---
# **`pre`**: an R package for deriving prediction rule ensembles
## Contents
- [Introduction](#introduction)
- [Example: A rule ensemble for predicting ozone levels](#example-a-rule-ensemble-for-predicting-ozone-levels)
- [Tools for interpretation](#tools-for-interpretation)
- [Importance measures](#importance-measures)
- [Explaining individual predictions](#explaining-individual-predictions)
- [Partial dependence plots](#partial-dependence-plots)
- [Assessing presence of interactions](#assessing-presence-of-interactions)
- [Correlations between selected terms](#correlations-between-selected-terms)
- [Tuning parameters of function pre](#tuning-parameters)
- [Dealing with missing values](#dealing-with-missing-values)
- [Go sparser with relaxed lasso fits](#go-sparser-with-relaxed-lasso-fits)
- [Generalized Prediction Ensembles: Combining MARS, rules and linear terms](#generalized-prediction-ensembles-combining-mars-rules-and-linear-terms)
- [Credits](#credits)
- [References](#references)
```{r, echo = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "inst/README-figures/README-",
dpi = 124
)
```
## Introduction
**`pre`** is an **R** package for deriving prediction rule ensembles for binary, multinomial, (multivariate) continuous, count and survival responses. Input variables may be numeric, ordinal and categorical. An extensive description of the implementation and functionality is provided in [@Fokkema20]. The package largely implements the algorithm for deriving prediction rule ensembles as described in [@Friedman08], with several adjustments:
1. The package is completely **`R`** based, allowing users better access to the results and more control over the parameters used for generating the prediction rule ensemble.
2. The unbiased tree induction algorithms of [@Hothorn06] is used for deriving prediction rules, by default. Alternatively, the (g)lmtree algorithm of [@Zeileis08] can be employed, or the classification and regression tree (CART) algorithm of [@Breiman84].
3. The package supports a wider range of response variable types.
4. The initial ensemble may be generated as a bagged, boosted and/or random forest ensemble.
5. Hinge functions of predictor variables may be included as baselearners, as in the multivariate adaptive regression splines (MARS) approach of [@Friedman91], using function `gpe()`.
6. Tools for explaining individual predictions are provided.
Note that **`pre`** is under development, and much work still needs to be done. Below, an introduction the the package is provided. [@Fokkema20] provides an extensive description of the fitting procedures implemented in function `pre()` and example analyses with more extensive explanations. An extensive introduction aimed at researchers in social sciences is provided in [@FokkemaStrobl20].
## Example: A rule ensemble for predicting ozone levels
To get a first impression of how function `pre()` works, we will predict Ozone levels using the `airquality` dataset. We fit a prediction rule ensemble using function `pre()`:
```{r, results = FALSE}
library("pre")
airq <- airquality[complete.cases(airquality), ]
set.seed(42)
airq.ens <- pre(Ozone ~ ., data = airq)
```
Note that it is necessary to set the random seed, to allow for later replication of results, because the fitting procedure depends on random sampling of training observations.
We can print the resulting ensemble (alternatively, we could use the `print` method):
```{r}
airq.ens
```
The first few lines of the printed results provide the penalty parameter value ($\lambda$) employed for selecting the final ensemble. By default, the '1-SE' rule is used for selecting $\lambda$; this default can be overridden by employing the `penalty.par.val` argument of the `print` method and other functions in the package. Note that the printed cross-validated error is calculated using the same data as was used for generating the rules and likely provides an overly optimistic estimate of future prediction error. To obtain a more realistic prediction error estimate, we will use function ```cvpre()``` later on.
Next, the printed results provide the rules and linear terms selected in the final ensemble, with their estimated coefficients. For rules, the `description` column provides the conditions. For linear terms (which were not selected in the current ensemble), the winsorizing points used to reduce the influence of outliers on the estimated coefficient would be printed in the `description` column. The `coefficient` column presents the estimated coefficient. These are regression coefficients, reflecting the expected increase in the response for a unit increase in the predictor, keeping all other predictors constant. For rules, the coefficient thus reflects the difference in the expected value of the response when the conditions of the rule are met, compared to when they are not.
Using the `plot` method, we can plot the rules in the ensemble as simple decision trees. Here, we will request the nine most important baselearners through specification of the `nterms` argument. Through the `cex` argument, we specify the size of the node and path labels:
```{r treeplot, fig.show = "hide"}
plot(airq.ens, nterms = 9, cex = .5)
```
```{r, echo=FALSE, warning=FALSE, message=FALSE, out.width = "600px"}
library("knitr")
include_graphics(sprintf("%streeplot-1.png", opts_current$get("fig.path")))
```
Using the `coef` method, we can obtain the estimated coefficients for each of the baselearners (we only print the first six terms here for space considerations):
```{r}
coefs <- coef(airq.ens)
coefs[1:6, ]
```
We can generate predictions for new observations using the `predict` method (only the first six predicted values are printed here for space considerations):
```{r}
predict(airq.ens, newdata = airq[1:6, ])
```
Using function `cvpre()`, we can assess the expected prediction error of the fitted PRE through $k$-fold cross validation ($k = 10$, by default, which can be overridden through specification of the `k` argument):
```{r}
set.seed(43)
airq.cv <- cvpre(airq.ens)
```
The results provide the mean squared error (MSE) and mean absolute error (MAE) with their respective standard errors. These results are saved for later use in `aiq.cv$accuracy`. The cross-validated predictions, which can be used to compute alternative estimates of predictive accuracy, are saved in `airq.cv$cvpreds`. The folds to which observations were assigned are saved in `airq.cv$fold_indicators`.
For tuning the parameters of function `pre()` so as to obtain optimal predictive accuracy, users are advised to use **`R`** package **`caret`**. A tutorial is provided as a vignette, accessible by typing `vignette("tuning", package = "pre")` in **`R`** or by going to https://cran.r-project.org/package=pre/vignettes/missingness.html in a browser and clicking on the corresponding link to the vignette.
## Tools for interpretation
Package **`pre`** provides several additional tools for interpretation of the final ensemble. These may be especially helpful for complex ensembles containing many rules and linear terms.
### Importance measures
We can assess the relative importance of input variables as well as baselearners using the `importance()` function:
```{r importance, fig.show = "hide"}
imps <- importance(airq.ens, round = 4)
```
```{r, echo=FALSE, warning=FALSE, message=FALSE, out.width = "400px"}
include_graphics(sprintf("%simportance-1.png", opts_current$get("fig.path")))
```
As we already observed in the printed ensemble, the plotted variable importances indicate that Temperature and Wind are most strongly associated with Ozone levels. Solar.R and Day are also associated with Ozone levels, but much less strongly. Variable Month is not plotted, which means it obtained an importance of zero, indicating that it is not associated with Ozone levels. We already observed this in the printed ensemble: Month did not appear in any of the selected terms. The variable and baselearner importances are saved for later use in `imps$varimps` and `imps$baseimps`, respectively.
### Explaining individual predictions
We can obtain explanations of the predictions for individual observations using function `explain()`:
```{r explain, fig.show = "hide"}
par(mfrow = c(1, 2))
expl <- explain(airq.ens, newdata = airq[1:2, ], cex = .8)
```
```{r, echo=FALSE, warning=FALSE, message=FALSE, out.width = "600px"}
include_graphics(sprintf("%sexplain-1.png", opts_current$get("fig.path")))
```
The values of the rules and linear terms for each observation are saved in `expl$predictors`, their contributions in `expl$contribution` and the predicted values in `expl$predicted.value`.
### Partial dependence plots
We can obtain partial dependence plots to assess the effect of single predictor variables on the outcome using the `singleplot()` function:
```{r singleplot, fig.show = "hide"}
singleplot(airq.ens, varname = "Temp")
```
```{r, echo=FALSE, warning=FALSE, message=FALSE, out.width = "400px"}
include_graphics(sprintf("%ssingleplot-1.png", opts_current$get("fig.path")))
```
We can obtain partial dependence plots to assess the effects of pairs of predictor variables on the outcome using the `pairplot()` function:
```{r pairplot, fig.show = "hide", warning=FALSE, message=FALSE}
pairplot(airq.ens, varnames = c("Temp", "Wind"))
```
```{r, echo=FALSE, warning=FALSE, message=FALSE, out.width = "400px"}
include_graphics(sprintf("%spairplot-1.png", opts_current$get("fig.path")))
```
Note that creating partial dependence plots is computationally intensive and computation time will increase fast with increasing numbers of observations and numbers of variables. Milborrow's [-@Milb18] **`plotmo`** package provides more efficient functions for plotting partial dependence, which also support `pre` models.
If the final ensemble does not contain many terms, inspecting individual rules and linear terms through the `print` method may be more informative than partial dependence plots. One of the main advantages of prediction rule ensembles is their interpretability: the predictive model contains only simple functions of the predictor variables (rules and linear terms), which are easy to grasp. Partial dependence plots are often much more useful for interpretation of complex models, like random forests for example.
### Assessing presence of interactions
We can assess the presence of interactions between the input variables using the `interact()` and `bsnullinteract()` funtions. Function `bsnullinteract()` computes null-interaction models (10, by default) based on bootstrap-sampled and permuted datasets. Function `interact()` computes interaction test statistics for each predictor variables appearing in the specified ensemble. If null-interaction models are provided through the `nullmods` argument, interaction test statistics will also be computed for the null-interaction model, providing a reference null distribution.
Note that computing null interaction models and interaction test statistics is computationally very intensive, so running the following code will take some time:
```{r interact, fig.show = "hide"}
set.seed(44)
nullmods <- bsnullinteract(airq.ens)
int <- interact(airq.ens, nullmods = nullmods)
```
```{r, echo=FALSE, warning=FALSE, message=FALSE, out.width = "350px"}
include_graphics(sprintf("%sinteract-1.png", opts_current$get("fig.path")))
```
The plotted variable interaction strengths indicate that Temperature and Wind may be involved in interactions, as their observed interaction strengths (darker grey) exceed the upper limit of the 90% confidence interval (CI) of interaction stengths in the null interaction models (lighter grey bar represents the median, error bars represent the 90% CIs). The plot indicates that Solar.R and Day are not involved in any interactions. Note that computation of null interaction models is computationally intensive. A more reliable result can be obtained by computing a larger number of boostrapped null interaction datasets, by setting the `nsamp` argument of function `bsnullinteract()` to a larger value (e.g., 100).
### Correlations between selected terms
We can assess correlations between the baselearners appearing in the ensemble using the `corplot()` function:
```{r corplot, fig.show='hide'}
corplot(airq.ens)
```
```{r, echo=FALSE, warning=FALSE, message=FALSE, out.width = "500px"}
include_graphics(sprintf("%scorplot-1.png", opts_current$get("fig.path")))
```
## Tuning parameters
To obtain an optimal set of model-fitting parameters, package **`caret`** @Kuhn08 provides a method `"pre"`. For a manual on how to optimize the parameters of function `pre` using **`caret`**'s `train` function, see the vignette on tuning:
```{r, eval=FALSE}
vignette("tuning", package = "pre")
```
or go to https://cran.r-project.org/package=pre/vignettes/tuning.html in a browser.
## Dealing with missing values
Some suggestions on how to deal with missing values are provided in the following vignette:
```{r, eval=FALSE}
vignette("missingness", package = "pre")
```
or go to https://cran.r-project.org/package=pre/vignettes/missingness.html in a browser.
## Go sparser with relaxed lasso fits
When sparsity (i.e., a final ensemble comprising only few terms) is of central importance, the so-called relaxed lasso can be employed. It allows for retaining a pre-specified (low) number of terms, with adequate predictive accuracy. An introduction and tutorial is provided in the following vignette:
```{r, eval=FALSE}
vignette("relaxed", package = "pre")
```
## Generalized Prediction Ensembles: Combining MARS, rules and linear terms
An even more flexible ensembling approach is implemented in function `gpe()`, which allows for fitting Generalized Prediction Ensembles: It combines the MARS (multivariate Adaptive Splines) approach of [@Friedman91] with the RuleFit approach of [@Friedman08]. In other words, `gpe()` fits an ensemble composed of hinge functions (possibly multivariate), prediction rules and linear functions of the predictor variables. See the following example:
```{r}
set.seed(42)
airq.gpe <- gpe(Ozone ~ ., data = airquality[complete.cases(airquality),],
base_learners = list(gpe_trees(), gpe_linear(), gpe_earth()))
airq.gpe
```
The results indicate that several rules, a single hinge (linear spline) function, and no linear terms were selected for the final ensemble. The hinge function and its coefficient indicate that Ozone levels increase with increasing solar radiation and decreasing wind speeds. The prediction rules in the ensemble indicate a similar pattern.
## Credits
I am very grateful to package co-author Benjamin Chistoffersen: https://github.com/boennecd, who developed `gpe` and contributed tremendously by improving functions, code and computational aspects. Furthermore, I am grateful for the many helpful suggestions of Stephen Milborrow, and for the contributions of Karl Holub (https://github.com/holub008) and Advik Shreekumar (https://github.com/adviksh).
## References