AB试验(六)A/B实验常见知识点的Python计算

AB试验(六)A/B实验常见知识点的Python计算

前面理论知识上提到了很多的知识点需要计算,作为一个实用主义的博主,怎么可以忍受空谈呢?所以本期就给大家分享如何利用Python对这些知识点进行计算。

均值类指标

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy import stats
import math
import seaborn as sns
from statsmodels.stats import weightstats as ws
from statsmodels.stats.proportion import proportion_effectsize
from statsmodels.stats.power import zt_ind_solve_power
from statsmodels.stats.power import tt_ind_solve_power
from statsmodels.stats.proportion import proportions_ztest
from statsmodels.stats.proportion import confint_proportions_2indep
from statsmodels.stats.multitest import multipletests
import pingouin as pg
from statsmodels.stats.proportion import proportion_effectsize# 绘图初始化
%matplotlib inline
sns.set(style="ticks")
plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号

构造历史样本

# 构造数据
np.random.seed(0)
x=np.random.normal(85, 23, 1000)
plt.hist(x, 30, density=True)
plt.show()

output_2_0

指标波动置信区间

  • 均值类指标波动置信区间=样本均值 $ \pm $z分数*标准误差(SE)
  • S E = s 2 n = ∑ ( x i − x ˉ ) 2 n ( n − 1 ) SE=\sqrt\frac{s^2}{n}=\sqrt{\frac{\sum(x_i-\bar{x})^{2}}{n(n-1)}} SE=ns2 =n(n1)(xixˉ)2
# 计算均值类指标波动置信区间
def numbers_cal_ci(x, alpha=0.05):'''x:均值类样本alpha:显著性水平return:样本波动的置信区间'''se=np.std(x, ddof = 1)/np.sqrt(x.size) # 标准误z=stats.norm.ppf(1-alpha/2) # z值l=np.mean(x)-z*ser=np.mean(x)+z*sereturn [l, r]numbers_cal_ci(x)
[82.55134770619344, 85.36684374925758]

样本量估算

  • 样本量:$ n=\frac{(Z_{1-{\frac{\alpha}{2}}}+Z_{1-\beta})2}{(\frac{\delta}{\sigma_{pooled}})2}=\frac{(Z_{1-{\frac{\alpha}{2}}}+Z_{power})2}{(\frac{\delta}{\sigma_{pooled}})2} $
  • $ \delta = \mu_{test} - \mu_{control}$
  • $ \sigma_{pooled}^2 = 2S^2 = \frac{2*\sum_{i}^{n}(x_i - \bar x)^2}{n-1} $ (以样本方差代替综合方差)
# 公式法计算样本量
def numbers_cal_sample(u1, u2, s, alpha=0.05, beta=0.2):'''u1:对照组均值u2:实验组均值s:样本标准差(历史数据计算得出)alpha:显著性水平,默认为0.05beta:默认为0.2return:均值类指标样本量'''z1=stats.norm.ppf(1-alpha/2)z2=stats.norm.ppf(1-beta)delta=u2-u1sigma2=2*s**2n=(z1+z2)**2/(delta**2/sigma2)return math.ceil(n)s=np.std(x, ddof = 1)
u1=85
u2=86print(numbers_cal_sample(u1,u2,s))# 第三方包计算样本量
def numbers_cal_sample_third(u1, u2, s, alpha=0.05, beta=0.2, ratio=1, alternative="two-sided"):'''u1:对照组均值u2:实验组均值s:样本标准差(历史数据计算得出)alpha:显著性水平,默认为0.05beta:默认为0.2ratio:对照组/实验组的比例alternative:检验方式[two-sided(default), larger, smaller]return:均值类指标样本量'''effect_size=(u2-u1)/sn=tt_ind_solve_power(effect_size=effect_size,alpha=alpha,power=1-beta,ratio=ratio,alternative=alternative)return math.ceil(n)print(numbers_cal_sample_third(u1,u2,s))
8099
8100

构造A/B实验样本

# 构造实验数据
np.random.seed(1)
control=np.random.normal(85.1, 23, 8108)
test=np.random.normal(86.01, 22.8, 8110)
sns.kdeplot(test, color='g', linestyle='-', linewidth=1.2, alpha=0.5)
sns.kdeplot(control, color='r', linestyle='--', linewidth=1.2, alpha=0.5)
plt.show()

