stratEst
FABIAN DVORAK
research | stratEst | sandWind

stratEst is a software package for the estimation of finite mixture models of discrete choice strategies in the statistical computing environment R (R Core Team, 2020). Discrete choice strategies can be customized by the user to fit the environment in which choices are made. The parameters of the strategy estimation model describe the behavior of each strategy and how frequent each strategy is in the population. The estimation function of the package uses the expectation maximization algorithm and the Newton-Raphson method to find the maximum likelihood estimates of the model parameters. The estimation function can also be used to fit a strategy estimation model with individual level covariates to explain the selection of strategies by individuals. The package contains functions for data processing and simulation, strategy generation, parameter tests, model checking, and model selection. This page provides a short introduction to the package. For a comprehensive introduction to the package, please see the published paper or the package vignette.

Please cite the following article when using the package:

Dvorak, F. (2023). stratEst: a software package for strategy frequency estimation. Journal of the Economic Science Association, 9, 337–349. https://doi.org/10.1007/s40881-023-00141-7

The most recent CRAN version of stratEst can be installed by executing the following command in the R console:

install.packages("stratEst")

The development version of the package can be installed from GitHub with the help of the package devtools (Wickham, Hester, and Chang 2020):

install.packages("devtools")
library(devtools)
install_github("fdvorak/stratEst")

After the installation, the package is loaded into memory and attached to the search path with the command:

library(stratEst)

Rock-paper-scissors: An introductory example

I illustrate the core features of the package on the basis of the game rock-paper-scissors. In each period of this game, two players simultaneously choose one of three possible actions: rock, paper or scissors. The winner of the period is determined by the following rule: rock crushes scissors, scissors cuts paper, and paper covers rock. If both players choose the same action, this results in a tie. Rock-paper-scissors has a unique Nash equilibrium. The Nash equilibrium suggest that every player uses the same strategy. This strategy plays each of the three actions with probability one-third.

The data set WXZ2015 contains the data of a rock-paper-scissors experiment conducted by Wang, Xu, and Zhou (2014). The data contains the observations 72 university students playing 300 periods of the rock-paper-scissors game in groups of six participants. In each period, each participant is randomly matched with another participant from the own group. In the experiment, 35.7 percent of all actions are rock (r), 32.2 percent are paper (p), and 32.1 percent are scissors (s) which seems to be fairly inline with the Nash equilibrium prediction.

There are many other choice strategies which can explain the observed distribution of choices. Wang et al. (2014) show that a conditional response strategy provides a better explanation for the data than the Nash equilibrium strategy. The conditional response strategy is more complex than Nash play as it takes the outcome of the previous period into account for the choice in current period.

The observed distribution of choices can also be explained by a finite mixture model of several strategies. To give an example, consider a uniform mixture of three types of players, one who always plays rock, one who always plays paper, and one who always plays scissors.

This example illustrates how to use the package in order to fit and compare different strategy estimation models to the data of the rock-paper-scissors experiment. The different strategy estimation models are selected to illustrate the features of the package and lack the theoretical justification provided by Wang et al. (2014) for the conditional response strategy.

Programming strategies

The strategy generation function of the package is stratEst.strategy(). The following code creates two strategies: a mixed strategy with unspecified choice probabilities, and the Nash strategy.

rps = c("r", "p", "s")
mixed = stratEst.strategy(choices = rps)
nash = stratEst.strategy(choices = rps, prob.choices = rep(1/3, 3))

The argument choices expects a character vector with the names of the choices. The argument prob.choice can be used to define the choice probabilities of the strategy. If printed out in the console, the strategies look like this:

print(mixed)
##   prob.r prob.p prob.s
## 1     NA     NA     NA
print(nash)
##   prob.r prob.p prob.s
## 1  0.333  0.333  0.333

The objects mixed and nash returned by the strategy function are data frames of class stratEst.strategy. Since the choice probabilities of the mixed strategy were not specified, the choice probabilities are NA. This indicates to the estimation function that these parameters should be estimated from the data.

The strategies nash and mixed are simple strategies in the sense that the choice probabilities of these strategies do not change from one situation to the next. The strategies only have one internal state and one associated set of choice probabilities. More complex strategies have several states with different sets of choice probabilities. The complex strategies transition from one state to the other after some input is observed. The input may be a specific situation or history of events in the game.

