Data and Simulation

In this document the data structures used in Rprobit are presented. Tools to work with the data are described. Subsequently, simulation of data sets is useful in a number of settings. Rprobit offers support for this, as will be discussed at the end of this vignette.

Data

Choice models are built to help explain the tradeoffs behind choices. Oftentimes the data for fitting the models comes in the form of a panel, providing a number of choices of a number of different deciders. The tradeoffs weigh the various alternatives according to corresponding characteristics. Beside the alternatives also the deciders often are characterized by different variables. Hence the choice data sets typically contain the following objects:

Therefore, choice data contains more information than typically stored inside a data.frame object.

Example: Mode Choice

A prime example for multinomial choice data is contained in the package mlogit (Croissant 2020):

library(mlogit)
data("ModeCanada", package = "mlogit")
head(ModeCanada)
#> # A tibble: 6 × 11
#>    case alt   choice  dist  cost   ivt   ovt  freq income urban noalt
#>   <int> <fct>  <int> <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl> <dbl> <int>
#> 1     1 train      0    83  28.2    50    66     4     45     0     2
#> 2     1 car        1    83  15.8    61     0     0     45     0     2
#> 3     2 train      0    83  28.2    50    66     4     25     0     2
#> 4     2 car        1    83  15.8    61     0     0     25     0     2
#> 5     3 train      0    83  28.2    50    66     4     70     0     2
#> 6     3 car        1    83  15.8    61     0     0     70     0     2

The data describes the choice between the four alternatives ‘train’, ‘air’, ‘bus’ and ‘car’ for trips in Canada between Montreal and Toronto (see (Croissant 2020) for more detail). The data object contains 4324 cases, treated here as N=4324N=4324 deciders with T=1T=1 choice occasions. The data consists in revealed preference survey data, wherein deciders were asked about their decision between the alternatives.

The choice set must conform to certain restrictions, see (Train 2009):

The alternatives differ in terms of their characteristics:

The deciders are characterized by the following variables:

The remaining variables provide the context. The data set is arranged in long format, wherein for each decision a number of rows in the data.frame is used. For each alternative one row is contained. The number of alternatives per decision need not be identical (some individuals may not own a car, eliminating this alternative). The context is described using the variables:

Data can also be stored in wide format, where for each decision one row in the data.frame is used. In this case, variables that vary over alternatives are coded in different columns indicated with trailing _car, _train etc. to indicate, which alternative the column corresponds to. Variable characterizing the deciders do not contain trailing indicators.

It follows that the choice data contains more structure than is contained in a data.frame.

The choice_data Object

In Rprobit data is stored in an object choice_data. We decided to use R6 classes for choice_data. See here for the perils of R6 classes.

These objects allow for the verification of the properties as well as a neat representation of the data set. Generating choice data sets based on data.frame objects in long format can be done as follows:

d_mode <- choice_data$new(
  df = ModeCanada,
  format = "long",
  column_decider_id = "case",
  column_alternative = "alt"
)

print(d_mode)
#> <choice_data object>
#> 
#> Data format: long
#> Deciders: 4324
#> Choice occasions: from 1 to 1
#> Alternatives: train, car, bus, air
#> Alternative-constant covariates: dist, income, urban, noalt
#> Alternative-varying covariates: cost, ivt, ovt, freq

Note, that the output also lists which variables vary over alternatives and which do not. This is important information for modeling, see the section on Model Definition.

head(d_mode$df, 8)
#>       case   alt choice dist  cost ivt ovt freq income urban noalt occasion
#> 1        1 train      0   83 28.25  50  66    4     45     0     2        1
#> 2        1   car      1   83 15.77  61   0    0     45     0     2        1
#> 15521    1   bus      0   NA    NA  NA  NA   NA     NA    NA    NA        1
#> 15522    1   air      0   NA    NA  NA  NA   NA     NA    NA    NA        1
#> 3        2 train      0   83 28.25  50  66    4     25     0     2        1
#> 4        2   car      1   83 15.77  61   0    0     25     0     2        1
#> 15523    2   bus      0   NA    NA  NA  NA   NA     NA    NA    NA        1
#> 15524    2   air      0   NA    NA  NA  NA   NA     NA    NA    NA        1

