Tutorials › Exam PA - Predictive Analytics › Fitting Gradient-Boosted Trees With xgboost In R

Exam PA · Gradient Boosting and Tuning · Tutorial 571 of 966

Fitting Gradient-Boosted Trees With xgboost In R

Prepare xgboost data correctly in R and connect tree depth, learning rate, and the number of rounds to the fitted model.

Advanced 12 min read

What You'll Learn

  • Build an xgboost DMatrix with predictors and correctly aligned labels
  • Select regression or binary classification objectives in R
  • Set and interpret max_depth, eta, and nrounds
  • Distinguish missing values from valid numeric zeros
  • Check dimensions, label values, and row order before fitting

Why xgboost Requires Careful Setup

The xgboost R interface makes it possible to fit gradient-boosted trees with a compact set of commands. The mechanics still matter: predictor rows must match the response labels, the objective must suit the outcome, and the main controls must be chosen deliberately. A model can run without an error and still answer the wrong question if, for example, its labels are out of order or a classification response is coded incorrectly.

This tutorial focuses on preparing an xgboost training matrix and fitting regression and binary classification objectives in R. The examples use numeric predictors. Correct treatment of categorical predictors depends on how they are represented and is a separate issue; do not assume that a factor can be supplied as an ordinary numeric feature.

Definition: An xgboost training matrix contains the predictor values for a set of observations, together with response labels in the same row order. In R, an xgb.DMatrix is the package’s data structure for supplying this information to training. The objective specifies the loss and response type being modelled; max_depth, eta, and nrounds control tree complexity, shrinkage, and the number of boosting iterations.

Preparing the Training Matrix

For numeric predictors, begin with a matrix whose rows are observations and whose columns are features. Keep a separate response vector, with one label for each row. The key alignment requirement is that label \(y_j\) describes predictor row \(j\). The R call `xgb.DMatrix(data = X, label = y)` stores the predictors and labels together for model fitting.

A basic preparation sequence is: create the numeric predictor matrix `X`; check its number of rows and columns; create the response vector `y`; verify that `length(y) == nrow(X)`; and build `dtrain <- xgb.DMatrix(data = X, label = y)`. The matrix may contain `NA` values to represent missing predictor values. A numeric zero is a value, not a missing-value marker, unless the analyst has deliberately specified another convention.

For a standard regression problem, the response is numeric and the objective is commonly `reg:squarederror`. For binary classification, the response must be coded as 0 or 1 and the objective is commonly `binary:logistic`. The response coding and objective should be checked together before fitting. A binary label of 2, for example, is not a valid substitute for the event label 1 in this setup.

Worked Example: Fitting a Small Regression Model

A hypothetical insurer wants to model a continuous claim-cost measure using two numeric predictors: policy duration in years and a building score. The four observations are \((1, 20)\), \((2, 35)\), \((3, 45)\), and \((4, 60)\), and the recorded costs, in thousands of dollars, are 8, 11, 15, and 18. Prepare a training matrix and fit a regression model using depth 2, learning rate 0.10, and 40 rounds.

Step 1: Write the predictors by row. There are four observations and two features, so `X <- matrix(c(1, 20, 2, 35, 3, 45, 4, 60), ncol = 2, byrow = TRUE)` has four rows. Its first row is \((1,20)\), its second row is \((2,35)\), and so on. The response vector is `y <- c(8, 11, 15, 18)`.

Step 2: Check alignment and dimensions. Here `nrow(X) = 4`, `ncol(X) = 2`, and `length(y) = 4`. Each response is in the same position as its observation’s predictor row. Therefore `dtrain <- xgb.DMatrix(data = X, label = y)` creates a training matrix with four labelled rows.

Step 3: Fit the stated model. Use `fit <- xgb.train(params = list(objective = "reg:squarederror", max_depth = 2, eta = 0.10), data = dtrain, nrounds = 40)`. This requests squared-error regression, trees of maximum depth 2, shrinkage factor 0.10, and 40 boosting iterations.

Check and common mistake. The row count and label count both equal four, the response is numeric, and the objective is for regression. A depth-2 binary tree can have at most \(2^2=4\) terminal leaves when fully split. A common mistake is to put all predictor values into a one-column matrix by omitting `ncol = 2`; the dimension check would then show one feature rather than two. This solution avoids that mistake by explicitly constructing and checking the two-column matrix. No fitted costs are asserted here: actual predictions depend on the fitted data and model.

Choosing Objective and Core Controls

