Skip to contents

The choicedata package simplifies working with choice data in R.

Installation

Install the released version from CRAN:

install.packages("choicedata")

Package design

The package breaks choice-data modeling into a series of objects. Each object contains the information needed for the next step.

The objects are designed to be modular and can be combined in various ways to create a range of modeling workflows.

Examples

Empirical data

The travel_mode_choice data set contains the revealed preferences of 210 travelers choosing between plane, train, bus, and car:

library("choicedata")
travel_mode_choice
#> # A tibble: 840 × 8
#>    individual mode  choice  wait  cost travel income  size
#>         <int> <chr>  <int> <int> <dbl>  <int>  <dbl> <int>
#>  1          1 plane      0    69 36.4     100   21.6     1
#>  2          1 train      0    34 19.1     372   21.6     1
#>  3          1 bus        0    35 15.4     417   21.6     1
#>  4          1 car        1     0  6.17    180   21.6     1
#>  5          2 plane      0    64 35.8      68   18.5     2
#>  6          2 train      0    44 19.1     354   18.5     2
#>  7          2 bus        0    53 15.4     399   18.5     2
#>  8          2 car        1     0  6.79    255   18.5     2
#>  9          3 plane      0    69 71.0     125   24.7     1
#> 10          3 train      0    34 60.5     892   24.7     1
#> # ℹ 830 more rows

We can transform the data from long (one row per choice alternative) to wide format (one row per choice occasion):

long_to_wide(
  data_frame = travel_mode_choice,
  column_alternative = "mode",
  column_decider = "individual"
)
#> # A tibble: 210 × 16
#>    individual income  size wait_plane wait_train wait_bus wait_car cost_plane
#>         <int>  <dbl> <int>      <int>      <int>    <int>    <int>      <dbl>
#>  1          1  21.6      1         69         34       35        0       36.4
#>  2          2  18.5      2         64         44       53        0       35.8
#>  3          3  24.7      1         69         34       35        0       71.0
#>  4          4  43.2      3         64         44       53        0       30.3
#>  5          5  27.8      2         64         44       53        0       37.0
#>  6          6  12.3      1         69         40       35        0       36.4
#>  7          7  27.8      1         45         34       35        0       91.4
#>  8          8   7.41     1         69         34       35        0       74.7
#>  9          9  24.7      1         69         34       35        0       36.4
#> 10         10  43.2      2         69         34       35        0       35.8
#> # ℹ 200 more rows
#> # ℹ 8 more variables: cost_train <dbl>, cost_bus <dbl>, cost_car <dbl>,
#> #   travel_plane <int>, travel_train <int>, travel_bus <int>, travel_car <int>,
#> #   choice <chr>

We can construct model design matrices:

mode_data <- choice_data(
  data_frame = travel_mode_choice,
  format = "long",
  column_choice = "choice",
  column_decider = "individual",
  column_alternative = "mode",
  column_ac_covariates = c("income", "size"),
  column_as_covariates = c("wait", "cost", "travel")
)

mode_effects <- choice_effects(
  choice_formula = choice_formula(
    formula = choice ~ cost | income | wait,
    error_term = "probit"
  ),
  choice_alternatives = choice_alternatives(
    alternatives = unique(travel_mode_choice$mode)
  ),
  choice_data = mode_data
)

mode_design <- design_matrices(mode_data, mode_effects)
mode_design[[1]] |> round()
#>       cost income_car income_plane income_train ASC_car ASC_plane ASC_train
#> bus     15          0            0            0       0         0         0
#> car      6         22            0            0       1         0         0
#> plane   36          0           22            0       0         1         0
#> train   19          0            0           22       0         0         1
#>       wait_bus wait_car wait_plane wait_train
#> bus         35        0          0          0
#> car          0        0          0          0
#> plane        0        0         69          0
#> train        0        0          0         34

Simulated choice

generate_choice_data() makes it straightforward to simulate choice data. The example below simulates 200 ranking tasks with three alternatives and recovers the data-generating parameters by optimizing the likelihood:

library("choicedata")

set.seed(1)

sim_effects <- choice_effects(
  choice_formula = choice_formula(
    formula = choice ~ x | y + 0 | z,
    error_term = "logit"
  ),
  choice_alternatives = choice_alternatives(
    J = 3,
    alternatives = c("A", "B", "C")
  )
)

sim_parameters <- generate_choice_parameters(sim_effects)

(sim_data <- generate_choice_data(
  choice_effects = sim_effects,
  choice_identifiers = generate_choice_identifiers(N = 200),
  choice_parameters = sim_parameters,
  choice_type = "ranked"
))
#> # A tibble: 200 × 13
#>    deciderID occasionID choice       y    x_A     x_B     x_C     z_A    z_B
#>  * <chr>     <chr>      <chr>    <dbl>  <dbl>   <dbl>   <dbl>   <dbl>  <dbl>
#>  1 1         1          B      -0.621   0.487  0.738   0.576  -0.305   1.51 
#>  2 2         1          A       0.594  -2.21   1.12   -0.0449 -0.0162  0.944
#>  3 3         1          C      -0.156   0.919  0.782   0.0746 -1.99    0.620
#>  4 4         1          A      -0.0538 -1.47  -0.478   0.418   1.36   -0.103
#>  5 5         1          B      -0.165  -1.38  -0.415  -0.394  -0.0593  1.10 
#>  6 6         1          A       0.769  -0.253  0.697   0.557  -0.689  -0.707
#>  7 7         1          B       1.43   -0.112  0.881   0.398  -0.612   0.341
#>  8 8         1          B      -0.0392  1.98  -0.367  -1.04    0.570  -0.135
#>  9 9         1          A       0.153   0.690  0.0280 -0.743   0.189  -1.80 
#> 10 10        1          C       0.291   2.17   0.476  -0.710   0.611  -0.934
#> # ℹ 190 more rows
#> # ℹ 4 more variables: z_C <dbl>, choice_A <int>, choice_B <int>, choice_C <int>

sim_likelihood <- choice_likelihood(
  choice_data = sim_data,
  choice_effects = sim_effects
)

true_vector <- switch_parameter_space(
  choice_parameters = sim_parameters,
  choice_effects = sim_effects
)

fit <- stats::optim(
  par = stats::rnorm(length(true_vector)),
  fn = function(par) {
    compute_choice_likelihood(
      choice_parameters = par,
      choice_likelihood = sim_likelihood,
      logarithm = TRUE,
      negative = TRUE
    )
  }
)

estimated_parameters <- switch_parameter_space(
  choice_parameters = fit$par,
  choice_effects = sim_effects
)

data.frame(dgp = true_vector, estimated = fit$par) |> round(2)
#>          dgp estimated
#> beta_1 -1.98     -2.36
#> beta_2  0.58      0.53
#> beta_3 -2.64     -2.83
#> beta_4  5.04      5.64
#> beta_5  1.04      1.18
#> beta_6 -2.59     -3.03

{Rprobit} (Bauer et al. 2023) provides maximum approximated composite marginal likelihood estimation for efficient probit choice modeling.

RprobitB (Oelschläger and Bauer 2025) provides Bayesian tools for estimating probit models.

Contact

You have a question, found a bug, or want to contribute? Please file an issue on GitHub.

References

Bauer, D., M. Batram, S. Büscher, and L. Oelschläger. 2023. Rprobit: Estimation of Multinomial Probit Models. https://github.com/dbauer72/Rprobit.
Oelschläger, L., and D. Bauer. 2025. RprobitB: Bayesian Probit Choice Modeling. https://CRAN.R-project.org/package=RprobitB.