To illustrate the concept, a strategy is generated which randomizes in the first period and subsequently imitates the choice of the previous period. The following code generates the strategy imitate:

last.choice = c(NA, rps)
imitate =  stratEst.strategy(choices = rps, inputs = last.choice,
                             num.states = 4,
                             prob.choices = c(rep(1/3, 3), 1, 0, 0,
                                              0, 1, 0, 0, 0, 1),
                             tr.inputs = rep(c(2, 3, 4), 4))

What changed compared to the previous function calls is that the argument inputs is used to define a set of inputs. This set contains the names of all possible choices in the last period of the game. The value NA in the set indicates that the input can be missing. This is the case in the first period when no information from the previous period exists. The argument num.states defines the number of states of the strategy. The argument tr.inputs defines the deterministic state transitions for all possible inputs in all states. The result is a strategy with four states. Each state is represented by one row of the object imitate.

print(imitate)
##   prob.r prob.p prob.s tremble tr(r) tr(p) tr(s)
## 1  0.333  0.333  0.333      NA     2     3     4
## 2  1.000  0.000  0.000      NA     2     3     4
## 3  0.000  1.000  0.000      NA     2     3     4
## 4  0.000  0.000  1.000      NA     2     3     4

The strategy imitate has a column named tr(x) for each possible input x. An exception is the element NA that indicates the missing input in the first period. The values supplied to the argument tr.inputs appear in row wise order.

The strategy imitate also contains a column with the name tremble. The values in this column indicate the probability to choose one of the choices not prescribed by the strategy in the current state. A tremble probability is usually necessary for a pure strategy with choice probabilities of zero and one. The purpose of the tremble probability is to avoid that a single deviation an individual from the choice pattern of the pure strategy results in a likelihood of zero that the individual uses the strategy. The tremble probability can be specified by the argument trembles. If the argument is missing, the probability of a tremble is NA. This signals to the estimation function that the parameter should be estimated from the data.

The strategy imitate transitions from one state to the other by the following deterministic rule.In the first period, the strategy observes the input NA since there is no information on the previous choice available. By convention, whenever the input is NA, the strategy moves to its start state. The start state of the strategy is the state represented by the first row. The strategy makes a choice according to the choice probabilities in the first row. In period two, the strategy observes the input (either rock, paper or scissors) and moves to the next state defined by the value of tr(input) in the current state. The values supplied to tr.inputs define the desired behavior of the strategy imitate. It randomly makes a choice in the first period, and subsequently plays rock after rock, paper after paper, and scissors after scissors.

Processing data

In order fit the strategies to the rock-paper-scissors data, the data must be in a suitable format. The function stratEst.data() can be used to reshape the raw data.

data.WXZ2014 <- stratEst.data(data = WXZ2014, choice = "choice",
                              input = c("choice"), input.lag = 1,
                              id = "id", game = "game",
                              period = "period")

To the first argument of the function, we pass a data.frame object with variables in columns. We need to specify the variable in the data which contains the discrete choices using the argument choice. The argument input allows us to select one or more variable names which serve as input for the strategies in the estimation. If we select more than one variable, the function concatenates the values of these variables to a unique factor level. For this example, we only need one input variable, the choices of the participants in the experiment. As the input should reflect the choice in the previous period we specify a lag of one period. The arguments id, game, and period uniquely identify the participant, and the period within the game.

The function stratEst.data() returns an data frame object of class stratEst.data. We can inspect this object by printing it to the console with the command print(data.WXZ2015).

Model estimation

The function has two mandatory arguments which are data and strategies. The object passed to argument data must be of class stratEst.data. The object passed to argument strategies must be a list of stratEst.strategy objects. The following code fits four different models to the rock-paper-scissors data:

model.nash <- stratEst.model(data = data.WXZ2014,
                             strategies = list("nash" = nash))
model.mixed <- stratEst.model(data = data.WXZ2014,
                              strategies = list("mixed" = mixed))
model.imitate <- stratEst.model(data = data.WXZ2014,
                                strategies = list("imitate" = imitate))
model.mixture <- stratEst.model(data = data.WXZ2014,
                                strategies = list("nash" = nash,
                                                  "imitate" = imitate))

The estimation function stratEst() estimates the model parameters and returns a list object of class stratEst.model. The elements of this list can be accessed with the syntax model$object where object is an object name in names(model). The generic function summary() prints a summary of a fitted model to the console.

