Machine Learning - Diagnostic - Analysis Report

From Q
Jump to navigation Jump to search
This page is currently under construction, or it refers to features which are under development and not yet available for use.
This page is under construction. Its contents are only visible to developers!

This feature can be used to obtain diagnostic information, referred to as the Grow Settings and Analysis Report, for a segmentation/tree output from Latent Class Analysis, Mixed-Mode Cluster Analysis, or Mixed-Mode Tree.

Usage

  1. First select any or multiple output(s) from a Mixed-Mode Tree, Mixed-Mode Cluster Analysis, and/or a Latent Class Analysis.
  2. Run Anything > Advanced Analysis > Cluster > Diagnostic > Analysis Report to obtain the grow settings and analysis report for each selected segmentation output.

Example

Below are the results of running a Latent Class Analysis using several variable sets from a survey examining respondents' preferences for different types of cola.

After selecting the output and runnning Anything > Advanced Analysis > Cluster > Diagnostic > Analysis Report, the following report is obtained.

Technical details

If you modify any of the input data to your tree or update/rerun your analysis, you will need to rerun this feature to obtain the new analysis report. The output containing the report will show an error informing you of this.

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 Functions to Generate Outputs");
includeWeb("QScript R Output Functions");

function AddAnalysisReportWidgets() {
    let selected = project.report.selectedRaw();
    let trees = selected.filter(s => s.type === "Tree");
    if (trees.length === 0)
    {
        log("Please select an output from Mixed-Mode Cluster Analysis, Latent Class Analysis, or Mixed-Mode Trees.");
        return false;
    }

    trees.forEach(function(tree) {
        let dependants = tree.dependants(false);
        const data_file = dependants[0].type === "Variable" ? dependants[0].question.dataFile : dependants[0].dataFile;
        let page = project.currentPage === undefined ? false : project.currentPage();
        if (!page) 
            page = tree.group;
	if (!tree.analysisReports)
	{
	    if (!!Q.isOnTheWeb && Q.isOnTheWeb())
		log("The Analysis Report was not saved with this output (" + tree.name +
		    ") when it was originally created. Please rerun it, which " +
		    "will ensure the report is saved and then try again.");
	    else
		log("Sorry, you must upgrade Q to use this feature.");
            return;
	}
	
        // Changing name of tree changes name of saved segments variable
        // Use this to identify segments variable in data file
        let question_names = data_file.questions.map(q => q.name);
        let tree_name = tree.name;
        const tmp_str = "TemporaryQuestionName65xg1t8ke82xhek";
        tree.name = tmp_str;
        let chg_idx = data_file.questions.map(q => q.name).indexOf(tmp_str);
        let segments = data_file.questions[chg_idx];
        segments.name = question_names[chg_idx];
        tree.name = tree_name;
        dependants = dependants.concat(segments);
	
        let disambiguate_names = false;
        let input_names = dependants.map(d => d.name);
        let r_code = "library(digest)\n" +
	    "library(flipTransformations)\n" +
               "inputs <- list(`" + input_names.join("`,`") + "`)\n" +
            "hashes <- vapply(inputs, function(v){\n" +
	    "    vals <- unlist(AsNumeric(v, FALSE))\n" +
	    "    attributes(vals) <- NULL\n" +
	    "    digest(vals)\n}, '')";
        let r_output = page.appendR(r_code);
	
        if (r_output.error !== null) {
            if (/R code is ambiguous/.test(r_output.error)) {
                disambiguate_names = true;
                input_names = disambiguateDependantNames(dependants);
                r_code = "library(digest)\n" + 
		    "library(flipTransformations)\n" +
                    "inputs <- list(" + input_names.join(",") + ")\n" +
            "hashes <- vapply(inputs, function(v){\n" +
	    "    vals <- unlist(AsNumeric(v, FALSE))\n" +
	    "    attributes(vals) <- NULL\n" +
	    "    digest(vals)\n}, '')";
                r_output.code = r_code;
                r_output.update();
            }else {
                log("Sorry, there was an error creating your report. " +
                    "Please contact support. The error returned by R was: " + r_output.error);
                return false;
            }    
        }   

        const md5_strs = r_output.data.get([]);
        r_output.deleteItem();
	let output_name = generateUniqueRObjectName("analysis.report");
        let names_list = disambiguate_names ? disambiguateDependantNames(dependants).join(",") : "`" + input_names.join("`,`") + "`";
        r_code = 'library(digest)\n' + 
	    'library(flipTransformations)\n' +
            'md5.orig <- c("' + md5_strs.join('", "') + '")\n' +
                'inputs <- list(' + names_list + ')\n' +
            "md5.current <- vapply(inputs, function(v){\n" +
	    "    vals <- unlist(AsNumeric(v, FALSE))\n" +
	    "    attributes(vals) <- NULL\n" +
	    "    digest(vals)\n}, '')\n" +
            'if (any(md5.current != md5.orig))\n' +
                '    stop("Your input data has changed. Please select your tree, modify/update it if necessary, and generate a new report.")\n' + 
                'reports <- c("' + tree.analysisReports.join('", "').replace(/\\/ig, "\\\\") + '")\n\n' +
                'library(flipFormat)\n' +
                output_name + ' <- CreateAnalysisReportWidget(reports)\n';
        r_output = page.appendR(r_code);
        // place widget to the left or right of tree output depending on which has more room
        if (tree.left > page.width - tree.left - tree.width)
            r_output.left = tree.left - 100;
        else 
            r_output.left = tree.left + 100;    
        r_output.top = tree.top;
	project.report.setSelectedRaw([r_output]);
        return;    
    });
    return true;
}

function disambiguateDependantNames(dependants) {
    return dependants.map(q => (q.type === "Question" ? "`" + q.dataFile.name + "`$Questions$`" + q.name + "`" : "`" + q.question.dataFile.name + "`$Variables$`" + q.name + "`"));

}

if (!AddAnalysisReportWidgets())
    conditionallyEmptyLog("QScript canceled.");

See also