Pular para o conteúdo principal
Base de Conhecimento da FocusVision

Basic and Advanced Shuffling

1:  Overview

Shuffling survey elements is a common task. To maintain consistency across the respondents' entire experience, we often need to shuffle elements in the same order as they were previously shown.

2:  Basic Shuffling

To shuffle a question's elements, add the shuffle="rows,cols,choices,groups" attribute to the question tag.

<checkbox label="Q1" atleast="1" shuffle="rows">
    <title>Please select items you're familiar with.</title>
    <row label="r1">Item 1</row>
    <row label="r2">Item 2</row>
    <row label="r3">Item 3</row>
    <row label="r4">Item 4</row>
</checkbox>

To shuffle a question's elements in the same order as a previous question, we can use the shuffleBy="QUESTION_LABEL" attribute.

<radio label="Q2" shuffle="rows" shuffleBy="Q1" rowCond="Q1[row]">
    <title>Which one is your favorite?</title>
    <row label="r1">Item 1</row>
    <row label="r2">Item 2</row>
    <row label="r3">Item 3</row>
    <row label="r4">Item 4</row>
</radio>

To use the shuffleBy attribute, the question's shuffled elements must be exactly the same as the original question. More often than not, the question's elements aren't the same and we'll need to manually set the shuffle order.

You can shuffle rows and cols in the same question (e.g. shuffle="rows,cols"), but shuffleBy will not stack this way (e.g. shuffleBy="Q1,Q2" is invalid). To use shuffleBy, the elements must be the same as the question to shuffle by.

3:  Advanced Shuffling

When it's necessary to manually shuffle question elements, we can use the shuffled element's order attribute. This will return a list containing the shuffle order

In the following example, Q2 is shuffled in the same order as Q1.

<checkbox label="Q1" atleast="1" shuffle="rows">
    <title>Please select items you're familiar with.</title>
    <row label="r1">Item 1</row>
    <row label="r2">Item 2</row>
    <row label="r3">Item 3</row>
    <row label="r4">Item 4</row>
</checkbox>
<suspend/>

<exec>Q2.rows.order = Q1.rows.order</exec>
<radio label="Q2" shuffle="rows" rowCond="Q1[row]">
    <title>Which one is your favorite?</title>
    <row label="r1">Item 1</radio>
    <row label="r2">Item 2</row>
    <row label="r3">Item 3</row>
    <row label="r4">Item 4</row>
</radio>

Using <exec> blocks and Python code, we can modify the shuffle order for any shuffled element.

3.1:  Obtaining the Shuffle Order

The order attribute will give us access to the shuffle order for any group of shuffled elements.

The rows and columns in the question below, Q1, are beling shuffled. Following is a table showing the various ways to access the shuffle order:

Python Code Value
Q1.rows.order [[Q1.r1], [Q1.r3], [Q1.r2], [Q1.r4]]
Q1.cols.order [[Q1.c3], [Q1.c1], [Q1.c4], [Q1.c2]]
[x.index for x in Q1.rows.order] [0, 2, 1, 3]
[x.label for x in Q1.cols.order] ['c3', 'c1', 'c4', 'c2']
[x.text for x in Q1.rows.order] ['1', '3', '2', '4']
Q1.cols.order[1].text == "1" True

The order attribute returns a list of objects relative to the shuffled items. Using this list, we can easily create logic to set the shuffle order to fit the needs of our project.

3.2:  Specifying the Shuffle Order

Setting the shuffle order for elements can be done by setting the value of the order attribute. Keep in mind that the system uses the index value for each shuffled item when setting shuffle order.

For example, given a shuffled question with 4 rows ("r1" - "r4"), we can set the shuffle order in the following ways:

