# Significance Testing - Independent Samples Column Means and Proportions Tests

This rule over-rides Q's automatic statistical testing and performs independent samples column means and proportion tests. It also includes options designed to mimic those in introductory statistics courses and in SPSS Custom Tables.

## Technical details

• Show significance needs to be set to Compare columns when applying this rule.
• The default formulas used in this rule are not the same as those used by Q to automatically test independent samples. If you wish to use Q's in-built testing for independent samples on data that has a dependent structure, the best way to achieve this is by first Stacking the data file. However, in most situations the default settings in this rule will correspond to the general defaults in Q. Reasons for differences are described in some of the following points.
• As with all Rules, this Rule works by over-riding/ignoring settings and safe-guards built into Q. Many of the settings that are over-ridden are designed to protect users from problems in their data, and if applying this Rule a higher degree of diligence is required in checking results, particularly if performing other modifications that make your version of Q work differently to the defaults.
• This rule over-rides most, but not all, settings in Statistical Assumptions. In particular:
• Bessel corrections are still used as inputs to computations of Standard Errors and Standard Deviations (i.e., those computed on the tables use this setting, and these then feed into the some of the tests).
• The Weights and significance determines the Effective Base n, which is an input to the Effective sample size used in this rule.
• In various situations, the formulas used for computing significance in this rule may give incorrect/misleading results (e.g., when the assumption of independence is inappropriate and when the various tests selected are wrong).
• Effective sample size is computed as Effective Base n * Column Population / Base Population.
• Weighted sample size is Population.
• When the Question in the Blue drop-down is a Pick One - Multi, Pick Any - Grid, or a Number - Grid, the sample size used in the test is the Base n (or Base Population, or Effective Base n, otherwise the Column n (or the Column Population, or Effective Column n) is used.
• Simplified Independent Complex Samples T-Test - Comparing Two Means is used when Numeric data variance assumptions (t-test) is set to Complex samples.
• Independent Samples T-Test - Unequal Variance is used when Numeric data variance assumptions (t-test) is set to Unequal variance. When this option is selected in conjunction with setting Base used in test of means to Weighted sample size and Multiple comparison correction is set to None, the resulting tests correspond to an an Independent Samples T-Test in SPSS with Equal variances not assumed.
• Independent Samples T-Test - Equal Variance is used when Numeric data variance assumptions (t-test) is set to Equal variance (categories compared). When this option is selected in conjunction with setting Base used in test of means to Weighted sample size and Multiple comparison correction to None, the resulting tests correspond to an an Independent Samples T-Test in SPSS with Equal variances assumed and also to Compare column means (t-tests) with Estimate variance only from the categories compared selected in SPSS Custom Tables, except where the columns are from multiple response questions.
• Independent Samples T-Test - Pooled Variance is used when Numeric data variance assumptions (t-test) is set to Equal variance (all non-ignored categories). When this option is selected in conjunction with setting Base used in test of means to Weighted sample size and Multiple comparison correction to None, the resulting tests correspond to Compare column means (t-tests) with Estimate variance only from the categories compared not selected in SPSS Custom Tables, except where the columns are from multiple response questions.
• Simplified Independent Complex Samples T-Test - Comparing Two Proportions is used when Proportions test is set to Complex samples t-test. Note that this test is not available via modifying settings in Statistical Assumptions, although it should give similar results to the default test, which uses the Second Order Rao-Scott Test of Independence of a Contingency Table (that test is not used in this Rule as the statistics used in its computation are not available on the table, and are instead computed from the raw data).
• Independent Samples Z-Test - Comparing Two Proportions (Pooled) is used when Proportions test is set to Pooled z-test. When this option is selected in conjunction with setting Base used in test of proportions to Weighted sample size and Multiple comparison correction is set to None, the resulting tests correspond to Compare column proportions (z-tests) in SPSS Custom Tables.
• Independent Samples Z-Test - Comparing Two Proportions (Un-Pooled) is used when Proportions test is set to Un-pooled z-test

### Specifying columns to compare

The setting called Set column comparisons can be used to specify which columns of the table should be compared. When this option is selected, you should enter the names of the columns that you would like to compare according to the notation described on the page Specifying comparisons.

Note that this option:

1. Automatically renames the columns in the standard alphabetic order (A, B, C, etc). This is equivalent to un-checking the option Recycle column letters in Edit > Table Options > Statistical Assumptions.
2. Ignores the spans in the table, so that you can perform comparisons between columns in different spans.

### Spans

As with Q's default Column Comparisons testing, spans in the table are used to specify which columns compare. Comparisons are only conducted within the lowest-level spans. As with the default testing, results will only be useful when the spans have been set up to accurately reflect the comparisons that you want to make.

When spans have not been set up to guide statistical testing, neither Q's default column comparison results nor the results provided by this rule will be useful. In both cases, the issue will manifest in slightly different ways, as shown in the following example.

The first table shows Q's default column comparisons where columns are compared in the lowest span, and the second shows the comparisons evaluated by this rule (where comparisons are only made within each span). In both cases the sensible set of columns to compare is the set of age categories, that is with both spans removed.

### Footers

The footer information relating to statistical testing will relate to Q's default test, and these should be turned off when you use this rule. This is done by editing the Table Options for the relevant tables and un-ticking the options relating to statistical testing in the Footers tab. Information about the settings used in the rule can be added to the table footer by ticking the option in the rule called Include settings information in footer.

## How to apply this rule

### For the first time in a project

• Select the table(s)/chart(s) that you wish to apply the rule to.
• Start typing the name of the Rule into the Search features and data box in the top right of the Q window.
• Click on the Rule when it appears in the QScripts and Rules section of the search results.

OR

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

### Additional applications of the rule

• Select a table or chart that has the rule and any table(s)/chart(s) that you wish to apply the rule to.
• Click on the Rules tab (bottom-left of the table/chart).
• Select the rule that you wish to apply.
• Click on the Apply drop-down and choose your desired option.
• Check New items to have it automatically applied to new items that you create. Use Edit > Project Options > Save as Template to create a new project template that automatically uses this rule.

## Removing the rule

• Select the table(s)/chart(s) that you wish to remove the rule from.
• Press the Rules tab (bottom-right corner).
• Press Apply next to the rule you wish to remove and choose the appropriate option.

## How to modify the rule

• Click on the Rules tab (bottom-left of the table/chart).
• Select the rule that you wish to modify.
• Click Edit Rule and make the desired changes. Alternatively, you can use the JavaScript below to make your own rule (see Customizing Rules).

## JavaScript

```// Version 2.1.3
)*d[2]/b.sqrt(d[3])):f=b.abs(a.studentt.inv(d[1]/2,d[2].length)*a.stdev(d[2],!0)/b.sqrt(d[2].length)),e[0]=d[0]-f,e[1]=d[0]+f,e},significant:function(a,b){return a<b}}),a.extend(a.fn,{normalci:function(b,c){return a.normalci(b,c,this.toArray())},tci:function(b,c){return a.tci(b,c,this.toArray())}})}(this.jStat,Math);

// Work out which spans are lowest in the table.
// These are the spans that are tested within.
function getLowestLevelSpans(spans) {
var lowest_spans = [];
var found_indices = [];
spans.forEach(function (span) {
if (span.indices.every(function (x) { return found_indices.indexOf(x) == -1; })) {
found_indices = found_indices.concat(span.indices);
lowest_spans.push(span);
}
});
return lowest_spans;
}

// checking suitability of data
var has_cc = table.statistics.indexOf('Column Comparisons') != -1;//checking if table has column letter showing
var has_average = table.statistics.indexOf('Average') != -1;//checking if table has averages
var has_percent = table.statistics.indexOf('%') != -1;
var has_column_percent = table.statistics.indexOf('Column %') != -1;
var has_row_percent = table.statistics.indexOf('Row %') != -1;
var has_column_n = table.availableStatistics.indexOf('Column n') != -1;
var has_column_se = table.availableStatistics.indexOf('Column Standard Error') != -1;
var blue_type = table.blueQuestion.questionType;
var has_below = false;
try {has_below = below_table.availableStatistics.indexOf('Column Names') != -1;}  catch (e) { }
var has_cc_below = has_below && below_table.availableStatistics.indexOf('Column Comparisons') != -1;
var is_summary =  table.brownQuestion == "SUMMARY";
var two_d_types = ["Pick One - Multi", "Number - Grid", "Pick Any - Grid"];
var two_d = two_d_types.indexOf(blue_type) != -1;
var has_row_n = table.availableStatistics.indexOf('Row n') != -1;
var is_proportion = has_percent || has_column_percent || has_row_percent;

if (!has_average && !is_proportion)
form.ruleNotApplicable("this rule requires the table to have percentages or an average");

// If column comparisons are not available for this table
// then stop.
if (table.availableStatistics.indexOf('Column Comparisons') == -1)
form.ruleNotApplicable("this rule requires the table to have Column Comparisons available") ;

var n_columns = table.numberColumns;
var n_rows = table.numberRows;

//// Creating form.

var grid_checkbox = form.newCheckBox("gridCheck", "Apply to Number - Grid and Pick Any - Grid questions SUMMARYs");
grid_checkbox.lineBreakAfter = true;
grid_checkbox.setDefault(true);
var pick_one_multi_checkbox = form.newCheckBox("pickOneMultiCheck", "Apply to Pick One - Multi SUMMARYs");
pick_one_multi_checkbox.lineBreakAfter = true;
pick_one_multi_checkbox.setDefault(true);
var crosstab_checkbox = form.newCheckBox("crosstabCheck", "Apply to crosstabs");
crosstab_checkbox.lineBreakAfter = true;
crosstab_checkbox.setDefault(false);
var apply_without_cc_checkbox = form.newCheckBox("ccOverrideCheck" ,"Apply when Column Comparisons are not shown on the table");
apply_without_cc_checkbox.setDefault(false);
apply_without_cc_checkbox.lineBreakAfter = true;
var set_comparisons_checkbox = form.newCheckBox("comparisonsBox", "Set column comparisons (e.g: A/B, C/D)");
set_comparisons_checkbox.lineBreakAfter = true;
set_comparisons_checkbox.setDefault = false;
var manual_comparisons = set_comparisons_checkbox.getValue();
var comparisons_text = null;
if (manual_comparisons) {
comparisons_text = form.newTextBox("comparisonsString");
comparisons_text.lineBreakAfter = true;
}
var footer_checkbox = form.newCheckBox("footercheck", "Include settings information in footer");
footer_checkbox.setDefault = false;
footer_checkbox.lineBreakAfter = true;

var alpha_lower_label = form.newLabel('Significance Level (alpha) for lowercase letters:');
var alpha_lower_up_down = form.newNumericUpDown('alpha'); //creating a control where a user can enter a number.
alpha_lower_up_down.lineBreakAfter = true;
alpha_lower_up_down.setDefault(0.05);
alpha_lower_up_down.setIncrement(0.0000001);
alpha_lower_up_down.setMinimum(0);
alpha_lower_up_down.setMaximum(1);
alpha_lower_up_down.lineBreakAfter = true;
var alpha_upper_label = form.newLabel('Significance Level (alpha) for UPPERCASE letters:');
var alpha_upper_up_down = form.newNumericUpDown('alphaUpper'); //creating a control where a user can enter a number.
alpha_upper_up_down.lineBreakAfter = true;
alpha_upper_up_down.setDefault(0.05);
alpha_upper_up_down.setIncrement(0.000000001);
alpha_upper_up_down.setMinimum(0);
alpha_upper_up_down.setMaximum(1);
alpha_upper_up_down.lineBreakAfter = true;

var multiple_comparison_correction =  ["None", "False Discovery Rate", "Bonferroni"];
var multiple_comparison_dropdown = form.newComboBox('multiple',multiple_comparison_correction);
var multiple_comparison_wording = form.newLabel("Multiple comparison correction:");
multiple_comparison_dropdown.setDefault(multiple_comparison_correction[1]);
multiple_comparison_dropdown.lineBreakAfter = true;

var variance_assumptions =  ["Complex samples", "Unequal variance", "Equal variance (categories compared)", "Equal variance (all non-ignored categories)"]
var variance_dropdown = form.newComboBox('variance',variance_assumptions);
var variance_wording = form.newLabel("Numeric data variance assumptions (t-test):");
variance_dropdown.lineBreakAfter = true;
variance_dropdown.setDefault(variance_assumptions[0]);

var base_assumptions =  ["Sample size", "Weighted sample size", "Effective sample size"];
var base_dropdown = form.newComboBox('base', base_assumptions);
var base_wording = form.newLabel("Base used in tests of means:");
base_dropdown.lineBreakAfter = true;
base_dropdown.setDefault(base_assumptions[0]);

var proportion_tests =  ["Complex samples t-test", "Pooled z-test", "Un-pooled z-test"]
var proportion_dropdown = form.newComboBox('proportion',proportion_tests);
var proportion_wording = form.newLabel("Proportions test:");
proportion_dropdown.lineBreakAfter = true;
proportion_dropdown.setDefault(proportion_tests[0]);

var base_dropdown_proportions = form.newComboBox('baseProportions', base_assumptions);
var base_wording_proportions = form.newLabel("Base used in tests of proportions:");
base_dropdown_proportions.lineBreakAfter = true;
base_dropdown_proportions.setDefault(base_assumptions[0]);

var min_label = form.newLabel('Minimum sample size in a column to include in testing:');
var min_box = form.newTextBox('textMinimum');
min_box.setDefault("2");
min_box.lineBreakAfter = true;

// Columns to ignore from testing.
var label_ignore = form.newLabel('Columns to ignore from testing:');
var ignore_box = form.newTextBox('textIgnore');
ignore_box.setDefault("NET");
var ignore_box1 = form.newTextBox('textIgnore1');
ignore_box1.setDefault("SUM");

// Creating the form.
form.setHeading('Independent Samples Column Means and Proportions');

var control_array = [grid_checkbox, pick_one_multi_checkbox, crosstab_checkbox, apply_without_cc_checkbox, set_comparisons_checkbox];
if (manual_comparisons)
control_array.push(comparisons_text);
control_array = control_array.concat([footer_checkbox, alpha_lower_label, alpha_lower_up_down, alpha_upper_label, alpha_upper_up_down, multiple_comparison_wording,
multiple_comparison_dropdown,variance_wording, variance_dropdown,
base_wording,base_dropdown,
proportion_wording, proportion_dropdown,
base_wording_proportions, base_dropdown_proportions,
min_label, min_box,
label_ignore, ignore_box, ignore_box1]);
form.setInputControls(control_array);

var labels_to_ignore = [ignore_box.getValue()];
var last_selection = ignore_box1.getValue();
while (last_selection != "") {
labels_to_ignore.push(last_selection);
var new_name = "textIgnore" + (control_array.length - 1);
form.setInputControls(control_array);
}

// Getting alpha and creating summary.
var lower_alpha = alpha_lower_up_down.getValue();
var upper_alpha = alpha_upper_up_down.getValue();
var alpha = Math.max(lower_alpha, upper_alpha);
var mean_assumption = variance_dropdown.requireValue();
var proportion_assumption = proportion_dropdown.requireValue()
form.setSummary("Independent Samples Column Means and Proportions Tests");

// Make sure column comparisons are shown on the table, unless user overrides
if (!has_cc && !apply_without_cc_checkbox.getValue())
form.ruleNotApplicable("this rule requires the table has 'Show significance' set to 'Compare columns' and that 'Column Comparisons' is selected in 'Statistics - Cells'");

var min_sample_size = min_box.getValue();
// Checking to see if rule needs to be applied.
// performing the testing
var grid_summary = (table.blueQuestion.questionType == "Pick Any - Grid" ||  table.blueQuestion.questionType == "Number - Grid") && table.brownQuestion == "SUMMARY";
var pick_multi_summary = table.blueQuestion.questionType == "Pick One - Multi" && table.brownQuestion == "SUMMARY";
if (pick_multi_summary) {
if(!pick_one_multi_checkbox.getValue())
form.ruleNotApplicable("the option 'Apply to Pick One - Multi SUMMARY tables' is not selected");
} else if (grid_summary) {
if (!grid_checkbox.getValue())
form.ruleNotApplicable("the option 'Apply to Number - Grid and Pick Any - Grid questions SUMMARYs' is not selected");
} else if (!crosstab_checkbox.getValue())
form.ruleNotApplicable("the option 'Apply to crosstabs' is not selected");

table.hypothesisTestingEnabled = false;

var comparisons_string = manual_comparisons ? comparisons_text.getValue() : "";

// Getting data.
var sd = has_average ? table.get('Standard Deviation') : null;
var sd_below = has_cc_below ? below_table.get('Standard Deviation') : null;
var standard_error = has_column_se ? table.get('Column Standard Error') : table.get('Standard Error');
var standard_error_below = has_cc_below ? below_table.get('Standard Error') : null;
var cc = table.get('Column Comparisons');
var cc_below = has_cc_below ? below_table.get('Column Comparisons') : null;

var base_type = (has_average ? base_dropdown.getValue() : base_dropdown_proportions.getValue());
var weighted_n = base_type == base_assumptions[1];
var effective_n = base_type == base_assumptions[2];

var base;
if (two_d) {
if (weighted_n)
base = table.get("Base Population");
else if (effective_n)
base = table.get("Effective Base n");
else
base = table.get("Base n");
} else {
if (weighted_n)
base = table.get("Column Population");
else if (effective_n) {
var col_pop = table.get("Column Population");
var base_pop = table.get("Base Population");
base = table.get("Effective Base n");
for (var r = 0; r < n_rows; r++)
for (var c = 0; c < n_columns; c++)
base[r][c] *= col_pop[r][c] / base_pop[r][c];

}
else
base = table.get("Column n");
}
var base_below;
var is_effective_sample_size = base_dropdown.getValue() == base_assumptions[2];

if (has_cc_below) {

if (base_dropdown.getValue() == base_assumptions[1]) // Weighted n.
base_below = below_table.get("Column Population");
else if (is_effective_sample_size) { // Effective sample size.
var missing_statistics_below = below_table.availableStatistics.indexOf('Effective Base n') == -1;
if (missing_statistics_below && table.netRows.length != 1)
form.ruleNotApplicable("requires the NET row to be shown on the table for when 'Effective sample size' is selected above");
var has_no_column_pop = below_table.availableStatistics.indexOf("Column Population") == -1;
var col_pop = below_table.get("Column Population");
var base_pop = below_table.get("Base Population");
if (missing_statistics_below) {
var effective_base_n = table.get("Effective Base n");
var base_below = [new Array(n_columns)];
var net_row = table.netRows[0]; // Effective sample size must be obtained from the NET row
for (var c = 0; c < n_columns; c++)
base_below[0][c] = effective_base_n[net_row][c] * (has_no_column_pop ? 1 : col_pop[0][c] / base_pop[0][c]); //When Column Population is not shown on tables (eg for Pick One - Multi) the cokumn population is the same as the base population and so no adjustement needs to be made
} else {
var effective_base_n = below_table.get('Effective Base n');
var base_below = [new Array(n_columns)];
for (var c = 0; c < n_columns; c++)
base_below[0][c] = effective_base_n[0][c] * (has_no_column_pop ? 1 : col_pop[0][c] / base_pop[0][c]);
}
}
else
base_below = below_table.get("Column n");
}

var values;
if (has_average)
values = table.get('Average');
else {
if (has_column_percent)
values = table.get('Column %');
else if (has_row_percent)
values = table.get('Row %');
else
values = table.get("%");
}
var values_below = has_cc_below ? below_table.get('Average') : null;

var cnames = table.get('Column Names');
var columns_compared = table.get('Columns Compared');
var cnames_below = has_below ? below_table.get('Column Names') : null;
var columns_compared_below = has_below ? below_table.get('Columns Compared') : null;

// Dealing with inconsistent structuring of arrays in statistics below
var cc_transposed_below = has_below && cc_below != null && cc_below[0].length != table.numberColumns;
var cnames_transposed_below = has_below && cnames_below != null && cnames_below[0].length != table.numberColumns;
var columns_compared_transposed_below = has_below && columns_compared_below != null && columns_compared_below[0].length != table.numberColumns;

cc_below_column = function(c) {return cc_transposed_below ? 0 : c};
cc_below_row = function(c) {return cc_transposed_below ? c : 0};

cnames_below_column = function(c) {return cnames_transposed_below ? 0 : c};
cnames_below_row = function(c) {return cnames_transposed_below ? c : 0};

columns_compared_below_column = function(c) {return columns_compared_transposed_below ? 0 : c};
columns_compared_below_row = function(c) {return columns_compared_transposed_below ? c : 0};

// write results
if (n_columns > 260)
form.ruleNotApplicable("this rule only works with 260 or fewer columns");

// creating column letters
var LETTERS = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
var letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
if (n_columns > 26) {
var new_lower = new Array(260);
var new_upper = new Array(260);
for (var l = 0; l < 26; l++)
for (var i = 0; i < 10; i++) {
var counter = i * 26 + l;
new_upper[counter] = LETTERS[l] + i;
new_lower[counter] = letters[l] + i;
}
LETTERS = new_upper;
letters = new_lower;
}

// Work out the spans in the table.
// Where the comparisons have been specified manually, ignore the existing
// spans and use the manual comparisons to define psuedo-spans.
var spans;
var indices_not_in_spans = [];
if (manual_comparisons) {
// Reset column names in table and below as this option ignores the spans
for (var col = 0; col < table.numberColumns; col++) {
for (var row = 0; row < table.numberRows; row++) {
cnames[row][col] = LETTERS[col];
}
if (has_below)
cnames_below[cnames_below_row(col)][cnames_below_column(col)] = LETTERS[col];
}
// Interepret the comparisons from the user's input
var comparison_strings = comparisons_string.split(",");
var comparisons = comparison_strings.map(function (string) { return string.trim().split("/"); });
spans = comparisons.map(function (a) {
return { indices: a.map(function (chr) { return letters.indexOf(chr.toLowerCase()) } ) };
});
var valid_spans = spans.map(function (span) { return span.indices.indexOf(-1) == -1 && span.indices.every(function (x) { return x < n_columns;} ); });
valid_spans.forEach(function (x, ind) {
if (!x)
form.ruleNotApplicable("unable to interpret comparison '" + comparison_strings[ind] + "'");
});
var indices_included = [];
} else {
spans = table.columnSpans;
// If no spans then inlude all columns
if (spans.length == 0) {
var indices = new Array(n_columns);
for (var i = 0; i < n_columns; i++)
indices[i] = i;
spans = [{"indices": indices}];
} else {
spans = getLowestLevelSpans(spans);
}
}
// Figure out which indices aren't in spans so that we
// replace the default column comparisons results with
// the no-test symbol.
var indices_included = [];
spans.forEach(function (span) {
indices_included = indices_included.concat(span.indices);
});
for (var j = 0; j < n_columns; j++) {
if (indices_included.indexOf(j) == -1)
indices_not_in_spans.push(j);
}
var n_spans = spans.length;

// Clearing out Statistics - Below.
if (has_below)
for (var c = 0; c < n_columns; c++) {
columns_compared_below[columns_compared_below_row(c)][columns_compared_below_column(c)] = "";
if (has_cc_below) {
cc_below[cc_below_row(c)][cc_below_column(c)] = "";
}
}

// Identifying NET columns
var column_labels = table.columnLabels;
var not_net_columns = new Array(n_columns);
for (var c = 0; c < n_columns; c++) {
var column_label = column_labels[c];
column_label = column_label.split('\r\n')[0]; // Removing sample size or column name letters
not_net_columns[c] = labels_to_ignore.indexOf(column_label) == -1;
}

// Preliminary calculations and clearing out some arrays
var net_columns = new Array(n_spans);
var group_var = new Array(n_rows);
var dfs = new Array(n_rows);
var gs = new Array(n_rows);
var variances = new Array(n_rows);
var valid = new Array(n_rows);

var group_var_below = new Array(1);
var dfs_below = new Array(1);
var gs_below = new Array(1);
var variances_below = new Array(1);
var valid_below = new Array(1);

var no_test_symbol = "-";
var start_r = has_cc_below ? -1 : 0; //using -1 for statistics - below
for (var r = start_r; r < n_rows; r++) {
var marginal = r == -1;
var group_var_row = new Array(n_spans);
var n_row = new Array(n_spans);
var df_row = new Array(n_spans);
var g_row = new Array(n_spans);
var variances_row = new Array(n_columns);
var valid_row = Array(n_spans);
for (var span = 0; span < n_spans; span++) {
var current_indices = spans[span].indices;
var n_columns_in_span = current_indices.length;
var g = 0;
var net = false;
var ss = 0.0;
var n = 0;
for (var c = 0; c < n_columns_in_span; c++) {
var column = current_indices[c];
var this_base = marginal ? (is_effective_sample_size ? base_below[0][column] : base_below[cnames_below_row(column)][cnames_below_column(column)]) : base[r][column];
var vald = not_net_columns[column] && this_base >= min_sample_size;
valid_row[column] = vald;
if (vald) {
g++;
var standard_dev, variance ;
if (has_average || marginal) {
standard_dev =  marginal ? sd_below[cc_below_row(column)][cc_below_column(column)] : sd[r][column];
variance = standard_dev * standard_dev;
var n_group = marginal ? (is_effective_sample_size ? base_below[0][column] : base_below[cc_below_row(column)][cc_below_column(column)]) : base[r][column];
if (!isNaN(variance))
ss += (n_group - 1) * variance;
n += n_group;
}
variances_row[column] = variance;
if (marginal)
cc_below[cc_below_row(column)][cc_below_column(column)] = "";
else
cc[r][column] = "";
} else {
if (marginal)
cc_below[cc_below_row(column)][cc_below_column(column)] = no_test_symbol;
else
cc[r][column] = no_test_symbol;
}
if (marginal) {
if (manual_comparisons)
cnames_below[cnames_below_row(column)][cnames_below_column(column)] = LETTERS[column];
else
cnames_below[cnames_below_row(column)][cnames_below_column(column)] = LETTERS[c];
columns_compared_below[columns_compared_below_row(column)][columns_compared_below_column(column)] = "";
} else {
if (manual_comparisons)
cnames[r][column] = LETTERS[column];
else
cnames[r][column] = LETTERS[c];
columns_compared[r][column] = "";
}
}
var df = n - g;
g_row[span] = g;
df_row[span] = df;
group_var_row[span] = ss / df;
}

if (marginal) {
group_var_below[0] = group_var_row;
gs_below[0] = g_row;
dfs_below[0] = df_row;
variances_below[0] = variances_row;
valid_below[0] = valid_row;
} else {
group_var[r] = group_var_row;
gs[r] = g_row;
dfs[r] = df_row;
variances[r] =  variances_row;
valid[r] = valid_row;
}
// Columns which are not included in testing should be given symbols for no testing
// and blanks for columns compared
indices_not_in_spans.forEach(function (column) {
if (marginal)
cc_below[cc_below_row(column)][cc_below_column(column)] = no_test_symbol;
else
cc[r][column] = no_test_symbol;
if (marginal)
columns_compared_below[columns_compared_below_row(column)][columns_compared_below_column(column)] = "";
else
columns_compared[r][column] = "";
});
}
var bonferroni = multiple_comparison_dropdown.getValue() == "Bonferroni";
var fdr = multiple_comparison_dropdown.getValue() == "False Discovery Rate";
var no_correction = !bonferroni && !fdr;

for (var r = start_r; r < table.numberRows; r++) {
for (var span = 0; span < n_spans; span++) {
var marginal = r == -1;
var current_indices = spans[span].indices;
var n_columns_in_span = current_indices.length;
// performing comparisons
var df = dfs[r];
var ps = new Array(n_columns_in_span);
var p_list = [];
for (var right_c = 1; right_c < n_columns_in_span; right_c++) {
ps[right_c] = new Array(n_columns_in_span);
for (var left_c = 0; left_c < right_c; left_c ++) {
var left_column = current_indices[left_c];
var right_column = current_indices[right_c];
var left_row_below = cnames_below_row(left_column);
var left_column_below = cnames_below_column(left_column);
var right_row_below = cnames_below_row(right_column);
var right_column_below = cnames_below_column(right_column);
var left_valid = marginal ? valid_below[0][left_column] : valid[r][left_column] ;
var right_valid = marginal ? valid_below[0][right_column] : valid[r][right_column];
if (left_valid && right_valid) {
var left_n =  marginal ? (is_effective_sample_size ? base_below[0][left_column] : base_below[left_row_below][left_column_below]) : base[r][left_column];
var right_n = marginal ? (is_effective_sample_size ? base_below[0][right_column] : base_below[right_row_below][right_column_below]) : base[r][right_column];
var left_value = marginal ? values_below[left_row_below][left_column_below] : values[r][left_column];
var right_value = marginal ? values_below[right_row_below][right_column_below] : values[r][right_column];
if (!has_average && !marginal) {
left_value /= 100;
right_value /= 100;
}
var net_column = net_columns[span];
var degrees_of_freedom, se, p;
var diff = right_value - left_value;
if(has_average || marginal) {
if (mean_assumption == "Complex samples") {
var left_se = marginal ? standard_error_below[left_row_below][left_column_below] : standard_error[r][left_column];
var right_se = marginal ? standard_error_below[right_row_below][right_column_below] : standard_error[r][right_column];
var standard_error_1_squared = Math.pow(left_se, 2);
var standard_error_2_squared = Math.pow(right_se, 2);
var D1 = Math.pow(standard_error_1_squared, 2) / (left_n - 1);
var D2 = Math.pow(standard_error_2_squared, 2) / (right_n - 1);
se = Math.pow(standard_error_1_squared + standard_error_2_squared, 0.5);
degrees_of_freedom = Math.pow(standard_error_1_squared + standard_error_2_squared, 2) / (D1 + D2);
} else if (mean_assumption == "Unequal variance") {
var var_left = marginal ? variances_below[0][left_column] : variances[r][left_column];
var var_right = marginal ? variances_below[0][right_column] : variances[r][right_column];
var standard_error_1_squared = var_left / left_n;
var standard_error_2_squared = var_right / right_n;
var D1 = Math.pow(standard_error_1_squared, 2) / (left_n - 1);
var D2 = Math.pow(standard_error_2_squared, 2) / (right_n - 1);
degrees_of_freedom = Math.pow(standard_error_1_squared + standard_error_2_squared, 2) / (D1 + D2);
se = Math.sqrt(standard_error_1_squared + standard_error_2_squared);
} else if (mean_assumption ==  "Equal variance (categories compared)") {
var var_left = marginal ? variances_below[0][left_column] : variances[r][left_column];
var var_right = marginal ? variances_below[0][right_column] : variances[r][right_column];
degrees_of_freedom = left_n + right_n - 2;
var s_2 = ((left_n - 1)* var_left + (right_n - 1) * var_right) / degrees_of_freedom;
se = Math.sqrt(s_2 *  (1 / left_n +  1 / right_n));
} else { // "Equal variance (all non-ignored categories)"
var total_var = marginal ? group_var_below[0][span] : group_var[r][span];
se = Math.sqrt(total_var * (1 / left_n +  1 / right_n));
degrees_of_freedom = marginal ? dfs_below[0][span] : dfs[r][span];
}
var t = se <= 0.0 ? 0 : diff / se ;
p = 2 * (1 - jStat.studentt.cdf(Math.abs(t), degrees_of_freedom));
} else { // Proportions test
if (marginal) {
} else {
// Marginal statistics cannot be proportions for testing purposes.
if (proportion_assumption == proportion_tests[0]) { // Complex samples t-test.
var left_se = standard_error[r][left_column];
var right_se = standard_error[r][right_column];
var standard_error_1_squared = Math.pow(left_se, 2);
var standard_error_2_squared = Math.pow(right_se, 2);
se = Math.pow(standard_error_1_squared + standard_error_2_squared, 0.5);
degrees_of_freedom = left_n + right_n - 1;
} else if (proportion_assumption == proportion_tests[1]) { // Pooled z-test.
var pooled_proportion = (left_value * left_n + right_value * right_n) / (left_n + right_n);
se = Math.sqrt(pooled_proportion * (1 - pooled_proportion) * (1 / left_n +  1 / right_n));
} else { // Un-pooled z-test
se = Math.sqrt(left_value * (1 - left_value)/ left_n + right_value * (1 - right_value)/ right_n);
}
var z = se <= 0.0 ? 0 : diff / se;
if (proportion_assumption == proportion_tests[0])
p = 2 * (1 - jStat.studentt.cdf(Math.abs(z), degrees_of_freedom));
else
p = 2 * (1 - jStat.normal.cdf(Math.abs(z), 0, 1));

}
}
if (!no_correction && diff != 0)
p_list.push(p);
ps[right_c][left_c] = p;
}
}
}
// multiple comparison corrections
var n_pvalues = p_list.length;
var m = 1;
if (n_pvalues > 0) {
if (!no_correction) {
if (bonferroni || n_pvalues == 1)
m = n_pvalues;
else if (fdr) {
var sorted_pvalues = p_list.sort(function(x, y) { return x - y });
var new_alpha = alpha / n_pvalues;
for (var i=0; i<n_pvalues; i++) {
var temp_alpha = alpha * (1 + i) / n_pvalues;
var cur_p = sorted_pvalues[i];
if (cur_p >= alpha)
break;
if (cur_p <= temp_alpha )
new_alpha = temp_alpha;
}
m = alpha / new_alpha;
}
}
}

// writing results
// Work out the set of letters in this span.
var SPAN_LETTERS = manual_comparisons ? current_indices.map(function (x) { return LETTERS[x]; }) : LETTERS;
var span_letters = manual_comparisons ? current_indices.map(function (x) { return letters[x]; }) : letters;
for (var right_c = 1; right_c < n_columns_in_span; right_c++) {
for (var left_c = 0; left_c < right_c; left_c ++) {
var left_column = current_indices[left_c];
var right_column = current_indices[right_c];
if (not_net_columns[left_column]  && not_net_columns[right_column]) {
var marginal = r == -1;
var left_value = marginal ? values_below[cnames_below_row(left_column)][cnames_below_column(left_column)] : values[r][left_column];
var right_value = marginal ? values_below[cnames_below_row(right_column)][cnames_below_column(right_column)] : values[r][right_column];
var p = ps[right_c][left_c];
p *= m; //applying bonferroni correction (if required)
if (p <= alpha){
if (left_value === undefined || right_value === undefined)
throw new Error('undefined values');
var left_lower = left_value < right_value;
var letter_column = left_lower ? left_c : right_c;
var insertion_column = left_lower ? right_column : left_column;
var letter = p <= upper_alpha ? SPAN_LETTERS[letter_column] : span_letters[letter_column];
if (marginal)
cc_below[cc_below_row(insertion_column)][cc_below_column(insertion_column)] += letter + " ";
else
cc[r][insertion_column] += letter + " ";
}
if (marginal) {
columns_compared_below[columns_compared_below_row(left_column)][columns_compared_below_column(left_column)] += SPAN_LETTERS[right_c];
columns_compared_below[columns_compared_below_row(right_column)][columns_compared_below_column(right_column)] += SPAN_LETTERS[left_c];
} else {
columns_compared[r][left_column] += SPAN_LETTERS[right_c];
columns_compared[r][right_column] += SPAN_LETTERS[left_c];
}
}
}
}
}
}

// If there are spans in the table, then any columns not in spans should
// get the no-test symbol.
for (var r = start_r; r < table.numberRows; r++) {
var marginal = r == -1;
indices_not_in_spans.forEach(function (column) {
if (marginal)
cc_below[cc_below_row(column)][cc_below_column(column)] = no_test_symbol;
else
cc[r][column] = no_test_symbol;
});
}

// Writing results back to the table.
table.set('Column Comparisons', cc);
table.set('Column Names', cnames);
table.set('Columns Compared', columns_compared);

// No column comparisons below the table, but Column Names
// still need to be updated to reflect those used in the
// cells of the table
if (has_below && !has_cc_below) {
for (var c = 0; c < table.numberColumns; c++)
cnames_below[cnames_below_row(c)][cnames_below_column(c)] = cnames[0][c]
}

if (has_below) {
below_table.set('Column Names', cnames_below);
if (has_cc_below) {
if (below_table.numberColumns != cc_below[0].length)
cc_below = Q.transpose(cc_below);
below_table.set('Column Comparisons', cc_below);
}
below_table.set('Columns Compared', columns_compared_below);
}

// Construct footer to indicate testing options
var footers = table.extraFooters;

if (footer_checkbox.getValue()) {
if (manual_comparisons)
footers.push("Comparisons: " + comparisons_string.toUpperCase());

if (upper_alpha < lower_alpha)
footers.push("Lower case letters: p < " + lower_alpha);

footers.push("Uppercase letters: p < " + upper_alpha);

if (!no_correction)
footers.push("Multiple comparison correction: " + multiple_comparison_dropdown.getValue());

footers.push("Numeric data variance assumption (t-test): " + variance_dropdown.getValue());
footers.push("Base used in tests of means: " + base_dropdown.getValue());
footers.push("Proportions test: " + proportion_dropdown.getValue());
footers.push("Base used in tests of proportions: " + base_dropdown_proportions.getValue());
footers.push("Minimum column sample size used in testing: " + min_box.getValue());
footers.push("Columns ignored: " + labels_to_ignore.join(", "));
}

footers.push("Columns are assumed to be independent in statistical testing");

table.extraFooters = footers;
```