A function which takes two lists of values of equal length with corresponding entries and returns a dict containing
    a variety of metrics.
    :param x_values: A list of values for the X-axis (experimental values).
    :param y_values: A list of values for the X-axis (predicted values).
    :param fcorrect_x_cutoff: See get_xy_dataset_statistics.
    :param fcorrect_y_cutoff: See get_xy_dataset_statistics.
    :param x_fuzzy_range: See get_xy_dataset_statistics.
    :param y_scalar: See get_xy_dataset_statistics.
    :param ignore_null_values: Remove records with missing data before running analysis.
    :param bootstrap_data: Set to True to enable bootstrapping. Bootstrapping data to estimate errors is an expensive operation so is disabled by default.
    :param expect_negative_correlation: See klab.benchmarking.analysis.ssm::parse_csv().
    :param STDev_cutoff: See klab.benchmarking.analysis.ssm::parse_csv().
    :return: A table of statistics.

def _get_xy_dataset_statistics(x_values, y_values,
                               fcorrect_x_cutoff = 1.0, fcorrect_y_cutoff = 1.0, x_fuzzy_range = 0.1, y_scalar = 1.0,
                               ignore_null_values = False, bootstrap_data = False,
                               expect_negative_correlation = False, STDev_cutoff = 1.0,
                               run_standardized_analysis = True,
                               check_multiple_analysis_for_consistency = True):
    '''
    A function which takes two lists of values of equal length with corresponding entries and returns a dict containing
    a variety of metrics.
    :param x_values: A list of values for the X-axis (experimental values).
    :param y_values: A list of values for the X-axis (predicted values).
    :param fcorrect_x_cutoff: See get_xy_dataset_statistics.
    :param fcorrect_y_cutoff: See get_xy_dataset_statistics.
    :param x_fuzzy_range: See get_xy_dataset_statistics.
    :param y_scalar: See get_xy_dataset_statistics.
    :param ignore_null_values: Remove records with missing data before running analysis.
    :param bootstrap_data: Set to True to enable bootstrapping. Bootstrapping data to estimate errors is an expensive operation so is disabled by default.
    :param expect_negative_correlation: See klab.benchmarking.analysis.ssm::parse_csv().
    :param STDev_cutoff: See klab.benchmarking.analysis.ssm::parse_csv().
    :return: A table of statistics.
    '''
    assert(len(x_values) == len(y_values))

    num_null_cases = 0
    if ignore_null_values:
        truncated_x_values, truncated_y_values = [], []
        for i in xrange(len(x_values)):
            if (x_values[i] == None) or (numpy.isnan(x_values[i])):
                num_null_cases += 1
            elif (y_values[i] == None) or (numpy.isnan(y_values[i])):
                num_null_cases += 1
            else:
                truncated_x_values.append(x_values[i])
                truncated_y_values.append(y_values[i])
        x_values = truncated_x_values
        y_values = truncated_y_values

    stats = dict(
        pearsonr = pearsonr(x_values, y_values),
        spearmanr = spearmanr(x_values, y_values),
        gamma_CC = gamma_CC(x_values, y_values),
        MAE = mae(x_values, y_values),
        normaltestx = normaltest_check(x_values),
        normaltesty = normaltest_check(y_values),
        kstestx = kstest(x_values, 'norm'),
        kstesty = kstest(y_values, 'norm'),
        ks_2samp = ks_2samp(x_values, y_values),
        fraction_correct = fraction_correct(x_values, y_values, x_cutoff = fcorrect_x_cutoff, y_cutoff = fcorrect_y_cutoff, ignore_null_values = ignore_null_values),
        fraction_correct_fuzzy_linear = fraction_correct_fuzzy_linear(x_values, y_values, x_cutoff = fcorrect_x_cutoff, y_cutoff = fcorrect_y_cutoff, x_fuzzy_range = x_fuzzy_range, y_scalar = y_scalar),
        n = len(x_values),
        num_null_cases = num_null_cases,
    )
    if bootstrap_data:
        stats.update(dict(
            pearsonr_error = bootstrap_xy_stat(x_values, y_values, pearsonr),
            MAE_error = bootstrap_xy_stat(x_values, y_values, mae),
            fraction_correct_error = bootstrap_xy_stat(x_values, y_values, fraction_correct, func_kwargs = {'x_cutoff' : fcorrect_x_cutoff, 'y_cutoff' : fcorrect_y_cutoff, 'ignore_null_values' : ignore_null_values}),
        ))

    # Call Samuel Thompson's analysis which uses confidence intervals when considering fraction correct-like metrics
    if run_standardized_analysis:
        std_stats = get_std_xy_dataset_statistics(x_values, y_values, expect_negative_correlation = expect_negative_correlation, STDev_cutoff = STDev_cutoff)
        fields_to_remove = []
        if check_multiple_analysis_for_consistency:
            # Make sure that any common analysis agrees
            fields_to_remove = sorted(set(std_stats.keys()).intersection(stats.keys()))
            for common_stat in fields_to_remove:
                s1, s2 = std_stats[common_stat], stats[common_stat]
                if isinstance(s1, tuple):
                    s1 = s1[0]
                if isinstance(s2, tuple):
                    s2 = s2[0]
                assert(abs(s1 - s2) < 0.001)
            for f in fields_to_remove:
                del std_stats[f]

        stats.update(std_stats)

    return stats