Zu Hauptinhalten wechseln

FV Decipher Unterstützung

Alle Themen, Ressourcen für FV Decipher benötigt.

 
decipher

Creating a MaxDiff Question - Indices Method

 

1:  About Creating a MaxDiff Question - Indices Method

The MaxDiff - indices question is a special question type that requires custom modifications to your survey to get up and running. To create this question in your survey, you will:

  • Setup the design file
  • Setup the quota sheet
  • Copy the MaxDiff - indices method template into your survey
  • Modify the template according to your design requirements

Before starting the steps in this document you should double check to confirm that the data set should be in the indices format. With the indices method, a design file with 12 attributes where only 4 are shown creates a data file that indicates which item from the entire list of attributes was selected.

The alternatives method is different, the data file that is created shows values 1-4 and indicates which attribute of those 4 was selected.  If you prefer to create a MaxDiff question with the alternatives method: click here.

If you want to use the survey builder instead of the shell environment to create a MaxDiff question, click here.

2:  Setting up the Design File

You can follow along by downloading this design file. To learn more about the design file setup: click here.

First, you'll extract the attributes from the design file. The attributes are the actual text elements that will be displayed to the respondent. If you're following along with the design file above, then pasted below are all of the attributes to be used in this MaxDiff. The order of attributes is important and each attribute should retain its position relative to the other attributes.

  • 1. Traditional
  • 2. Innovative
  • 3. Steady
  • 4. Fast-paced
  • 5. Technology-oriented
  • 6. Community-focused
  • 7. Industry leader
  • 8. Expert
  • 9. Consultative
  • 10. Customer-focused
  • 11. Revenue-focused
  • 12. Proactive

The next step is to convert the raw design file like one shown above into a tab-delimited text document. This can be done by going to the raw design sheet, selecting "Save as", and choosing the tab-delimited format. In Microsoft Excel, there is an option for this. In LibreOffice Calc, you can save the sheet as comma-separate values (CSV) and select the {TAB} character in the "Field delimiter" dropdown. Verify that your tab-delimited text file is named design.txt. Then edit your file name and manually replace ".txt" with the ".dat" extension and save (design.dat).  The design file has now been prepared and you should upload it to the project's directory.

3:  Setting Up the Quota Sheet

Setting up the quotas is relatively easy. Each respondent is assigned a single version and will see however many tasks are present for each version. In our example, there are 10 versions accommodated with 12 tasks. You must create a marker for each version present in your design file. Since our design example has 10, we only need to create 10 randomly assigned markers. You may name these markers anything, but it's good practice to name them "ver_1" to "ver_10". It is not uncommon to have around 100 different versions. The final quota setup is illustrated below, in a sheet called "Q24_MaxDiff":

If you change the name of the markers, they should still end with _# where # represents the version number as defined in the design file.

After you have created your quota sheet to match your design, upload quota.xls the file to your project's directory.

The final step is to link everything together in our project's XML. Fortunately, most of the heavy lifting has been been automated for us with a template.

4:  Copying the MaxDiff Indices Template into the Survey

Copy the following MaxDiff template into your survey.xml to initialize the MaxDiff survey elements. Copy it to the location where the MaxDiff question is deployed. This code sets up the logic and question style necessary to achieve the MaxDiff effect. The next step is to modify this template to match your MaxDiff design.

    <note>MaxDiff Indices Template --Start--</note>
    <exec when="init">
def setupMaxDiffFile(fname, fileDelimiter="\t"):
    try:
        f = open("%s/%s" % (gv.survey.path, fname))
        mdObj = [ line.strip("\r\n").split(fileDelimiter) for line in f.readlines() ]
        d = dict( ("v%s_t%s" % (row[0], row[1]), row[2:]) for row in mdObj )
    except IOError:
        d = {}
    return d