output_7_0

样本比例校验

  • 样本比例服从二项分布
  • 概率类指标置信区间=样本均值 $ \pm $z分数*标准误差(SE)
  • S E = p ( 1 − p ) n SE=\sqrt{\frac{{p}(1-{p})}{n}} SE=np(1p)
# 样本比例校验
def two_sample_proportion_test(n1, n2, p1, p2, alpha=0.05):'''n1:对照组实际样本量n2:实验组实际样本量p1:对照组预设样本比例p2:实验组预设样本比例alpha:显著性水平return:(对照组样本比例置信区间,实验组样本比例置信区间)'''se=np.sqrt((p1*p2)/(n1+n2))z=stats.norm.ppf(1-alpha/2)# 计算两组样本比例置信区间control_prob=[p1-z*se, p1+z*se]test_prob=[p2-z*se, p2+z*se]# 计算实际两组样本比例control_prob_real=n1/(n1+n2)test_prob_real=n2/(n1+n2)# 判断样本比例是否在置信区间内if control_prob_real>=control_prob[0] and control_prob_real<=control_prob[1]:control_flag=1else:control_flag=0if test_prob_real>=test_prob[0] and test_prob_real<=test_prob[1]:test_flag=1else:test_flag=0# 判断是否检验通过if control_flag==1&test_flag==1:print('两样本比例校验: 通过')else:print('两样本比例校验不通过')print(f'对照组样本比例置信区间:{control_prob}', f'对照组实际样本比例:{control_prob_real}')print(f'实验组样本比例置信区间:{test_prob}', f'实验组实际样本比例:{test_prob_real}')n1=control.size
n2=test.size
p1=p2=0.5
two_sample_proportion_test(n1, n2, p1, p2)
两样本比例校验: 通过

特征一致性校验

详见下面的特征分布一致性校验

计算效应量

# 公式法计算效应量
def numbers_cal_es(x, y):'''x:对照组样本y:实验组样本return: cohen d方法计算的效应量es'''n1, n2 = len(x), len(y)s1, s2 = np.var(x, ddof=1), np.var(y, ddof=1)s = math.sqrt(((n1 - 1) * s1 + (n2 - 1) * s2) / (n1 + n2 - 2))u1, u2 = np.mean(x), np.mean(y)return (u1 - u2) / s# 第三方包计算效应量
def numbers_cal_es_third(x, y):'''x:对照组样本y:实验组样本return: cohen d方法计算的效应量es'''return pg.compute_effsize(x, y, eftype='cohen')# 输入参数为统计量
def numbers_cal_es_stats(u1, s1, n1, u2, s2, n2):'''n1:对照组样本量n2:实验组样本量s1:对照组样本方差s2:实验组样本方差u1:对照组均值u2:实验组均值return: cohen d方法计算的效应量es'''s = math.sqrt(((n1 - 1) * s1 + (n2 - 1) * s2) / (n1 + n2 - 2))return (u1 - u2) / s
numbers_cal_es_third(test, control)
0.04827141870721603
  1. 样本大小、显著性水平、功效、效应值,给定任意三个值,可以推算出第四个值。这里的 e s = δ σ p o o l e d es=\frac{\delta}{\sigma_{pooled}} es=σpooledδ
  2. 在A/B/n实验中,通过比较不同实验组与对照组的效应值大小选择最优实验组。

显著性检验