The objective should reflect the type of outcome. For a continuous response, `reg:squarederror` is a standard squared-error objective. For a binary response coded 0/1, `binary:logistic` is a standard logistic objective. These choices do not by themselves decide how a business will use predictions: a classification decision threshold is a separate choice.

The three controls emphasized here have distinct jobs. A larger maximum depth permits more splits along a path and can represent more complex interactions. A smaller learning rate, called `eta` in xgboost, shrinks each new tree’s contribution. The number of rounds, `nrounds`, is how many trees are added. They should be understood together rather than treated as interchangeable settings.

ControlMeaningPractical interpretation
max_depthMaximum depth of an individual treeGreater depth allows more complex within-tree splits; depth 1 gives a stump-like tree
etaLearning-rate shrinkage applied to each treeSmaller values make each update more conservative
nroundsNumber of boosting iterationsEach round adds another tree to the ensemble
objectiveLoss and response formulationChoose a regression or classification objective appropriate to the labels

For example, a depth of 1 restricts each tree to a single split, while a depth of 3 permits a path with up to three splits. In a fully developed binary tree of depth \(d\), the maximum number of leaves is \(2^d\); actual trees can have fewer. A smaller `eta` usually calls for considering more rounds to build the ensemble gradually, but neither a large nor small number of rounds is automatically appropriate. The chosen count should be supported by a validation procedure rather than by training performance alone.

Key relation: In an additive boosting model, each round contributes a tree prediction multiplied by the learning rate. If \(F_0(x)\) is the initial model and \(h_m(x)\) is the tree added at round \(m\), the model after \(M\) rounds has the form $$ F_M(x) = F_0(x) + \eta \sum_{m=1}^{M} h_m(x). $$

This expression explains the roles of `eta` and `nrounds`: changing `eta` changes the size of each contribution, while changing \(M\) changes how many contributions are accumulated. It does not mean that the total effect is simply \(M\eta\), because the trees \(h_m\) are fitted sequentially and their predictions vary by observation.

Why Shrinkage Scales Each Tree

The additive expression follows directly from the update rule. Suppose the current model is \(F_{m-1}(x)\), and the next fitted tree proposes an update \(h_m(x)\). With learning rate \(\eta\), the update is scaled before it is added:

$$ F_m(x) = F_{m-1}(x) + \eta h_m(x). $$

Expanding the first few updates gives \(F_1(x)=F_0(x)+\eta h_1(x)\), then \(F_2(x)=F_0(x)+\eta h_1(x)+\eta h_2(x)\). Repeating this substitution through round \(M\) yields \(F_M(x)=F_0(x)+\eta\sum_{m=1}^{M}h_m(x)\). Thus shrinkage applies to every tree’s update, not just the final tree. The derivation describes the additive structure; it does not claim that each fitted tree is the same or that increasing rounds always improves performance.

Worked Example: Fitting Binary Classification

A hypothetical roadside-assistance program records whether each of five service requests leads to a repeat call within seven days. The numeric features are request age in days and distance to the service center in kilometres. The rows are \((2,5)\), \((4,8)\), \((6,12)\), \((8,15)\), and \((10,22)\); the repeat-call labels in that order are \(0, 0, 1, 0, 1\). Fit a binary model with depth 1, `eta = 0.05`, and 80 rounds.

Step 1: Create the matrix and labels. Use `X <- matrix(c(2, 5, 4, 8, 6, 12, 8, 15, 10, 22), ncol = 2, byrow = TRUE)` and `y <- c(0, 0, 1, 0, 1)`. The matrix dimensions are \(5\) by \(2\), and the label vector has length \(5\).

Step 2: Check the binary coding. The labels are all either 0 or 1. There are two positive outcomes and three negative outcomes, so the observed positive fraction is \(2/5=0.4\). This is a check of the supplied labels, not a claim about fitted probabilities.

Step 3: Build and train. Create `dtrain <- xgb.DMatrix(data = X, label = y)`, then fit `fit <- xgb.train(params = list(objective = "binary:logistic", max_depth = 1, eta = 0.05, eval_metric = "logloss"), data = dtrain, nrounds = 80)`. The objective is binary logistic, the metric is log loss, and the requested controls are depth 1, learning rate 0.05, and 80 rounds.

Check and common mistake. There are five rows, five labels, and exactly two labels equal to 1. A common error is to treat the value 0 as missing and set it as the missing-value marker. That would discard valid zero-valued feature information. Here no special missing marker is needed, and zero-valued predictors, if present, should remain valid numeric values. This method avoids the error by using the default missing-value representation and checking the labels separately.

Missing Values, Row Order, and Fit Checks

