---
title: "Consensus ranking of variable importance"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Consensus ranking of variable importance}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
```

Ask a random forest which variables matter and the answer depends on how you
ask. Mean decrease in impurity, permutation importance, SHAP, LOCO and
drop-column each return a different ordering. So does the same method under a
different seed, or a different fold. Reporting one ranking, from one method, on
one fit, is standard practice and is not defensible.

`rankimp` treats each of those sources as a **judge** expressing a ranking over
the variables, and asks what the judges agree on.

## The consensus

```{r}
library(rankimp)

judges <- rbind(
  permutation_seed1 = c(1, 2, 3, 4),
  permutation_seed2 = c(1, 2, 3, 4),
  shap              = c(1, 3, 2, 4),
  impurity          = c(2, 1, 3, 4)
)
colnames(judges) <- c("income", "age", "balance", "region")

consensus_rank(judges)
```

The consensus is the **Kemeny median**: the ranking minimising the total
Kemeny-Snell distance to the judges,

$$\pi^{*} = \arg\min_{\pi} \sum_{k=1}^{K} d_{KS}(\pi, \pi_k).$$

`tau_x` is the average Emond-Mason correlation between the consensus and the
judges. Read it as "how much agreement was there to summarise". A consensus with
`tau_x = 0.3` is a number, not a conclusion.

## Starting from importance scores

Judges usually arrive as importance scores rather than ranks.
`importance_to_rank()` converts them, and it converts ties honestly: variables a
judge scores identically get the same rank rather than an arbitrary order.

```{r}
importance <- rbind(
  permutation = c(income = 0.31, age = 0.12, balance = 0.12, region = 0.03),
  impurity    = c(income = 0.44, age = 0.20, balance = 0.05, region = 0.02)
)

importance_to_rank(importance)
```

Note that the permutation judge cannot separate `age` from `balance`, and says
so.

## Building the panel from fitted models

Hand-building the score matrix is fine for a demonstration and tedious in
practice. `importance_judges()` builds it from fitted models directly, here
two methods interrogating one forest, the smallest panel worth having:

```{r, eval = requireNamespace("randomForest", quietly = TRUE)}
set.seed(1)
fit <- randomForest::randomForest(mpg ~ ., data = mtcars, ntree = 200)

J <- importance_judges(
  fit,
  methods = c("permutation", "mdi"),
  data    = mtcars,
  target  = "mpg"
)
J

consensus_rank(J)$ranking
```

Models (`fit_list`), seeds (`seeds`) and resamples of the data (`resamples`)
enter the same way, and every extra axis multiplies the panel; see
`?importance_judges`.

## Ties are information

Because the Kemeny median is taken over weak orderings, it can return ties too.
Two variables the panel genuinely cannot order come out equal, which is the
thing an average of Borda scores can never say.

```{r}
tied <- rbind(
  c(1, 1, 3, 4),
  c(1, 1, 3, 4),
  c(2, 1, 3, 4)
)
colnames(tied) <- c("income", "age", "balance", "region")

consensus_rank(tied)$ranking
```

## Not every judge deserves an equal vote

Impurity-based importance is biased towards predictors with many distinct
values. A panel that mixes it with out-of-bag permutation importance should say
so rather than let the bias average out.

```{r}
consensus_rank(judges, weights = c(1, 1, 1, 0.5))$ranking
```

`judge_weights()` builds these vectors from a panel, either one weight per
method or weights proportional to each judge's agreement with the rest, and a
panel built by `importance_judges(..., weights = )` carries them into
`consensus_rank()` on its own.

## What comes next

A consensus without a standard error is still one number.
`vignette("stability")` covers the bootstrap rank confidence sets that say which
parts of the ordering are real, and `vignette("method-disagreement")` covers
what to do when the judges do not agree at all.