def setupMaxDiffItemsI(d, vt, question):
    item_index = dict( (r.o.label.strip("item"), r.index) for r in question.rows )

    items = d[vt]

    for r in question.rows:
        if r.o.label.strip("item") not in items:
            r.disabled = True

    question.rows.order = [ item_index[i] for i in items ]

    print "*****STAFF ONLY*****"
    print "Version_Task: %s" % vt
    for i in range(len(items)):
        print "Item %s: %s" % (i+1,items[i])
    </exec>
    
    <exec when="init">Q1_md = setupMaxDiffFile("md_design.dat")</exec>
    
    <quota overquota="noqual" sheet="Q1_Maxdiff"/>
    
    <number label="Q1_Version" size="3" optional="1" verify="range(1,16)" where="execute">
      <title>Q1 - MaxDiff Version</title>
      <exec>
print p.markers
for x in p.markers:
    if "/Q1_Maxdiff/ver_" in x:
        Q1_Version.val = int(x.split("_")[-1])
        break
      </exec>
    </number>
    <suspend/>
    
    <exec>p.startTime = timeSpent()</exec>
    
    <loop label="Q1_md_loop" vars="task" randomizeChildren="0">
      <title>Q1 - MaxDiff Loop</title>
      <block label="Q1_md_block" randomize="1">
        <radio label="Q1_[loopvar: task]" adim="cols" grouping="cols" shuffle="rows" unique="1" ss:questionClassNames="Q1_maxdiff">
          <title>Title update [MDcount]</title>
          <comment>Select one</comment>
          <exec>
setupMaxDiffItemsI( Q1_md, "v%d_t%d" % (Q1_Version.val, [loopvar: task]), Q1_[loopvar: task])
p.MDcount = str(Q1_md_loop_expanded.order.index([loopvar: task]-1)+1)
          </exec>
          <col label="best">Most Important</col>
          <col label="worst">Least Important</col>
    <row label="item1">Item 1</row>
    <row label="item2">Item 2</row>
    <row label="item3">Item 3</row>
    <row label="item4">Item 4</row>
    <row label="item5">Item 5</row>
    <row label="item6">Item 6</row>
    <row label="item7">Item 7</row>
    <row label="item8">Item 8</row>
    <row label="item9">Item 9</row>
    <row label="item10">Item 10</row>
    <row label="item11">Item 11</row>
    <row label="item12">Item 12</row>
    <row label="item13">Item 13</row>
    <row label="item14">Item 14</row>
    <row label="item15">Item 15</row>         
<style name="question.header" mode="before">
            <![CDATA[
    <style type="text/css">
    .Q1_maxdiff tr.maxdiff-header-legend {
        background-color: transparent;
        border-bottom: 2px solid #d9d9d9;
    }
    .Q1_maxdiff tr.maxdiff-header-legend th.legend {
        background-color: transparent;
        border: none;
    }
    .Q1_maxdiff tr.maxdiff-row td.element {
        border-left: none;
        border-right: none;
        border-top: none;
        border-bottom: 1px solid #d9d9d9;
        text-align: center;
    }
    .Q1_maxdiff tr.maxdiff-row th.row-legend {
        background-color: transparent;
        border-left: none;
        border-right: none;
        border-top: none;
        border-bottom: 1px solid #d9d9d9;
        text-align: center;
    }
    </style>
            ]]>
</style>
 
<style name="question.top-legend">
            <![CDATA[
\@if ec.simpleList
    $(legends)
\@else
    <$(tag) class="maxdiff-header-legend row row-col-legends row-col-legends-top ${"mobile-top-row-legend " if mobileOnly else ""}${"GtTenColumns " if ec.colCount > 10 else ""}colCount-$(colCount)">
        ${"%s%s" % (legends.split("</th>")[0],"</th>")}
       $(left)
        ${"%s%s" % (legends.split("</th>")[1],"</th>")}
    </$(tag)>
    \@if not simple
  </tbody>
  <tbody>
    \@endif
\@endif
            ]]>
</style>
 
<style name="question.row">
            <![CDATA[
\@if ec.simpleList
    $(elements)
\@else
    <$(tag) class="maxdiff-row row row-elements $(style) colCount-$(colCount)">
        ${"%s%s" % (elements.split("</td>")[0],"</td>")}
        $(left)
        ${"%s%s" % (elements.split("</td>")[1],"</td>")}
    </$(tag)>
\@endif
            ]]>
