Resources

Conjoint Analysis

 /****************************************************************/
 /*          S A S   S A M P L E   L I B R A R Y                 */
 /*                                                              */
 /*    NAME: MR2010H                                             */
 /*   TITLE: Conjoint Analysis                                   */
 /* PRODUCT: STAT                                                */
 /*  SYSTEM: ALL                                                 */
 /*    KEYS: marketing research                                  */
 /*   PROCS: TRANSREG, OPTEX, FACTEX, PLAN, IML                  */
 /*    DATA:                                                     */
 /*                                                              */
 /* SUPPORT: saswfk                      UPDATE:  01Oct2010      */
 /*     REF: MR-2010H Conjoint Analysis                          */
 /*    MISC: This file contains all of the sample code for       */
 /*          the "Conjoint Analysis" report,                     */
 /*          the 10Oct2010 edition, for SAS 9.2.                 */
 /*                                                              */
 /*          You must install the following macros from          */
 /* http://support.sas.com/techsup/tnote/tnote_stat.html#market  */
 /*                                                              */
 /*          ChoicEff Efficient Choice Designs                   */
 /*          MktAllo  Process a Choice Allocation Study Data Set */
 /*          MktBal   Balanced Experimental Design               */
 /*          MktBIBD  Balanced Incomplete Block Design           */
 /*          MktBlock Block an Experimental Design               */
 /*          MktBSize Balanced Incomplete Block Design Sizes     */
 /*          MktDes   Efficient Experimental Designs             */
 /*          MktDups  Eliminate Duplicate Runs or Choice Sets    */
 /*          MktEval  Evaluate an Experimental Design            */
 /*          MktEx    Efficient Experimental Designs             */
 /*          MktKey   Aid Making the MktRoll KEY= Data Set       */
 /*          MktLab   Change Names, Levels in a Design           */
 /*          MktMDiff MaxDiff (Best-Worst) Choice Modeling       */
 /*          MktMerge Merge a Choice Design with Choice Data     */
 /*          MktOrth  List Orthogonal Designs MktEx Can Make     */
 /*          MktPPro  Optimal Partial Profile Designs            */
 /*          MktRoll  Roll Out a Design Into a Choice Design     */
 /*          MktRuns  Experimental Design Sizes                  */
 /*          PhChoice Customize PROC PHREG for Choice Models     */
 /*                                                              */
 /****************************************************************/

options ls=80 ps=60 nonumber nodate;

***************** Begin TRANSREG Output Customization *****************;

proc template;
   edit Stat.Transreg.ParentUtilities;
      column Label Utility StdErr tValue Probt Importance Variable;
      header title;
      define title; text 'Part-Worth Utilities'; space=1; end;
      define Variable; print=off; end;
      end;
   run;

* Delete edited template, restore original template;
proc template;
   delete Stat.Transreg.ParentUtilities;
   run;

proc template;
   source stat.transreg;
   run;

proc template;
   edit Stat.Transreg.ParentUtilities;
      column Label Utility StdErr tValue Probt Importance Variable;
      header title;
      define title; text 'Part-Worth Utilities'; space=1; end;
      define Variable; print=off; end;
      end;
   run;

********************** Begin Candy Example Code ***********************;

options ls=80 ps=60 nonumber nodate;
title;

title 'Preference for Chocolate Candies';

data choc;
   input Chocolate $ Center $ Nuts $& Rating;
   datalines;
Dark  Chewy  Nuts       7
Dark  Chewy  No Nuts    6
Dark  Soft   Nuts       6
Dark  Soft   No Nuts    4
Milk  Chewy  Nuts       9
Milk  Chewy  No Nuts    8
Milk  Soft   Nuts       9
Milk  Soft   No Nuts    7
;

ods exclude notes mvanova anova;
proc transreg utilities separators=', ' short;
   title2 'Metric Conjoint Analysis';
   model identity(rating) = class(chocolate center nuts / zero=sum);
   run;

ods graphics on;

ods exclude notes anova liberalanova conservanova
            mvanova liberalmvanova conservmvanova;
proc transreg utilities separators=', ' plots=transformations;
   title2 'Nonmetric Conjoint Analysis';
   model monotone(rating) = class(chocolate center nuts / zero=sum);
   output;
   run;

************* Begin Basic Frozen Diet Entrees Example Code ************;

options ls=80 ps=60 nonumber nodate;
title;

title 'Frozen Diet Entrees';

%mktruns(3 3 3 2)

%mktex(3 3 3 2, n=18)

%mktex(3 3 3 2, n=18, seed=151)

%mktlab(data=randomized, vars=Ingredient Fat Price Calories)

* Due to machine differences, you may not get the same design if you run
* the step above, so here is the design that was used in the book;
data randomized; input x1-x4 @@; datalines;
3 2 3 1 3 1 2 1 1 1 3 1 3 3 1 2 2 1 1 1 2 3 3 1 2 2 2 1 2 2 2 2 1 3 2 1
2 1 1 2 3 1 2 2 1 2 1 1 1 2 1 2 1 3 2 2 3 2 3 2 3 3 1 1 2 3 3 2 1 1 3 2
;
%mktlab(vars=Ingredient Fat Price Calories)

proc format;
   value if  1='Chicken'  2='Beef'     3='Turkey';
   value ff  1='8 Grams'  2='5 Grams'  3='2 Grams';
   value pf  1='$2.59'    2='$2.29'    3='$1.99';
   value cf  1='350'      2='250';
   run;

data sasuser.dietdes;
   set final;
   format ingredient if. fat ff. price pf. calories cf.;
   run;

