glmtree: logistic regression trees

glmtree builds decision trees with a logistic regression at each leaf: the tree cuts the population into segments, and each segment gets its own, interpretable, scoring function. The segmentation is searched jointly with the regressions by a stochastic EM algorithm, as described in Formalization and study of statistical problems in Credit Scoring, Ehrhardt A. (see the manuscript or the web article).

Status. The package was archived on CRAN on 2025-10-03 (a vignette needed a suggested package that was missing on one check flavor) and a re-submission is in preparation. Meanwhile, install it from GitHub.

Installation

# install.packages("pak")
# pak::pak("adimajo/glmtree")
install.packages("glmtree", repos = c('https://adimajo.r-universe.dev', 'https://cloud.r-project.org'))

Example

library(glmtree)

set.seed(1)
data <- generateData(n = 2000, scenario = "tree")   # 3 segments, 3 different regressions
features <- c("x1", "x2", "x3")

fit <- glmtree(data[, features], data$y, K = 5, iterations = 100,
               ctree_controls = partykit::ctree_control(alpha = 0.1, minbucket = 100, maxdepth = 4))
fit                                                  # number of segments, criterion, the tree
predict(fit, data[1:5, features])                    # probabilities
predict(fit, data[1:5, features], type = "segment")  # segments
fit@best.tree$glms                                   # the logistic regression of each segment

partykit also exports a function called glmtree() (model-based recursive partitioning). If you attach both packages, call ours as glmtree::glmtree(). See vignette("glmtree") for the interface (validation, test, criterion, burn_in, patience, ...).

What to expect: measured results

The numbers below come from inst/benchmarks/real_data.R (20 random stratified 70/30 splits per dataset, 100 SEM iterations, K = 5; the full table and the raw results are in inst/benchmarks/results/). They are test-set Gini coefficients (2 AUC - 1, higher is better), means over the splits. Tree-based methods use leaves of at least 5% of the training data and depth 4. glmtree selects the tree with the BIC on the training data, glmtree (valid.) uses 25% of the training data as a validation set and the Gini. MOB is partykit::glmtree(), the model-based recursive partitioning, with all features both as regressors and as partitioning variables.

Dataset logit ctree MOB random forest glmtree glmtree (valid.)
Simulated, 3 segments (n = 3000) 0.248 0.568 0.698 0.657 0.650 0.644
Simulated, 1 regression (n = 3000) 0.672 0.619 0.672 0.606 0.672 0.670
German credit (1000 × 20) 0.552 0.439 0.436 0.579 0.514 0.393
Pima diabetes (532 × 7) 0.704 0.591 0.704 0.665 0.697 0.633
ISLR Default (10000 × 3) 0.896 0.867 0.896 0.849 0.894 0.893
AER CreditCard (1319 × 10) 0.670 0.608 0.654 0.692 0.667 0.668
ISLR Caravan (5822 × 85) 0.439 0.400 0.439 0.426 0.413 0.379

What this says, honestly:

Reproduce with Rscript inst/benchmarks/real_data.R 20 100 and Rscript inst/benchmarks/convergence.R (they need partykit, ranger, scorecard, ISLR, AER; about an hour).

Documentation

References