# 均值类指标检验,输出t统计量,p值,差值的置信区间
def numbers_cal_significant(x, y, alpha=0.05, alternative='two-sided', levene_print=True):'''x:实验组样本y:对照组样本alpha:显著性水平return:t统计量,p值,差异值的置信区间'''levene_p=stats.levene(x, y)[1]if levene_p>alpha:equal_var=Trueusevar='pooled'if levene_print:print('方差齐性校验结果:方差相同')else:equal_var=Falseusevar='unequal'if levene_print:print('方差齐性校验结果:方差不同')# 计算检验统计量及p值t, p = stats.ttest_ind(x, y, alternative=alternative, equal_var=equal_var)# 计算置信区间des1=ws.DescrStatsW(x)des2=ws.DescrStatsW(y)comp=ws.CompareMeans(des1, des2)l, r=comp.tconfint_diff(alpha=alpha, alternative=alternative, usevar=usevar)return (t, p , [l, r])# 输入参数为统计量 忽略方差齐性检验(认为方差未知但相同)
def numbers_cal_significant_stats(u1, s1, n1, u2, s2, n2, alpha=0.05):'''n1:对照组样本量n2:实验组样本量s1:对照组样本方差s2:实验组样本方差u1:对照组均值u2:实验组均值'''# 计算检验统计量及p值std1, std2 = math.sqrt(s1), math.sqrt(s2)t, p = stats.ttest_ind_from_stats(u1, std1, n1, u2, std2, n2)# 计算置信区间mu_diff = u1-u2df = n1+n2-2sw = np.sqrt(((n1-1)*s1+(n2-1)*s2)/df)d = sw*np.sqrt(1/n1+1/n2)tl, tr = stats.t.interval(1-alpha, df)l, r = mu_diff+tl*d, mu_diff+tr*d return (t, p , [l, r])print(numbers_cal_significant(test, control))u1, u2 = np.mean(test), np.mean(control)
n1, n2 = len(test), len(control)
s1, s2 = np.var(test, ddof=1), np.var(control, ddof=1)
print(numbers_cal_significant_stats(u1, s1, n1, u2, s2, n2))
方差齐性校验结果:方差相同
(3.0736804306795347, 0.0021178678616854507, [0.40032026505442575, 1.8096106890034274])
(3.0736804306795347, 0.0021178678616854507, [0.4003202650543972, 1.809610689003399])

在日常业务中,考虑到样本数据量过大,一般会直接计算好样本统计量。

概率类指标

构造历史样本

# 构造数据
x=stats.bernoulli.rvs(0.4, size=10000, random_state=0)
plt.hist(x, 30, density=True)
plt.show()

output_12_0

指标波动置信区间

  • 概率类指标置信区间=样本均值 $ \pm $z分数*标准误差(SE)
  • S E = p ( 1 − p ) n SE=\sqrt{\frac{{p}(1-{p})}{n}} SE=np(1p)
# 计算概率类指标置信区间
def prob_cal_ci(p, n, alpha=0.05):'''p1:样本概率alpha:显著性水平return:样本波动的置信区间'''se=np.sqrt((p*(1-p))/n)z=stats.norm.ppf(1-alpha/2)l=p-z*ser=p+z*sereturn [l, r]p=np.mean(x)
n=x.size
prob_cal_ci(p, n)
[0.38462208374405027, 0.4037779162559497]

样本量估算

  • 样本量:$ n=\frac{(Z_{1-{\frac{\alpha}{2}}}+Z_{1-\beta})2}{(\frac{\delta}{\sigma_{pooled}})2}=\frac{(Z_{1-{\frac{\alpha}{2}}}+Z_{power})2}{(\frac{\delta}{\sigma_{pooled}})2} $
  • $ \delta = \mu_{test} - \mu_{control}$
  • $ \sigma_{pooled}^2=p_{test}(1-p_{test})+p_{control}(1-p_{control}) $
  • $ p_{test}=p_{control}+\delta $
# 公式法计算样本量
def prob_cal_sample(p1, p2, alpha=0.05, beta=0.2):'''p1:对照组均值p2:实验组均值alpha:显著性水平,默认为0.05beta:默认为0.2return:概率类指标样本量'''z1=stats.norm.ppf(1-alpha/2)z2=stats.norm.ppf(1-beta)delta=p2-p1sigma2=p2*(1-p2)+p1*(1-p1)n=(z1+z2)**2/(delta**2/sigma2)return math.ceil(n)# 第三方包计算样本量
def prob_cal_sample_third(p1, p2, alpha=0.05, beta=0.2, ratio=1, alternative="two-sided"):'''p1:对照组均值p2:实验组均值alpha:显著性水平,默认为0.05beta:默认为0.2ratio:对照组/实验组的比例alternative:检验方式[two-sided(default), larger, smaller]return:均值类指标样本量'''effect_size=proportion_effectsize(prop1=p1, prop2=p2)n=zt_ind_solve_power(effect_size=effect_size,alpha=alpha,power=1-beta,ratio=ratio,alternative=alternative)return math.ceil(n)p1=0.39
p2=0.42print(prob_cal_sample(p1, p2))
print(prob_cal_sample_third(p1, p2))
4200
4202