proc print; run;

%mkteval(data=sasuser.dietdes)

%mktdups(linear, data=sasuser.dietdes)

title;
data _null_;
   file print;
   set sasuser.dietdes;
   put ///
       +3 ingredient 'Entree' @50 '(' _n_ +(-1) ')' /
       +3 'With ' fat 'of Fat and ' calories  'Calories' /
       +3 'Now for Only ' Price +(-1) '.'///;
   if mod(_n_, 6) = 0 then put _page_;
   run;

title 'Frozen Diet Entrees';

data results;
   input combo1-combo18;
   datalines;
17 6 8 7 10 5 4 16 15 1 11 2 9 14 12 13 3 18
;

proc transpose out=results(rename=(col1=combo)); run;

data results; set results; Rank = _n_; drop _name_; run;

proc sort; by combo; run;

data results(drop=combo);
   merge sasuser.dietdes results;
   run;

proc print; run;

ods exclude notes anova liberalanova conservanova
            mvanova liberalmvanova conservmvanova;
proc transreg utilities order=formatted separators=', ';
   model monotone(rank / reflect) =
         class(Ingredient Fat Price Calories / zero=sum);
   output out=utils p ireplace;
   run;

proc sort; by descending prank; run;

proc print label;
   var ingredient fat price calories rank trank prank;
   label trank = 'Reflected Rank'
         prank = 'Utilities';
   run;

*********** Begin Advanced Frozen Diet Entrees Example Code ***********;

options ls=80 ps=60 nonumber nodate;
title;

title 'Frozen Diet Entrees';

proc format;
   value if  1='Chicken'  2='Beef'     3='Turkey';
   value ff  1='8 Grams'  2='5 Grams'  3='2 Grams';
   value pf  1='$2.59'    2='$2.29'    3='$1.99';
   value cf  1='350'      2='250';
   run;

%mktex(3 3 3 2, n=18, seed=205)

%mktlab(data=randomized, vars=Ingredient Fat Price Calories)

%mkteval(data=final)

title 'Frozen Diet Entrees';

proc format;
   value if  1='Chicken'  2='Beef'     3='Turkey';
   value ff  1='8 Grams'  2='5 Grams'  3='2 Grams';
   value pf  1='$2.59'    2='$2.29'    3='$1.99';
   value cf  1='350'      2='250';
   run;

%mktex(3 3 3 2, n=18, seed=205)

%mktex(3 3 3 2,                     /* 3 three-level and a two-level factor */
       n=22,                        /* 22 runs                              */
       init=randomized,             /* initial design                       */
       holdouts=4,                  /* add four holdouts to init design     */
       options=nodups,              /* no duplicate rows in design          */
       seed=368)                    /* random number seed                   */

* Due to machine differences, you may not get the same design if you run
* the step above, so here is the design that was used in the book;
data randomized; input x1-x4 w @@; datalines;
2 3 1 2 . 2 2 1 2 1 3 3 3 1 1 3 3 3 2 1 3 1 1 1 1 1 3 1 1 1 1 3 1 2 1 1
1 2 1 1 2 2 1 1 1 3 2 2 2 1 2 2 3 1 . 2 3 2 2 1 3 1 1 2 1 2 1 3 2 1 3 2
1 1 . 1 2 3 1 1 1 1 2 2 1 1 2 2 2 . 2 3 2 1 1 3 2 2 1 1 1 2 3 2 1 2 1 3
1 1
;

proc print data=randomized; run;

%mkteval(data=randomized(where=(w=1)), factors=x:)
%mkteval(data=randomized(drop=w))

%mktlab(data=randomized, out=sasuser.dietdes,
        vars=Ingredient Fat Price Calories,
        statements=format Ingredient if. fat ff. price pf. calories cf.)

proc print; run;

title;
data _null_;
   file print;
   set sasuser.dietdes;
   put ///
       +3 ingredient 'Entree' @50 '(' _n_ +(-1) ')' /
       +3 'With ' fat 'of Fat and ' calories  'Calories' /
       +3 'Now for Only ' Price +(-1) '.'///;
   if mod(_n_, 6) = 0 then put _page_;
   run;

title 'Frozen Diet Entrees';

%let m = 22; /* number of combinations */

* Read the input data and convert to ranks;
data ranks(drop=i k c1-c&m);
   input c1-c&m;
   array c[&m];
   array r[&m];
   do i = 1 to &m;
      k = c[i];
      if 1 le k le &m then do;
         if r[k] ne . then
            put 'ERROR: For subject ' _n_ +(-1) ', combination ' k
                'is given more than once.';
         r[k] = i; /* Convert to ranks. */
         end;
      else put 'ERROR: For subject ' _n_ +(-1) ', combination ' k
                'is invalid.';
      end;

   do i = 1 to &m;
      if r[i] = . then
         put 'ERROR: For subject ' _n_ +(-1) ', combination ' i
             'is not given.';
      end;
   name = 'Subj' || put(_n_, z2.);
   datalines;
