QScript Data Reduction Functions

From Q
Jump to: navigation, search

The functions on this page can be helpful when writing scripts to work with the Data Reduction of a question.

For built-in functions for working with data reduction objects, see QScript DataReduction.

To make these functions available when writing a QScript or Rule see JavaScript Reference.

dataReductionContainsLabels(question, label_array)

Returns true if all of the labels in label_array are contained in the data reduction for the input question.

getDataReductionLabelsForValues(target_value_array, data_reduction_labels, data_reduction_value_array)

This function returns an array containing the labels in the input data_reduction_labels that match the values in the input target_value_array. The input data_reduction_value_array should have elements that match the order of data_reduction_labels. The labels and value array should be obtained using the function etAllLabelsAndValuesInDataReduction().

getAllUnderlyingValues(question)

Returns an array of objects corresponding to the codes associated with values in the data reduction. Each object has the properties:

  1. label: The data reduction label for this code.
  2. array: The array of values associated with this code.

getAllUnderlyingVariables(question)

Returns an array of objects corresponding to the codes associated with variables in the data reduction. Each object has the properties:

  1. label: The data reduction label for this code.
  2. array: The array of variable names associated with this code.

getMergedCategoriesFromPickOne(question)

Returns an array describing which categories in the Pick One (or Pick One - Multi) question have been merged (excluding the NET). Each entry in the array has:

  1. name: The name of the merged category.
  2. values: An array of the underlying values for the categories that have been merged.

largestDimension(question)

For categorical question types, return the largest number of categories in the table (i.e. between the rows and columns).

mergeCategoriesLessThanOrEqualTo(percentage)

This function returns the percentage value and labels of table rows in a specific question which are less than or equal to a specified percentage value and merges them together in a table.

Source Code

// MANIPULATION OF DATA REDUCTION

// Returns true if all of the labels in label_array are contained in the data reduction for the input question
function dataReductionContainsLabels(question, label_array) {
    var data_reduction = question.dataReduction;
    return label_array.filter(function (x) { return data_reduction.contains(x);}).length == label_array.length;
}


function getDataReductionLabelsForValues(target_value_array, data_reduction_labels, data_reduction_value_array) {
    var num_targets = target_value_array.length;
    var num_labels = data_reduction_labels.length;
    var value_matched;
    var results = [];
    for (var j = 0; j < num_targets; j++) {
        value_matched = false;
        for (var k = 0; k < num_labels; k++) {
            if (equalArraysOfIntegers([target_value_array[j]], data_reduction_value_array[k])){
                results.push(data_reduction_labels[k]);
                value_matched = true;
                break;
            }
        }
        if (!value_matched)
            throw "No label found for value " + target_value_array[j];
    }
    return results;
}

 
// Returns an array of objects corresponding to the codes associated with values in the
// data reduction.
//
// Each object has the properties:
//
// 1. label:    The data reduction label for this code.
// 2. array:    The array of values associated with this code.
function getAllUnderlyingValues(question) {
    var question_type = question.questionType;
    if (question_type.indexOf("Pick One") == -1 && question_type != "Pick Any - Compact")
        return null;
    var data_reduction = question.dataReduction;
    var labels;
    if (question_type == "Pick One")
        labels = data_reduction.rowLabels;
    else // Pick One Multi
        labels = data_reduction.transposed ? data_reduction.columnLabels : data_reduction.rowLabels;
    if (labels == null)
        return null;
    var num_value_codes = labels.length;
    var all_underlying_values = [];
    for (var j = 0; j < num_value_codes; j++)
        all_underlying_values.push({label: labels[j], array: data_reduction.getUnderlyingValues(j)});
    return all_underlying_values;
}
 
// Returns an array of objects corresponding to the codes associated with variables in the
// data reduction.
//
// Each object has the properties:
//
// 1. label:    The data reduction label for this code.
// 2. array:    The array of variable names associated with this code.
function getAllUnderlyingVariables(question) {
    var question_type = question.questionType;
    if (question_type != "Pick Any" && question_type != "Pick One - Multi")
        return null;
    var data_reduction = question.dataReduction;
    var labels;
    if (question_type == "Pick Any")
        labels = data_reduction.rowLabels;
    else
        labels = data_reduction.transposed ? data_reduction.rowLabels : data_reduction.columnLabels;
    var num_variable_codes = labels.length;
    var all_underlying_variables = [];
    for (var j = 0; j < num_variable_codes; j++) {
        if (question_type == "Pick One - Multi")
            all_underlying_variables.push({label: labels[j], array: data_reduction.getUnderlyingVariables(~j)});
        else
            all_underlying_variables.push({label: labels[j], array: data_reduction.getUnderlyingVariables(j)});
    }
    return all_underlying_variables;
}