构造A/B实验样本

# 构造实验数据
control=stats.bernoulli.rvs(0.401, size=4203, random_state=1)
test=stats.bernoulli.rvs(0.433, size=4206, random_state=1)
plt.hist(control, 30, color='g', linestyle='-')
plt.hist(test+0.03, 30, color='r', linestyle='--')
plt.show()

out17

样本比例校验

# 样本比例校验
n1=control.size
n2=test.size
p1=p2=0.5
two_sample_proportion_test(n1, n2, p1, p2)
两样本比例校验: 通过

特征一致性校验

详见下面的特征分布一致性校验

计算效应量

# 公式法计算效应量
def prob_cal_es(p1, p2):return 2*(np.arcsin(math.sqrt(p1)) - np.arcsin(math.sqrt(p2)))# 第三方包计算效应量
def prob_cal_es_third(p1, p2):return proportion_effectsize(p1, p2)p1, p2 = np.mean(test), np.mean(control)
prob_cal_es_third(p1, p2)
0.06779547842527989

显著性检验

# 概率类指标检验,输出z统计量,p值,差值的置信区间# 手动计算两比例样本置信区间
def two_proprotions_confint(count1, nobs1, count2, nobs2, alpha=0.05):'''count1:转化次数1nobs1:观察次数1count2:转化次数2nobs2:观察次数2alpha:显著性水平return:置信区间'''prop_x=count1/nobs1prop_y=count2/nobs2s2=prop_x*(1-prop_x)/nobs1+prop_y*(1-prop_y)/nobs2se=np.sqrt(s2)# 计算zconfidence=1-alphaz=stats.norm(loc=0, scale=1).ppf(confidence+alpha/2)# 计算置信区间prop_diff = prop_x - prop_yl=prop_diff-z*ser=prop_diff+z*sereturn (l, r)def prob_cal_significant(count1, nobs1, count2, nobs2, alpha=0.05):'''count1:转化次数1nobs1:观察次数1count2:转化次数2nobs2:观察次数2alpha:显著性水平return:z统计量,p值,差异值的置信区间'''counts=np.array([count1,count2])nobs=np.array([nobs1,nobs2])# 计算检验统计量及p值z, p=proportions_ztest(counts, nobs)
#     # 计算置信区间
#     l,r=two_proprotions_confint(count1, nobs1, count2, nobs2, alpha=alpha)# 第三方包计算l,r=confint_proportions_2indep(count1, nobs1, count2, nobs2, alpha=alpha)return (z, p , [l, r])count1=test.sum()
nobs1=test.size
count2=control.sum()
nobs2=control.sizeprob_cal_significant(count1, nobs1, count2, nobs2)
(3.1077966572498585,0.0018848770736162972,[0.012362934368799147, 0.054539748981100436])

Bootstrapping

指标波动置信区间

  • 多次重复抽样得到样本均值的分布
  • 通过经验法(百分位法),即按样本均值大小排序剔除前后2.5%的区间作为置信区间
  • 同样本多次AA实验计算置信区间类似,实践中更多应用Bootstrapping法,故不做详述
# 构造单样本数据
np.random.seed(0)
bt=np.random.randint(10, 5000, 10000)
plt.hist(bt, 30, density=True)
plt.show()

output_22_0

# 定义单次抽样函数
def bootstrap_one(data, leng=100, func=np.mean):'''data:数据leng:抽样长度,大于0的数,当长度为(0,1)间的浮点数时,定义为抽样比例。func:计算函数,默认为均值return:一次抽样的函数计算结果'''leng=int(leng*len(data)) if leng<1 else int(leng)bs_sample=np.random.choice(data, leng)return func(bs_sample)
# bootstrap计算指标波动置信区间
def bootstrap_cal_ci(data, leng=100, func=np.mean, scale=100, alpha=0.05):'''data:数据leng:抽样长度,大于0的数,当长度为(0,1)间的浮点数时,定义为抽样比例。func:计算函数,默认为均值scale:抽样次数alpha:显著性水平return:置信区间和标准误'''bs_result=np.empty(scale)for i in range(scale):np.random.seed(i)bs_result[i]=bootstrap_one(data)l,r=np.percentile(bs_result, [alpha/2*100, (1-alpha/2)*100])se=np.std(bs_result) # 输出多次抽样均值的标准差,即抽样均值标准误return ([l,r], se)bootstrap_cal_ci(bt, scale=10000)
([2223.547, 2792.2925], 144.78451383376327)
# 观察标准误 随着抽样次数的增多逐渐趋于稳定x=[]
se=[]
for i in range(100, 10000, 100):x.append(i)se.append(bootstrap_cal_ci(bt, scale=i)[1])plt.plot(x, se, 'r-')plt.title('标准误差随抽样次数的变化趋势')
plt.xlabel('scale')
plt.ylabel('se')
plt.show()