4 3 7 21 12 10 6 19 1 16 18 11 20 14 17 15 2 22 9 8 13 5
4 12 3 1 19 7 10 6 11 21 16 2 18 20 15 9 14 22 13 17 5 8
4 3 7 12 19 21 1 6 10 18 16 11 20 15 2 14 9 17 22 8 13 5
4 12 1 10 21 14 18 3 7 2 17 13 19 11 22 20 16 15 6 9 5 8
4 21 14 11 16 3 12 22 19 18 10 17 8 20 7 1 6 2 9 13 15 5
4 21 16 12 3 14 11 22 18 19 7 10 1 17 8 6 2 20 9 13 15 5
12 4 19 1 3 7 6 21 18 11 16 2 10 20 9 15 14 17 22 8 13 5
4 21 3 16 14 11 12 22 18 10 19 20 17 8 7 6 1 2 13 15 9 5
4 21 3 16 11 14 22 12 18 10 20 19 17 8 7 6 1 13 15 2 9 5
4 3 14 11 21 12 16 22 19 10 18 20 17 1 7 8 2 13 9 6 15 5
15 22 17 21 6 11 13 19 4 12 3 18 9 7 1 10 8 20 14 16 5 2
12 4 3 7 21 19 1 18 11 6 16 2 14 10 17 22 20 9 15 8 13 5
;

proc transpose data=ranks out=ranks2;
   id name;
   run;

data both;
   merge sasuser.dietdes ranks2;
   drop _name_;
   run;

proc print label;
   title2 'Data and Design Together';
   run;

proc format;
   value wf 1 = 'Active'
            . = 'Holdout'
            0 = 'Simulation';
   run;

%mktex(3 3 3 2, n=3*3*3*2)
%mktlab(data=design, vars=Ingredient Fat Price Calories)

data all;
   set both final(in=f);
   if f then w = 0;
   format w wf.;
   run;

proc print data=all(Obs=25 drop=subj04-subj12) label;
   title2 'Some of the Final Data Set';
   run;

ods exclude notes mvanova anova;
proc transreg data=all utilities short separators=', '
   method=morals outtest=utils;
   title2 'Conjoint Analysis';
   model identity(subj: / reflect) =
         class(Ingredient Fat Price Calories / zero=sum);
   weight w;
   output p ireplace out=results coefficients;
   run;

proc print data=results(drop=_depend_ t_depend_ intercept &_trgind) label;
   title2 'Predicted Utility';
   where w ne 0 and _depvar_ le 'Identity(Subj02)' and not (_type_ =: 'M');
   by _depvar_;
   label p_depend_ = 'Predicted Utility';
   run;

ods output KendallCorr=k PearsonCorr=p;
ods listing close;
proc corr nosimple noprob kendall pearson
   data=results(where=(w=.));
   title2 'Holdout Validation Results';
   var p_depend_;
   with t_depend_;
   by notsorted _depvar_;
   run;
ods listing;

data both(keep=subject pearson kendall);
   length Subject 8;
   merge p(rename=(p_depend_=Pearson))
         k(rename=(p_depend_=Kendall));
   subject = input(substr(_depvar_, 14, 2), best2.);
   run;

proc print; run;

data results2;
   set results;
   if not (index(_depvar_, '11'));
   run;

data utils2;
   set utils;
   if not (index(_depvar_, '11'));
   run;

proc sort data=results2(where=(w=0)) out=sims(drop=&_trgind);
   by _depvar_ descending p_depend_;
   run;

data sims; /* Pull out first 10 for each subject. */
   set sims;
   by _depvar_;
   retain n 0;
   if first._depvar_ then n = 0;
   n = n + 1;
   if n le 10;
   drop w _depend_ t_depend_ n _name_ _type_ intercept;
   run;

proc print data=sims label;
   by _depvar_ ;
   title2 'Simulations Sorted by Decreasing Predicted Utility';
   title3 'Just the Ten Most Preferred Combinations are Printed';
   label p_depend_ = 'Predicted Utility';
   run;

proc contents data=utils2 position;
   ods select position;
   title2 'Variables in the OUTTEST= Data Set';
   run;

proc print data=utils2 label;
   title2 'R-Squares';
   id _depvar_;
   var value;
   format value 4.2;
   where statistic = 'R-Square';
   label value = 'R-Square' _depvar_ = 'Subject';
   run;

data im;
   set utils2;
   if n(importance);   /* Exclude all missing, including specials.*/
   _depvar_ = scan(_depvar_, 2);   /* Discard transformation.     */
   label    = scan(label, 1, ','); /* Use up to comma for label.  */
   keep importance _depvar_ label;
   run;

proc transpose data=im out=im(drop=_name_ _label_);
   id label;
   by notsorted _depvar_;
   var importance;
   label _depvar_ = 'Subject';
   run;

proc print label;
   title2 'Importances';
   format _numeric_ 2.;
   id _depvar_;
   run;

proc means mean;
   title2 'Average Importances';
   run;

data im2;
   set im;
   label c1 = 'Ingredient' c2 = 'Fat' c3 = 'Price' c4 = 'Calories';
   c1 = put(ingredient, 2.) || substr('  ******', 1, ceil(ingredient / 15));
   c2 = put(fat       , 2.) || substr('  ******', 1, ceil(fat        / 15));
   c3 = put(price     , 2.) || substr('  ******', 1, ceil(price      / 15));
   c4 = put(calories  , 2.) || substr('  ******', 1, ceil(calories   / 15));
   run;

proc print label;
   title2 'Importances';
   var c1-c4;
   id _depvar_;
   run;

proc print data=results2 label;
   title2 'Part-Worth Utilities';
   where _type_ = 'M COEFFI';
   id _name_;
   var &_trgind;
   run;

proc fastclus data=results2 maxclusters=3 out=clusts;
   where _type_ = 'M COEFFI';
   id _name_;
   var &_trgind;
   run;

proc sort; by cluster; run;

proc print label;
   title2 'Part-Worth Utilities, Clustered';
   by cluster;
   id _name_;
   var &_trgind;
   run;

****************** Begin Spaghetti Sauce Example Code *****************;

