How To Disentangle Piping and Looping in Q

From Q
Jump to navigation Jump to search

What is piping and looping?

Sometimes in surveys the same question is asked multiple times, but with the insertion of a different item every time the question is asked. The insertion of the item is what is known as piping. The item could be a brand, a product, a concept or perhaps a statement (or other). The multiple presentation of the same question is called looping. The order of presentation of the items within a loop be can rotated or randomised. This page shows you how you can work with this data in Q for analysis.

An illustration of the problem

A worked example is captured in this File:Depiping delooping example.QPack. In a loop, the same question “How often do you drink <BRAND>?” could be asked three times. Each iteration of the loop pertains to a different brand (the item that’s piped in). Suppose there were 6 possible brands (Coke, Diet Coke, Coke Zero, Pepsi, Pepsi Max, Diet Pepsi), but only 3 questions asking about how often each get drunk.

One respondent gets asked:

  • How often do you drink Coke?
  • How often do you drink Pepsi?
  • How often do you drink Fanta?

But another respondent is asked:

  • How often do you drink Pepsi?
  • How often do you drink Diet Coke?
  • How often do you drink Pepsi Max?

And so forth, with the brands rotating/randomising across respondents.

The data may be stored in variables that indicate the order of presentation as opposed to variables that store information about the specific items. In cola example above, the datafile could have 3 variables:

  • var1 = How often do you drink <BRAND_1>?
  • var2 = How often do you drink <BRAND_2>?
  • var3 = How often do you drink <BRAND_3>?

For one case BRAND_1 is Coke at var1, whereas for another BRAND_3 is Coke, and in another case is not presented with Coke at all (because only 3 brands were asked about, and there are 6 potential brands).

Storing the data in that way makes sense from a data collection perspective, but it is not useful from an analysis perspective. A researcher typically to know the frequency of drinking each brand, not the frequency of drinking whatever brand was presented first.

Building on the above cola example, if you had six possible brands included in the loop, then, for analysis purposes, you would need 6 variables:

  • varA = How often do you drink Coke?
  • varB = How often do you drink Diet Coke?
  • varC = How often do you drink Coke Zero?
  • varE = How often do you drink Pepsi?
  • varF = How often do you drink Pepsi Max?
  • varG = How often do you drink Diet Pepsi?

The variables A through G above we term delooped/depiped variables.

The item key

The rotation of the items used in the piping are typically stored in separate variable(s), which we term the item key. The item key is what the survey platform draws on to generate each question (which, as discussed, changes for each respondent).

In our cola example, a question could have been asked earlier in the survey: “Which of the following colas have you consumed in the past month?”, and then up to three of the colas are stored in the item key.

So in the above cola example there would likely be three variables that form an item key:

  • hBrand1 – First brand selected for the loop
  • hBrand2 – Second brand selected for the loop
  • hBrand3 – Third brand selected for the loop

Normally the item key is hidden from the respondent. It is only used for the purposes of the (online) survey scripting. Because it’s often hidden, it can often have “h”, “hidden”, “d”, or “dummy” in the variable label(s).

The challenge for the researcher is to use the item key to generate new variables that pertain to each possible item. That is, getting the variables de-looped and/or de-piped. In the cola example above, that is creating varA through varG.

We present 4 possible solutions for de-looping and/or de-piping the data structured as per the above example.

Solution 1: Get your data provider to set up the data for you

The best solution is to get your data provider to do this for you. That is, to set up your variables in such a way that they are de-looped and de-piped. The datafile specifications should be adhered to (so that automatic setup of questions is optimized).

Solution 2: Create a JavaScript variable

If you cannot get the data set up by your data supplier, then you can create new variables in Q. This can be accomplished using a JavaScript variable.

An example of the expression one may write:

x = 1 
if (hBrand1 == x) var1;
else if (hBrand2 == x) var2;
else if (hBrand3 == x) var3;
else NaN;

The above code, from the illustrated example, checks the value each variable in the hBrand set. If any of the hBrand variables equals 1 (i.e., the first brand, e.g.: “Coke), then it returns the corresponding piped variable. So the resultant variable would be for “Coke” only.

An example of JavaScript variables untangling a pipe/loop can be seen in this example File:Depiping delooping example.QPack. The last 3 variables have been constructed with JavaScript (right-click > Insert Variable(s) > JavaScript Formula > Numeric). If you edit these variables (right-click > Edit Variable) you cans how the expression uses the variables the item key (the hBRAND Pick One - Multi question) de-pipe question 5.

Solution 3: Using QScripts and Merge Functions

Another way to achieve this is to use a combination of point-and-click functions. Although this works, it can be very convoluted for large variable sets and grids, and when there are lots of variables that need de-piping.

It is a multi-step process:

Step 1: Make sure the item key and target question is set up appropriately If the item key is a Pick One – Multi, you will need to split it into separate Pick One variables (right-click > Split Variables from Question). This is so they can be used as filter variables in the next step. You may need to split variables from you target question in the same way so that the appropriate filter is applied to the appropriate question (eg: the first item key to only the first iteration in the target question loop).

