jhelvy / cbcTools

An R package with tools for designing choice based conjoint (cbc) survey experiments and conducting power analyses
https://jhelvy.github.io/cbcTools/
Other
5 stars 5 forks source link

cbc_design returns a Bayesian D-Efficient design as a list instead of dataframe #20

Closed mmardehali closed 1 year ago

mmardehali commented 1 year ago

After updating the R packages (which updated cbcTools from v0.3.4 to v0.4.0 for me), I noticed that my code wasn't functioning properly anymore. After checking the updates to the package, and my code, I noticed that the output of cbc_design function has changed from Data Frame to List. I suspect this has to do with the addition of reporting db_err. In my old code, I used to isolate only the design for the first respondent (respID = 1) to inspect the design for balance and overlap, I call it the first design. I did this because in my case (1 block, unlabeled experiment) cbc_design repeats the first design for all respondents (as determined by n_resp). This was causing the problem in my old code, as I was extracting the first design using the head function. By manipulating the object type (using as.data.frame), I am now getting the first design correctly, however, db_err is now added as a column and repeats the same value for all rows. I was wondering if it's possible that:

  1. the output of cbc_design can only consist of the efficient design (so cbc_design returns a data frame that contains only the profileID, respID, qID, altID, obsID, attributes, and blockID).
  2. the value of db_err can be accessed independently so it can be assigned to a variable and reported separately (e.g. DBError <- Design(db_err), or a similar method)

While obviously the output list from cbc_design can be manipulated to present a separate data frame for the design, and a value for db_err that can then be assigned to a variable, I think presenting the output as described above creates less complexity for using other functions in this package (e.g. inspecting the design using cbc_balance and cbc_overlap, or choice and power analysis simulation). Currently, the raw output of cbc_design, at least in my case, is incompatible with other functions in the package.

Here's my code, and the output will be pasted below the code:

library("cbcTools")
library("logitr")

Nresp = 150 #identify number of respondents for which the design is optimized
Nchoice = 16 #global variable for the number of choice sets
Nalt = 4 #global variable for the number of alternatives per choice set
HeadN <- Nalt*Nchoice #secondary variable to only print out the first respID set in the DB-Efficient design, other respIDs are just repetition of the same design.

#create full-factorial design based on attributes and levels
profiles <- cbc_profiles(
  cost = seq(20, 30, 5), #generates sequence of numbers between 20 and 30 inclusive, 5 by 5. --Ratio Data
  brand = c("O", "HH"), #--Nominal Data
  usrrt = seq(30, 20, -5), #generates sequence of numbers between 4.8 and 3.2 inclusive, -0.8 by -0.8, to have the highest rating first so the top generated profile is the best. --Ratio Data
  accinf = c("H", "M", "L", "U") #--Ordinal Data
)

#restrict dominant alternative and worst alternative
rstrct_profiles <- cbc_restrict(
  profiles,
  cost == 20 & brand == "O" & usrrt == 30 & accinf == "H", #exclude dominant alternative with the best levels for each attribute
  cost == 30 & brand == "HH" & usrrt == 20 & accinf == "U" #exclude the worst alternative with the worst levels for each attribute
)

#create DB-efficient design from restricted full-factorial design with zero priors
design_dbeff <- cbc_design(
  profiles = rstrct_profiles,
  n_resp = Nresp,
  n_alts = Nalt, #number of alternatives in each choice set
  n_q = Nchoice, #number of "questions" or choice sets
  n_start = 20, #numeric value indicating the number of random start designs to use
  priors = list(
    cost = 0,
    brand = 0,
    usrrt = 0,
    accinf = c(0, 0, 0)
  ),
  max_iter = 1000,
  method = "Modfed",
  keep_db_error = TRUE,
  parallel = TRUE
)

typeof(design_dbeff)

DF_design_dbeff <- as.data.frame(design_dbeff)

FirstDesign <- DF_design_dbeff[1:HeadN,] #Only save the design for the first respID, the rest of the respIDs are repititions of the first design
FirstDesign

Output:

> typeof(design_dbeff)
[1] "list"
> 
> DF_design_dbeff <- as.data.frame(design_dbeff) #converting list to data frame
> 
> FirstDesign <- DF_design_dbeff[1:HeadN,] #Only save the design for the first respID, the rest of the respIDs are repititions of the first design
> FirstDesign
   design.profileID design.respID design.qID design.altID design.obsID design.cost design.brand design.usrrt design.accinf design.blockID    db_err