If the long data set does not contain information on all alternatives for all choices, they are added when the choice_data object is created (Caution, this may take a while for larger data sets, as the procedure loops over the decisions).

Printing the object shows that the data object contains more information than the data.frame. The class has the following fields (all are read-only):

These are all accessible from the object. For example, the choice counts and the corresponding frequencies are stored in the object:

d_mode$choice_counts
#> train   car   bus   air 
#>   623  2213    16  1472
d_mode$choice_frequencies
#>       train         car         bus         air 
#> 0.144079556 0.511794635 0.003700278 0.340425532

Example 2: A Wide Data Set

A second example data set concerns the choice between two different trips in the Netherlands, see also (Croissant 2020). In a stated choice experiment, deciders have been asked to choose between two routes within the Dutch public transport system. The two alternatives ‘A’ and ‘B’ given are characterized via the following variables:

The data set is in wide format. Each row corresponds to one choice. The values of the variables for different alternatives are provided using a trailing _A or _B for the variable labels.

The data set can be imported into our data structure as for the long case:

data("Train", package = "mlogit")

head(Train)
#>   choiceid id choice price_A price_B time_A time_B change_A change_B comfort_A
#> 1        1  1      A    2400    4000    150    150        0        0         1
#> 2        2  1      A    2400    3200    150    130        0        0         1
#> 3        3  1      A    2400    4000    115    115        0        0         1
#> 4        4  1      B    4000    3200    130    150        0        0         1
#> 5        5  1      B    2400    3200    150    150        0        0         1
#> 6        6  1      B    4000    2400    115    130        0        0         0
#>   comfort_B
#> 1         1
#> 2         1
#> 3         0
#> 4         0
#> 5         0
#> 6         0
d_train <- choice_data$new(
  df = Train,
  column_decider_id = "id",
  column_occasion_id = "choiceid"
)
print(d_train)
#> <choice_data object>
#> 
#> Data format: wide
#> Deciders: 235
#> Choice occasions: from 5 to 19
#> Alternatives: A, B
#> Alternative-constant covariates: none
#> Alternative-varying covariates: price, time, change, comfort
head(d_train$df, 6)
#>   choiceid id choice price_A price_B time_A time_B change_A change_B comfort_A
#> 1        1  1      A    2400    4000    150    150        0        0         1
#> 2        2  1      A    2400    3200    150    130        0        0         1
#> 3        3  1      A    2400    4000    115    115        0        0         1
#> 4        4  1      B    4000    3200    130    150        0        0         1
#> 5        5  1      B    2400    3200    150    150        0        0         1
#> 6        6  1      B    4000    2400    115    130        0        0         0
#>   comfort_B
#> 1         1
#> 2         1
#> 3         0
#> 4         0
#> 5         0
#> 6         0

The data is kept in the wide format.

Linking Data with a Model

Raw data sets as given above are the basis for modeling. Oftentimes one collects data sets with more variables than are finally used in the model. Also transformations of variables (such as interaction terms, quadratic terms or indicator variables derived from variables) are frequently applied.

Other recoding takes place in the modeling. To this end mlogit uses a three-part formula to define models, categorizing the regressors into type I to type III, see the vignette on Model Definition for details. Internally Rprobit recodes the variables to convert all variables into type I variables taking identifiability restrictions into account.

One data set can be linked to a number of different models. Rprobit provides methods to calculate the design matrix for a given data set corresponding to a particular model:

m_mode <- mod$new(
  formula = choice ~ cost + ivt | income | 0,
  alt = length(d_mode$alt_names),
  alt_names = d_mode$alt_names,
  ref_alt = d_mode$alt_names[1]
)