Step 2: Create new variables that expand the item x iteration Use the QScript Filter One Question by Another from the Automate library. Select the item key as the filter, and the piped question as the target. This will create new variables. Repeat this as necessary if there are multiple variables in the item key.

Step 3: Merge each of the item variables back into one (to remove the order effect) Select all the newly variables that pertain to a particular item, and then merge questions. Do this as many times as there are items. You may wish to hide the input questions, and set the resultant variables back into a question (see: Set Question).

Solution 4: Write your own custom QScript

Solutions 2 and 3 above can become very repetitive. To help automate the process you can develop your own QScript.

An example QScript is shown below, which you can adapt to your purposes. Please note that in this example it makes certain assumptions about the set-up of the data in the project, such that the item key needs to be a Pick One – Multi (and so the categories are consistent across each variable in the item key). The QScript essentially creates a new JavaScript variables (as per solution 2) many times over.

includeWeb("QScript Selection Functions");
includeWeb("QScript Utility Functions");
includeWeb("QScript Functions to Generate Outputs");
includeWeb("QScript Data Reduction Functions");
includeWeb("QScript Value Attributes Functions");

function modified_copyValueAttributesForVariable(from_variable, to_variable){
   var n = from_variable.uniqueValues
   var values = from_variable.uniqueValues;
   var qt = to_variable.question.questionType;
   var qt_old = from_variable.question.questionType;
        //    if (qt != qt_old)
        //         alert("Question types are different.");
   for (var i=0; i<values.length; i++){
	var v = values[i];
	if (qt != "Text" && qt != "Text - Multi"){
		to_variable.valueAttributes.setLabel(v,from_variable.valueAttributes.getLabel(v));
		to_variable.valueAttributes.setIsMissingData(v,from_variable.valueAttributes.getIsMissingData(v));
		if (qt == "Pick Any" || qt == "Pick Any - Compact" || qt == "Pick Any - Grid")
			to_variable.valueAttributes.setCountThisValue(v,from_variable.valueAttributes.getCountThisValue(v));
		else
			to_variable.valueAttributes.setValue(v,from_variable.valueAttributes.getValue(v));
	 };
   };
};

if (!main())
    log("QScript cancelled.");
else 
    conditionallyEmptyLog("QScript finished.");

function main() {
    
    // Get the user to select one data file when there is more than one.
    var data_file = requestOneDataFileFromProject();
    
    // Get the user to select the rotation question
    var rotationPOM = getAllQuestionsByTypes([data_file], ["Pick One - Multi"]);
    var rotation = selectOneQuestion("Select the Pick One - Multi question that stores the rotations:", rotationPOM, true);
 
    //Calculates the number of iterations involved
    var rotation_variables = rotation.variables;  
    var n_iterations = rotation_variables.length
    
    //Gets non-Missing values from the rotation question
    indicator = valueAttributesMissingValueIndicatorArray(rotation)
    
    var d = rotation.uniqueValues;
    var brand_values = [];
    for (i = 0; i < d.length; i++) {
        if (!isNaN(d[i]) && indicator[i] == 0) {
            brand_values.push(d[i]);
        }
    }
    var brand_labels = [];
    brand_labels = nonMissingValueLabels(rotation);

    var loops = [];

    // Get the user to select the first question
    for (i = 1; i < (n_iterations + 1); i++) {
        var x = getAllQuestionsByTypes([data_file], ["Pick One", "Pick One - Multi","Pick Any","Number","Number - Multi","Pick Any - Grid"]);
        var x = selectOneQuestion("Select iteration " + i + " of the loop", x, true);
        loops.push(x) 
        }

    var loops_variables = loops.map (function (v) { return v.variables; } ); 
    var n_variables = loops_variables[0].length

    // Note: b rotates the brand
        
    for (b = 0; b < brand_values.length; b++) {
        var new_q_vars = [];
        for (p = 0; p < n_variables; p++) {

            var expression = "var _result = NaN;\r\n"
            for (i = 0; i <n_iterations; i++) {
                if (i > 0) 
                        expression += "else "; expression += "if (" + rotation_variables[i].name + " == " + brand_values[b] + ") _result =  " + loops_variables[i][p].name +";\r\n"        
                    }
                    expression += "_result;"

            var new_name = loops_variables[0][p].name + "_deloop";
            new_name = preventDuplicateVariableName(data_file, new_name);
            var new_label = preventDuplicateQuestionName(data_file, loops_variables[0][p].label);
            var new_name = preventDuplicateVariableName(data_file, new_name);
            var new_var = data_file.newJavaScriptVariable(expression, false, new_name, new_label, null); 
            new_var.question.name = loops_variables[0][p].question.name + p + " - " + brand_labels[b];
            var type = loops_variables[0][p].variableType;
            new_var.variableType =  type;
            data_file.moveAfter([new_var],data_file.variables[data_file.variables.length-1]);
            modified_copyValueAttributesForVariable(loops_variables[0][p],new_var);
            new_q_vars.push(new_var);
            };
        var new_q_name = loops[0].name + " - " + brand_labels[b];
        var question_type = loops[0].questionType;
        data_file.setQuestion(new_q_name, question_type, new_q_vars);
    };
    return true
}