Hi everybody,
I have a question about designing a DCE. I have 2 unlabeled alternatives (Alt1 and Alt2) and also a no-choice option. I have 4 attributes (X1, X2, X3, X4) with their levels mentioned below:
X1: %5 %10 %15 %20
X2: 0=no 1=yes
X3: 0=no 1=yes
X4: 30 40 50 60
Based on what I read, I created this orthogonal design. Here is the design;
%mktruns(4 2 2 4 4 2 2 4) /* factor level list for all attrs and alts */
%mktex(4 2 2 4 4 2 2 4, /* factor level list for all attrs and alts */
n=64, /* number of choice sets */
seed=17) /* random number seed
%mktblock(data=randomized, /* block randomized design */
nblocks=2, /* create 2 blocks of 32 choice sets */
out=blocked, /* output data set for blocked design */
seed=17) /* random number seed */
data key;
input
Brand $ Knd $ Omg $ Mrk $ Price $; datalines;
Alt1 x1 x2 x3 x4
Alt2 x5 x6 x7 x8
None . . . . .
;
%mktroll(design=blocked, /* make choice design from blocked */
/* linear arrangement from %mktblock */
key=key, /* use rules in KEY data set */
alt=brand, /* alternative name variable is Place */
out=work.food64, /* permanent data set for results */
keep=block) /* keep the blocking variable */
proc format;
value knd 1 = ’%5’ 2 = ’%10’ 3 = ’%15’ 4 = ’%20’ . = ’ ’;
value omg 1 = ’no’ 2 = ’yes’ . = ’ ’;
value mrk 1 = ’no’ 2 = ’yes’ . = ’ ’;
value price 1 = $30 2 = $40 3 = $50 4 = $60 . = ’ ’;
run;
data work.food64;
set work.food64;
format knd knd. omg omg. mrk mrk. price price.;
run;
proc print data=work.food64(obs=192);
by set; id set;
run;
When I examined the generated choice sets, I noticed that in some choice sets, every level was the same except for price. For example, the choice set shown below;
What can I do to avoid these choice sets?
Thank you so much!
After cleaning up the quotes, etc., I added a restrictions macro before the mktex step. In it, I count (using Boolean logic where true = 1 and false = 0) the number of pairs across alternatives that are the same. Then if 3 or more are the same, I set badness to the number that are the same. Now when I specify this macro in mktex, it will insert the macro code deep into mktex, ensuring that mktex will try its best to eliminate combinations that you don't want. Note that it is easy to make a mistake and specify restrictions that cannot possibly be satisfied. It is also possible to specify restrictions that are valid, but provide the macro insufficient direction on how to get to the desired result. Hence, I quantified the badness, how far we were from satisfying the condition, rather than a simple binary badness. If you have further questions, ping me, @WarrenKuhfeld, and I will try my best to answer. Also, as was previously pointed out, it is best to spell out things like DCE in your title. If I had seen it in the group, I might not have realized that you were asking a question I could answer. I would have immediately known if I had seen discrete choice. Good luck.
%mktruns(4 2 2 4 4 2 2 4) /* factor level list for all attrs and alts */
%macro res;
_sum = (x1 = x5) + (x2 = x6) + (x3 = x7) + (x4 = x8);
bad = 0;
if _sum >= 3 then bad = _sum;
%mend;
%mktex(4 2 2 4 4 2 2 4, /* factor level list for all attrs and alts */
restrictions=res,
n=64, /* number of choice sets */
seed=17) /* random number seed
%mktblock(data=randomized, /* block randomized design */
nblocks=2, /* create 2 blocks of 32 choice sets */
out=blocked, /* output data set for blocked design */
seed=17) /* random number seed */
data key;
input
Brand $ Knd $ Omg $ Mrk $ Price $; datalines;
Alt1 x1 x2 x3 x4
Alt2 x5 x6 x7 x8
None . . . . .
;
%mktroll(design=blocked, /* make choice design from blocked */
/* linear arrangement from %mktblock */
key=key, /* use rules in KEY data set */
alt=brand, /* alternative name variable is Place */
out=work.food64, /* permanent data set for results */
keep=block) /* keep the blocking variable */
proc format;
value knd 1 = '%5' 2 = '%10' 3 = '%15' 4 = '%20' . = ' ';
value omg 1 = 'no' 2 = 'yes' . = ' ';
value mrk 1 = 'no' 2 = 'yes' . = ' ';
value price 1 = $30 2 = $40 3 = $50 4 = $60 . = ' ';
run;
data work.food64;
set work.food64;
format knd knd. omg omg. mrk mrk. price price.;
run;
proc print data=work.food64(obs=192);
by set; id set;
run;
First thing is to watch the "quote" character used. Your proc format is showing the use of "smart" quotes using a curly character. This is not treated as a quote by SAS.
Note
proc format; value knd 1 = ’%5’ 2 = ’%10’ 3 = ’%15’ 4 = ’%20’ . = ’ ’; value omg 1 = ’no’ 2 = ’yes’ . = ’ ’; value mrk 1 = ’no’ 2 = ’yes’ . = ’ ’; value price 1 = $30 2 = $40 3 = $50 4 = $60 . = ’ ’; run;
Use the typed quote using the ' or " key on the key board
proc format; value knd 1 = '%5' 2 = '%10' 3 = '%15' 4 = '%20' . = ' '; run;
Those curly quotes typically come from copy/paste of a document, usually a word processing document, to make "pretty text" and can cause so odd behaviors. Depending on some of your system settings you may not see them all the time but will appear.
With no idea of what the macros you show do, as in the code, or input data can't actually say why your output may be such.
One thing is that use of the structure with the same data set on DATA and SET statements completely replaces the source data set. So somewhere in one of those macros you may have replaced your data set variables in such a way those variables only contain one value.
data work.food64; set work.food64;
Okay ... a bit of context here ... for the readers of your post.
This is about a Discrete Choice Experiment and you are using the (quite old) SAS macro's explained on this page:
SAS Macros for Experimental Design and Choice Modeling
Here are some good resources (also discussing topics like number and size of choice sets):
Maybe @WarrenKuhfeld can help further?
Ciao, Koen
After cleaning up the quotes, etc., I added a restrictions macro before the mktex step. In it, I count (using Boolean logic where true = 1 and false = 0) the number of pairs across alternatives that are the same. Then if 3 or more are the same, I set badness to the number that are the same. Now when I specify this macro in mktex, it will insert the macro code deep into mktex, ensuring that mktex will try its best to eliminate combinations that you don't want. Note that it is easy to make a mistake and specify restrictions that cannot possibly be satisfied. It is also possible to specify restrictions that are valid, but provide the macro insufficient direction on how to get to the desired result. Hence, I quantified the badness, how far we were from satisfying the condition, rather than a simple binary badness. If you have further questions, ping me, @WarrenKuhfeld, and I will try my best to answer. Also, as was previously pointed out, it is best to spell out things like DCE in your title. If I had seen it in the group, I might not have realized that you were asking a question I could answer. I would have immediately known if I had seen discrete choice. Good luck.
%mktruns(4 2 2 4 4 2 2 4) /* factor level list for all attrs and alts */
%macro res;
_sum = (x1 = x5) + (x2 = x6) + (x3 = x7) + (x4 = x8);
bad = 0;
if _sum >= 3 then bad = _sum;
%mend;
%mktex(4 2 2 4 4 2 2 4, /* factor level list for all attrs and alts */
restrictions=res,
n=64, /* number of choice sets */
seed=17) /* random number seed
%mktblock(data=randomized, /* block randomized design */
nblocks=2, /* create 2 blocks of 32 choice sets */
out=blocked, /* output data set for blocked design */
seed=17) /* random number seed */
data key;
input
Brand $ Knd $ Omg $ Mrk $ Price $; datalines;
Alt1 x1 x2 x3 x4
Alt2 x5 x6 x7 x8
None . . . . .
;
%mktroll(design=blocked, /* make choice design from blocked */
/* linear arrangement from %mktblock */
key=key, /* use rules in KEY data set */
alt=brand, /* alternative name variable is Place */
out=work.food64, /* permanent data set for results */
keep=block) /* keep the blocking variable */
proc format;
value knd 1 = '%5' 2 = '%10' 3 = '%15' 4 = '%20' . = ' ';
value omg 1 = 'no' 2 = 'yes' . = ' ';
value mrk 1 = 'no' 2 = 'yes' . = ' ';
value price 1 = $30 2 = $40 3 = $50 4 = $60 . = ' ';
run;
data work.food64;
set work.food64;
format knd knd. omg omg. mrk mrk. price price.;
run;
proc print data=work.food64(obs=192);
by set; id set;
run;
Thank you very much for the response. I ran my code again. But I noticed a new problem. When I examine the choice sets that were created, I think that some choice sets are not realistic. For example, the choice set shown below;
The Alt1 has good features ( %10 yes yes ) but it has a low price. The Alt2 has no good features ( %5 no no ) but has a higher price. What can I do to avoid these choice sets?
Thank you very much again.
You can build additional restrictions into the restrictions macro. You must ensure that you are adding additional sources to the badness criterion. I'll play with it and post again later today or tomorrow.
Thank you very much again for answering my follow-up questions.
Thank you very much for your support. I will consider your suggestions for my studies.
See if you like this better. The additional restrictions are as follows:
_sum = (x1 >= x5) + (x2 >= x6) + (x3 >= x7); if _sum = 0 | _sum = 3 then bad = bad + 1; _sum = (x1 <= x5) + (x2 <= x6) + (x3 <= x7); if _sum = 0 | _sum = 3 then bad = bad + 1;
First, I count the number of times attributes 1-3 in alternative 1 dominate attributes 1-3 in alternative 2 using Boolean arithmetic. Then I increase the badness criterion if there are not 1 or 2 attributes in those first three for alternative 1. Then I do the same thing for alternative 2. I let price do whatever it wants because nothing is dominated in the rest of the design. If this is not precisely what you want, restrictions can be tweaked or augmented. Be aware though that every time you impose a restriction, it decreases the efficiency of the design, the ability to estimate all the parameters. I always recommend (and I should have thought to say this in my last answer, but I have been retired for 6 years) that you use the ChoicEff macro to check the final choice design efficiency. There are lots of examples of it in my choice design writings.
%mktruns(4 2 2 4 4 2 2 4) /* factor level list for all attrs and alts */
%macro res;
_sum = (x1 = x5) + (x2 = x6) + (x3 = x7) + (x4 = x8);
bad = 0;
if _sum >= 3 then bad = _sum;
_sum = (x1 >= x5) + (x2 >= x6) + (x3 >= x7);
if _sum = 0 | _sum = 3 then bad = bad + 1;
_sum = (x1 <= x5) + (x2 <= x6) + (x3 <= x7);
if _sum = 0 | _sum = 3 then bad = bad + 1;
%mend;
%mktex(4 2 2 4 4 2 2 4, /* factor level list for all attrs and alts */
restrictions=res,
n=64, /* number of choice sets */
seed=17) /* random number seed */
%mktblock(data=randomized, /* block randomized design */
nblocks=2, /* create 2 blocks of 32 choice sets */
out=blocked, /* output data set for blocked design */
seed=17) /* random number seed */
data key;
input
Brand $ Knd $ Omg $ Mrk $ Price $; datalines;
Alt1 x1 x2 x3 x4
Alt2 x5 x6 x7 x8
None . . . . .
;
%mktroll(design=blocked, /* make choice design from blocked */
/* linear arrangement from %mktblock */
key=key, /* use rules in KEY data set */
alt=brand, /* alternative name variable is Place */
out=work.food64, /* permanent data set for results */
keep=block) /* keep the blocking variable */
proc format;
value knd 1 = '%5' 2 = '%10' 3 = '%15' 4 = '%20' . = ' ';
value omg 1 = 'no' 2 = 'yes' . = ' ';
value mrk 1 = 'no' 2 = 'yes' . = ' ';
value price 1 = $30 2 = $40 3 = $50 4 = $60 . = ' ';
run;
data work.food64;
set work.food64;
format knd knd. omg omg. mrk mrk. price price.;
run;
proc print data=work.food64(obs=192);
by set; id set;
run;
I ran the macros. As you said, efficiency decreased as the restriction increased:
I ran the macros again by restricting only the first feature ( %5 %10 %15 %20 ) with the following restriction:
%macro res;
if x1 = x5 then bad = 1;
%mend;
Its efficiency is as follows:
It seems okay. I hope I didn't do anything wrong. Thank you again for your answers. 🙏
If there is no mistake in this restriction,
%macro res;
if x1 = x5 then bad = 1;
%mend;
I think it will be the most suitable for the purposes of my work. Thank you again for all your help.
Syntactically it is valid, but it is not going to avoid dominated alternatives and other things you wanted. Designing a realistic choice study is not strictly about maximizing design efficiency. We can never actually even know what efficiency is. All we can do is assume a beta vector, typically zero, and go from there. More important is ensuring the design is realistic--no dominated or unrealistic choice sets--and further ensure that all parameters that make sense can be estimated with some realistic degree of precision. Hence the recommendation to use the ChoicEff macro to evaluate the final design. These topics are discussed in great detail here. https://support.sas.com/techsup/technote/mr2010c.pdf I know it can be a bit technical at times, but I tried really hard to spell out everything I have ever known on this topic in this piece. Best of luck in your research!
It's finally time to hack! Remember to visit the SAS Hacker's Hub regularly for news and updates.
ANOVA, or Analysis Of Variance, is used to compare the averages or means of two or more populations to better understand how they differ. Watch this tutorial for more.
Find more tutorials on the SAS Users YouTube channel.