options ls=80 ps=60 nonumber nodate;
title;

title 'Spaghetti Sauces';

%mktruns(3 3 2 2 5)

%mktruns(3 3 2 2 5, interact=1*5)

title 'Spaghetti Sauces';

proc format;
   value br 1='Pregu'       2='Sundance'  3='Tomato Garden';
   value me 1='Vegetarian'  2='Meat'      3='Italian Sausage';
   value mu 1='Mushrooms'   2='No Mention';
   value in 1='All Natural' 2='No Mention';
   value pr 1='1.99' 2='2.29' 3='2.49' 4='2.79' 5='2.99';
   run;

%macro resmac;
   Brand    = {'P' 'S' 'T'}[x1];
   Meat     = {'V' 'M' 'I'}[x2];
   Mushroom = {'M' ' '}[x3];
   Natural  = {'A' ' '}[x4];
   Price    = {1.99 2.29 2.49 2.79 2.99}[x5];
   bad      = (meat = 'I' & natural = 'A') +
              (price = 1.99 & (meat = 'M' | meat = 'I'));
   %mend;

%mktex(3 3 2 2 5,                   /* all of the factor levels             */
       interact=1*5,                /* x1*x5 interaction                    */
       n=30,                        /* 30 runs                              */
       seed=289,                    /* random number seed                   */
       restrictions=resmac)         /* name of restrictions macro           */

* Due to machine differences, you may not get the same design if you run
* the step above, so here is the design that was used in the book;
data randomized; input x1-x5 @@; datalines;
1 2 2 2 4 3 1 2 2 4 1 2 1 1 2 3 1 1 1 3 2 1 1 2 1 1 3 2 2 3 3 1 2 2 5 3
3 1 2 2 1 1 1 2 3 1 1 2 2 2 2 1 1 2 4 3 1 1 2 1 2 2 2 2 2 2 2 1 2 5 1 3
1 2 4 3 3 1 2 5 2 1 1 1 2 1 2 1 1 5 3 2 2 2 3 2 2 1 1 3 1 1 2 1 1 2 2 2
1 4 3 1 2 1 1 2 3 2 2 3 2 1 2 1 1 2 1 2 1 5 1 3 2 2 5 3 1 2 1 2 1 1 1 2
1 3 2 1 1 4
;

%mktlab(data=randomized, vars=Brand Meat Mushroom Ingredients Price,
        statements=format brand br. meat me. mushroom mu.
                   ingredients in. price pr.,
        out=sasuser.spag)

%mkteval(data=sasuser.spag)

proc print data=sasuser.spag; run;

options ls=80 ps=74 nonumber nodate;
title;

data _null_;
   set sasuser.spag;
   length lines $ 500 aline $ 60;
   file print linesleft=ll;

   * Format meat level, preserve 'Italian' capitalization;
   aline = lowcase(put(meat, me.));
   if aline =: 'ita' then substr(aline, 1, 1) = 'I';

   * Format meat differently for 'vegetarian';
   if meat > 1
      then lines = 'Try ' || trim(put(brand, br.)) ||
                   ' brand spaghetti sauce with ' || aline;
      else lines = 'Try ' || trim(put(brand, br.)) ||
                   ' brand ' || trim(aline) || ' spaghetti sauce ';

   * Add mushrooms, natural ingredients to text line;
   n = (put(ingredients, in.) =: 'All');
   m = (put(mushroom,    mu.) =: 'Mus');

   if n or m then do;
      lines = trim(lines) || ', now with';

      if m then do;
         lines = trim(lines) || ' ' || lowcase(put(mushroom, mu.));
         if n then lines = trim(lines) || ' and';
         end;
      if n then lines = trim(lines) || ' ' ||
                        lowcase(put(ingredients, in.)) || ' ingredients';
      end;

   * Add price;
   lines = trim(lines) ||
          '.  A 26 ounce jar serves four adults for only $' ||
          put(price, pr.) || '.';

   * Print cover page, with subject number, instructions, and rating scale;
   if _n_ = 1 then do;
      put ///// +41 'Subject: ________' ////
          +5 'Please rate your willingness to purchase the following' /
          +5 'products on a nine point scale.' ///
          +9 '1   Definitely Would Not Purchase This Product' ///
          +9 '2'  ///
          +9 '3   Probably Would Not Purchase This Product' ///
          +9 '4'  ///
          +9 '5   May or May Not Purchase This Product' ///
          +9 '6'  ///
          +9 '7   Probably Would Purchase This Product' ///
          +9 '8'  ///
          +9 '9   Definitely Would Purchase This Product' /////
          +5 'Please rate every product and be sure to rate' /
          +5 'each product only once.' //////
          +5 'Thank you for your participation!';
      put _page_;
      end;

   if ll < 8 then put _page_;

   * Break up description, print on several lines;

   start = 1;
   do l = 1 to 10 until(aline = ' ');

      * Find a good place to split, blank or punctuation;
      stop = start + 60;
      do i = stop to start by -1 while(substr(lines, i, 1) ne ' '); end;
      do j = i to max(start, i - 8) by -1;
         if substr(lines, j, 1) in ('.' ',') then do; i = j; j = 0; end;
         end;

      stop = i; len = stop + 1 - start;
      aline = substr(lines, start, len);
      start = stop + 1;
      if l = 1 then put +5 _n_ 2. ') ' aline;
      else          put +9 aline;
      end;

   * Print rating scale;
   put +9 'Definitely                                      Definitely ' /
       +9 'Would Not   1   2   3   4   5   6   7   8   9   Would      ' /
       +9 'Purchase                                        Purchase   ' //;
   run;