</style>
        </radio>
      </block>
      
      <looprow label="1">
        <loopvar name="task">1</loopvar>
      </looprow>
      
      <looprow label="2">
        <loopvar name="task">2</loopvar>
      </looprow>
      
      <looprow label="3">
        <loopvar name="task">3</loopvar>
      </looprow>
      
      <looprow label="4">
        <loopvar name="task">4</loopvar>
      </looprow>
      
      <looprow label="5">
        <loopvar name="task">5</loopvar>
      </looprow>
      
      <looprow label="6">
        <loopvar name="task">6</loopvar>
      </looprow>
      
      <looprow label="7">
        <loopvar name="task">7</loopvar>
      </looprow>
      
      <looprow label="8">
        <loopvar name="task">8</loopvar>
      </looprow>
      
      <looprow label="9">
        <loopvar name="task">9</loopvar>
      </looprow>
      
      <looprow label="10">
        <loopvar name="task">10</loopvar>
      </looprow>
      
      <looprow label="11">
        <loopvar name="task">11</loopvar>
      </looprow>
      
      <looprow label="12">
        <loopvar name="task">12</loopvar>
      </looprow>
      
      <looprow label="13">
        <loopvar name="task">13</loopvar>
      </looprow>
      
      <looprow label="14">
        <loopvar name="task">14</loopvar>
      </looprow>
      
      <looprow label="15">
        <loopvar name="task">15</loopvar>
      </looprow>
    
    </loop>
    
    <float label="Q1_Timer" size="15" where="execute">
      <title>Q1 - MaxDiff Timer (Minutes)</title>
      <exec>Q1_Timer.val = (timeSpent() - p.startTime) / 60.0</exec>
    </float>
    
    <note>MaxDiff Indices Template --End--</note>

5:  Updating the Template

Let's walk through each section of the code in the template above to get a better understanding of what's going on and update it where necessary so that it matches your MaxDiff question design. Click here to skip the walk through and go to the final result.

5.1:  Beginning of the MAXDIFF Template - Informational

def setupMaxDiffFile(fname, fileDelimiter="\t"):
    try:
        f = open("%s/%s" % (gv.survey.path, fname))
        mdObj = [ line.strip("\r\n").split(fileDelimiter) for line in f.readlines() ]
        d = dict( ("v%s_t%s" % (row[0], row[1]), row[2:]) for row in mdObj )

