Filtering - Create Filters from Selected Data

From Q
Jump to: navigation, search

This QScript creates a new filter for each category of the selected questions. These filters can then be applied to any tables and charts in the report.

Technical details

You are presented with a list of the questions in the study, and you should select those that you wish to have filters created from. This list is restricted to questions of the type Pick One, Pick One - Multi, Pick Any, Pick Any - Grid, and Pick Any - Compact. The filters for a question are combined into to a new question, and a table is added to your report to show each of these new questions.

For Pick Any - Grid questions, a copy of the question is made and each variable is set as a filter. For all other question types this QScript will identify changes that have been made in the tables (e.g. merging and renaming of categories) and the filters that are created will reflect those changes.

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

includeWeb('QScript Questionnaire Functions');
includeWeb('QScript Value Attributes Functions');
includeWeb('QScript Utility Functions');
includeWeb('QScript Selection Functions');
includeWeb('QScript Functions to Generate Outputs');
includeWeb('QScript Data Reduction Functions');
includeWeb('QScript Functions for Combining Categories');
 
if (!main())
    conditionallyEmptyLog("QScript cancelled.");
else
    conditionallyEmptyLog("QScript finished.");
 
 
function main() { 
 
    var allowed_types = ["Pick One", "Pick One - Multi", "Pick Any", "Pick Any - Grid", "Pick Any - Compact"];
 
    // On the web just take from what is selected.
    var web_mode = (!!Q.isOnTheWeb && Q.isOnTheWeb());
 
    var selected_questions;
    if (web_mode) {
        selected_questions = project.report.selectedQuestions();
        var sorted_selection = splitArrayIntoApplicableAndNotApplicable(selected_questions, function (q) { return allowed_types.indexOf(q.questionType) != -1 });
        selected_questions = sorted_selection.applicable;
        var not_applicable_questions = sorted_selection.notApplicable;
    } else {
        var selected_datafiles = dataFileSelection();
        var candidate_questions = getAllQuestionsByTypes(selected_datafiles, ["Pick One", "Pick One - Multi", "Pick Any", "Pick Any - Grid", "Pick Any - Compact"]);
        var num_candidates = candidate_questions.length;
 
        if (num_candidates == 0) {
            log("No categorical questions found.");
            return false;
        }
 
        selected_questions = selectManyQuestions("This QScript will generate a new filter variable for each category from the questions that you select. Please choose which questions you would like to use:", candidate_questions, true).questions;
    }
 
 
    if (selected_questions.length == 0) {
        log("No applicable data selected.");
        return false;
    }
 
 
    var filter_questions = selected_questions.map(function (q) { return createFiltersForAllCategories(q); } );
    filter_questions = filter_questions.filter(function (q) { return q != null; });
 
    if (web_mode) {
        log("Added filters for:");
        log(selected_questions.map(function(q) { return q.name; }).join("\r\n"));
 
        if (not_applicable_questions.length > 0) {
            log("\r\n");
            log("Could not add filters for:")
            log(not_applicable_questions.map(function (q) { return q.name; }).join("\r\n"));
        }
 
    } else {
        generateGroupOfSummaryTables("New filter questions", filter_questions);
        log("Tables showing questions containing your new filters have been added to your report. New filters are available from the Filters drop-down menus.");
    }
 
    return true; 
}
 
