Overview
Conjoint, discrete choice model, and some shelf testing all use the framework discussed in this article. A standard conjoint generally arrives as an Excel file containing several worksheets. Those will generally include instructions, layouts, attributes, levels, and design. In some instances, price and other attributes may need to be calculated for each task. When programmed, a conjoint will present a respondent with a series of tasks or exercises; nomenclature will vary by client. Each task will be presented as a unique page where the respondent will select one of several concepts/options/products displayed. The options displayed may be the same or similar items with variances in attributes, e.g., price, size, features, etc.
As with surveys, each conjoint will present a unique set of challenges. The latest baseline demo can be found here.
Preparation
Before proceeding, you must have a design, attributes/levels, and a layout example of a task.
Step 1: Prepare/review the design/data file
- Clients will send the design stacked (all options on one row) or flattened (each option on it's own row). We can use both stacked or flattened data, the process in this article will use stacked data.
- Modify the column headings and add an id column in a copy of the design file. Leaving the rest of the data as is will minimize chances for error. When labeling the column headings in your design file, keep the following in mind:
- You're using a sheet so use lowercase labels.
- You want to edit the client file as little as possible. In most cases this means adding a working row where you can lower casing, removing spaces, and/or abbreviating column headers. Once you've verified your new column headings line up remove the original heading row. For example "Option 1/Attribute 1 - Price" might become "o1_a1". For a flattened file you won't need to worry about the option portion of the column header.
- The attributes generally correspond to rows so making sure the labels are similar and contain the row id will make things easier downstream.
- Insert an id column, this will be the key to importing the data later. It's easiest to use an excel formula to create the id. This formula for cell A2 will be something like "=b2*10+c2" where column "b" is the version/block and column "c" is the task/question. If there are more than 9 tasks then the first multiplier would be 100.
- Copy the entire contents of the modified design sheet and paste the values into a new sheet to clear out any formulas. This file will be uploaded at a later step.
Step 2: Write the survey code
-
First we insert the conjoint intro text OR a placeholder for such
-
Next we set up the version selection variables. The method used below allows QTEST_VERS to show for any testmode, the QVERS question then uses that selection as a weight. In live mode, the weights will be equal and the version will automatically be selected by count. This allows for a seamless transition between test and live mode without any source edits required to turn off the control variables used in testing.
-
Set up your task order.
-
We start with a set value that will default the QRND_TO_TM (RaNDomize Task Order in Test Mode) to "no" in testmode. It's easiest to verify the design if the tasks are not randomized.
-
Then we show the QRND_TO_TM to testmode respondents so they can override it as needed.
-
In QTASKORD we use a ternary operation inside the order function to set a sequential order if QRND_TO_TM is set to "no" and a random order otherwise (live mode). That order is then assigned to our task list.
Sequential order for test, random for live
-
-
Break out the scenario numbers for each task. Tasks are randomized for live respondents, but we still want to show them text like 'task 4 of 12' so they know where they are. To do this, we have a text variable that uses Perl to untangle things. This should never need updated.
-
For an order string like "3,1,4,2,5", the following block would create: QSCENARIO1 = 2, QSCENARIO2 = 4, QSECNARIO3 = 1, QSCENARIO4 = 3, QSCENARIO5 = 5.
-
In the
cvalue
we're doing the following line by line:-
create an array using the comma separated values in the random order variable
-
create a string containing the number of items in the array
-
use a foreach loop to go over each item between 1 and the number of items in the array, each item is represented by $i in the loop
-
if the order array item in this index (
$i-1
;is used to access the index value, e.g. 1-1=0 for the first item) is the same as the current block ID -
return the value of
$i
-
use
last
to end the loop
-
-
To ease manipulation and translation of the conjoint text, we next create
setlists
for the leading column as well as the attribute levels. The attribute level text should be provided in a worksheet in the client provided excel. When possible use excel formulas to merge the level id and text and then copy the results to the survey source.- Note: Leader cell and attribute levels that should have mousovers should have the mouseover text added as option data. In this example we use "EX" as the option data id:
- Any additional text should be setup as set text after the lists
-
Define a conjoint options list (ordering and option conditioning will carry through to the conjoint exercise)
Set up row leader and attribute level lists, conjoint option list, and stand alone set textssetlist: DCM_ROWS
1. Brand
2. Partner
3. Annual Fee {{EX: Prepaid}}
# Attibute Levels:
setlist: ATT_1_LEVELS
1. Brand A
2. Brand B
3. Brand C
4. Brand D
setlist: ATT_2_LEVELS
1. No Partner {{EX: No partner company participating}}
2. Partner A {{EX: Partner company A participating with flights}}
3. Partner B {{EX: Partner company B participating with rental cars}}
4. Partner C {{EX: Partner company C participating with product discounts}}
setlist: ATT_3_LEVELS
1. $695
2. $895
3. $995
4. $1,195
setlist: CONJOINT_OPTS
1. Option 1
2. Option 2
3. Option 3
4. Option 4
settext: QROWTEXT
text: Select One
settext: SELECT
text: Select
- Now that we have the lists which will export their option text to the translation tool we need to setup set texts that we can call in the .tx file.
- These set texts are placed inside an inline block w/ the "translate: n" tag, this prevents unnecessary content from getting into the translation tool.
- The set texts use "html" instead of "text" to prevent "freetext" wrappers from interfering with the conjoint.tx code.
- The blocks are repeated for the items with mousover text so the "explain" function can be added to the set text. NOTE: the "text" tag must be used to get mouseover text.
Now for the main event in the survey code - the conjoint questions themselves. We set these up in a block fed by our ordered list (TASKLIST).
- Text that needs to be above the question text must be set up manually, as is done at the settext widget containing the task/scenario-shown notice in the code example below.
- The question and instruction text are pulled into the conjoint .tx file as defined in the question.
- Options must all be defined at a list OR locally at the survey. This method calls the option collection defined at the question so having only one helps that work better.
-
template: conjoint.tx
invokes the custom .tx file that is used to change the look and feel of the question. -
template_data
defines parameters for information used in the rendering of the .tx file.- The fist set of 6 lines defines Perl variables and populates them with row data from the design file. Each row pulled is data for an option at the conjoint. Because the data is stacked we pull a row for each option.
- Each row is the selected version ($QVERS) multiplied by 100 plus the current task id multiplied by 10 plus the option id. For example, option 1 of task 5 of version 3 would return a row value of '351'. This formula is similar to the one used in Excel to create the row ids.
- After the the Perl variables we have the return command which sets up the XSlate variables and populates them with the Perl variables we just created. This will allow us to dig into that row data and look at the attribute levels in the .tx file.
- A follow up question is included in this example and it shows once the conjoint selection has been made by the respondent.
Step 3: Write the template file
Template files are essentially text files (saved as .tx) that control the html of a page. These files are rendered in advance of anything else, so we have a lot of flexibility when using them. One giant advantage is that we can apply a change, save the file, and refresh our live or test case to see the change take effect. Template files use a language called Xslate (more about the specifics here). For our purposes, we will be focused on a handful of details:
- Xslate lines begin with
%%
.- Comment lines begin with
%% #
. - Xslate variable injections look like
[% $id %]
. This is where an Xslate variable is injected into the HTML. - Xslate conditional injection follows the format:
[% if condition {result if true} %]
. - Xslate for loops start like
%% for [1..3] -> $i {
In the loop, variable injection is used to place$i
where you'd want 1 through 3 to appear when the loop expands. Loops can be nested - in the nested loop, a new variable must be defined for iteration. In loops we can call special values like[%~$i.count %]
which returns a value like '1', '2', '3', etc., even if the variable contains values like 'a', 'b', 'c', etc. - When using an template on a question we can access bits of that question, the id, text, instructions, and option collection:
-
[% $id %]
will return a value like "QCON_TASK_1",[% $text %]
will return the question text, etc. - $option_collection.collection can be called as the feeder for a for loop. Within that loop we can access the option elements like id, text, and even option data if defined:
-
[% $opt.id %]
will return the option id,[% $opt.text %]
will return the option text,[% $opt.foo %]
would return the value in the "foo" column of an option sheet or{{foo: bar}}
from a list.
-
-
- Xslate variable concatenations follow the format:
__ATT_[% $row %]_[% __ROOT__['data']['o' ~ $opt.id '_a' ~ $row] %]__
. Be advised that__ROOT__
is an Xslate specific command, NOT a call to an IntelliSurvey set text. This command tells the system to perform this concatenation before doing anything else on the page. This allows the variables to be rendered for the respondents properly. Following root is an Xslate concatenation that uses~
to combine strings and variable results. For this example, we would expect a result like '__ATT_1_3__' that would pull the appropriate set text to display the level for that attribute/option cell.
- Comment lines begin with
Creating references to IntelliSurvey variables or set text does NOT require the concatenation noted above - you can simply put the Xslate injection where it needs to be, e.g. $Q14R[% $i %]
or __explanation_[% $row_name %]__
.
The following breaks down an uncommented template file in sections.
The first set of lines remains static and is used to set up the HTML fieldset container opening and the question and instruction text:
Next, the opening of the conjoint table.
The classes used for the rows and cells follow a convention where each cell will get a row and cell class. Both numbering systems start at 0 so that 'r0' indicates the header row and 'c0' indicates the leading label column.
In this instance we don't have a header row, just a leader column so we use a for loop for the rows with another for loop nested within for the columns.
In the survey source code we set up a row list, and use the ids from that list to feed the first for
loop. If using sequential numbers, you can use 1..3
as we've done below; if using sequential letters, you can use 'a'..'e'.
The for loops start with the for declaration, the list of items to iterate through, and then the identifier for the iteration. In the first for below we're cycling through the numbers 1 to 12 and we're using "$row" to pipe in that number. Because the numbers need to be inline we wrap "$row" in the Xslate square bracket percent sign where we want it piped into our HTML, e.g. "r[% $row %]". In this case we also pipe the iteration into a set text call to pull in the leader cell text.
We use a nested for loop to break out the remaining column cells for each row. In this loop we're accessing the option collection from the question with $option_collection.collection. NOTE, you can access option data here as well as the id and text values.
In the loop we setup:
- the td element,
- a label element that is linked to the the radio option to allow clicks on the cell to trigger the radio button question,
- and a set text call, the set text call is the crux of where we're grabbing data from our design:
- We've already discussed how $row and $opt.id are pulling in values from the for list, and we've gone over __ROOT__ so here we'll look at this syntax "['opt' ~ $opt.id]['att' ~ $row]"
- In the survey source one of the last things we did was pull row data into Perl variables ($opt1, $opt2, etc.) and then pass those Perl variables over to Xslate variables of the same name. Now we need to access information from those variables for the specific cell we're rendering
- Inside of a set of brackets we concatenate 'opt' with the $opt.id value to build our option/row reference
- Inside of an abutted set of brackets we concatenate 'att' with the $row value to build the attribute/column reference
- For example if we were on the first of both loops (row 1, option 1) the code would build a call like "['opt1']['att1']" and that would pull the value for the "att1" column of the first row data we imported.
- Continuing with our example lets say that the att1 column contains a value of 5 for the row in question, the final set text call will look like __ATT_1_5__
- Because the level number corresponds with the set text we created in the source the appropriate level text will show
Now for the respondent input row. Using an Xslate for
loop to create this row helps ensure that we don't muck things up. Prior to using for
loops, we would copy the first row, paste X times for each option we needed to create, and then manually (shudder) change the numbers. Now we can simply call in the option collection and let the system hash out the rest.
- The HTML input element with
type=hidden
andvalue=[% $id %]
is vital to ensure this code aligns with the survey code.- Inside the
for
loop, we are building a cell and input for each option's radio button. The code is all set to act like a standard table where clicking in the cell selects the option. You shouldn't ever have to change these contents.
- Inside the
We close the file out with some cell select script, the CSS that formats the table, and a final bit of Xslate that pulls in any script native to the question widget.
Step 4: Test and test again
You should always run several tests of your conjoint and double-check that it does the following:
Retains data: Answer a question, proceed, back up. The question should be populated with your answer.
Stores the data in the correct locations: Answer a question, submit, and check the "Data Quickview" to make sure the option you selected is the option listed.
Represents the design correctly: Verify the design by spot checking several versions. Some designs will provide a sheet with the text values shown; for others you may need to use both the design and labels files for verification purposes.
Randomizes for live respondents: Run the link without a Testmode parameter. The id in the URL should be a randomly generated series of letters and numbers. On the first scenario you should see 'scenario 1 of X'. Right-click in one of the option cells and inspect - the element should have an ID other than 1, e.g. Q14X_9. In some instances, 1 will be the first in a random order; if so, check the second screen as well.
Comments
0 comments
Please sign in to leave a comment.