# Short Tutorial

In this tutorial, we show how to use the `ocf` package to estimate and make inference about the conditional choice probabilities and the covariates’ marginal effects.

Before diving in the coding, we provide an overview of the statistical problem at hand.

## Ordered Choice Models

We postulate the existence of a latent and continuous outcome variable \(Y_i^*\), assumed to obey the following regression model:

\[ Y_i^* = g \left( X_i \right) + \epsilon_i \] where \(X_i\) consists of a set of raw covariates, \(g \left( \cdot \right)\) is a potentially non-linear regression function, and \(\epsilon_i\) is independent of \(X_i\).

An observational rule links the observed outcome \(Y_i\) to the latent outcome \(Y_{i}^*\) using unknown threshold parameters \(- \infty = \zeta_0 < \zeta_1 < \dots < \zeta_{M - 1} < \zeta_M = \infty\) that define intervals on the support of \(Y_i^*\), with each interval corresponding to one of the \(M\) categories or classes of \(Y_i\):

\[ \zeta_{m - 1} < Y_i^* \leq \zeta_{m} \implies Y_i = m, \quad m = 1, \dots, M \]

The statistical targets of interest are the conditional choice probabilities:

\[ p_m \left( X_i \right) := \mathbb{P} \left( Y_i = m | X_i \right) \]

and the marginal effect of the \(j\)-th covariate on \(p_m \left( \cdot \right)\):

\[ \nabla^j p_m \left( x \right) := \begin{cases} \frac{\partial p_m \left( x \right)}{\partial x_j}, & \text{if } x_j \text{ is continuous} \\ p_m \left( \lceil x_j \rceil \right) - p_m \left( \lfloor x_j \rfloor \right), & \text{if } x_j \text{ is discrete} \end{cases} \] where \(x_j\) is the \(j\)-th element of the vector \(x\) and \(\lceil x_j \rceil\) and \(\lfloor x_j \rfloor\) correspond to \(x\) with its \(j\)-th element rounded up and down to the closest integer.

## Code

For illustration purposes, we generate a synthetic data set. Details about the employed DGP can be retrieved by running `help(generate_ordered_data)`.

    ## Generate synthetic data.
    set.seed(1986)

    n <- 100
    data <- generate_ordered_data(n)

    sample <- data$sample
    Y <- sample$Y
    X <- sample[, -1]

    table(Y)
    R> Y
    R>  1  2  3
    R> 31 37 32

    head(X)
    R>            x1 x2         x3 x4            x5 x6
    R> 1 -0.04625141  1 -1.7879732  0 -1.0515868012  1
    R> 2  0.28000082  0 -1.1553030  0 -0.4285613418  0
    R> 3  0.25317063  1  1.6677330  0  0.1621459072  0
    R> 4 -0.96411077  0 -0.1587051  0  0.3587438820  0
    R> 5  0.49222664  0 -1.4020533  1  0.0004035277  0
    R> 6 -0.69874551  1 -0.4450061  0 -0.3183447897  0

### Conditional Probabilities

To estimate the conditional probabilities, the `ocf` function constructs a collection of forests, one for each category of `Y` (three in this case). We can then use the forests to predict out-of-sample using the `predict` method. `predict` returns a matrix with the predicted probabilities and a vector of predicted class labels (each observation is labelled to the highest-probability class).

    ## Training-test split.
    train_idx <- sample(seq_len(length(Y)), floor(length(Y) * 0.5))

    Y_tr <- Y[train_idx]
    X_tr <- X[train_idx, ]

    Y_test <- Y[-train_idx]
    X_test <- X[-train_idx, ]

    ## Fit ocf on training sample. Use default settings.
    forests <- ocf(Y_tr, X_tr)

    ## Summary of data and tuning parameters.
    summary(forests)
    R> Call:
    R> ocf(Y_tr, X_tr)
    R>
    R> Data info:
    R> Full sample size:   50
    R> N. covariates:      6
    R> Classes:            1 2 3
    R>
    R> Relative variable importance:
    R>    x1    x2    x3    x4    x5    x6
    R> 0.369 0.075 0.253 0.101 0.172 0.030
    R>
    R> Tuning parameters:
    R> N. trees:           2000
    R> mtry:               3
    R> min.node.size       5
    R> Subsampling scheme: No replacement
    R> Honesty:            FALSE
    R> Honest fraction:    0

    ## Out-of-sample predictions.
    predictions <- predict(forests, X_test)

    head(predictions$probabilities)
    R>         P(Y=1)    P(Y=2)     P(Y=3)
    R> [1,] 0.3543743 0.5375517 0.10807401
    R> [2,] 0.4571570 0.4057421 0.13710091
    R> [3,] 0.1173914 0.4803692 0.40223943
    R> [4,] 0.6342519 0.3132168 0.05253126
    R> [5,] 0.3482501 0.3873043 0.26444560
    R> [6,] 0.6284292 0.2999011 0.07166965

    table(Y_test, predictions$classification)
    R>
    R> Y_test  1  2  3
    R>      1 12  3  1
    R>      2  6  6  7
    R>      3  3  2 10