options ls=80 ps=60 nonumber nodate;


data rawdata;
   missing _;
   input subj @5 (rate1-rate30) (1.);
   name = compress('Sub' || put(subj, z3.));
   if nmiss(of rate:) = 0;
   datalines;
  1 319591129691132168146121171191
  2 749173216928911175549891841791
  3 449491116819413186158171961791
  4 113671192128346342124175311248
  5 129691338588961195396561941794
  6 748155415915911162519464963651
  7 161781515449111141118191941791
  8 868796159978718194399663711697
  9 111391139899112191116171841981
 10 314128191112638913451328116114
 11 628681215895731479459245161191
 12 859364518935921186559992741891
 13 548481115718213165246192941491
 14 1139812951994_9466149198915699
 15 128591437575761195378461931774
 16 636144515915821131519163765441
 17 171891624549111141116191951891
 18 979797149879917191489764711897
 19 2214922399981121.1116161941991
 20 21612819111253771.441316117113
 21 539591559692212146136111191272
 22 949175527938911196569995983893
 23 149382117719311184126131951891
 24 111231194117142352135183111148
 25 139791549699871197597751951894
 26 417341314925911162419563843441
 27 151791525559111161119191951791
 28 968595179978718194399974811599
 29 131191229989111191117171831991
 30 317139191112559915561.39117113
 31 419591419341121236112121151151
 32 329154117917911164539692452893
 33 217154135628421166237142981791
 34 114561191246314133112154111271
 35 129691459689961197499591961996
 36 829256918919911181719597952761
 37 161771434459111131117191831771
 38 778594159977816182289573711496
 39 111291129788111191114151741981
 40 213127191111528912331315115112
 41 769692159491491387579111191193
 42 939162116917911193749891773591
 43 718393116619321166119171981591
 44 121773194167287561317188612556
 45 119691437699991195497671951696
 46 637165314912911153319354741551
 47 1617914144191111311181918317_1
 48 978696159966719192399663911497
 49 222491149799114191116181841991
 50 413119191113639914461339116315
 51 118591558191111176119121171571
 52 849162217918711133339691521891
 53 639591117719312156286161961892
 54 115891193579459593125198412798
 55 117791218587771186474471841674
 56 969155516919911183719595967454
 57 161681515439111141118191941691
 58 86.598167978818194599674911597
 59 221391139788112191116171721981
 60 413149191113658914482249116115
 61 118391159391152168156121441591
 62 749154117917811177649681941591
 63 668583138728412189278291991793
 64 111982395219898874163189811299
 65 118791438469951196295551941694
 66 589165512814511172439553873771
 67 151671415539111141119191931691
 68 878796169969618194389664711498
 69 221291139987112191116172741981
 70 315129191114647923463428216425
 71 116561126392423436266112151472
 72 557146117917811156349892842891
 73 429481115719611176148133961991
 74 112471191149564151353197311538
 75 116691119587861194375351821772
 76 97817451891694119.719494974672
 77 161671716739111141119191931791
 78 859795158968517185589863721795
 79 321491259797113191126161651991
 80 313126191111435714441227116112
 81 717491137792122566155511191182
 82 659143116927711172358591842691
 83 447682155729721176369282961993
 84 155661193139294451141147722677
 85 128691347698991197496671951893
 86 569194215913911154519254694661
 87 161791525349111141118191841791
 88 668596159857716192199872511497
 89 111291139777113191117171751971
 90 213117191112625911141227115112
 91 457593419984197258675335199251
 92 235211115916544163738962543693
 93 219678111446351185113122161988
 94 327271171234936122133257214939
 95 138481418565711494364471871444
 96 935563423519262282519395986964
 97 151991425649611371318291941981
 98 76637713997561419469975.731499
 99 211391379998114391129284951991
100 212219171111537912111119119532
;

proc transpose data=rawdata(drop=subj) out=temp(drop=_name_);
    id name;
    run;

data inputdata; merge sasuser.spag temp; run;

ods exclude notes mvanova anova;
proc transreg data=inputdata utilities short separators='' ', '
   lprefix=0 outtest=utils method=morals;
   title2 'Conjoint Analysis';
   model identity(sub:) =
         class(brand | price meat mushroom ingredients / zero=sum);
   output p ireplace out=results1 coefficients;
   run;

data model;
   set utils;
   if statistic in ('R-Square', 'Adj R-Sq', 'Model');
   Subj = scan(_depvar_, 2);
   if statistic = 'Model' then do;
      value = numdf;
      statistic = 'Num DF';
      output;
      value = dendf;
      statistic = 'Den DF';
      output;
      value = dendf + numdf + 1;
      statistic = 'N';
      end;
   output;
   keep statistic value subj;
   run;

proc transpose data=model out=summ;
   by subj;
   idlabel statistic;
   id statistic;
   run;

data summ2(drop=list);
   length list $ 1000;
   retain list;
   set summ end=eof;
   if adj_r_sq < 0.3 then do;
      Small = '*';
      list = trim(list) || ' ' || subj;
      end;
   if eof then call symput('droplist', trim(list));
   run;

%put &droplist;

proc print label data=summ2(drop=_name_ _label_); run;