design_mode <- d_mode$build_design(m_mode)
design_mode[[1]]$X[[1]]
#>        cost ivt income_car income_bus income_air ASC_car ASC_bus ASC_air
#> train 28.25  50          0          0          0       0       0       0
#> car   15.77  61         45          0          0       1       0       0
#> bus      NA  NA          0         45          0       0       1       0
#> air      NA  NA          0          0         45       0       0       1
m_mode2 <- mod$new(
  formula = choice ~ cost | income | ivt,
  alt = length(d_mode$alt_names),
  alt_names = d_mode$alt_names,
  ref_alt = d_mode$alt_names[1]
)

design_mode <- d_mode$build_design(m_mode2)
design_mode[[1]]$X[[1]]
#>        cost income_car income_bus income_air ASC_car ASC_bus ASC_air ivt_train
#> train 28.25          0          0          0       0       0       0        50
#> car   15.77         45          0          0       1       0       0         0
#> bus      NA          0         45          0       0       1       0         0
#> air      NA          0          0         45       0       0       1         0
#>       ivt_car ivt_bus ivt_air
#> train       0       0       0
#> car        61       0       0
#> bus         0      NA       0
#> air         0       0      NA

For the estimation (see details in Model Fitting) it is numerically advantageous to calculate the differences with respect to the chosen alternative:

design_mode <- d_mode$build_design(m_mode, difference = TRUE)
design_mode[[1]]$X[[1]]
#>              cost ivt income_car income_bus income_air ASC_car ASC_bus ASC_air
#> train - car 12.48 -11        -45          0          0      -1       0       0
#> bus - car      NA  NA        -45         45          0      -1       1       0
#> air - car      NA  NA        -45          0         45      -1       0       1
design_mode <- d_mode$build_design(m_mode2, difference = TRUE)
design_mode[[1]]$X[[1]]
#>              cost income_car income_bus income_air ASC_car ASC_bus ASC_air
#> train - car 12.48        -45          0          0      -1       0       0
#> bus - car      NA        -45         45          0      -1       1       0
#> air - car      NA        -45          0         45      -1       0       1
#>             ivt_train ivt_car ivt_bus ivt_air
#> train - car        50     -61       0       0
#> bus - car           0     -61      NA       0
#> air - car           0     -61       0      NA

Simulation

In research data sets often are obtained by simulation. Reasons for this may be:

Rprobit provides a limited support for simulation. It is our experience that most simulations are very problem specific, making it easier to program the simulation individually rather than including this functionality into the package. Nevertheless, there are a number of situations, where Rprobit can be of help.

Simulating a Full Data Set Including the Characteristics

For investigation of a new method or demonstration purposes we may simulate data sets of given characteristics including a random choice of the characteristics. In Rprobit this can be done by specifying:

These objects may be defined in a sequence such as the following (for details on the definition of the model see the vignette Model Definition):

The corresponding simulated data set is returned as a data.frame.

set.seed(1)
m_sim <- mod$new(
  formula = choice ~ quality + price | income | time,
  re = "quality",
  alt = 2,
  alt_names = c("bus", "car"),
  ref_alt = "bus"
)

d_sim <- choice_data$new()
theta <- rnorm(m_sim$length_theta)
theta[1L] <- -1 # negative price coefficient

simulated <- d_sim$simulate(
  mod = m_sim,
  theta = theta,
  N = 3,
  Tp = c(1, 2, 3),
  sd = 2 # standard deviation of centered normal distribution for covariates
)