output_25_0

指标差值置信区间

# 构造两样本数据
np.random.seed(1)
test=np.random.randint(10, 200, 9997)
control=np.random.randint(30, 120, 10003)plt.subplot(1, 2, 1)
plt.hist(test, 30, density=True)
plt.subplot(1, 2, 2)
plt.hist(control, 30, density=True)
plt.tight_layout()
plt.show()

output_26_0

# bootstrap计算样本差值置信区间
def bootstrap_cal_ci_2samples(x, y, leng=100, func=np.mean, scale=100, alpha=0.05):'''x:数据1y:数据2leng:抽样长度,大于0的数,当长度为(0,1)间的浮点数时,定义为抽样比例。func:计算函数,默认为均值scale:抽样次数alpha:显著性水平return:置信区间'''bs_diff=np.empty(scale)for i in range(scale):np.random.seed(i)bs_diff[i]=bootstrap_one(x)-bootstrap_one(y)# 计算差异的置信区间l,r=np.percentile(bs_diff, [alpha/2*100, (1-alpha/2)*100])return [l,r]bootstrap_cal_ci_2samples(test, control, scale=10000)
[18.939500000000013, 42.65124999999999]

多重检验问题校正

def multiple_tests_adjust(pvalues, method='fdr_bh', alpha=0.05):'''pvalues:p值序列method:校正方式,默认为bh法alpha:需比较的显著性水平,默认为0.05return:(拒绝H0的结果序列, 校正后的p值序列, Bonferroni校正的alpha)'''result=multipletests(pvalues, method=method, alpha=alpha)reject=result[0]adjust_p=result[1]adjust_alpha=result[3] # Bonferroni方法return reject,adjust_p,adjust_alphapvalues=[0.02, 0.03, 0.18, 0.04, 0.002, 0.005]
multiple_tests_adjust(pvalues)
(array([ True,  True, False,  True,  True,  True]),array([0.04 , 0.045, 0.18 , 0.048, 0.012, 0.015]),0.008333333333333333)

特征分布一致性校验

实践中往往需要保证不同样本特征分布是相似的,例如A/B中的实验组和对照组、机器学习的训练集和测试集等

数据准备

from faker import Faker
from faker.providers import BaseProvider, internet 
from random import randint
from scipy.stats import bernoulli
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split 
from scipy import stats
from scipy.stats import kstest
from scipy.stats import ks_2samp
from collections import defaultdict
import toad 
import matplotlib.pyplot as plt
import seaborn as sns# 绘图初始化
%matplotlib inline
sns.set(style="ticks")
plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
# 自定义数据
fake = Faker('zh_CN')
class MyProvider(BaseProvider):def myCityLevel(self):cl = ["一线", "二线", "三线", "四线+"]return cl[randint(0, len(cl) - 1)]def myGender(self):g = ['F', 'M']return g[randint(0, len(g) - 1)]def myDevice(self):d = ['Ios', 'Android']return d[randint(0, len(d) - 1)]
fake.add_provider(MyProvider)# 构造假数据
uid=[]
cityLevel=[]
gender=[]
device=[]
age=[]
activeDays=[]
for i in range(100000):uid.append(i+1)cityLevel.append(fake.myCityLevel())gender.append(fake.myGender())device.append(fake.myDevice())age.append(fake.random_int(min=18, max=65))activeDays.append(fake.random_int(min=0, max=180))raw_data= pd.DataFrame({'uid':uid,'cityLevel':cityLevel,'gender':gender,'device':device,'age':age,'activeDays':activeDays,})raw_data.head()

image-20230206155137711

模拟A/B实验分流