The function stratEst.check() can be used to inspect the global model fit. It summarizes the log likelihood of the model, the number of free model parameters, and the values of three information criteria. The three information criteria are the Akaike information criterion (aic), the Bayesian information criterion (bic), and Integrated classification likelihood (icl).

models <- list(model.nash, model.mixed, model.imitate, model.mixture)
compare <- do.call(rbind, unlist(lapply(models, stratEst.check),
                                 recursive = F))
rownames(compare) <- c("model.nash", "model.mixed", "model.imitate",
                       "model.mixture")
print(compare)
##                 loglike free.par      aic      bic      icl
## model.nash    -23730.03        0 47460.05 47460.05 47460.05
## model.mixed   -23704.04        2 47412.09 47416.64 47416.64
## model.imitate -23205.91        1 46413.82 46416.10 46416.10
## model.mixture -22358.43        2 44720.87 44725.42 44728.34

We see that the fit of the model with the mixed strategy is better than the fit of the model with the Nash strategy. The values of the information criteria indicate that this is true even if we take into account that the model with the mixed strategy has more free parameters. The estimated choice probabilities of the mixed strategy can be accessed with the command model.mixed$probs.par. The estimated choice probabilities reflect the overall distribution of choices. We can test whether the estimated choice probabilities differ from one-third using the function stratEst.test(). With the option par = "probs", the function performs a t test for each estimated choice probability:

t.probs <- stratEst.test(model = model.mixed, par = "probs", values = 1/3, plot = FALSE)
print(t.probs)
##             estimate    diff std.error  t.value df p.value
## probs.par.1   0.3223 -0.0111    0.0014  -8.0838 70       0
## probs.par.2   0.3566  0.0232    0.0013  17.6404 70       0
## probs.par.3   0.3212 -0.0122    0.0012 -10.3417 70       0

The model with the strategy imitate yields a better fit than the model with the mixed strategy despite having one free parameter less. However, the best global fit is obtained the mixture model of nash and imitate. The log likelihood of the mixture model is substantially larger than the log likelihood of all other models. The following commands print the estimated shares and strategies of this model the console:

print(model.mixture$shares, digits = 2)
##       nash imitate
## share 0.58    0.42
print(model.mixture$strategies, digits = 3)
## $nash
##   prob.r prob.p prob.s tr(p) tr(r) tr(s) tremble
## 1  0.333  0.333  0.333     1     1     1      NA
## 
## $imitate
##   prob.r prob.p prob.s tremble tr(r) tr(p) tr(s)
## 1  0.333  0.333  0.333      NA     2     3     4
## 2  1.000  0.000  0.000   0.391     2     3     4
## 3  0.000  1.000  0.000   0.391     2     3     4
## 4  0.000  0.000  1.000   0.391     2     3     4

The estimated shares suggest that each strategy is used by approximately half of the participants in the experiment. The fitted tremble parameter of the strategy imitate indicates that a different choice than the one predicted by the strategy is chosen in 39 percent of all observations. In these observations, the strategy suggests that players randomly pick one of the other choices.

Replication examples

Dal Bo and Frechette, 2011

This example illustrates how to replicate the strategy estimation results of the seminal strategy estimation study by Dal Bo and Frechette (2011). The study reports results on the evolution of cooperation in the indefinitely repeated prisoner’s dilemma across six different treatments. The six treatments differ in the stage-game parameters and the continuation probability \(\delta\) of the repeated game.

The stage-game parameters are depicted in Figure below where the parameter R is either 32, 40 or 48. For each value of R two treatments exist with \(\delta\) of 1/2 or 3/4 resulting in 2 times three between subject design with six treatments overall.

Stage game of Dal Bo and Frechette (2011):
  C D
C R,R 12,50
D 50,12 25,25

Dal Bo and Frechette (2011) report the results of treatment-wise strategy frequency estimation for six candidate strategies: Always Defect (ALLD), Always Cooperate (ALLC), Tit-For-Tat (TFT), Grim-Trigger (GRIM), Win-Stay-Lose-Shift (WSLS), and a trigger strategy with two periods of punishment (T2). The six strategies are the elemenets of the list strategies.DF2011. The Tit-For-Tat strategy looks like this:

plot(strategies.DF2011$TFT, title = "TFT")