proc transreg data=inputdata(drop=&droplist) utilities short noprint
   separators='' ', ' lprefix=0 outtest=utils method=morals;
   title2 'Conjoint Analysis';
   model identity(sub:) =
         class(brand | price meat mushroom ingredients / zero=sum);
   output p ireplace out=results2 coefficients;
   run;

   /* Uncomment this code if you want it to run

%let min = 1;
%let max = 9;
%let by  = 1;
%let inter = 20;
%let list = &min to &max by &by;
data a;
   do u = &list;
      logit = exp(u);
      btl   = u;
      sumb  + btl;
      suml  + logit;
      end;

   do u = &list / &inter;
      logit = exp(u);
      btl = u;
      max = abs(u - (&max)) < (0.5 * (&by / &inter));
      btl = btl / sumb;
      logit = logit / suml;
      output;
      end;
   label u = 'Probability of Choice';
   run;

proc sgplot data=a;
   title 'Simulator Comparisons';
   series x=u y=logit / curvelabel='Logit' lineattrs=graphdata1;
   series x=u y=btl   / curvelabel='BTL' lineattrs=graphdata2;
   series x=u y=max   / curvelabel='Maximum Utility' lineattrs=graphdata3;
   yaxis label='Utility';
   run;

   */

                        /*---------------------------------------*/
                        /* Simulate Market Share                 */
                        /*---------------------------------------*/
%macro sim(data=_last_, /* SAS data set with utilities.          */
           idvars=,     /* Additional variables to display with  */
                        /* market share results.                 */
           weights=,    /* By default, each subject contributes  */
                        /* equally to the market share           */
                        /* computations.  To differentially      */
                        /* weight the subjects, specify a vector */
                        /* of weights, one per subject.          */
                        /* Separate the weights by blanks.       */
           out=shares,  /* Output data set name.                 */
           method=max   /* max   - maximum utility model.        */
                        /* btl   - Bradley-Terry-Luce model.     */
                        /* logit - logit model.                  */
                        /* WARNING: The Bradley-Terry-Luce model */
                        /* and the logit model results are not   */
                        /* invariant under linear                */
                        /* transformations of the utilities.     */
          );            /*---------------------------------------*/

options nonotes;

%if &method = btl or &method = logit %then
   %put WARNING: The Bradley-Terry-Luce model and the logit model
results are not invariant under linear transformations of the
utilities.;
%else %if &method ne max %then %do;
   %put WARNING: Invalid method &method..  Assuming method=max.;
   %let method = max;
   %end;

* Eliminate coefficient observations, if any;
data temp1;
   set &data(where=(_type_ = 'SCORE' or _type_ = ' '));
   run;

* Determine number of runs and subjects.;
proc sql;
   create table temp2 as select nruns,
      count(nruns) as nsubs, count(distinct nruns) as chk
      from (select count(_depvar_) as nruns
      from temp1 where _type_ in ('SCORE', ' ') group by _depvar_);
   quit;

data _null_;
   set temp2;
   call symput('nruns', compress(put(nruns, 5.0)));
   call symput('nsubs', compress(put(nsubs, 5.0)));
   if chk > 1 then do;
      put 'ERROR: Corrupt input data set.';
      call symput('okay', 'no');
      end;
   else call symput('okay', 'yes');
   run;

%if &okay ne yes %then %do;
   proc print;
     title2 'Number of runs should be constant across subjects';
     run;
   %goto endit;
   %end;

%else %put NOTE: &nruns runs and &nsubs subjects.;

%let w = %scan(&weights, %eval(&nsubs + 1), %str( ));
%if %length(&w) > 0 %then %do;
   %put ERROR: Too many weights.;
   %goto endit;
   %end;

* Form nruns by nsubs data set of utilities;
data temp2;
   keep _u1 - _u&nsubs &idvars;
   array u[&nsubs] _u1 - _u&nsubs;

   do j = 1 to &nruns;

      * Read ID variables;
      set temp1(keep=&idvars) point = j;

      * Read utilities;
      k = j;
      do i = 1 to &nsubs;
         set temp1(keep=p_depend_) point = k;
         u[i] = p_depend_;
         %if &method = logit %then u[i] = exp(u[i]);;
         k = k + &nruns;
         end;

      output;
      end;

   stop;
   run;

* Set up for maximum utility model;
%if &method = max %then %do;

   * Compute maximum utility for each subject;
   proc means data=temp2 noprint;
      var _u1-_u&nsubs;
      output out=temp1 max=_sum1 - _sum&nsubs;
      run;

   * Flag maximum utility;
   data temp2(keep=_u1 - _u&nsubs &idvars);
      if _n_ = 1 then set temp1(drop=_type_ _freq_);
      array u[&nsubs] _u1 - _u&nsubs;
      array m[&nsubs] _sum1 - _sum&nsubs;
      set temp2;
      do i = 1 to &nsubs;
         u[i] = ((u[i] - m[i]) > -1e-8); /* < 1e-8 is considered 0 */
         end;
      run;

   %end;

* Compute sum for each subject;
proc means data=temp2 noprint;
   var _u1-_u&nsubs;
   output out=temp1 sum=_sum1 - _sum&nsubs;
   run;

* Compute expected market share;
data &out(keep=share &idvars);
   if _n_ = 1 then set temp1(drop=_type_ _freq_);
   array u[&nsubs] _u1 - _u&nsubs;
   array m[&nsubs] _sum1 - _sum&nsubs;
   set temp2;

   * Compute final probabilities;
   do i = 1 to &nsubs;
      u[i] = u[i] / m[i];
      end;

   * Compute expected market share;
   %if %length(&weights) = 0 %then %do;
      Share = mean(of _u1 - _u&nsubs);
      %end;

   %else %do;
      Share = 0;
      wsum  = 0;
      %do i = 1 %to &nsubs;
         %let w = %scan(&weights, &i, %str( ));
         %if %length(&w) = 0 %then %let w = .;
         if &w < 0 then do;
            if _n_ > 1 then stop;
            put "ERROR: Invalid weight &w..";
            call symput('okay', 'no');
            end;
         share = share + &w * _u&i;
         wsum  = wsum  + &w;
         %end;
      share = share / wsum;
      %end;
   run;

