abtest.stats_util
ABTest - stats_util¤
ab_dist(stderr, d_hat=0, group_type='control')
¤
Function from https://towardsdatascience.com/the-math-behind-a-b-testing-with-example-code-part-1-of-2-7be752e1d06f Distribution object depending on group type
Examples:
Parameters: stderr (float): pooled standard error of two independent samples d_hat (float): the mean difference between two independent samples group_type (string): 'control' and 'test' are supported
Returns: dist (scipy.stats distribution object)
Source code in dietbox/abtest/stats_util.py
def ab_dist(stderr, d_hat=0, group_type="control"):
"""Function from
https://towardsdatascience.com/the-math-behind-a-b-testing-with-example-code-part-1-of-2-7be752e1d06f
Distribution object depending on group type
Examples:
Parameters:
stderr (float): pooled standard error of two independent samples
d_hat (float): the mean difference between two independent samples
group_type (string): 'control' and 'test' are supported
Returns:
dist (scipy.stats distribution object)
"""
if group_type == "control":
sample_mean = 0
elif group_type == "test":
sample_mean = d_hat
# create a normal distribution which is dependent on mean and std dev
dist = scs.norm(sample_mean, stderr)
return dist
cal_confidence_interval(sample_mean=0, sample_std=1, sample_size=1, sig_level=0.05)
¤
Confidence interval assuming normal distribution
Based the z score. Reference: https://www.statisticshowto.datasciencecentral.com/probability-and-statistics/confidence-interval/
Source code in dietbox/abtest/stats_util.py
def cal_confidence_interval(sample_mean=0, sample_std=1, sample_size=1, sig_level=0.05):
"""Confidence interval assuming normal distribution
Based the z score.
Reference: https://www.statisticshowto.datasciencecentral.com/probability-and-statistics/confidence-interval/
"""
z = cal_z_score(sig_level)
left = sample_mean - z * sample_std / np.sqrt(sample_size)
right = sample_mean + z * sample_std / np.sqrt(sample_size)
return (left, right)
cal_conversion_rate(X_total, X_converted)
¤
Conversion rate for a specific group
Source code in dietbox/abtest/stats_util.py
def cal_conversion_rate(X_total, X_converted):
"""Conversion rate for a specific group"""
X_cr = X_converted / X_total if X_total != 0 else 0
return X_cr
cal_conversion_uplift(A_total, B_total, A_converted, B_converted)
¤
Uplift in conversion rate
Relative uplift is the relative conversion rate difference between the test groups
Source code in dietbox/abtest/stats_util.py
def cal_conversion_uplift(A_total, B_total, A_converted, B_converted):
"""Uplift in conversion rate
Relative uplift is the relative conversion rate difference between the test groups
"""
try:
the_uplift = (
cal_conversion_rate(B_total, B_converted)
- cal_conversion_rate(A_total, A_converted)
) / cal_conversion_rate(A_total, A_converted)
except Exception as ee:
raise Exception(ee)
return the_uplift
cal_difference_standard_error(A_total, B_total, A_converted, B_converted)
¤
Standard error of the difference for the AB test
Source code in dietbox/abtest/stats_util.py
def cal_difference_standard_error(A_total, B_total, A_converted, B_converted):
"""Standard error of the difference for the AB test"""
A_se = cal_standard_error(A_total, A_converted)
B_se = cal_standard_error(B_total, B_converted)
return np.sqrt(A_se ** 2 + B_se ** 2)
cal_p_value(data, test=None)
¤
p-value for the ratio test
Source code in dietbox/abtest/stats_util.py
def cal_p_value(data, test=None):
"""p-value for the ratio test"""
if test is None:
test = "binom"
if test == "binom":
A_total, B_total, A_converted, B_converted = (
data.get("A_total"),
data.get("B_total"),
data.get("A_converted"),
data.get("B_converted"),
)
if data.get("A_cr") and data.get("B_cr"):
p_A = data.get("A_cr")
p_B = data.get("B_cr")
else:
p_A = cal_conversion_rate(A_total, A_converted)
p_B = cal_conversion_rate(B_total, B_converted)
return scs.binom_test(B_converted, n=B_total, p=p_A, alternative="greater")
# return scs.binom(A_total, p_A).sf(B_total * p_B)
elif test == "mannwhitney":
A_data, B_data = data.get("A_series"), data.get("B_series")
_, test_mannwhitney_p_value = scs.mannwhitneyu(x=A_data, y=B_data)
return test_mannwhitney_p_value
cal_pooled_probability(A_total, B_total, A_converted, B_converted)
¤
Pooled probability for two samples
This is better used as an intermediate value.
Source code in dietbox/abtest/stats_util.py
def cal_pooled_probability(A_total, B_total, A_converted, B_converted):
"""Pooled probability for two samples
This is better used as an intermediate value.
"""
probability = (A_converted + B_converted) / (A_total + B_total)
return probability
cal_pooled_std_err(pooled_prob_inp, A_total, B_total)
¤
Pooled standard error for two samples
For more information about the definition, refer to wikipedia: https://en.wikipedia.org/wiki/Pooled_variance
Source code in dietbox/abtest/stats_util.py
def cal_pooled_std_err(pooled_prob_inp, A_total, B_total):
"""Pooled standard error for two samples
For more information about the definition, refer to wikipedia:
https://en.wikipedia.org/wiki/Pooled_variance
"""
# Pooled standard error
pp = pooled_prob_inp
pld_std_err = np.sqrt(pp * (1 - pp) * (1 / A_total + 1 / B_total))
return pld_std_err
cal_standard_error(X_total, X_converted)
¤
standard error
Source code in dietbox/abtest/stats_util.py
def cal_standard_error(X_total, X_converted):
"""standard error"""
X_cr = cal_conversion_rate(X_total, X_converted)
return np.sqrt(X_cr * (1 - X_cr) / X_total)
cal_z_score(significance_level=None, two_tailed=None)
¤
z score
z score requires at least one parameter which is the significance level. By default, significance level for this function is assumed to be 0.05.
It is better that the user read this Nature article before using this function: https://www.nature.com/articles/d41586-019-00857-9
Source code in dietbox/abtest/stats_util.py
def cal_z_score(significance_level=None, two_tailed=None):
"""z score
z score requires at least one parameter which is the significance level.
By default, significance level for this function is assumed to be 0.05.
It is better that the user read this Nature article before using this function:
https://www.nature.com/articles/d41586-019-00857-9
"""
if significance_level is None:
significance_level = 0.05
if two_tailed is None:
two_tailed = True
## construct a z distribution
z_distribution = scs.norm()
if two_tailed:
significance_level = significance_level / 2
no_rejection_level = 1 - significance_level
else:
no_rejection_level = 1 - significance_level
## generate z distribution
z_score = z_distribution.ppf(no_rejection_level)
return z_score