The strategy TFT chooses between the alternatives defect (d) and cooperate (c). State transitions are triggered by four different inputs: The four inputs reflect the combination of actions in the last period. The first letter represents the own action in the last period, and the second letter the action of the other player. All strategies in the list strategies.DF2011 have the same structure of choices and inputs.

The data frame DF2011 contains the experimental data collected by Dal Bo and Frechette (2011). The data can be inspected in the console with the command print(DF2011).

The following code creates a stratEst.data frame which fits the structure of strategies:

data.DF2011 <- stratEst.data(data = DF2011, choice = "choice",
                             input = c("choice","other.choice"),
                             input.lag = 1)

The options input = c("choice","other.choice") and input.lag = 1 create the input variable by concatenating the own and the other player’s choice of the previous period. The following model estimation commmand can be used to replicate the findings of Dal Bo and Frechette (2011):

model.DF2011 <- stratEst.model(data = data.DF2011,
                               strategies = strategies.DF2011,
                               sample.id = "treatment" )

The command estimates one vector of shares and one tremble parameter for each treatment. The estimated shares are the strategy shares reported in the first column of Table 7 on page 424 of Dal Bo and Frechette (2011).

print(round(do.call(rbind, model.DF2011$shares), 2))
##                  ALLD ALLC GRIM  TFT WSLS   T2
## treatment.D5R32  0.92 0.00 0.00 0.08 0.00 0.00
## treatment.D5R40  0.78 0.08 0.04 0.10 0.00 0.00
## treatment.D5R48  0.53 0.07 0.00 0.38 0.02 0.00
## treatment.D75R32 0.65 0.00 0.00 0.35 0.00 0.00
## treatment.D75R40 0.11 0.30 0.27 0.33 0.00 0.00
## treatment.D75R48 0.00 0.08 0.12 0.56 0.00 0.24

Fudenberg, Rand and Dreber, 2011

Fudenberg, Rand and Dreber (2011) conduct a prisoner’s dilemma experiment in which intended choices are implemented with noise. The stage-game payoffs are such that cooperation means paying a cost \(c\) to give a benefit \(b\) to the other player. The authors run four between subjects treatments. The cost \(c\) is fixed at 2 points experimental currency in every treatment. The benefit to cost ratio \(b/c\) varies across treatments and took the values 1.5, 2, 2.5, and 4.

Because of the noisy implementation of choices, Fudenberg et al. (2011) add several lenient and forgiving strategies to the original set of candidate strategies used by Dal Bo and Frechette (2011). The augmented set of strategies is available as list object strategies.FRD2012. The choices of the strategies are d(defect), and c (cooperate). The four inputs reflect the four different combinations of the own choice, and the choice of the other player in the previous period.

The data frame FRD2012 contains the raw data of the experiment. It contains two variables that indicate own choice and the choice of the other player in the last period. These two variables are passed to the argument inputs of the data generation function:

data.FRD2012 <- stratEst.data(data = FRD2012, choice = "choice",
                              input =c("last.choice","last.other"))

The following code replicates the strategy shares reported by Fudenberg et al. (2011) in Table 3 on page 733 of the paper.

model.FRD2012 <- stratEst.model(data = data.FRD2012,
                                strategies = strategies.FRD2012,
                                sample.id = "bc")
print(round(do.call(rbind, model.FRD2012$shares), 2))
##        ALLC  TFT TF2T TF3T T2FT T2F2T GRIM GRIM2 GRIM3 ALLD DTFT
## bc.1.5 0.00 0.19 0.05 0.01 0.06  0.00 0.14  0.06  0.06 0.29 0.14
## bc.2   0.03 0.06 0.00 0.03 0.07  0.11 0.07  0.18  0.28 0.17 0.00
## bc.2.5 0.00 0.09 0.17 0.05 0.02  0.11 0.11  0.02  0.24 0.14 0.05
## bc.4   0.07 0.09 0.18 0.13 0.05  0.09 0.06  0.07  0.10 0.14 0.03

For the data the treatment \(b/c\) = 4, the estimation function finds a better solution with a larger log likelihood than the solution reported by Fudenberg et al. (2011).

Dvorak, Fischbacher and Schmelz, 2020

Dvorak, Fischbacher and Schmelz (2020) study conformity and anticonformity in a binary choice experiment. Participants are matched in groups of three and compete for a monetary reward with the other two group members. In some choices, one group member is informed about the choices of two other group members before making the own choice. For these choices, the experimental design allows to predict the prefered alternative of the participant.