options notes;

%if &okay ne yes %then %goto endit;

proc sort;
   by descending share &idvars;
   run;

proc print label noobs;
   title2 'Expected Market Share';
   title3 %if       &method = max %then "Maximum Utility Model";
          %else %if &method = btl %then "Bradley-Terry-Luce Model";
          %else                         "Logit Model";;
   run;

%endit:

%mend;

title 'Spaghetti Sauces';

%sim(data=results2, out=maxutils, method=max,
     idvars=price brand meat mushroom ingredients);

title 'Spaghetti Sauces';

%sim(data=results2, out=btl, method=btl,
     idvars=price brand meat mushroom ingredients);

%sim(data=results2, out=logit, method=logit,
     idvars=price brand meat mushroom ingredients);

title 'Spaghetti Sauces';

proc format;
   invalue inbrand 'Preg'=1 'Sun' =2 'Tom' =3;
   invalue inmeat  'Veg' =1 'Meat'=2 'Ital'=3;
   invalue inmush  'Mush'=1 'No'  =2;
   invalue iningre 'Nat' =1 'No'  =2;
   invalue inprice '1.99'=1 '2.29'=2 '2.49'=3 '2.79'=4 '2.99'=5;
   run;

data simulat;
   input brand       : inbrand.
         meat        : inmeat.
         mushroom    : inmush.
         ingredients : iningre.
         price       : inprice.;
   datalines;
Preg  Veg   Mush  Nat  1.99
Sun   Veg   Mush  Nat  1.99
Tom   Veg   Mush  Nat  1.99
Preg  Meat  Mush  Nat  2.49
Sun   Meat  Mush  Nat  2.49
Tom   Meat  Mush  Nat  2.49
Preg  Ital  Mush  Nat  2.79
Sun   Ital  Mush  Nat  2.79
Tom   Ital  Mush  Nat  2.79
;

data inputdata2(drop=&droplist);
   set inputdata(in=w) simulat;
   Weight = w;
   run;

proc print;
   title2 'Simulation Observations Have a Weight of Zero';
   id weight;
   var brand -- price;
   run;

ods exclude notes mvanova anova;
proc transreg data=inputdata2 utilities short noprint
   separators=', ' lprefix=0 method=morals outtest=utils;
   title2 'Conjoint Analysis';
   model identity(sub:) =
         class(brand | price meat mushroom ingredients / zero=sum);
   output p ireplace out=results3 coefficients;
   weight weight;
   run;

data model;
   set utils;
   if statistic in ('R-Square', 'Adj R-Sq', 'Model');
   Subj = scan(_depvar_, 2);
   if statistic = 'Model' then do;
      value = numdf;
      statistic = 'Num DF';
      output;
      value = dendf;
      statistic = 'Den DF';
      output;
      value = dendf + numdf + 1;
      statistic = 'N';
      end;
   output;
   keep statistic value subj;
   run;

proc transpose data=model out=summ;
   by subj;
   idlabel statistic;
   id statistic;
   run;

proc print label data=summ(drop=_name_ _label_); run;

data results4;
   set results3;
   where weight = 0;
   run;

%sim(data=results4, out=shares2, method=max,
     idvars=price brand meat mushroom ingredients);

data simulat2;
   input brand       : inbrand.
         meat        : inmeat.
         mushroom    : inmush.
         ingredients : iningre.
         price       : inprice.;
   datalines;
Preg Meat Mush Nat 2.29
Sun  Meat Mush Nat 2.29
Tom  Meat Mush Nat 2.29
Preg Ital Mush Nat 2.49
Sun  Ital Mush Nat 2.49
Tom  Ital Mush Nat 2.49
;

data inputdata3(drop=&droplist);
   set inputdata(in=w) simulat simulat2;
   weight = w;
   run;

ods exclude notes mvanova anova;
proc transreg data=inputdata3 utilities short noprint
   separators=', ' lprefix=0 method=morals outtest=utils;
   title2 'Conjoint Analysis';
   model identity(sub:) =
         class(brand | price meat mushroom ingredients / zero=sum);
   output p ireplace out=results5 coefficients;
   weight weight;
   run;

data model;
   set utils;
   if statistic in ('R-Square', 'Adj R-Sq', 'Model');
   Subj = scan(_depvar_, 2);
   if statistic = 'Model' then do;
      value = numdf;
      statistic = 'Num DF';
      output;
      value = dendf;
      statistic = 'Den DF';
      output;
      value = dendf + numdf + 1;
      statistic = 'N';
      end;
   output;
   keep statistic value subj;
   run;

proc transpose data=model out=summ;
   by subj;
   idlabel statistic;
   id statistic;
   run;

proc print label data=summ(drop=_name_ _label_); run;

data results6;
   set results5;
   where weight = 0;
   run;

%sim(data=results6, out=shares3, method=max,
     idvars=price brand meat mushroom ingredients);

title 'Spaghetti Sauces';

proc sort data=shares2;
   by price brand meat mushroom ingredients;
   run;

proc sort data=shares3;
   by price brand meat mushroom ingredients;
   run;

data both;
   merge shares2(rename=(share=OldShare)) shares3;
   by price brand meat mushroom ingredients;
   if oldshare = . then Change = 0;
   else change = oldshare;
   change = share - change;
   run;