1                10             1          1            1            1          25           HH           25             H              1 0.4261433
2                25             1          1            2            1          25            O           25             M              1 0.4261433
3                64             1          1            3            1          25           HH           25             U              1 0.4261433
4                37             1          1            4            1          25            O           30             L              1 0.4261433
5                14             1          2            1            2          30            O           20             H              1 0.4261433
6                61             1          2            2            2          25            O           25             U              1 0.4261433
7                35             1          2            3            2          30           HH           20             M              1 0.4261433
8                46             1          2            4            2          25           HH           25             L              1 0.4261433
9                63             1          3            1            3          20           HH           25             U              1 0.4261433
10               40             1          3            2            3          25           HH           30             L              1 0.4261433
11               22             1          3            3            3          25           HH           30             M              1 0.4261433
12                6             1          3            4            3          20            O           25             H              1 0.4261433
13               50             1          4            1            4          30            O           20             L              1 0.4261433
14               17             1          4            2            4          30           HH           20             H              1 0.4261433
15               20             1          4            3            4          30            O           30             M              1 0.4261433
16               59             1          4            4            4          30           HH           30             U              1 0.4261433
17               25             1          5            1            5          25            O           25             M              1 0.4261433
18               38             1          5            2            5          30            O           30             L              1 0.4261433
19               63             1          5            3            5          20           HH           25             U              1 0.4261433
20               59             1          5            4            5          30           HH           30             U              1 0.4261433
21               53             1          6            1            6          30           HH           20             L              1 0.4261433
22               66             1          6            2            6          20            O           20             U              1 0.4261433
23               33             1          6            3            6          20           HH           20             M              1 0.4261433
24               14             1          6            4            6          30            O           20             H              1 0.4261433
25                5             1          7            1            7          30           HH           30             H              1 0.4261433
26               56             1          7            2            7          30            O           30             U              1 0.4261433
27               49             1          7            3            7          25            O           20             L              1 0.4261433
28               34             1          7            4            7          25           HH           20             M              1 0.4261433
29               63             1          8            1            8          20           HH           25             U              1 0.4261433
30               20             1          8            2            8          30            O           30             M              1 0.4261433
31                6             1          8            3            8          20            O           25             H              1 0.4261433
32               45             1          8            4            8          20           HH           25             L              1 0.4261433
33               30             1          9            1            9          20            O           20             M              1 0.4261433
34                8             1          9            2            9          30            O           25             H              1 0.4261433
35               69             1          9            3            9          20           HH           20             U              1 0.4261433
36               47             1          9            4            9          30           HH           25             L              1 0.4261433
37               17             1         10            1           10          30           HH           20             H              1 0.4261433
38               53             1         10            2           10          30           HH           20             L              1 0.4261433
39                4             1         10            3           10          25           HH           30             H              1 0.4261433
40               32             1         10            4           10          30            O           20             M              1 0.4261433
41                4             1         11            1           11          25           HH           30             H              1 0.4261433
42               19             1         11            2           11          25            O           30             M              1 0.4261433
43               61             1         11            3           11          25            O           25             U              1 0.4261433
44               46             1         11            4           11          25           HH           25             L              1 0.4261433
45               34             1         12            1           12          25           HH           20             M              1 0.4261433
46               68             1         12            2           12          30            O           20             U              1 0.4261433
47                7             1         12            3           12          25            O           25             H              1 0.4261433
48               46             1         12            4           12          25           HH           25             L              1 0.4261433
49               43             1         13            1           13          25            O           25             L              1 0.4261433
50                3             1         13            2           13          20           HH           30             H              1 0.4261433
51               54             1         13            3           13          20            O           30             U              1 0.4261433
52               23             1         13            4           13          30           HH           30             M              1 0.4261433
53                8             1         14            1           14          30            O           25             H              1 0.4261433
54               42             1         14            2           14          20            O           25             L              1 0.4261433
55                9             1         14            3           14          20           HH           25             H              1 0.4261433
56               65             1         14            4           14          30           HH           25             U              1 0.4261433
57               28             1         15            1           15          25           HH           25             M              1 0.4261433
58               10             1         15            2           15          25           HH           25             H              1 0.4261433
59               43             1         15            3           15          25            O           25             L              1 0.4261433
60               60             1         15            4           15          20            O           25             U              1 0.4261433
61               68             1         16            1           16          30            O           20             U              1 0.4261433
62               27             1         16            2           16          20           HH           25             M              1 0.4261433
63                6             1         16            3           16          20            O           25             H              1 0.4261433
64               17             1         16            4           16          30           HH           20             H              1 0.4261433
mmardehali commented 1 year ago

Current fix: I use the following code to circumvent the complexity of converting list to data frame (shown above):

DBerr <- design_dbeff$db_err
DF_design_dbeff <- as.data.frame(design_dbeff$design)
FirstDesign <- DF_design_dbeff[1:HeadN,]

This allows for assigning db_err value to DBerr, reading the actual design as a data frame and assigning it to DF_design_dbeff, and saving only the first design. The first design (or DF_design_dbeff) can now be used seamlessly with the rest of the cbc functions.

jhelvy commented 1 year ago

I added the keep_db_error argument so that you can get both the design and DB error back. If you set this to TRUE, it returns a list with the two objects db_err and design. So like you just did in your example, you can extract these using the $ symbol. This is all documented if you check the help for this function: ?cbc_design

This was implemented in v0.3.4 as noted in this issue.