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.
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.
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 2The 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 deciders with 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:
cost: monetary costsivt: in vehicle time,ovt: out vehicle time,frequency: frequency of service (with 0 for cars)The deciders are characterized by the following variables:
incomeurban: indicator of living placeThe 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:
case: identifying the choice occasion (multiple lines
can carry the same case id)alt: the alternative to which the row corresponds;
typically a string, could be an integerchoice: Boolean, indicating whether this alternative
was chosennoalt: integer, counting the number of alternatives
contained in this choice.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.
choice_data ObjectIn 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, freqNote, 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 1If 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):
N: number of decidersTp: vector of choice occasions per deciderdf: data.frame holding the dataformat: "long" or "wide"alt_names: vector of names of alternativeschoice_counts: table of countschoice_frequencies: table of frequencies of
choicestheta_dgp: relevant for simulation, see below.These are all accessible from the object. For example, the choice counts and the corresponding frequencies are stored in the object:
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:
price: in Dutch guilderstime: travel time in minuteschange: integer value indicating the number of times,
the train needs to be changedcomfort: category of comfort.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 0d_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, comforthead(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 0The data is kept in the wide format.
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 1m_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 NAFor 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 1design_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 NAIn 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.
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:
mod objectThese objects may be defined in a sequence such as the following (for details on the definition of the model see the vignette Model Definition):
quality and
price; one type II variable income; and one
type III variable time. The dependent variable is called
choice. The coefficient for quality is
random.income only over decidersd_sim sets up the object emptytheta contains the parameters. If it is not supplied, a
parameter vector is drawn from independent standard normal
variables.simulate() generates the data: Here
deciders with and
choice occasions respectively are simulated. The covariates are drawn
independently from centered normal distributions with standard deviation
specified as sd = 2.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.2246924Additionally 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.2246924In order to memorize the data generating process, d_sim
also stores the parameter vector used for simulation (but not the
model).
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:
data.frame is manipulated by subtracting for the price of the ‘bus’
alternative.redraw_choices is applied to obtain the
new choicesprice of bus.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.2246924In this way, we may obtain many different draws to use them in a bootstrapping procedure.