proc sort;
   by descending share price brand meat mushroom ingredients;
   run;

options missing=' ';
proc print noobs;
   title2 'Expected Market Share and Change';
   var price brand meat mushroom ingredients
       oldshare share change;
   format oldshare -- change 6.3;
   run;
options missing=.;

********************** Begin Syntax Sample Code ***********************;

options ls=80 ps=60 nonumber nodate;
title;

proc format;
   value wf 1  = 'Active'
            .  = 'Holdout'
            0  = 'Simulation';
   run;

   /* * Uncomment this code if you want it to run;

data x;
   do x = 0.1 to 14 by 0.25;
      y = log(x) + sin(x) + 0.3 * cos(x * 5);
      output;
      end;
   run;

proc transreg;
   model ide(y / name=(mon)) = monotone(x);
   output p;
   id y;
   run;

proc transreg;
   model ide(y / name=(spl)) = spl(x / nkn=5);
   output p;
   id y pmon;
   run;

proc transreg;
   model ide(y / name=(msp)) = mspl(x / nkn=5);
   output p;
   id y pmon pspl;
   run;

proc transreg;
   model ide(y / name=(ide)) = ide(x);
   output p;
   id y pmon pspl pmsp;
   run;

data; set;
   pspl + 6; spy = y + 6;
   pmsp + 4; msy = y + 4;
   pmon + 2; moy = y + 2;
   run;

proc template;
   define statgraph plot;
      begingraph;
         entrytitle  'Functions Available in PROC TRANSREG';
         layout overlay / xaxisopts=(display=none)
                          yaxisopts=(display=none);
            seriesplot   x=x y=pspl  / lineattrs=graphdata4
                                       curvelabelattrs=graphdata4
                                       curvelabel='Smooth Spline Function';
            seriesplot   x=x y=pmsp  / lineattrs=graphdata3
                                       curvelabelattrs=graphdata3
                                       curvelabel='Smooth Monotone Spline';
            seriesplot   x=x y=pmon  / lineattrs=graphdata2
                                       curvelabelattrs=graphdata2
                                       curvelabel='Monotone Step';
            seriesplot   x=x y=pide  / lineattrs=graphdata1
                                       curvelabelattrs=graphdata1
                                       curvelabel='Line';
            scatterplot   x=x y=spy / markerattrs=graphdata4;
            scatterplot   x=x y=msy / markerattrs=graphdata3;
            scatterplot   x=x y=moy / markerattrs=graphdata2;
            scatterplot   x=x y=y   / markerattrs=graphdata1;
            endlayout;
        endgraph;
      end;
   run;

%modstyle(parent=statistical, name=mystat)
ods listing style=mystat gpath='png';
ods graphics / reset=index imagename="curve1";

proc sgrender template=plot; run;

   */

* Create data set for use later.;
data a;
   array v[5] x1-x5;
   array rating[100];
   array ranking[100];
   w = 1;
   do i = 1 to 25;
      do j = 1 to 100;
         rating[j]  = ceil(9 * uniform(7));
         ranking[j] = ceil(25 * uniform(7));
         end;
      price = ceil(3 * uniform(7));
      do j = 1 to 5;
         v[j] = floor(abs(normal(7)));
         end;
      output;
      end;
   run;

ods listing close;

ods exclude notes mvanova anova;
proc transreg data=a utilities short method=morals;
   model identity(rating1-rating100) = class(x1-x5 / zero=sum);
   output p ireplace;
   weight w;
   run;

ods exclude notes anova liberalanova conservanova
            mvanova liberalmvanova conservmvanova;
proc transreg data=a utilities short maxiter=500 method=morals;
   model monotone(ranking1-ranking100 / reflect) = class(x1-x5 / zero=sum);
   output p ireplace;
   weight w;
   run;

ods exclude notes anova liberalanova conservanova
            mvanova liberalmvanova conservmvanova;
proc transreg data=a utilities short maxiter=500 method=morals;
   model mspline(ranking1-ranking100 / reflect) =
         class(x1-x5 / zero=sum);
   output p ireplace;
   weight w;
   run;

ods exclude notes anova liberalanova conservanova
            mvanova liberalmvanova conservmvanova;
proc transreg data=a utilities short maxiter=500 method=morals;
   model mspline(ranking1-ranking100 / reflect nknots=3) =
         class(x1-x5 / zero=sum);
   output p ireplace;
   weight w;
   run;

ods exclude notes anova liberalanova conservanova
            mvanova liberalmvanova conservmvanova
            liberalutilities liberalfitstatistics;
proc transreg data=a utilities short maxiter=500 method=morals;
   model identity(rating1-rating100) = class(x1-x3 / zero=sum)
         identity(x4) monotone(x5);
   output p ireplace;
   weight w;
   run;

ods exclude notes anova liberalanova conservanova
            mvanova liberalmvanova conservmvanova
            liberalutilities liberalfitstatistics;
proc transreg data=a utilities short maxiter=500 method=morals;
   model identity(rating1-rating100) = monotone(x1-x5) mspline(price);
   output p ireplace;
   weight w;
   run;

ods exclude notes anova liberalanova conservanova
            mvanova liberalmvanova conservmvanova
            liberalutilities liberalfitstatistics;
proc transreg data=a utilities short maxiter=500 method=morals;
   model identity(rating1-rating100) =
         class(x1-x5 / zero=sum)
         mspline(price / knots=2 2 2 3 3 3);
   output p ireplace;
   weight w;
   run;

ods listing;