using BotSharp.Algorithm.HiddenMarkovModel.MathUtils.Distribution; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace BotSharp.Algorithm.HiddenMarkovModel.MathUtils.Statistics { /// /// For categorical variable with values either SUCCESS or FAILURE /// /// Hypothesis testing for a single proportion: /// 1. Set the hypothesis: /// H_0 : p = null_value /// H_A : p < or > or != null_value /// 2. Calculate point estimate p_hat /// 3. Check conditions: /// 1. Independence: Sampled observations must be independent (random sample / assignment & if sampleing without replacement, n < 10 % of the population) /// 2. Sample size / skew: np >= 10, and n(1-p) >= 10 /// 4. Draw sampling distribution, shade p-value, calcuate Z = (p_hat - p) / SE, SE = sqrt (p * (1-p) / n) (Since H_0 is assumed to be true prior, p = null_value) /// 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, fail to reject H_0; the data do not provide convincing evidence for H_A. /// public class HypothesisTestingForProportion { /// /// One or two-sided test for categorical variable with levels { SUCCESS, FAILURE}. /// /// Hypotheses are: /// H_0 : p = null_value /// H_A : p != null_value /// where p is the proportion of SUCCESS in the population /// /// Condition for CLT for proportion must be checked. /// /// p-value = P(observed or more extreme proportion of SUCCESS | H_0 is true) /// /// point estimate of the sample proportion of SUCCESS in the population /// /// p-value, which P(observed or more extreme proportions of SUCCESS | H_0 is true) /// significance level, alpha /// /// /// True if H_0 is rejected public static bool RejectH0(double sample_proportion, double null_value, int sample_size, out double pValue, double significance_level = 0.05, bool one_sided = false, bool useSimulation = false, int simulationCount = 500) { int expected_success_count = (int)(sample_proportion * sample_size); int expected_failure_count = (int)((1 - sample_proportion) * sample_size); if (expected_failure_count < 10 || expected_success_count < 10 || useSimulation) // if CLT for proportion does not hold, switch to simulation { double[] sim_sample = new double[simulationCount]; int simulationSampleSize = (int)System.Math.Max(10 / sample_proportion, 10 / (1 - sample_proportion)) * 2; for (int i = 0; i < simulationCount; ++i) { int successCount = 0; for (int j = 0; j < simulationSampleSize; ++j) { if (DistributionModel.GetUniform() <= null_value) { successCount++; } } sim_sample[i] = (double)successCount / simulationSampleSize; } double observed_or_extreme_observation_count = 0; for (int i = 0; i < simulationCount; ++i) { if (sim_sample[i] >= sample_proportion) { observed_or_extreme_observation_count++; } } pValue = (double)observed_or_extreme_observation_count / simulationCount; return pValue < significance_level; } else { //Since H_0 is assumed to be true, p = null_value double p = null_value; double SE = System.Math.Sqrt(p * (1 - p) / sample_size); double p_hat = sample_proportion; // point estimate double Z = (p_hat - p) / SE; pValue = (1 - Gaussian.GetPercentile(System.Math.Abs(Z))) * (one_sided ? 1 : 2); return pValue < significance_level; } } /// /// One or two-sided test for two classes, which are categorical variables with levels { SUCCESS, FAILURE} in /// /// Hypotheses are: /// H_0 : p1 = p2 = null_value /// H_A : p1 != p2 /// where p1 and p2 are the proportion of SUCCESS in the population for class 1 and class 2 /// /// Condition for CLT for proportion must be checked. /// /// p-value = P(observed or more extreme proportion difference (p_hat1 - p_hat2) of SUCCESS | H_0 is true) /// /// point estimate of the sample proportion of SUCCESS for class 1 in the population /// point estimate of the sample proportion of SUCCESS for class 2 in the population /// sample size for class 1 /// sample size for class 2 /// p-value, which P(observed or more extreme proportions of SUCCESS | H_0 is true) /// significance level, alpha /// /// True if H_0 is rejected public static bool RejectH0(double p_hat1, double p_hat2, int n1, int n2, out double pValue, double significance_level = 0.05, bool one_sided = false, bool useSimulation = false, int simulationCount = 500) { double pooled_proportion = (p_hat1 * n1 + p_hat2 * n2) / (n1 + n2); ; double null_value = pooled_proportion; if (pooled_proportion * n1 < 10 || (1 - pooled_proportion) * n1 < 10 || pooled_proportion * n2 < 10 || (1 - pooled_proportion) * n2 < 10 || useSimulation) // if CLT for proportion does not hold, switch to simulation { double[] sim_sample = new double[simulationCount]; int simulationSampleSize = (int)(System.Math.Max(10 / pooled_proportion, 10 / (1 - pooled_proportion))) * 2; for (int i = 0; i < simulationCount; ++i) { int successCount1 = 0; int successCount2 = 0; for (int j = 0; j < simulationSampleSize; ++j) { if (DistributionModel.GetUniform() <= null_value) { successCount1++; } if (DistributionModel.GetUniform() <= null_value) { successCount2++; } } int diff = successCount1 - successCount2; sim_sample[i] = (double)diff / simulationSampleSize; } double point_estimate = p_hat1 - p_hat2; int observed_or_more_extreme_event_count = 0; for (int i = 0; i < simulationCount; ++i) { if (point_estimate > 0) { if (sim_sample[i] >= point_estimate) { observed_or_more_extreme_event_count++; } } else { if (sim_sample[i] <= point_estimate) { observed_or_more_extreme_event_count++; } } } pValue = (double)observed_or_more_extreme_event_count / simulationCount; return pValue < significance_level; } else { //Since H_0 is assumed to be true, p = null_value double p = null_value; double SE = System.Math.Sqrt(p * (1 - p) / n1 + p * (1 - p) / n2); double Z = (p_hat1 - p_hat2) / SE; pValue = (1 - Gaussian.GetPercentile(System.Math.Abs(Z))) * (one_sided ? 1 : 2); return pValue < significance_level; } } } }