# 数据随机切分,模拟实验分流
test, control= train_test_split(raw_data.copy(), test_size=.5, random_state=0)
test['flag'] = 'test'
control['flag'] = 'control'
df = pd.concat([test, control])
df.head()

image-20230206155153927

数据探索

# 查看离散变量的分布
fig, ax =plt.subplots(1, 3, constrained_layout=True, figsize=(12, 3))
for i, x in enumerate(['cityLevel', 'gender', 'device']):sns.countplot(x=x, data=df, hue='flag', ax=ax[i])
plt.show()# 查看连续变量的分布
fig, ax =plt.subplots(1, 2, constrained_layout=True, figsize=(12, 3))
for i, x in enumerate(['age', 'activeDays']):sns.histplot(x=x, data=df, hue='flag', ax=ax[i])
plt.show()

output_4_0

output_4_1

  • 构造数据是随机的,因此在不同维度上表现的基本都是取值概率相等。这里目的是验证特征分布的一致性,暂不考虑实际业务场景
  • 整体上看实验、对照组在各特征上的分布接近

样本相似性校验

卡方检验

  • 通过列联表检验不同离散变量对分组是否有影响
  • 将连续变量分箱为离散变量
# 连续变量离散化
df['age_bins'] = pd.qcut(df['age'], q=5, labels=range(5))
df['activeDays_bins'] = pd.cut(df['activeDays'], bins=[-1, 20, 120, 180], labels=['低', '中', '高'])for x in ['cityLevel', 'gender', 'device', 'age_bins', 'activeDays_bins']:df_ = pd.pivot_table(df, values = ['uid'], index = [x], columns='flag', aggfunc = 'count')chi2, p, _, _, = stats.chi2_contingency(df_)print(f'{x}:','不相似' if p<0.05 else '相似')
cityLevel: 相似
gender: 相似
device: 相似
age_bins: 相似
activeDays_bins: 不相似

KS检验

  • 检验一个分布f(x)与理论分布g(x)是否一致
  • 两个观测值分布是否有显著差异的检验方法
# 验证样本是否符合正态分布
# P值远小于显著性水平,拒绝原假设,即不服从正态分布
kstest(df['age'], 'norm')
KstestResult(statistic=1.0, pvalue=0.0)
# 回归正题,验证两样本分布是否一致
for x in ['cityLevel', 'gender', 'device', 'age', 'activeDays']:_, p = ks_2samp(test[x], control[x])print(f'{x}:','不相似' if p<0.05 else '相似')
cityLevel: 相似
gender: 相似
device: 相似
age: 相似
activeDays: 不相似

相对熵

  • KL散度是用于衡量分布P相对于分布Q的差异性。二者越相似,KL散度越小。
  • 同kl散度,JS散度的取值范围在0-1之间,完全相同时为0。
def JS_divergence(p,q):M=(p+q)/2return 0.5*stats.entropy(p, M, base=2)+0.5*stats.entropy(q, M, base=2)
# 连续变量离散化
df['age_bins'] = pd.qcut(df['age'], q=5, labels=range(5))
df['activeDays_bins'] = pd.cut(df['activeDays'], bins=[-1, 20, 120, 180], labels=['低', '中', '高'])for x in ['cityLevel', 'gender', 'device', 'age_bins', 'activeDays_bins']:a=df[df['flag']=='test'][x].value_counts()/df[df['flag']=='test'].sizeb=df[df['flag']=='control'][x].value_counts()/df[df['flag']=='control'].sizeprint(f'{x} KL:', round(stats.entropy(a, b),4), '/', 'JS: ', round(JS_divergence(a, b),4))
cityLevel KL: 0.0 / JS:  0.0001
gender KL: 0.0 / JS:  0.0
device KL: 0.0 / JS:  0.0
age_bins KL: 0.0 / JS:  0.0
activeDays_bins KL: 0.0001 / JS:  0.0

PSI

  • PSI反映了验证样本在各分数段的分布与建模样本分布的稳定性。在建模中,我们常用来筛选特征变量、评估模型稳定性
  • 可以用来衡量两个分布的差异有多大,当两个随机分布完全一样时,PSI = 0;反之,差异越大,PSI越大。类似于相对熵
  • 一半psi<0.02为稳定