A reliable fit starts with simple checks that catch errors before training. Confirm that every predictor column is numeric for the setup shown here, that the response has one entry per row, and that the row order has not changed between forming \(X\) and \(y\). If a data frame is sorted or filtered after making the response vector, the two can silently become misaligned.

When predictor values are missing, use `NA` to mark them in an ordinary R matrix and pass the matrix to `xgb.DMatrix`. Do not replace missing entries with zero unless zero is genuinely the intended value or a separate, justified preprocessing choice. A missing label is different: the response is required for a supervised training row, so resolve or exclude rows with unknown outcomes according to the analysis plan.

Also inspect the objective and parameter values before fitting. `max_depth` should be a positive integer for the finite tree depths used here; `eta` should be positive, with smaller values representing more conservative updates; and `nrounds` should be a positive integer. These controls describe the model specification, but do not establish that it generalizes. Model selection and validation are needed to assess performance.

Worked Example: Preserving Missingness and Row Alignment

A hypothetical property-maintenance dataset has three observations. The two numeric predictors are building age and a condition score, and the numeric response is a repair-cost index. In the stated row order, the predictors are \((5, 80)\), \((NA, 65)\), and \((12, 50)\), and the responses are \(3.2, 4.1, 5.5\). Fit a regression model with depth 2, `eta = 0.10`, and 25 rounds.

Step 1: Represent the data without changing its meaning. Use `X <- matrix(c(5, 80, NA, 65, 12, 50), ncol = 2, byrow = TRUE)` and `y <- c(3.2, 4.1, 5.5)`. The matrix has three rows and two columns. The missing building age remains `NA`; the condition score of 65 remains observed.

Step 2: Verify correspondence. The first response, 3.2, belongs to row \((5,80)\); 4.1 belongs to \((NA,65)\); and 5.5 belongs to \((12,50)\). There are three rows and three response values. Make `dtrain <- xgb.DMatrix(data = X, label = y)`.

Step 3: Fit the regression objective. Run `fit <- xgb.train(params = list(objective = "reg:squarederror", max_depth = 2, eta = 0.10), data = dtrain, nrounds = 25)`. The response is continuous, so squared-error regression is consistent with the stated task.

Check and common mistake. The dimensions are \(3\) by \(2\), the label length is 3, and the one unknown predictor is indicated by `NA`. A tempting but incorrect calculation is to replace the missing age with 0 and treat it as an actual age of zero. That changes the data: \(0\) means an observed value, whereas `NA` means unavailable. The correction is to retain `NA` as shown and ensure the response remains in its original row order.

Common Implementation Traps

  • Reversing rows and columns. R matrices are filled by column by default. When values are listed row by row, use `byrow = TRUE` and inspect the dimensions and first few rows.
  • Using labels that do not match the objective. Regression needs a numeric outcome; `binary:logistic` needs binary labels coded 0/1.
  • Confusing tree depth with number of rounds. Depth controls complexity within each tree. Rounds control how many trees are added.
  • Assuming more rounds or greater depth must help. These settings can increase model flexibility, but they do not guarantee better performance on new observations.
  • Treating zero as missing. A valid zero is not the same as `NA` and should not be erased by a missing-value setting.

The same care applies when adapting a model to actuarial work: document the response definition, units, row ordering, objective, and chosen controls. This implementation guidance is aligned with the SOA Exam PA (Predictive Analytics) syllabus.

Check Your Understanding

For each problem, identify the appropriate setup and check the key dimensions or control interpretation.

  1. A regression dataset has 12 rows, three numeric predictors, and a numeric response. What should the dimensions of \(X\) be, and how many labels should be supplied? (Answer: \(12\) by \(3\), with 12 labels.)
  2. A binary outcome vector is \(0,1,0,1,1,0\). How many positive labels are present, and which objective from this tutorial is appropriate? (Answer: 3 positive labels; `binary:logistic`.)
  3. A model uses `max_depth = 3`, `eta = 0.05`, and `nrounds = 60`. What does each control specify, and what is the maximum leaf count for a fully split depth-3 binary tree? (Answer: maximum tree depth, per-tree shrinkage, and number of boosting iterations; at most \(2^3=8\) leaves.)
  4. A two-column predictor matrix has five rows, but the response vector has four values. Should training proceed as written? (Answer: No. The response length must match the five predictor rows; correct the missing or mismatched observation and preserve row alignment.)
  5. A feature contains the observed value 0 for one row and `NA` for another. Which value should mark the missing observation in the matrix? (Answer: Use `NA` for the missing value and retain 0 as an observed numeric value.)
Clinical Biostats