Preliminary Project Setup - Reverse Scales in Question(s)

From Q
Jump to navigation Jump to search

Reverse scales in selected nominal and nominal-multi variable sets This QScript reverses the order of the categories on Pick One and Pick One - Multi questions. This means that the entries in the Value columns of the Value Attributes will be reversed, and the ordering of the labels in the table will also be reversed. This process will ignore any categories that have been set as Missing Data.

Example

Reverse.png

Technical details

You will be asked:

  1. If you want Q to guess which questions in the project are scales. If you select No then you will be shown a list of all applicable questions.
  2. Which questions you want to reverse.
  3. If there are any Don't Know style categories in the questions that you have selected then you will be asked if you want to include them in the scale reversal. Click No to exclude these categories.

How to apply this QScript

  • Start typing the name of the QScript into the Search features and data box in the top right of the Q window.
  • Click on the QScript when it appears in the QScripts and Rules section of the search results.

OR

  • Select Automate > Browse Online Library.
  • Select this QScript from the list.

Customizing the QScript

This QScript is written in JavaScript and can be customized by copying and modifying the JavaScript.

Customizing QScripts in Q4.11 and more recent versions

  • Start typing the name of the QScript into the Search features and data box in the top right of the Q window.
  • Hover your mouse over the QScript when it appears in the QScripts and Rules section of the search results.
  • Press Edit a Copy (bottom-left corner of the preview).
  • Modify the JavaScript (see QScripts for more detail on this).
  • Either:
    • Run the QScript, by pressing the blue triangle button.
    • Save the QScript and run it at a later time, using Automate > Run QScript (Macro) from File.

Customizing QScripts in older versions

  • Copy the JavaScript shown on this page.
  • Create a new text file, giving it a file extension of .QScript. See here for more information about how to do this.
  • Modify the JavaScript (see QScripts for more detail on this).
  • Run the file using Automate > Run QScript (Macro) from File.

JavaScript

// Prelimiary Project Setup - Reverse Scales
// This script will reverse the scales of selected questions.
// This means that the values will be reversed, so if the value for 'Strongly Agree' is 1 
// and the value for 'Strongly Disagree' is 5, then the value attributes will be changed 
// so that the new value for 'Strongly Agree' is 5 and the new value for 'Strongly Disagree' is 1
// In addition, the data reduction will also be reversed, so that categories at the bottom are
// moved to the top.
 
includeWeb('QScript Utility Functions');
includeWeb('QScript Questionnaire Functions');
includeWeb('QScript Selection Functions');
includeWeb('QScript Value Attributes Functions');
includeWeb('QScript Functions to Generate Outputs');
 
if (!main())
    log('QScript cancelled.');
 
function main() {
    // Ask the user to choose which data files to use
    var data_file = requestOneDataFileFromProject(false, true);
    var is_displayr = (!!Q.isOnTheWeb && Q.isOnTheWeb());
    var structure_name = is_displayr ? "variable sets" : "questions";
    var qname = is_displayr ? "Displayr" : "Q";

    if (askYesNo(qname + " will now show you a list of " + structure_name +
        " to choose from. Would you like " + qname + " to show only " + 
        structure_name + " that look like scales?")) {
        var relevant_questions = getAllScaleQuestions([data_file]);
    } else {
        relevant_questions = getAllQuestionsByTypes([data_file], ["Pick One", "Pick One - Multi"]);
    }
    
    var relevant_questions = getAllScaleQuestions([data_file]);
    if (relevant_questions.length > 0) {
        // Generate a list of applicable questions along with the highest and lowest category labels
        var question_label_strings = [];
        var highest_and_lowest_label;
        relevant_questions.forEach(function (question) {
            highest_and_lowest_label = getHighestAndLowestValueAndLabel(question);
            question_label_strings.push(truncateStringForSelectionWindow(question.name) + "  (" 
                + highest_and_lowest_label.lowest + " ... " + highest_and_lowest_label.highest + ")");
        });
 
        // Prompt the user to select the questions that they want to use
        var selected_indices = selectMany("Please choose which " +
            structure_name + " you would like to reverse:\r\n" +
            "(highest and lowest value labels are shown in brackets)", question_label_strings);
        var selected_questions = getElementsOfArrayBySelectedIndices(relevant_questions, selected_indices);
 
        // Check selected questions for 'Don't Know' categories
        var dk_check_obj = checkToIncludeQuestionsWithDkInReversal(selected_questions);
        var include_dk = dk_check_obj.includeDK;
        var questions_containing_dk = dk_check_obj.questionsContainingDK;
        var dk_question_names = questions_containing_dk.map(function (q) { return q.name;});
       
        // Process selected questions
        selected_questions.forEach(function (current_question) {
            var is_dk = dk_question_names.indexOf(current_question.name) > -1;
            var value_attributes = current_question.valueAttributes;

            // Get all relevant information about all of the unique values
            var values_object = current_question.uniqueValues.map(function (v) {
                var current_label = value_attributes.getLabel(v);
                var exclude_as_dk = (is_dk && !include_dk && isDontKnow(current_label))
                return { sourceValue: v,
                         currentValue: value_attributes.getValue(v),
                         currentLabel: current_label,
                         keepInSort: !(value_attributes.getIsMissingData(v) || exclude_as_dk),
                         newValue: null };
            });

            // Figure out which values need to be recoded and discard the rest
            var values_object = values_object.filter(function (obj) {
                return obj.keepInSort;
            });

            // Reverse the values
            reverseValues(current_question, values_object);

            // Reorder the data reduction according to the recoded values.
            // This is consistent with Q's default ordering with the smallest value
            // at the top.
            rectifyDataReduction(current_question, values_object);
        });
    } else {
        log("The data file contains no appropriate " + structure_name + ".");
        return false;
    }
    // Add tables for recoded questions
    if (!is_displayr)
    {
        generateGroupOfSummaryTables('Questions with reversed scale values', selected_questions);
        log('Tables showing the questions with reversed scales have been added to your report.');
    } else
    {
        log("The following variable sets have reversed scale values: " + 
            selected_questions.map(function(q) { return(q.name); }));
        project.report.setSelectedRaw(selected_questions);
    }
    return true;
}
 
