import numpy as np
from scipy.stats import norm
def estimate_effect_size(n, sd, alpha=0.05, power=0.8):
"""
Расчет ожидаемой разницы в средних для заданного размера выборки
:param n: размер выборки
:param sd: стандартное отклонение
:return: ожидаемая разница в средних
"""
M = (norm.ppf(q=1-alpha/2) + norm.ppf(q=power))**2
return math.sqrt(2 * M * sd**2 / n) / sd
n = 1004
sd = 8
estimate_effect_size(sd, n)
>> 0.12504086769660908def estimate_sample_size(effect_size, sd, alpha=0.05, power=0.8):
"""
Расчет N для баланса 50/50
:param effect_size: стандартизированный ожидаемый эффект (mean * lift / sd)
:param sd: ско одной группы
:return: N
"""
M = (norm.ppf(q=1-alpha/2) + norm.ppf(q=power))**2
return 2 * M * sd**2 / (effect_size * sd) **2
mean = 100
lift = 0.01
sd = 8
effect_size = mean * lift / sd
estimate_sample_size(effect_size, sd)
>> 1004.6566059966833from statsmodels.stats.power import tt_ind_solve_power
n = 500
sd = 1
effect_size = 0.17718780696593192
print(f"""
estimate_sample_size: {estimate_sample_size(effect_size, sd)}
estimate_effect_size: {estimate_effect_size(n, sd)}
tt_ind_solve_power nobs: {tt_ind_solve_power(effect_size=effect_size, alpha=alpha, power=power, ratio=1)}
tt_ind_solve_power effect_size: {tt_ind_solve_power(nobs1=n, alpha=alpha, power=power, ratio=1)}
""")
# Out:
# >> estimate_sample_size: 1004.0
# >> estimate_effect_size: 0.125
# >> tt_ind_solve_power nobs: 1004.9588076137142
# >> tt_ind_solve_power effect_size: 0.12505866628063328import numpy as np
from scipy.stats import ttest_ind_from_stats
# num – числитель, den – знаменатель
num_1 = np.array([1,2,3,4,5])
den_1 = np.array([2,3,4,5,6])
num_2 = np.array([1,2,3,8,5])
den_2 = np.array([2,9,4,5,6])
def deltamethod(x, y, independent = False, bc = False):
n = len(x)
mux = np.mean(x)
muy = np.mean(y)
v11 = np.var(y,ddof=1)
v22 = np.var(x,ddof=1)
if independent == True:
v12 = 0
else:
v12 = np.cov(x,y)[0][1]
rto = muy / mux
est = rto - 1
if bc == True:
est = est +muy/mux**3*v22/n - 1/mux**2*v12/n
sdest = (v11 / mux**2) - (2 * muy / mux**3 * v12) + (muy**2 / mux**4 * v22)
sdest = np.sqrt(sdest)
return est, sdest
# Считаем оценку ratio и дисперсий
rto_1, sdest_1 = deltamethod(num_1,den_1,independent=True,bc=True)
rto_2, sdest_2 = deltamethod(num_2,den_2,independent=True,bc=True)
# Для effect_size
lift = 0.1
effect_size = rto_1 * lift / sdest_1
# Дополнительно: демонстрация использования в раcчете t-теста
ttest_ind_from_stats(
mean1 = rto_1,
std1 = sdest_1,
nobs1 = 5,
mean2 = rto_2,
std2 = sdest_2,
nobs2 = 5,
equal_var = False)
# >Ttest_indResult(statistic=-0.16000188053475753, pvalue=0.877216384822124)from statsmodels.stats.power import tt_ind_solve_power
mean = 0.9 # рассчитанное среднее за 2-5 недель
sd = 0.2 # рассчитанное стандартное отклонение за тот же период
power = 0.8
alpha = 0.05
lift = 0.1 # хотим увидеть 10% изменение в метрику
effect_size = mean * lift / sd # 0.45
# Результат – наблюдения на одну выборку (соотвественно, умножаем на 2)
print(
tt_ind_solve_power(effect_size=effect_size, alpha=alpha, power=power, nobs1=None, ratio=1)
)
# > 78.49161726401from statsmodels.stats.power import tt_ind_solve_power
nobs = 78.49161726401 # возьмем то же самое число, что в прошлом примере
power = 0.8
alpha = 0.05
# Результат – допустимый минимальный effect_size
print(tt_ind_solve_power(alpha=alpha, power=power, nobs1=nobs, ratio=1))
# > 0.45