BotSharp/BotSharp.Algorithm/HiddenMarkovModel/MathUtils/Statistics/HypothesisTesting.cs

327 lines
19 KiB
C#
Raw Normal View History

2018-09-17 12:31:54 +00:00
using BotSharp.Algorithm.HiddenMarkovModel.MathUtils.Distribution;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BotSharp.Algorithm.HiddenMarkovModel.MathUtils.Statistics
{
/// <summary>
/// Hypothesis testing framework:
/// 1. Start with a null hypothesis (null_value) that represents the status quo (i.e. common believe).
/// 2. Create an alternative hypothesis (H_A) that represents the research questions, i.e. what is being tested for.
/// 3. Conduct a hypothesis test under the assumption that the null hypothesis is true, either via simulation or theoretical methods (i.e. methods that rely on Central Limit Theorem)
/// 4. If thte test results suggest that the data do not provide convincing evidence for the alternative hypothesis, we stick with the null hypothesis. If the do, then we reject
/// the null hyptothesis in favor of the alternative
///
/// p-value = P(observed or more extreme outcome | null_value true)
/// p-value is the probability of observing data at least as favorable to the alternative hypothesis as our current data set, if the null hyptothesis was true
/// 1. If the p-value is low (lower than the significance level, alpha, which is usually 5%) we say the it would be very unlikely to observe the data if the null hypothesis were true, and hence reject H_0
/// 2. If the p-value is high (higher than alpha) we say that is likely to observe the data even if the null hypothesis were true, and hence do not reject H_0
///
/// Steps for Hypothesis testing for a single mean:
/// 1. Set the hypotheses: H_0: mu = null value
/// H_A: mu < or > or != null value
/// 2. Calculate the point estimate (usually the sample mean)
/// 3. Check CLT (i.e., Central Limit Theorem) condictions:
/// I. Independence: Sampled observations must be independent (random sample/assignment & if sampling without replacement, n < 10% of population)
/// II. Sample size/skew: n>=30, larger if the population distribution is very skewed
/// 4. Draw sample distribution, shade p-value, calculate test statistics Z = (point_estimate - null_value) / SE
/// 5. Make a decision, and interpret it in context of the research question:
/// if p-value < alpha, reject H_0; the data provide convincing evidence for H_A
/// if p-value > alpha, do not reject H_0; the data do not provide convincing evidence for H_A
///
/// There are two types of decision errors in hypothesis testing:
/// Type I error is rejecting H_0 when H_0 is true
/// Type II error is failing to reject H_0 when H_A is true
/// (Usually from a social point of view, Type I error is the worst error as it means rejecting something that is true and status quo, but this depends on scenario and personal preferences)
/// By definition:
/// P(Type I error | H_0 is true) = alpha, where alpha is the significance_level => prefer small significance level, increasing significance level increases the Type I error rate.
/// On the other hand, decreasing significance_level increases the Type II error
///
/// The power of a test is the probability of correctly reject H_0, the probability of doing so is (1-beta), where beta = P(Type II error | H_A is true)
/// beta depends on the effect size, which is the difference between point estimate and the null_value => if the effect size is large, then beta is small
///
/// Real differences between the point estimate and null_value are easier to detect with larger sample. However, very large samples will result in a statistical significance even
/// for tiny differences between sample mean (i.e. point estimate) and the null value (the difference refers to the effect size), even when the difference is not practically significant.
///
/// Note that in case the sample size is smaller than 30, the CLT does not hold and Student's t distribution should be used for the population statistics instead of normal distribution
/// </summary>
public class HypothesisTesting
{
/// <summary>
/// Two-sided or one-sided test for a single mean
///
/// A two-sided hypothesis with threshold of alpha (i.e. significance level) is equivalent to a confidence interval of CL (i.e. confidence level) = 1 - alpha
/// A one-sided hypothesis with threshold of alpha is equivalent to a confidence interval of CL = 1 - alpha * 2
///
/// If H_0 is rejected, a confidence interval that agrees with the result of the hypothesis test should not include the null_value
/// If H_0 is failed to be rejected, a confidence interval that agrees with the result of the hypothesis should include the null_value
/// </summary>
/// <param name="values">value sample for the varabiel</param>
/// <param name="null_value">The null hypothesis value that true population mean, mu = null_value</param>
/// <param name="significance_level"></param>
/// <param name="one_sided">True if the test is one-sided</param>
/// <returns>True if the null hypothesis H_0 : (mu == null_value) is rejected</returns>
public bool RejectH0_ByCI(double[] values, double null_value, double significance_level = 0.05, bool one_sided = false)
{
double confidence_level = 1 - significance_level * (one_sided ? 2 : 1);
double[] confidence_interval = ConfidenceInterval.GetConfidenceInterval(values, confidence_level);
return null_value < confidence_interval[0] || null_value > confidence_interval[1];
}
/// <summary>
/// Two-sided or one-sided test for a single mean
///
/// Given that:
/// H_0 : mu = null_value
/// H_A : mu != null_value
///
/// By Central Limit Theorem:
/// sample_mean ~ N(mu, SE)
///
/// p-value = (sample_mean is at least ||null_value-point_estimate|| away from the null_value) | mu = null_value)
/// if(p-value < significance_level) reject H_0
/// </summary>
/// <param name="sample"></param>
/// <param name="null_value"></param>
/// <param name="significance_level"></param>
/// <param name="one_sided">True if the test is one_sided</param>
/// <returns></returns>
public static bool RejectH0(double[] sample, double null_value, out double pValue, double significance_level = 0.05, bool one_sided = false, bool useStudentT = false)
{
double pointEstimate = Mean.GetMean(sample);
double standardError = StandardError.GetStandardError(sample); //SE is the estimated standard deviation of the true population mean, mu
double test_statistic = System.Math.Abs(pointEstimate - null_value) / standardError; //This assumes that H_0 is true, that is, the true population mean, mu = null_value
double percentile = 0;
if (sample.Length < 30 || useStudentT) //if sample size is smaller than 30, then CLT for population statistics such as sample mean no longer holds and Student's t distribution should be used in place of the normal distribution
{
percentile = StudentT.GetPercentile(test_statistic, sample.Length - 1);
}
else
{
percentile = Gaussian.GetPercentile(test_statistic);
}
pValue = pValue = (1 - percentile) * (one_sided ? 1 : 2);
return pValue < significance_level;
}
/// <summary>
/// Two-sided or one-sided test for a single statistic
///
/// Given that:
/// H_0 : mu = null_value
/// H_A : mu != null_value
///
/// By Central Limit Theorem:
/// sample_mean ~ N(mu, SE)
///
/// p-value = (sample_mean is at least ||null_value-point_estimate|| away from the null_value) | mu = null_value)
/// if(p-value < significance_level) reject H_0
/// </summary>
/// <param name="point_estimate">point estimate of the population statistics (e.g., sample mean, sample median, etc.)</param>
/// <param name="null_value"></param>
/// <param name="SE">standard error of the population statistics</param>
/// <param name="significance_level"></param>
/// <param name="one_sided"></param>
/// <returns></returns>
public static bool RejectH0(double point_estimate, double null_value, double SE, int sampleSize, out double pValue, double significance_level = 0.05, bool one_sided = false, bool useStudentT = false)
{
double test_statistic = System.Math.Abs(point_estimate - null_value) / SE; //This assumes that H_0 is true, that is, the true population mean, mu = null_value
double percentile = 0;
if (sampleSize < 30 || useStudentT) //if sample size is smaller than 30, then CLT for population statistics such as sample mean no longer holds and Student's t distribution should be used in place of the normal distribution
{
percentile = StudentT.GetPercentile(test_statistic, sampleSize - 1);
}
else
{
percentile = Gaussian.GetPercentile(test_statistic);
}
pValue = (1 - percentile) * (one_sided ? 1 : 2);
return pValue < significance_level;
}
/// <summary>
/// Two-sided test for whether statitics of two variables are equal in the true population, var1 and var2 are independent
///
/// Hypotheses are:
/// H_0: mu_var1 = mu_var2
/// H_1: mu_var1 != mu_var2
///
/// The hypotheses can be written as
/// H_0: mu_var1 - mu_var2 = 0
/// H_1: mu_var1 - mu_var2 != 0
///
/// By Central Limt Theorem:
/// sample_mean_var1 - sample_mean_var2 ~ N(0, SE), where null_value = 0 and SE is the standard error of the sampling distribution
/// </summary>
/// <param name="sample_for_var1">value sample for variable 1</param>
/// <param name="sample_for_var2">value sample for variable 2</param>
/// <param name="significance_level"></param>
/// <returns></returns>
public bool RejectH0_ByCI(double[] sample_for_var1, double[] sample_for_var2, double significance_level = 0.05, bool one_sided = false)
{
double confidence_level = 1 - significance_level * (one_sided ? 2 : 1);
double[] confidence_interval = ConfidenceInterval.GetConfidenceIntervalForDiff(sample_for_var1, sample_for_var2, confidence_level);
double null_value = 0;
return null_value < confidence_interval[0] || null_value > confidence_interval[1];
}
/// <summary>
/// Two-sided or one-sided test for whether statitics of two variables are equal in the true population, var1 and var2 are paired and dependent
///
/// Hypotheses are:
/// H_0: mu_var1 = mu_var2
/// H_1: mu_var1 != mu_var2
///
/// The hypotheses can be written as
/// H_0: mu_var1 - mu_var2 = 0
/// H_1: mu_var1 - mu_var2 != 0
///
/// By Central Limt Theorem:
/// sample_mean_var1 - sample_mean_var2 ~ N(0, SE), where null_value = 0 and SE is the standard error of the sampling distribution
/// </summary>
/// <param name="sample_for_paired_data">a random sample consisting data paired together, var1 and var2, var1 and var2 are not independent</param>
/// <param name="one_sided">True if the test is one-sided</param>
/// <param name="significance_level"></param>
/// <returns></returns>
public bool RejectH0_PairedData_ByCI(Tuple<double, double>[] sample_for_paired_data, double significance_level = 0.05, bool one_sided = false)
{
int sample_size = sample_for_paired_data.Length;
double[] diff = new double[sample_size];
for (int i = 0; i < sample_size; ++i)
{
diff[i] = sample_for_paired_data[i].Item1 - sample_for_paired_data[i].Item2;
}
double confidence_level = 1 - significance_level * (one_sided ? 2 : 1);
double[] confidence_interval = ConfidenceInterval.GetConfidenceInterval(diff, confidence_level);
double null_value = 0;
return null_value < confidence_interval[0] || null_value > confidence_interval[1];
}
/// <summary>
/// Two-sided or one-sided test for whether statitics of two variables are equal in the true population, var1 and var2 are independent
///
/// Hypotheses are:
/// H_0: mu_var1 = mu_var2
/// H_1: mu_var1 != mu_var2
///
/// The hypotheses can be written as
/// H_0: mu_var1 - mu_var2 = 0
/// H_1: mu_var1 - mu_var2 != 0
///
/// By Central Limt Theorem:
/// sample_mean_var1 - sample_mean_var2 ~ N(0, SE), where null_value = 0 and SE is the standard error of the sampling distribution
///
/// p-value = (sample_mean is at least ||null_value-point_estimate|| away from the null_value) | mu = null_value)
/// </summary>
/// <param name="sample_for_var1">value sample for variable 1</param>
/// <param name="sample_for_var2">value sample for variable 2</param>
/// <param name="one_sided">True if the test is one-sided</param>
/// <param name="significance_level"></param>
/// <returns></returns>
public bool RejectH0(double[] sample_for_var1, double[] sample_for_var2, out double pValue, double significance_level = 0.05, bool one_sided = false, bool useStudentT = false)
{
double pointEstimate = Mean.GetMean(sample_for_var1) - Mean.GetMean(sample_for_var2);
double null_value = 0;
double SE = StandardError.GetStandardError(sample_for_var1, sample_for_var2);
double test_statistic = System.Math.Abs(pointEstimate - null_value) / SE;
double percentile = 0;
if (sample_for_var1.Length < 30 || sample_for_var2.Length < 30 || useStudentT) //if sample size is smaller than 30, then CLT for population statistics such as sample mean no longer holds and Student's t distribution should be used in place of the normal distribution
{
int df = System.Math.Min(sample_for_var1.Length - 1, sample_for_var2.Length - 1);
percentile = StudentT.GetPercentile(test_statistic, df);
}
else
{
percentile = Gaussian.GetPercentile(test_statistic);
}
pValue = (1 - percentile) * (one_sided ? 1 : 2);
return pValue < significance_level;
}
/// <summary>
/// Two-sided or one-sided test for whether statitics of two variables are equal in the true population, var1 and var2 are paired and dependent
///
/// Hypotheses are:
/// H_0: mu_var1 = mu_var2
/// H_1: mu_var1 != mu_var2
///
/// The hypotheses can be written as
/// H_0: mu_var1 - mu_var2 = 0
/// H_1: mu_var1 - mu_var2 != 0
///
/// By Central Limt Theorem:
/// sample_mean_var1 - sample_mean_var2 ~ N(0, SE), where null_value = 0 and SE is the standard error of the sampling distribution
///
/// p-value = (sample_mean is at least ||null_value-point_estimate|| away from the null_value) | mu = null_value)
/// </summary>
/// <param name="sample_for_paired_data">a random sample consisting data paired together, var1 and var2, var1 and var2 are not independent</param>
/// <param name="one_sided">True if the test is one-sided</param>
/// <param name="significance_level"></param>
/// <returns></returns>
public bool RejectH0_PairedData(Tuple<double, double>[] sample_for_paired_data, out double pValue, double significance_level = 0.05, bool one_sided = false, bool useStudentT = false)
{
int sample_size = sample_for_paired_data.Length;
double[] diff = new double[sample_size];
for (int i = 0; i < sample_size; ++i)
{
diff[i] = sample_for_paired_data[i].Item1 - sample_for_paired_data[i].Item2;
}
double point_estimate = Mean.GetMean(diff);
double null_value = 0;
double SE = StandardError.GetStandardError(diff);
double test_statistic = System.Math.Abs(point_estimate - null_value) / SE;
double percentile = 0;
if (sample_for_paired_data.Length < 30 || useStudentT) //if sample size is smaller than 30, then CLT for population statistics such as sample mean no longer holds and Student's t distribution should be used in place of the normal distribution
{
percentile = StudentT.GetPercentile(test_statistic, sample_for_paired_data.Length - 1);
}
else
{
percentile = Gaussian.GetPercentile(test_statistic);
}
pValue = (1 - percentile) * (one_sided ? 1 : 2);
return pValue < significance_level;
}
/// <summary>
/// Check whether variable 1 is truely greater than variable 2 at 0.95 statistical significance confidence level, var1 and var2 are independent
/// </summary>
/// <param name="sample_for_var1">value sample for variable 1</param>
/// <param name="sample_for_var2">value sample for variable 2</param>
/// <param name="confidence_level"></param>
/// <returns></returns>
public bool AreGreater(double[] sample_for_var1, double[] sample_for_var2, double confidence_level = 0.95)
{
double[] confidence_interval_for_var1 = ConfidenceInterval.GetConfidenceInterval(sample_for_var1, confidence_level);
double[] confidence_interval_for_var2 = ConfidenceInterval.GetConfidenceInterval(sample_for_var2, confidence_level);
return confidence_interval_for_var1[0] > confidence_interval_for_var2[1];
}
/// <summary>
/// Check whether variable 1 is truely less than variable 2 at 0.95 statistical significance confidence level
/// </summary>
/// <param name="sample_for_var1">value sample for variable 1</param>
/// <param name="sample_for_var2">value sample for variable 2</param>
/// <param name="confidence_level"></param>
/// <returns></returns>
public bool AreLessThan(double[] sample_for_var1, double[] sample_for_var2, double confidence_level = 0.95)
{
double[] confidence_interval_for_var1 = ConfidenceInterval.GetConfidenceInterval(sample_for_var1, confidence_level);
double[] confidence_interval_for_var2 = ConfidenceInterval.GetConfidenceInterval(sample_for_var2, confidence_level);
return confidence_interval_for_var1[1] < confidence_interval_for_var2[0];
}
}
}