Dvorak et al. (2020) find that two-thirds of the participants follow a conformist strategy. The conformist strategy generally follows the own preference if the choices of the other group members are in line with the own preference. It frequently deviates from the own preference and chooses the other alternative if the choices of the other group members are not in line with the own preference.

The remaining one-third of the participants follows an anticonformist strategy. The anticonformist strategy generally follows the own preference if the choices of the other group members are not in line with the own preference. It frequently deviates from the own preference the choices of the other group members are in line with the own preference.

The fitted choice parameters of the strategies are:

print(strategies.DFS2020)
## $anticonformist
##   prob.follow prob.deviate tr(not in line) tr(in line)
## 1       0.823        0.177               1           2
## 2       0.404        0.596               1           2
## 
## $conformist
##   prob.follow prob.deviate tr(not in line) tr(in line)
## 1       0.425        0.575               1           2
## 2       0.860        0.140               1           2

The data set DFS2020 contains the experimental data of Dvorak et al. (2020). The variables "choice" indicates if the choice of the participant follows the own preference or deviates from the own preference. The variable "others.choices" indicates if the choices of the two other two group members are in line with the preference of the participant or not.

The data set additionally contains two the variables which are used as covariates of the strategy estimation model by Dvorak et al. (2020). The first is an intercept, which is one for every observation. The second is the score of the participant in a post-experimental conformity questionnaire (Mehrabian and Stefl, 1995). The mean conformity score is -0.078 with a standard deviation of 1.02.

The following command creates a stratEst.data object with the variable ```others.choices} as input:

data.DFS2020 <- stratEst.data(data = DFS2020,
                              input = c("others.choices"))

The model with covariates is estimated with the command:

model.DFS2020 <- stratEst.model(data = data.DFS2020 ,
                                strategies = strategies.DFS2020,
                                covariates = c("intercept",
                                               "conformity.score"))

The estimated coefficients are:

print(model.DFS2020$coefficients)
##                  anticonformist conformist
## intercept                     0  0.8273618
## conformity.score              0  0.8697285

The first strategy is the reference category of the structural model. The coefficients for the reference category are always zero. The second column contains the estimated coefficients for the conformist strategy. The estimated coefficients indicate that prior probability to use the conformist strategy increases with the score in the conformity questionnaire. The individual prior probabilities of the participants are returned as object model.DFS2020$prior.assignment.

The estimated coefficient of the intercept can be used to calculate the estimated prior probability to use the conformist for a participant with a conformity score of zero. The prior probability is exp(0.83)/(1 + exp(0.83)) = 0.69. A participant who scores on standard deviation higher than average in the conformity questionnaire has a prior probability of exp(0.83 + 0.87)/(1 + exp(0.83 + 0.87)) = 0.85 to use the conformist strategy.

The function stratEst.test() can be used to test whether the estimated coefficients differ from zero.

test.coefficients <- stratEst.test(model.DFS2020, par = "coefficients", plot = FALSE)
print(test.coefficients)
##                    estimate   diff std.error t.value  df p.value
## coefficients.par.1   0.8274 0.8274    0.3065  2.6997 103  0.0081
## coefficients.par.2   0.8697 0.8697    0.3184  2.7319 103  0.0074

The function stratEst.check() can be used to assess the global and local model fit based on the Pearson \(\chi^{2}\) test statistic.

check.DFS2020 <- stratEst.check(model.DFS2020, chi.tests = TRUE,
                                bs.samples = 100)
print(check.DFS2020$chi.global)
##                    chi^2       min     mean     max p.value
## model.DFS2020 0.08554165 0.1041894 2.041561 7.13527       1
print(check.DFS2020$chi.local)
##                    chi^2      min      mean       max p.value
## anticonformist  52.29308 23.08893  47.44783  86.94558    0.34
## conformist     117.70654 63.90004 108.58108 151.07597    0.34

The distribution of the test statistics is approximated by drawing 100 bootstrap samples to limit computation time. The p value of the global test indicates the probability of the data given that the estimated model is the true model. The p value of the local test for the anticonformist strategy indicates the probability of the data of the subset of participants classified as anticonformist given that the fitted strategy is the true strategy. The p value of the test for the conformist strategy can be interpreted in the same way. Hence, both tests address the null hypothesis that the model is the true data generating model.

References

© Fabian Dvorak. Created with Rmarkdown, knitr, and pandoc.