To produce consistent and asymptotically normal predictions, we need to set the `honesty` argument to TRUE. This makes the `ocf` function using different parts of the training sample to construct the forests and compute the predictions.

    ## Honest forests.
    honest_forests <- ocf(Y_tr, X_tr, honesty = TRUE)

    honest_predictions <- predict(honest_forests, X_test)
    head(honest_predictions$probabilities)
    R>         P(Y=1)    P(Y=2)    P(Y=3)
    R> [1,] 0.3476025 0.3216374 0.3307601
    R> [2,] 0.3865934 0.3244973 0.2889093
    R> [3,] 0.2351550 0.4112832 0.3535617
    R> [4,] 0.4237396 0.3527212 0.2235392
    R> [5,] 0.3394056 0.3034435 0.3571509
    R> [6,] 0.4069484 0.3231956 0.2698560

To estimate standard errors for the predicted probabilities, we need to set the `inference` argument to TRUE. However, this works only if `honesty` is TRUE, as the formula for the variance is valid only for honest predictions. Notice that the estimation of the standard errors can considerably slow down the routine. However, we can increase the number of threads used to construct the forests by using the `n.threads` argument.

    ## Compute standard errors. Do not run.
    # honest_forests <- ocf(Y_tr, X_tr, honesty = TRUE, inference = TRUE, n.threads = 0) # Use all CPUs.
    # head(honest_forests$predictions$standard.errors)

### Covariates’ Marginal Effects

To estimate the covariates’ marginal effects, we post-process the conditional probability predictions. This is performed by the `marginal_effects` function that can estimate mean marginal effects, marginal effects at the mean, and marginal effects at the median, according to the `eval` argument.

    ## Marginal effects at the mean.
    me_atmean <- marginal_effects(forests, eval = "atmean") # Try also 'eval = "atmean"' and 'eval = "mean"'.
    print(me_atmean) # Try also 'latex = TRUE'.
    R> ocf marginal effects results
    R>
    R> Data info:
    R> Number of classes:    3
    R> Sample size:          50
    R>
    R> Tuning parameters:
    R> Evaluation:           atmean
    R> Bandwidth:            0.1
    R> Number of trees:      2000
    R> Honest forests:       FALSE
    R> Honesty fraction:     0
    R>
    R> Marginal Effects:
    R>    P'(Y=1) P'(Y=2) P'(Y=3)
    R> x1  -0.119  -0.283   0.402
    R> x2  -0.986   0.724   0.262
    R> x3  -0.058  -0.027   0.085
    R> x4  -0.507  -1.498   2.006
    R> x5   0.028   0.048  -0.075
    R> x6   0.000   0.000   0.000

    plot(me_atmean)

Sometimes, we are only interested in the marginal effects of a subset of the available covariates. To spare some time, we can use the `these_covariates` argument to estimate marginal effects of only those covariates. This argument also allows the user to declare whether the covariates are to be treated as `"continuous"` of `"discrete"` (marginal effect estimation is handled differently according to the covariate’s nature). `these_covariates` must be a named list, with entries’ names specifying the target covariates and entries specifying the covariates’ types. If not used, covariates’ types are inferred by the routine using basic heuristics (as in the example above).

    ## Marginal effects at the mean.
    target_covariates <- list("x1" = "continuous", "x2" = "discrete", "x4" = "discrete")
    me_atmean <- marginal_effects(forests, eval = "atmean", these_covariates = target_covariates)

    plot(me_atmean)

As before, we can set the `inference` argument to TRUE to estimate the standard errors. Again, this requires the use of honest forests and can considerably slow down the routine.

    ## Compute standard errors.
    honest_me_atmean <- marginal_effects(honest_forests, eval = "atmean", inference = TRUE)
    print(honest_me_atmean) # Try also 'latex = TRUE'.
    R> ocf marginal effects results
    R>
    R> Data info:
    R> Number of classes:    3
    R> Sample size:          50
    R>
    R> Tuning parameters:
    R> Evaluation:           atmean
    R> Bandwidth:            0.1
    R> Number of trees:      2000
    R> Honest forests:       TRUE
    R> Honesty fraction:     0.5
    R>
    R> Marginal Effects:
    R>    P'(Y=1) P'(Y=2) P'(Y=3)
    R> x1  -0.018  -0.009   0.027
    R> x2  -0.368   0.115   0.254
    R> x3  -0.015   0.006   0.009
    R> x4  -0.132  -0.490   0.622
    R> x5  -0.003   0.030  -0.027
    R> x6   0.000   0.000   0.000

    plot(honest_me_atmean)