Python Code Resulting Order
Q1.rows.order = Q0.rows.order Q0's row order
Q1.rows.order = Q0.cols.order Q0's col order
Q1.rows.order = Q0.choices.order Q0's choice order
Q1.rows.order = [0, 1, 2, 3] Ascending
Q1.rows.order = [0, 1, 2, 3, 4, 5, 6] Ascending
Q1.rows.order = [0] Ascending
Q1.rows.order = [Q1.r1, Q1.r2, Q1.r3, Q1.r4] Ascending
Q1.rows.order = [3, 2, 1, 0] Descending
Q1.rows.order = [6, 5, 4, 3, 2, 1, 0] Descending
Q1.rows.order = [Q1.r4, Q1.r3, Q1.r2, Q1.r1] Descending
Q1.rows.order = Q1.rows[::-1] Descending
Q1.rows.order = [3] "r4", "r1", "r2", "r3"
Q1.rows.order = [1, 2] "r2", "r3", "r1", "r4"
Q1.rows.order = Q1.rows.order Random (unnecessary)
Q1.rows.order = random.sample(xrange(4), 4) Random (unnecessary)
random.shuffle(Q1.rows.order) Random (unnecessary)

The table above illustrates that there are many ways to approach setting the shuffle order. Don't forget that you can set randomize="0" on any element to keep it in the same position and prevent it from being shuffled.

For example, if there is an exclusive option that should always remain on the bottom of the list, set randomize="0" on the element and it will remain in its anchored position.

<exec>Q1.rows.order = Q0.rows.order</exec>
<checkbox label="Q1" atleast="1" shuffle="rows">
    <title>Which items are you aware of?</title>
    <row label="r1">Item 1</row>
    <row label="r2">Item 2</row>
    <row label="r3">Item 3</row>
    <row label="r4">Item 4</row>
    <row label="r5" exclusive="1" randomize="0">None of the above</row>
</checkbox>

3.3:  Recording the Shuffle Order

The order for all shuffled elements can be found in the "Random Order" file located in the "Tracking Files" section of the data downloads.

We can also create virtual number question to track the shuffle order for questions individually. For example:

<radio label="Q1" shuffle="rows">
    <title>Please select one item.</title>
    <row label="r1">Item 1</row>
    <row label="r2">Item 2</row>
    <row label="r3">Item 3</row>
    <row label="r4">Item 4</row>
</radio>

<number label="Q1_ROW_ORDER" size="1" onLoad="copy('Q1', rows=True)">
    <title>Q1 Row Order</title>
    <virtual>
assignRandomOrder('Q1', 'rows')
    </virtual>
</number>

Since this is a virtual question, it can be added to your project at any time.

In the example above, the function assignRandomOrder("Q1", "rows") creates an additional table in the report that tracks the order of each row item. Notice that we used the onLoad="copy(...)" mutator to load the same rows from Q1 into Q1_ROW_ORDER. For example, if the rows were shuffled in descending order, then the values for ("r1", "r2", "r3", "r4") would be (4, 3, 2, 1).

Here's what the Q1_ROW_ORDER table may look like for a single respondent:

The table above tells us that this respondent's shuffle order was ("r1", "r2", "r4", "r3").

In addition to the function, assignRandomOrder, the function getRandomOrder is available too. This will return a list of labels representing the shuffle order for the question.

Calling getRandomOrder("Q1", "rows") for the respondent above will return the list, ["r1", "r2", "r4", "r3"].

Demonstrated in the examples below, we can also use the assignRandomOrder function to track the way in which questions were shuffled within a <block> tag.

4:  Examples

Here are a few examples to demonstrate some of the advanced shuffling techniques discussed above. Read along, view the XML source code and check out the demos for each example.

4.1:  Shuffle Question Elements

In the following example, we'll maintain the order of the brands shown at Q1 across the entire survey using several different shuffling methods.

<checkbox label="Q1" atleast="1" shuffle="rows">
  <title>
    Which brands are you aware of?
  </title>
  <comment>Please select all that apply</comment>
  <row label="r1">Brand 1</row>
  <row label="r2">Brand 2</row>
  <row label="r3">Brand 3</row>
  <row label="r4">Brand 4</row>
</checkbox>
<suspend/>


<checkbox label="Q2" onLoad="copy('Q1', rows=True)" shuffle="rows" shuffleBy="Q1" rowCond="Q1[row]">
  <title>
    Which brands have you purchased in the past 6 months?
  </title>
  <comment>Please select all that apply</comment>
</checkbox>
<suspend/>


<exec>Q3.rows.order = Q1.rows.order</exec>
<radio label="Q3" onLoad="copy('Q1', rows=True)" shuffle="rows" rowCond="Q1[row]">
  <title>
    Which brand is your favorite?
  </title>
  <comment>Please select one</comment>