toad.metrics.PSI(test.iloc[:,1:-1], control.iloc[:,1:-1])
cityLevel     0.000075
gender        0.000054
device        0.000003
age           0.001624
activeDays    0.007159
dtype: float64

总结

本文基本涵盖了A/B实验过程中的所有计算,因此将这些函数全部保存在ABTestFunc.py中,即可构造属于自己的AB测试模块~

共勉~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/115647.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一起学数据结构(11)——快速排序及其优化

上篇文章中&#xff0c;解释了插入排序、希尔排序、冒泡排序、堆排序及选择排序的原理及具体代码实现本片文章将针对快速排序&#xff0c;快速排序的几种优化方法、快速排序的非递归进行解释。 目录 1. 快速排序原理解析以及代码实现&#xff1a; 2. 如何保证相遇位置的值一…

嵌入式硬件库的基本操作方式与分析

本次要介绍的开源软件是 c-periphery&#xff1a; https://github.com/vsergeev/c-periphery一个用 C 语言编写的硬件外设访问库。 我们可以用它来读写 Serial、SPI、I2C 等&#xff0c;非常适合在嵌入式产品上使用。 我们可以基于它优秀的代码框架&#xff0c;不断地扩展出更…

Prometheus接入AlterManager配置邮件告警(基于K8S环境部署)

文章目录 一、配置AlterManager告警发送至邮箱二、Prometheus接入AlterManager配置三、部署PrometheusAlterManager(放到一个Pod中)四、测试告警 注意&#xff1a;请基于 PrometheusGrafana监控K8S集群(基于K8S环境部署)文章之上做本次实验。 一、配置AlterManager告警发送至邮…

C++——特殊类设计

目录 一.不能被拷贝的类 1.C98做法 2.C11做法 二.只能在堆上实例化的类 1.实现方式一 2.实现方式二 三.只能在栈上创建的对象 四.不能被继承的类 1.C98方式 2.C11方式 五.只能创建一个对象的类 1.设计模式 2.单例模式 一.不能被拷贝的类 拷贝只会放在两个场景中&a…

visual studio Qt 开发环境中手动添加 Q_OBJECT 导致编译时出错的问题

问题简述 创建项目的时候&#xff0c;已经添加了类文件&#xff0c;前期认为不需要信号槽&#xff0c;就没有添加宏Q_OBJECT,后面项目需要&#xff0c;又加入了宏Q_OBJECT&#xff0c;但是发现只是添加了一个宏Q_OBJECT&#xff0c;除此之外没有改动其它的代码&#xff0c;原本…

基于springboot实现地方废物回收机构平台管理系统【项目源码+论文说明】

基于springboot实现地方废物回收机构管理系统演示 摘要 网络的广泛应用给生活带来了十分的便利。所以把地方废物回收机构管理与现在网络相结合&#xff0c;利用java技术建设地方废物回收机构管理系统&#xff0c;实现地方废物回收机构的信息化。则对于进一步提高地方废物回收机…

如何提高广告投放转化率?Share Creators 资产库与Appsflyer营销数据的全面结合

如何提高广告投放转化率&#xff1f;Share Creators 资产库与Appsflyer营销数据的全面结合 全球经济进入了低迷期。 营销成本越来越高&#xff0c; 营销需要更务实&#xff0c;注重投入产出比。众所周知&#xff0c;除了渠道、客群画像以外&#xff0c; 优秀的广告设计图&#…

c进阶测试题

选择题 1.请问该程序的输出是多少&#xff08;C&#xff09; #include<stdio.h> int main(){unsigned char i 7;int j 0;for(;i > 0;i - 3){ j;} printf("%d\n", j);return 0; }A. 2 B. 死循环 C. 173 D. 172 首先unsigned char型是不会为负数&#xff…

flask入门(四)前后端数据传输

文章目录 1、flask后端接收来自前端的数据1&#xff09;如果前端提交的方法为POST2&#xff09;如果前段提交的方法是GET 2、flask后端向前端传数据3、案例参考文献 1、flask后端接收来自前端的数据 1&#xff09;如果前端提交的方法为POST 后端接收时的代码&#xff1a; xx…

我试图扯掉这条 SQL 的底裤。只能扯一点点,不能扯多了