This is the beginning of our MAXDIFF class. It takes in the name of our design file as an argument (e.g. "design.dat"). Line 4 in the code above creates a dictionary object where the key represents the version and task number and the value is the attributes items to display. (e.g. {"v1_t1" : [5,1,11,10], ... } where "v1_t1" means version #1, task #1 and the 4 items to show are 5, 1, 11 and 10.)

Since this section is for informational purposes only, you do not need to update this code.

5.2:  Calling the Indices Function - Informational

    def setupMaxDiffItemsI(d, vt, question):
    item_index = dict( (r.o.label.strip("item"), r.index) for r in question.rows )

    items = d[vt]

    for r in question.rows:
        if r.o.label.strip("item") not in items:
            r.disabled = True

    question.rows.order = [ item_index[i] for i in items ]

    print "*****STAFF ONLY*****"
    print "Version_Task: %s" % vt
    for i in range(len(items)):
        print "Item %s: %s" % (i+1,items[i])

This is the function (setupMaxDiffItemsI) that is called if we are using the indices data format. It takes in 3 values, the string corresponding to the items we're going to show in the dictionary that was created (e.g. "v3_t1"), the question to apply the function to (e.g. Q1), and the string representing the <res> tag to reference (e.g. "Q1" for "Q1_mditem_1"). You will see how we call this function in just a moment.

Since this section is for informational purposes only, you do not need to update this code.

This function prints helpful debugging information in the respondent view. You must be logged in to see this.

5.3:  Updating the Design File Name

<exec when="init">
Q1_md = setupMaxDiffFile("md_design.dat")
</exec>

This is where you initialize the MAXDIFF class. It takes in the name of the design file we uploaded to the project's directory. Update md_design.dat to reflect the name of the design file you uploaded to your project directory (e.g. design.dat).

5.4:  Globally Updating the MaxDiff Question Label

From within the template, do a global find and replace to replace all instances of Q1 to match your question label.

5.5:  Updating the Sheet Name in the Quota File

The following code calls the "Q1_MaxDiff" quota sheet and will assign the respondent a version number. You should update the sheet name in the quota call to match the sheet name in the quota file you created to track the version.

<quota overquota="noqual" sheet="Q1_MaxDiff"/>

5.6:  Updating the Range Specification

Update the range on the _version question to match the number of versions in the quota for your MaxDiff question. 

<number label="Q1_Version" size="3" optional="1" verify="range(1,16)" where="execute">
      <title>Q1 - MaxDiff Version</title>
      <exec>
print p.markers
for x in p.markers:
    if "/Q1_Maxdiff/ver_" in x:
        Q1_Version.val = int(x.split("_")[-1])
        break
      </exec>
    </number>
    <suspend/>

5.7:  Updating the Sheet Name/Default Version Markers

If you updated the quota sheet name or the default version markers, be sure to update the following line of code (if "/Q1_Maxdiff/ver_" in x:) to reflect your changes.

for x in p.markers:
    if "/Q1_Maxdiff/ver_" in x:
        Q1_Version.val = int(x.split("_")[-1])
        break
      </exec>
    </number>
    <suspend/> 

 

5.8:  Updating the Question and Loop

This is the MaxDiff question element. It's incorporated into a <loop> and will run for as many tasks that are provided in the <looprow>s. The <style> tags were shortened for the sake of saving space. They simply re-structure the questions format so that a column is presented on both sides of the row options. Here's a list of items that should be updated to reflect your project design:

  • Update the <radio> question's <title> and <comment> tags.  Title and comment tags represent the question and instruction text (respectively) that displays to the respondents for each task in the MaxDiff.
  • Verify that shuffle="rows" is specified for this question.  Unlike the alternatives method, this attribute is required for the indices method.
  • If the client requests to randomize tasks in addition to what the randomization design file is already doing, change the randomizeChildren value to "1".   By default it is set to "0" and it is typically left unchanged.
  • Update the question's <col> text (e.g. "Most favorite", "Least favorite")
  • Replace the rows in the template to have one row per attribute with rows programmed in attribute order.  The template provides for 15 rows (<row>).
  • Update the number of tasks (looprow) to match the number of tasks in your design file.
    <loop label="Q1_md_loop" vars="task" randomizeChildren="0">
      <title>Q1 - MaxDiff Loop</title>
      <block label="Q1_md_block" randomize="1">
        <radio label="Q1_[loopvar: task]" adim="cols" grouping="cols" shuffle="rows" unique="1" ss:questionClassNames="Q1_maxdiff">
          <title>Title update [MDcount]</title>
          <comment>Select one</comment>
          <exec>
setupMaxDiffItemsI( Q1_md, "v%d_t%d" % (Q1_Version.val, [loopvar: task]), Q1_[loopvar: task])
p.MDcount = str(Q1_md_loop_expanded.order.index([loopvar: task]-1)+1)
          </exec>
          <col label="best">Most Important</col>
          <col label="worst">Least Important</col>
    <row label="item1">Item 1</row>
    <row label="item2">Item 2</row>
    <row label="item3">Item 3</row>
    <row label="item4">Item 4</row>
    <row label="item5">Item 5</row>
    <row label="item6">Item 6</row>
    <row label="item7">Item 7</row>
    <row label="item8">Item 8</row>
    <row label="item9">Item 9</row>
    <row label="item10">Item 10</row>
    <row label="item11">Item 11</row>
    <row label="item12">Item 12</row>
    <row label="item13">Item 13</row>
    <row label="item14">Item 14</row>
    <row label="item15">Item 15</row>
      <style name="question.header" mode="before">
        <![CDATA[
        ...
     ]]>
     </style>
     <style name="question.row">
        <![CDATA[
        ...
      ]]>
      </style>
      <style name="question.top-legend">
         <![CDATA[
         ...
       ]]>
       </style>
     </radio>
  </block>
      
      <looprow label="1">
        <loopvar name="task">1</loopvar>
      </looprow>
      
      <looprow label="2">
        <loopvar name="task">2</loopvar>
      </looprow>
      
      <looprow label="3">
        <loopvar name="task">3</loopvar>
      </looprow>
      
      <looprow label="4">
        <loopvar name="task">4</loopvar>
      </looprow>
      
      <looprow label="5">
        <loopvar name="task">5</loopvar>
      </looprow>
      
      <looprow label="6">
        <loopvar name="task">6</loopvar>
      </looprow>
      
      <looprow label="7">
        <loopvar name="task">7</loopvar>
      </looprow>
      
      <looprow label="8">
        <loopvar name="task">8</loopvar>
      </looprow>
      
      <looprow label="9">
        <loopvar name="task">9</loopvar>
      </looprow>
      
      <looprow label="10">
        <loopvar name="task">10</loopvar>
      </looprow>
      
      <looprow label="11">
        <loopvar name="task">11</loopvar>
      </looprow>
      
      <looprow label="12">
        <loopvar name="task">12</loopvar>
      </looprow>
      
      <looprow label="13">
        <loopvar name="task">13</loopvar>
      </looprow>
      
      <looprow label="14">
        <loopvar name="task">14</loopvar>
      </looprow>
      
      <looprow label="15">
        <loopvar name="task">15</loopvar>
      </looprow>
    
    </loop>

5.9:  Recording the Respondent's Time - Informational

This is the question where the respondent's time is recorded. It is recorded in minutes and will be available in the report.

<float label="Q1_Timer" size="15" where="execute">
      <title>Q1 - MaxDiff Timer (Minutes)</title>
      <exec>Q1_Timer.val = (timeSpent() - p.startTime) / 60.0</exec>
    </float>

Since this section is for informational purposes only, you do not need to update this code.

6:  Results of Template Modifications

Below is the revised code adapted to fit the needs of our example design file. For the sake of a good example, it was renamed to "Q24" instead of "Q1" and uses the indices data format. Click here to see a working example of this MaxDiff.

The example design file may be downloaded by clicking here, and the example quota.xls file is available here.

    <note>MaxDiff Indices Template --Start--</note>
    <exec when="init">
def setupMaxDiffFile(fname, fileDelimiter="\t"):
    try:
        f = open("%s/%s" % (gv.survey.path, fname))
        mdObj = [ line.strip("\r\n").split(fileDelimiter) for line in f.readlines() ]
        d = dict( ("v%s_t%s" % (row[0], row[1]), row[2:]) for row in mdObj )
    except IOError:
        d = {}
    return d

def setupMaxDiffItemsI(d, vt, question):
    item_index = dict( (r.o.label.strip("item"), r.index) for r in question.rows )

    items = d[vt]

    for r in question.rows:
        if r.o.label.strip("item") not in items:
            r.disabled = True

    question.rows.order = [ item_index[i] for i in items ]

    print "*****STAFF ONLY*****"
    print "Version_Task: %s" % vt
    for i in range(len(items)):
        print "Item %s: %s" % (i+1,items[i])
    </exec>
    
    <exec when="init">Q24_md = setupMaxDiffFile("design.dat")</exec>
    
    <quota overquota="noqual" sheet="Q24_Maxdiff"/>
    
    <number label="Q24_Version" size="3" optional="1" verify="range(1,10)" where="execute">
      <title>Q24 - MaxDiff Version</title>
      <exec>
print p.markers
for x in p.markers:
    if "/Q24_Maxdiff/ver_" in x:
        Q24_Version.val = int(x.split("_")[-1])
        break
      </exec>
    </number>
    <suspend/>
    
    <exec>p.startTime = timeSpent()</exec>
    
    <loop label="Q24_md_loop" vars="task" randomizeChildren="0">
      <title>Q24 - MaxDiff Loop</title>
      <block label="Q24_md_block" randomize="1">
        <radio label="Q24_[loopvar: task]" adim="cols" grouping="cols" shuffle="rows" unique="1" ss:questionClassNames="Q24_maxdiff">
          <title>Title update [MDcount]</title>
          <comment>Select one</comment>
          <exec>
setupMaxDiffItemsI( Q24_md, "v%d_t%d" % (Q24_Version.val, [loopvar: task]), Q24_[loopvar: task])
p.MDcount = str(Q24_md_loop_expanded.order.index([loopvar: task]-1)+1)
          </exec>
          <col label="best">Most Important</col>
          <col label="worst">Least Important</col>
    <row label="item1">Traditional</row>
    <row label="item2">Innovative</row>
    <row label="item3">Steady</row>
    <row label="item4">Fast-paced</row>
    <row label="item5">Technology-oriented</row>
    <row label="item6">Community-focused</row>
    <row label="item7">Industry leader</row>
    <row label="item8">Expert</row>
    <row label="item9">Consultative</row>
    <row label="item10">Customer-focused</row>
    <row label="item11">Revenue-focused</row>
    <row label="item12">Proactive</row>

<style name="question.header" mode="before">
            <![CDATA[
    <style type="text/css">
    .Q24_maxdiff tr.maxdiff-header-legend {
        background-color: transparent;
        border-bottom: 2px solid #d9d9d9;
    }
    .Q24_maxdiff tr.maxdiff-header-legend th.legend {
        background-color: transparent;
        border: none;
    }
    .Q24_maxdiff tr.maxdiff-row td.element {
        border-left: none;
        border-right: none;
        border-top: none;
        border-bottom: 1px solid #d9d9d9;
        text-align: center;
    }
    .Q24_maxdiff tr.maxdiff-row th.row-legend {
        background-color: transparent;
        border-left: none;
        border-right: none;
        border-top: none;
        border-bottom: 1px solid #d9d9d9;
        text-align: center;
    }
    </style>
            ]]>
</style>
 
<style name="question.top-legend">
            <![CDATA[
\@if ec.simpleList
    $(legends)
\@else
    <$(tag) class="maxdiff-header-legend row row-col-legends row-col-legends-top ${"mobile-top-row-legend " if mobileOnly else ""}${"GtTenColumns " if ec.colCount > 10 else ""}colCount-$(colCount)">
        ${"%s%s" % (legends.split("</th>")[0],"</th>")}
       $(left)
        ${"%s%s" % (legends.split("</th>")[1],"</th>")}
    </$(tag)>
    \@if not simple
  </tbody>
  <tbody>
    \@endif
\@endif
            ]]>
</style>
 
<style name="question.row">
            <![CDATA[
\@if ec.simpleList
    $(elements)
\@else
    <$(tag) class="maxdiff-row row row-elements $(style) colCount-$(colCount)">
        ${"%s%s" % (elements.split("</td>")[0],"</td>")}
        $(left)
        ${"%s%s" % (elements.split("</td>")[1],"</td>")}
    </$(tag)>
\@endif
            ]]>
</style>
        </radio>
      </block>
      
      <looprow label="1">
        <loopvar name="task">1</loopvar>
      </looprow>
      
      <looprow label="2">
        <loopvar name="task">2</loopvar>
      </looprow>
      
      <looprow label="3">
        <loopvar name="task">3</loopvar>
      </looprow>
      
      <looprow label="4">
        <loopvar name="task">4</loopvar>
      </looprow>
      
      <looprow label="5">
        <loopvar name="task">5</loopvar>
      </looprow>
      
      <looprow label="6">
        <loopvar name="task">6</loopvar>
      </looprow>
      
      <looprow label="7">
        <loopvar name="task">7</loopvar>
      </looprow>
      
      <looprow label="8">
        <loopvar name="task">8</loopvar>
      </looprow>
      
      <looprow label="9">
        <loopvar name="task">9</loopvar>
      </looprow>
      
      <looprow label="10">
        <loopvar name="task">10</loopvar>
      </looprow>
      
      <looprow label="11">
        <loopvar name="task">11</loopvar>
      </looprow>
      
      <looprow label="12">
        <loopvar name="task">12</loopvar>
      </looprow>
               
    </loop>
    
    <float label="Q24_Timer" size="15" where="execute">
      <title>Q1 - MaxDiff Timer (Minutes)</title>
      <exec>Q1_Timer.val = (timeSpent() - p.startTime) / 60.0</exec>
    </float>
    
    <note>MaxDiff Indices Template --End--</note>