// This function checks the selected questions for 'Don't Know' categories.
// If any are found, the user is prompted to continue to to cancel
// The function returns an object containing an array of the questions with
// 'Don't know' categories and a flag telling whether the user cancelled or not
function checkToIncludeQuestionsWithDkInReversal(selected_questions) {
    var num_selected_questions = selected_questions.length;
 
    // Check selected questions for 'Dont Know' options
    var questions_containing_dk = [];
    for (var j = 0; j < num_selected_questions; j++) {
        current_question = selected_questions[j];
        var dk_labels = nonMissingValueLabels(current_question).filter(isDontKnow);
        if (dk_labels.length > 0)
            questions_containing_dk.push(current_question);
    }
 
    // Warn user that their questions contain 'Dont Know' options
    if (questions_containing_dk.length > 0) {
        var is_displayr = (!!Q.isOnTheWeb && Q.isOnTheWeb());
        var structure_name = is_displayr ? "variable sets" : "questions";
        var dk_message = "Some of the " + structure_name +
            " that you have selected contain 'Don't Know' categories (for example: ";
        for (var j = 0; j < Math.min(questions_containing_dk.length, 2); j++) {
            if (j > 0) {
                dk_message += ", "
            }
            dk_message += questions_containing_dk[j].name;
        }
        dk_message += ").\r\nDo you wish to include the 'Don't Know' option in the scale reversal?";
        var include_dk = askYesNo(dk_message);
    }
 
    return {questionsContainingDK: questions_containing_dk, includeDK: include_dk}
}

// Having recoded the question, arrange the codes in the table so that they are consistent with the new ordering
function rectifyDataReduction(question, values_object) {
    var data_reduction = question.dataReduction;
    // Sort the values object according to the recoded values (newValue)
    values_object.sort(function (x,y) {
        return  x.newValue - y.newValue;
    });

    // Get the labels that correspond to values (categories). If this is a Pick One - 
    // Multi question then it can vary between row and column labels depending 
    // on whether or not the table is transposed.
    var labels = data_reduction.rowLabels;
    if (question.questionType == "Pick One - Multi" && data_reduction.transposed)
        labels = data_reduction.columnLabels;
    

    // Move the codes around in the table
    var last_top = null;
    values_object.forEach(function (obj) {
        var current_top_source_value = obj.sourceValue;
        // Find the code whose value matches this and move it after the last code
        // that was moved.
        labels.forEach(function (label) {
            var underlying_values = data_reduction.getUnderlyingValues(label);
            if (underlying_values.length == 1  && underlying_values[0] == current_top_source_value) {
                data_reduction.moveAfter(label, last_top);
                last_top = label;
            }
        });
    });
}

// Reverse the values in the input question according to the details in
// values_object. Store the new values for each source value in values_object
function reverseValues(question, values_object) {
    var value_attributes = question.valueAttributes;
    for (var j = 0; j < values_object.length; j++) {
        setValueForVariablesInQuestion(question, values_object[j].sourceValue, values_object[values_object.length - 1 - j].currentValue); 
        values_object[j].newValue = values_object[values_object.length - 1 - j].currentValue;
    }
}

See also