function createFiltersForAllCategories(question, web_mode) {
    var q_type = question.questionType;
    var new_filter_question;
    var new_name = preventDuplicateQuestionName(question.dataFile, question.name + " - Filters");
    var merged_categories;
 
    // Pick Any - Grid simply gets converted to a Pick Any because there is
    // no way to treat merged categories.
    if (q_type == "Pick Any - Grid") {
        new_filter_question = question.duplicate(new_name);
        new_filter_question.questionType = "Pick Any";
    } else if (q_type == "Pick One - Multi") {
        // Flatten and merge using existing methods
        var merged_categories = getMergedCategoriesFromPickOne(question);
        new_filter_question = pickOneMultiToPickAnyFlattenAndMergeByRows(question, merged_categories, true, false);
        new_filter_question.name = preventDuplicateQuestionName(question.dataFile, new_filter_question.name.replace("(flattened)", "- Filters"));
        new_filter_question.questionType = "Pick Any - Grid";
        if (!new_filter_question.isValid)
        {
            new_filter_question.questionType = "Pick Any";
        }
    } else {
        // For other question types we work out which categories are
        // in the data reduction (excluding the main NET).
        if (["Pick One", "Pick Any - Compact"].indexOf(q_type) > -1)
            merged_categories = getMergedCategoriesFromPickOne(question);
        else if (q_type == "Pick Any")
            merged_categories = getMargedCategoriesFromPickAny(question);
        else
            throw "Not applicable for " + q_type + "questions.";
 
        // Create a new variable for each category in the data reduction
        var variables = question.variables;
        var data_file = question.dataFile;
        var new_vars = [];
        var num_vars = variables.length;
        var last_var = variables[num_vars-1];
        var new_name_prefix = "FILTER" + makeid() +"_";
        merged_categories.forEach(function (obj, ind) {
            var expression;
            if(q_type == "Pick Any")
                expression = mergePickAnyExpression(obj.variables);
            else if (q_type == "Pick Any - Compact")
                expression = mergePickAnyCompactExpression(variables.map(function (v) { return v.name; }), obj.values);
            else if (q_type == "Pick One")
                expression = mergePickOneExpression(variables[0].name, obj.values, true);
 
            var new_var = question.dataFile.newJavaScriptVariable(expression, 
                                                  false, 
                                                  new_name_prefix + ind + 1, 
                                                  obj.name, 
                                                  last_var);
            new_var.variableType = "Categorical";
            last_var = new_var;
            new_vars.push(new_var);
        });
 
        // Form the question
        if (new_vars.length > 0) {
            var new_q_name = preventDuplicateQuestionName(data_file, question.name + " - Filters");
            var new_filter_question = data_file.setQuestion(new_q_name, "Pick Any", new_vars);
        } else
            return null;
    }
 
    // Set the properties of the new question
    new_filter_question.isFilter = true;
    setLabelForVariablesInQuestion(new_filter_question, 0, "Not Selected");
    setLabelForVariablesInQuestion(new_filter_question, 1, "Selected");
    return new_filter_question;
}
 
// 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(q) {
    var value_attributes = q.valueAttributes;
    var non_missing_values = q.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(q);
    // 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;
}
 
// Returns an array describing the data reduction of a pick any question
// excluding the NET. Each entry in the array corresponds to a code from
// the data reduction, and has:
// - name: The name of the category.
// - variables: The names of the variables in the category.
function getMargedCategoriesFromPickAny(q) {
    var data_reduction = q.dataReduction;
    var net_labels = ["NET"];
    if (fileFormatVersion() > 8.41)
        net_labels = data_reduction.netRows.map(function (x) { return data_reduction.rowLabels[x]; });
    var merging_objects = [];
    data_reduction.rowLabels.forEach(function (label) {
        if (net_labels.indexOf(label) == -1)
            merging_objects.push({ name: label, variables: data_reduction.getUnderlyingVariables(label).map(function (v) { return v.name; }) });
    });
    return merging_objects;
}
 
// Generate an JavaScript expression for merged categories in a Pick Any question.
function mergePickAnyExpression(variable_names) {
    var nan_bit = "isNaN(" + variable_names[0] + ")";
    var main_bit = "(" + variable_names[0];
    if (variable_names.length > 1) {
        for (var j = 1; j < variable_names.length; j++) {
            nan_bit += " || isNaN(" + variable_names[j] + ")";
            main_bit += " || " + variable_names[j];
        }
    }
    return nan_bit + " ? NaN : " + main_bit + ") ? 1 : 0;";
}
 
// Generate an JavaScript expression for merged categories in a Pick Any - Compact question.
function mergePickAnyCompactExpression(variable_names, codes) {
    var missing_check = "(" + variable_names.map(function (name) { return "Q.IsMissing(" + name + ")"; } ).join(" && ") + ") ? NaN : (";
    var term_array = [];
    variable_names.forEach(function (name) {
        codes.forEach(function (code) {
            term_array.push("Q.Source(" + name + ") == " + code);
        });
    });
    return missing_check + term_array.join(" || ") + ") ? 1 : 0;";
}

Prior to the 15th of December, 2015, this page was known as Create New Variables - Create Filters from Selected Data

See also