QScript Scale Functions

From Q
Jump to navigation Jump to search

The functions below are used by the scaling QScripts under Create New Variables - Scale Within Case and Create New Variables - Scale Within Variable.

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

scaleOneQuestion(question, type, within_case)

This function takes one QScript Question and performs a scale transformation to it. See the next function for a description of the type and within_case arguments.

scaleQuestions(type, within_case)

This function uses variable sets selected by the user in Data Setsprompts the user to select questions and performs a scale transformation to each selection based on the type argument. Possible values for type are "rank", "standardize", "center", and "unit". If within_case is true, then the scaling is performed within case/respondent; otherwise, it is performed within variable. This is a wrapper for scaleOneQuestion.

Source Code

includeWeb("QScript Utility Functions");
includeWeb("QScript Selection Functions");
includeWeb("QScript Functions to Generate Outputs");
includeWeb("QScript R Output Functions");
 
function scaleOneQuestionOrVariable(input, type, within_case) {
    const is_displayr = inDisplayr();
    const structure_name = is_displayr ? "variable set" : "question";
    
    let single_variable = input.type == "Variable"; 
    let input_name = single_variable ? input.label : input.name;
    let reference_name = input.name;
    let data_reference = (single_variable) ? generateDisambiguatedVariableName(input) : generateDisambiguatedQuestionName(input);
    let data_file = single_variable ? input.question.dataFile : input.dataFile;
    let new_question_name = (type == "unit" ? "unit scale" : type) + " within " +
                            (within_case ? "case" : "variable") + " for " + input_name;

    function capitalizeFirstLetter(string) {
        return string.charAt(0).toUpperCase() + string.slice(1);
    }

    new_question_name = capitalizeFirstLetter(new_question_name);

    new_question_name = preventDuplicateQuestionName(data_file, new_question_name);
    let temp_var_name = randomVariableName(16); // temporary name, random to (almost) guarantee uniqueness
    let variables = single_variable ? [input] : input.variables; 

    if (variables.length === 1) { 
        if (within_case) {
            log('Scaling within case for ' + structure_name + ' ' + input_name + ' is not meaningful ' + 'since it consists of only a single variable.');
            return false;
        }

        if (variables[0].variableType === "Date" && type != "rank") {
            log('The selected scaling type is not meaningful for Date variables. The only scaling which can be applied for ' + input_name + ' is Ranks Within Variable.');
            return false;
        }
    }

    let new_question_type = variables.length === 1 ? "Number" : "Number - Multi";
    let expression = 'flipTransformations::ScaleVariableSet(' + data_reference + ', type = "' + type + 
                     '", within.case = ' + within_case.toString().toUpperCase() + ')';

    let new_r_question;
    try {
        new_r_question = data_file.newRQuestion(expression, new_question_name, temp_var_name, variables[variables.length - 1]);
        new_r_question.questionType = new_question_type;
        insertAtHoverButtonIfShown(new_r_question);
    } catch (e) {
        function errorFun(e){
            log("The transform could not be computed for " + structure_name + " " + input_name + " : " + e.message);
            return false;
        }          
        return errorFun(e);
    }

    // Replace temporary variable names
    let base_var_name = reference_name.replace(/[^a-zA-Z0-9_@\#\$\\]/g, '_').toLowerCase() + "_" + type + "_scaled";
    nameSequentialVariables(new_r_question.variables, base_var_name);
    let top_group_name = type.slice(0,1).toUpperCase() + type.slice(1) + (type == "unit" ? " scale " : "") + 
                   " within " + (within_case ? "case" : "variable") + " Transformation";
    reportNewRQuestion(new_r_question, top_group_name);
    return true;
}

function scaleQuestions(type, within_case) {
    
    let allowed_types = ["Nominal - Multi", "Ordinal - Multi", "Numeric - Multi", "Numeric - Grid"];
    let selections;
    if (within_case) {
        // Only keep entire questions which are of an allowed type
        selections = selectInputQuestions(allowed_types);
    } else {
        // Single variables are allowed, as are variable sets.
        // If a variable within a variable set is selected,
        // but not all variables within the set are selected
        // then just scale the selected variables
        let user_selections = getAllUserSelections();
        let selected_questions = user_selections.selected_questions;
        let selected_variables = user_selections.selected_variables;
        // User or QScript test in Q has nothing selected, allow to select questions
        // from prompt.
        if (!inDisplayr() && selected_questions.length + selected_variables.length == 0) {
            allowed_types = allowed_types.concat(["Nominal", "Ordinal", "Numeric", "Date"]);
            selected_questions = selectInputQuestions(allowed_types); 
        }
        if (selected_questions.some(q => q.questionType.indexOf("Text") > -1)){
            log("Text variables cannot be scaled.");
            selected_questions = selected_questions.filter(q => q.questionType != "Text");
        }
        let selected_varnames = selected_variables.map(x => x.name);
        let kept_single_variables = [];
        let partial_questions_selected = [];
        selected_variables.forEach(function (v) {
            let complete_variables = v.question.variables.map(x => x.name);
            if (!complete_variables.every(x => selected_varnames.indexOf(x) > -1)) {
                partial_questions_selected.push(v.question.name);
                kept_single_variables.push(v);
            }
        });
        selected_questions = selected_questions.filter(q => partial_questions_selected.indexOf(q.name) == -1);
        selections = selected_questions.concat(kept_single_variables);
    }
    if (!selections)
        return false;

    let results = [];
    for (let i = 0; i < selections.length; i++)
        results.push(scaleOneQuestionOrVariable(selections[i], type, within_case));

    return results.some(function(x){return(x)});
}

See also