head(simulated, 6)
#>   id occasion choice quality_bus quality_car price_bus  price_car     income
#> 1  1        1    car   1.4766494 -4.42939977  1.187803 -0.1122575 -0.2055755
#> 2  2        1    bus   1.1515627  2.24986184  1.837955 -0.3115910  0.7753432
#> 3  2        2    car  -0.6107768 -0.08986722  1.564273 -2.9415048 -0.1076101
#> 4  3        1    car   3.0235623 -0.03238053  0.149130 -0.9563001 -2.7541191
#> 5  3        2    bus   0.7796865  1.88767242 -3.978703  0.8358831 -0.8299891
#> 6  3        3    bus  -1.2424812  1.64244239  1.239651  2.7173591 -0.7885799
#>     time_bus   time_car
#> 1 -0.1186268  1.1133264
#> 2  2.2000507 -1.3775114
#> 3  1.5263515 -1.4149903
#> 4 -0.3290472  0.7291639
#> 5 -0.5067234  1.5370658
#> 6  1.3939268 -0.2246924

Additionally it is also stored in the object d_sim due to the R6 functionality.

head(d_sim$df, 6)
#>   id occasion choice quality_bus quality_car price_bus  price_car     income
#> 1  1        1    car   1.4766494 -4.42939977  1.187803 -0.1122575 -0.2055755
#> 2  2        1    bus   1.1515627  2.24986184  1.837955 -0.3115910  0.7753432
#> 3  2        2    car  -0.6107768 -0.08986722  1.564273 -2.9415048 -0.1076101
#> 4  3        1    car   3.0235623 -0.03238053  0.149130 -0.9563001 -2.7541191
#> 5  3        2    bus   0.7796865  1.88767242 -3.978703  0.8358831 -0.8299891
#> 6  3        3    bus  -1.2424812  1.64244239  1.239651  2.7173591 -0.7885799
#>     time_bus   time_car
#> 1 -0.1186268  1.1133264
#> 2  2.2000507 -1.3775114
#> 3  1.5263515 -1.4149903
#> 4 -0.3290472  0.7291639
#> 5 -0.5067234  1.5370658
#> 6  1.3939268 -0.2246924
d_sim$choice_counts
#> bus car 
#>   3   3

In order to memorize the data generating process, d_sim also stores the parameter vector used for simulation (but not the model).

Redrawing Choices

Using independent normally drawn characteristics may not be wanted. Also in bootstrapping applications one may want to simulate different choices being taken for the same model. Rprobit supports this by implementing a method to redraw choice given the choice data. Therein the characteristics and the context is left identical, only the choices are simulated newly based on the corresponding choice probabilities.

To demonstrate this feature, consider the following procedure:

set.seed(1)
modified <- simulated
modified$price_bus <- modified$price_bus - 5

redrawn <- d_sim$redraw_choices(
  df = modified,
  mod = m_sim,
  column_decider_id = "id"
)

head(redrawn)
#>   id occasion choice quality_bus quality_car price_bus  price_car     income
#> 1  1        1    car   1.4766494 -4.42939977 -3.812197 -0.1122575 -0.2055755
#> 2  2        1    bus   1.1515627  2.24986184 -3.162045 -0.3115910  0.7753432
#> 3  2        2    bus  -0.6107768 -0.08986722 -3.435727 -2.9415048 -0.1076101
#> 4  3        1    bus   3.0235623 -0.03238053 -4.850870 -0.9563001 -2.7541191
#> 5  3        2    bus   0.7796865  1.88767242 -8.978703  0.8358831 -0.8299891
#> 6  3        3    bus  -1.2424812  1.64244239 -3.760349  2.7173591 -0.7885799
#>     time_bus   time_car
#> 1 -0.1186268  1.1133264
#> 2  2.2000507 -1.3775114
#> 3  1.5263515 -1.4149903
#> 4 -0.3290472  0.7291639
#> 5 -0.5067234  1.5370658
#> 6  1.3939268 -0.2246924
d_sim$choice_counts
#> bus car 
#>   5   1

In this way, we may obtain many different draws to use them in a bootstrapping procedure.

References

Croissant, Y. 2020. “Estimation of Random Utility Models in R: The mlogit Package.” Journal of Statistical Software 95 (11). https://doi.org/10.18637/jss.v095.i11.
Train, K. 2009. Discrete Choice Methods with Simulation. Cambridge University Press. https://doi.org/10.1017/CBO9780511805271.