// Returns an array describing which categories in the pick one (- multi)
// question have been merged (excluding the NET). Each entry in the array has:
// - name: The name of the merged category
// - values: An array of the underlying values for the categories that have
//           been merged
function getMergedCategoriesFromPickOne(question) {
    var value_attributes = question.valueAttributes;
    var non_missing_values = question.uniqueValues.filter(function (x) {
        return !value_attributes.getIsMissingData(x);
    }).sort();
 
    // Get the set of values for each code in the data reduction
    var merging_objects = getAllUnderlyingValues(question);
    if (merging_objects == null)
        return null;
    // Filter out the set of values corresoponding to the NET as
    // we don't want to keep them.
    merging_objects = merging_objects.filter(function (obj) {
        return obj.array.sort().toString() != non_missing_values.toString();
    });
    merging_objects = merging_objects.map(function (obj) {
        return { name: obj.label, values: obj.array };
    });
 
    return merging_objects;
} 

function largestDimension(question) {
    var num_categories;
    var q_type = question.questionType;
    if (["Pick One", "Pick Any", "Pick Any - Compact"].indexOf(q_type) > -1)
        num_categories = question.dataReduction.rowLabels.length;
    else if (["Pick One - Multi", "Pick Any - Grid"].indexOf(q_type) > -1)
        num_categories = Math.max(question.dataReduction.rowLabels.length, question.dataReduction.columnLabels.length);
    else
        num_categories = 0;
    return num_categories;
}

function mergeCategoriesLessThanOrEqualTo(percentage) {


    function mergeSmallCategoriesInQuestion(question, table_output, percentage) {
        var data_reduction = question.dataReduction;
        var values = table_output.get("%");
        var labels = table_output.rowLabels;
        var codes_to_merge = [];
        var last_non_merged_code = null;
        var net_rows = getNetRowsOrColumns(data_reduction, false);
        var last_net_label = labels[net_rows[net_rows.length - 1]];
        values.forEach(function (val, ind) {
            if (val <= percentage)
                codes_to_merge.push(labels[ind]);
            else if (labels[ind] != last_net_label)
                last_non_merged_code = labels[ind];
        });

        if (codes_to_merge.length > 1) {
            var merged_name = codes_to_merge.join(" + ");
            data_reduction.merge(codes_to_merge, merged_name);
            data_reduction.moveAfter(merged_name, last_non_merged_code);
            return true;
        } else
            return false;
    }

    // If we're on the web then the user can select in the report or in data.
    // If we're not then the user can only select from the report.
    var web_mode = (!!Q.isOnTheWeb && Q.isOnTheWeb());
    var selected_questions = web_mode ? project.report.selectedQuestions() 
                                      : uniqueQObjects(getQuestionsSelectedInTables().map(function (obj) { return obj.question; }));

    if (selected_questions.length == 0) {
        log("No " + (web_mode ? "data" : "questions") + " or tables selected.");
        return false;
    }

    var validity = splitArrayIntoApplicableAndNotApplicable(selected_questions, function (q) { return ["Pick One", "Pick Any", "Pick Any - Compact"].indexOf(q.questionType) > -1; });

    var applicable_questions = validity.applicable;
    var not_applicable_questions = validity.notApplicable;

    if (applicable_questions.length == 0) {
        if (web_mode)
            log("No applicable data selected.");
        else
            log("No Pick One, Pick Any, or Pick Any - Compact questions selected.");
        return false;
    }

   
    var modified = [];
    var not_modified = [];
    applicable_questions.forEach(function (q) {
        var output = getSummaryTableOutput(q);
          
        if (output != null) {
            var merge_result = mergeSmallCategoriesInQuestion(q, output, percentage);
            if (merge_result)
                modified.push(q);
            else
                not_modified.push(q);
        }    
                   
    });

    if (web_mode) {
        if (modified.length > 0)
            logQuestionList("Modified:", modified);

        if (not_modified.length > 0) {
            if (modified.length > 0)
                log("\r\n");
            logQuestionList("No categories to merge:", not_modified);
        }

        if (not_applicable_questions.length > 0) {
            if (applicable_questions.length > 0 || not_modified.length > 0)
                log("\r\n");
            logQuestionList("Data of the wrong type:", not_applicable_questions);
        }   
    } else {
        if (modified.length > 0 || not_modified.length > 0) {
            var new_group_name = "Questions with small categories merged: " + percentage + "%";
            var new_group = generateGroupOfSummaryTables(new_group_name, modified);
            if (not_modified.length > 0)
                generateSubgroupOfSummaryTables("No categories to merge", new_group, not_modified);
            if (not_applicable_questions.length > 0)
                generateSubgroupOfSummaryTables("Wrong question type", new_group, not_applicable_questions);
            if (fileFormatVersion() > 8.65)
                project.report.setSelectedRaw([new_group.subItems[0]]);     
        }     
    }

    return true;
}

See also