</radio>
<suspend/>


<exec>Q4.cols.order = Q1.rows.order</exec>
<checkbox label="Q4" atleast="1" shuffle="rows,cols" colCond="col.label == 'c5' or Q1.rows[col.index]">
  <title>
    Please check which brand has these characteristics?
  </title>
  <comment>Please select all that apply</comment>
  <col label="c1">Brand 1</col>
  <col label="c2">Brand 2</col>
  <col label="c3">Brand 3</col>
  <col label="c4">Brand 4</col>
  <col label="c5" exclusive="1" randomize="0">None of these</col>
  <row label="r1">Awesome</row>
  <row label="r2">Cool</row>
  <row label="r3">Fast</row>
</checkbox>
<suspend/>


<exec>
Characteristics_Loop_expanded.order = Q4.rows.order
</exec>
<loop label="Characteristics_Loop" vars="characteristic" randomizeChildren="1">
    <block label="bCharacteristics">

<radio label="Q5_[loopvar: label]" colCond="Q4[col].[loopvar: label]" shuffle="cols" exec="Q5_[loopvar: label].cols.order = Q4.cols.order">
  <title>
    Which brand is the most [loopvar: characteristic]?
  </title>
  <comment>Please select one</comment>
  <col label="c1">Brand 1</col>
  <col label="c2">Brand 2</col>
  <col label="c3">Brand 3</col>
  <col label="c4">Brand 4</col>
</radio>
<suspend/>

    </block>
    <looprow label="r1"><loopvar name="characteristic">awesome</loopvar></looprow>
    <looprow label="r2"><loopvar name="characteristic">cool</loopvar></looprow>
    <looprow label="r3"><loopvar name="characteristic">fast</loopvar></looprow>
</loop>

In the example above, we use several techniques to create a consistent survey:

  • shuffle="rows,cols" to shuffle the question's elements
  • rowCond="..."/colCond="..." to conditionally show each row/col
  • <exec>...</exec> to manually set the shuffle order
  • onLoad="copy(...)" to copy question elements from one to another
  • randomize="0" to prevent an element from being shuffled
  • Loop_expanded.order to shuffle the loop's children elements consistently with the original shuffle at Q4

Demo

Click here to see this example in a live survey.

Check all the boxes at Q1 and notice the order remains throughout the entire survey.

4.2:  Shuffle Questions

Using the <block> element and the randomizeChildren="1" attribute, we can shuffle entire questions. In the example below, we'll shuffle the sections pertaining to each brand by the original question, Q1. Each section will be contained in another <block> element which should have randomize="1" specified. We'll also include a virtual question to track the order in which each section was shown.

<checkbox label="Q1" atleast="1" shuffle="rows">
  <title>
    Which brands have you heard of?
  </title>
  <comment>Please select all that apply</comment>
  <row label="r1">Brand 1</row>
  <row label="r2">Brand 2</row>
  <row label="r3">Brand 3</row>
  <row label="r4">Brand 4</row>
</checkbox>
<suspend/>


<exec>Brands_Section.order = Q1.rows.order</exec>
<block label="Brands_Section" randomizeChildren="1">

  <block label="Brand_1" randomize="1" cond="Q1.r1">
     <radio label="B1_1" type="rating" values="order">
       <title>
         On a scale from 1 - 5, how do you feel about Brand 1?
       </title>
       <comment>Please select one</comment>
       <col label="c1">1</col>
       <col label="c2">2</col>
       <col label="c3">3</col>
       <col label="c4">4</col>
       <col label="c5">5</col>
     </radio>
     <suspend/>
  </block>

  <block label="Brand_2" randomize="1" cond="Q1.r2">
     <radio label="B2_1" type="rating" values="order" onLoad="copy('B1_1', cols=True)">
       <title>
         On a scale from 1 - 5, how do you feel about Brand 2?
       </title>
       <comment>Please select one</comment>
     </radio>
     <suspend/>
  </block>

  <block label="Brand_3" randomize="1" cond="Q1.r3">
     <radio label="B3_1" type="rating" values="order" onLoad="copy('B1_1', cols=True)">
       <title>
         On a scale from 1 - 5, how do you feel about Brand 3?
       </title>
       <comment>Please select one</comment>
     </radio>
     <suspend/>
  </block>

  <block label="Brand_4" randomize="1" cond="Q1.r4">
     <radio label="B4_1" type="rating" values="order" onLoad="copy('B1_1', cols=True)">
       <title>
         On a scale from 1 - 5, how do you feel about Brand 4?
       </title>
       <comment>Please select one</comment>
     </radio>
     <suspend/>
  </block>