之前不是写分页嘛,分页肯定就要说到 limit 关键字嘛。 然后我啪的一下扔了一个链接出来: https://dev.mysql.com/doc/refman/8.0/en/limit-optimization.html 这个链接就是 MySQL 官方文档,这一章节叫做“对 Limit 查询的优化”,针对 limit 和 order by 组合的场景进行了较…

【MySQL】存储引擎

存储引擎 查看存储引擎设置表的存储引擎创建表时指定存储引擎修改表的存储引擎 引擎介绍InnoDB引擎: 具备外键支持的十五存储引擎MyISAM引擎: 主要的非事务处理存储引擎Archive引擎: 用于数据存档Blackhole引擎: 丢弃写操作,读操作返回空内容CSV引擎: 读取数据时,以逗号分隔各个…

redis底层数据结构

总所周知&#xff0c;redis支持五种数据类型String、Hash、List、Set、ZSet。在支持这些复杂数据结构的同时&#xff0c;redis不仅需要保证读写的性能&#xff0c;还能提供各种微操作&#xff0c;比如直接修改Hash字典中的某个field的值&#xff0c;或者直接往ZSet中插入某个值…

Failed to start The nginx HTTP and reverse proxy server.

本章教程主要分享一下&#xff0c;当nginx 启动时&#xff0c;遇到报这个错误时的一个解决问题思路。 目录 1、观察报错信息 2、尝试性解决 1、观察报错信息 根据日志的信息&#xff0c;我们至少可以知道2个比较信息。 1、操作用户执行命令是在非root权限下进行操作的。 2、Ad…

Xcode14创建github远程仓库Token

1.点击Create a Token on GitHub 2.在打开的网页中,登陆GitHub 3.点击生成Token 这是不能为空 4.Token创建成功如下: 5.复制Token到Xcode然后点击Sign In登陆 正在创建远程我仓库 正在将本地仓库代码推入远程仓库 创建成功

C++项目——云备份-②-第三方库认识

文章目录 专栏导读1. json 认识1.1 JSON 数据结构的特点 2. jsoncpp库认识3. json实现序列化案例4. json实现反序列化案例5. bundle文件压缩库认识6. bundle库实现文件压缩案例7.bundle库实现文件解压缩案例8.httplib库认识9. httplib库搭建简单服务器案例10. httplib库搭建简单…

YOLO目标检测——密集人群人头检测数据集【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;在公共场所&#xff0c;如车站、商场、景区等&#xff0c;可以通过人头目标检测技术来监测人群流量数据集说明&#xff1a;人头检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富标签说明&#xff1a;使用lableimg标注软件标注…

python实现excel的数据提取

一文带你实现excel表格的数据提取 今天记录一下如何使用python提取Excel中符合特定条件的数据 在数据处理和分析的过程中&#xff0c;我们经常需要从Excel表格中提取特定条件下的数据。Python的pandas库为我们提供了方便的方法来进行数据查询和过滤。 Pandas 是 Python 语言…

SELECT COUNT(*) 会造成全表扫描吗?

前言 SELECT COUNT(*)会不会导致全表扫描引起慢查询呢&#xff1f; SELECT COUNT(*) FROM SomeTable 网上有一种说法&#xff0c;针对无 where_clause 的 COUNT(*)&#xff0c;MySQL 是有优化的&#xff0c;优化器会选择成本最小的辅助索引查询计数&#xff0c;其实反而性能…

基于深度学习实现一张单图,一个视频,一键换脸,Colab脚本使用方法,在线版本,普通人也可以上传一张图片体验机器学习一键换脸

基于深度学习实现一张单图,一个视频,一键换脸,Colab脚本使用方法,在线版本,普通人也可以上传一张图片体验机器学习一键换脸。 AI领域人才辈出,突然就跳出一个大佬“s0md3v”,开源了一个单图就可以进行视频换脸的项目。 项目主页给了一张换脸动图非常有说服力,真是一图…

Fiber Golang:Golang中的强大Web框架

揭示Fiber在Go Web开发中的特点和优势 在不断发展的Web开发领域中&#xff0c;选择正确的框架可以极大地影响项目的效率和成功。介绍一下Fiber&#xff0c;这是一款令人印象深刻的Golang&#xff08;Go语言&#xff09;Web框架。以其飞快的性能和强大的特性而闻名&#xff0c;…