</block>

<number label="vBrand_Order" size="1">
  <virtual>assignRandomOrder('Brands_Section', 'children')</virtual>
  <title>
    Order of Brands Shown
  </title>
  <row label="Brand_1">Brand 1</row>
  <row label="Brand_2">Brand 2</row>
  <row label="Brand_3">Brand 3</row>
  <row label="Brand_4">Brand 4</row>
</number>

Extracted from the "Tracking Files" data download, we can see that each brand section was, indeed, shuffled by Q1:

Q1_rows         Brands_Section_children
r4,r3,r2,r1     Brand_4,Brand_3,Brand_2,Brand_1
r1,r3,r4,r2     Brand_1,Brand_3,Brand_4,Brand_2
r2,r4,r1,r3     Brand_2,Brand_4,Brand_1,Brand_3
r2,r1,r4,r3     Brand_2,Brand_1,Brand_4,Brand_3
r1,r2,r4,r3     Brand_1,Brand_2,Brand_4,Brand_3
r2,r3,r1,r4     Brand_2,Brand_3,Brand_1,Brand_4
r3,r1,r2,r4     Brand_3,Brand_1,Brand_2,Brand_4
r3,r1,r4,r2     Brand_3,Brand_1,Brand_4,Brand_2
r2,r1,r3,r4     Brand_2,Brand_1,Brand_3,Brand_4
...
...

We can also see this information replicated in the vBrand_Order virtual question that we created:

vBrand_OrderBrand_1	vBrand_OrderBrand_2	vBrand_OrderBrand_3	vBrand_OrderBrand_4
4	                3	                2	                1
1	                4	                2	                3
3	                1	                4	                2
2	                1	                4	                3
1	                2	                4	                3
3	                1	                2	                4
2	                3	                1	                4
2	                4	                1	                3
2	                1	                3	                4
...

4.3:  Recording Shuffle Order for Loops

Below is example of how to use assignRandomOrder() to obtain the order of looprows in a loop. The loop is labeled l1 and the looprows are labeled r1, r2, and r3. In the assignRandomOrder() function, you will reference the loop label followed by _expanded. The labels for the rows will be in the following format: [loop label]_[looprow label]_expanded.

<loop label="l1" randomizeChildren="1" vars="q1">
  <title>L1 Loop</title>
  <block label="b1" builder:title="default loop block">
    <text 
    label="q2_[loopvar: label]"
    optional="0"
    randomize="0"
    size="25">
      <title>What do you like about [loopvar: q1]?</title>
      <comment>be specific</comment>
    </text>
  </block>

  <looprow label="r1" cond="(q1.r1)">
    <loopvar name="q1">Brand 1</loopvar>
  </looprow>

  <looprow label="r2" cond="(q1.r2)">
    <loopvar name="q1">Brand 2</loopvar>
  </looprow>

  <looprow label="r3" cond="(q1.r3)">
    <loopvar name="q1">Brand 3</loopvar>
  </looprow>
</loop>

<number 
  label="l1_LOOP_ORDER"
  size="1">
  <title>l1 Loop Order</title>
  <virtual>
assignRandomOrder('l1_expanded', 'children')
  </virtual>

  <row label="l1_r1_expanded">Loop 1</row>
  <row label="l1_r2_expanded">Loop 2</row>
  <row label="l1_r3_expanded">Loop 3</row>
</number>

Demo

Click here to see this example in a live survey.

Check all the boxes at Q1 and notice the order remains throughout the entire survey.

In the example above, we wrapped each brand-specific question in a <block> with randomize="1" set. This is because, in a typical survey, there would be many brand-specific questions to ask. Since each block only contains one question, we could remove them and the shuffling would still be consistent. We would, however, need to update our rows at vBrand_Order to reflect the question labels instead of the block labels (e.g. "B4_1" instead of "Brand_4").

  • Este artigo foi útil?