Hypothesis-Testing

總和的 A/B 測試比率

  • March 20, 2019

語境

考慮一家在線銷售商品的公司的以下場景。一個用戶可以購買多個項目(即一籃子項目),其中一些特別重要並且被專門跟踪(我們稱它們為明星項目)。

我們希望測試算法的變化(例如推薦、風險評估、添加目標等等……),這可能會影響明星商品的銷售數量總銷售額

  • 這是一個標準的 A/B 測試設置 -隨機化單元位於用戶級別
  • A/B測試的目的是比較算法變化的影響:對照組有原始算法,變體有新算法
  • 一個關鍵的興趣指標被定義為明星商品銷售額總銷售額的比率。這是每個 A 或 B 組範圍內所有用戶的所有事務的總和。
  • 這意味著分析單元處於事務級別,與隨機化單元不同
  • 整個測試期間(例如 2 週)計算指標

所用指標的詳細信息

給定組 A 和一組用戶 UA=u1,u2,,uNA ,每個用戶都參與了一個數字 tun 的交易。套裝 TA 在測試期間,A 組中所有用戶的所有交易是 TA=tu11,tu12,tunm .

組 A 感興趣的度量是針對組 A 範圍內的所有事務定義的。總和是在事務級別,而不是用戶級別。

$$ \text{Metric}A = \frac{\sum{t_{i} \in T_A} \text{sales star items }}{\sum_{t_{i} \in T_A} \text{sales }$ }$

當然,我們可以修改定義來計算用戶級別的平均值,這將簡化一切,但這不是正在使用的指標。

問題

什麼樣的統計檢驗可以用於這樣的度量?另一個考慮因素是,儘管我們可以安全地假設用戶是 iid,但假設單個購買籃對於同一用戶來說是 iid 很可能是錯誤的。

以下是我遇到的一些想法,過去是否有任何 A/B 測試從業者遇到過類似的指標?

  • 比例 z 檢驗
  • 自舉和折刀
  • 增量法
  • 更改指標(最後的手段)

https://en.wikipedia.org/wiki/Ratio_estimator

編輯 - 一些說明

這個問題背後的原因是我經常看到在這種情況下使用比例的 z 檢驗。用於 A/B 測試的流行工具通常默認使用比例測試,業務用戶很少檢查測試有效所需的基本假設。@dnqxt 下面的答案就是一個很好的例子:“只需使用 z 比例檢驗!” - 但我希望看到一個嚴格的統計理由來說明為什麼(或為什麼不)這個測試可以在這種情況下使用。

我個人認為使用比例 z 檢驗在這裡不起作用,因為購買事件的銷售不是伯努利試驗。我認為我們不能說以分母出售的每一美元都可以被視為伯努利試驗,導致分子出售 0 或 1 個星項目美元。此外,由於隨機化單元處於用戶級別,同一用戶的購買事件不是獨立的(但我認為這是次要問題)。我在這裡可能是錯的,所以請隨時證明這一點!

我們還可以更改度量,使其成為伯努利/二項式,通過使用計數收斂到正常,但這將是最後的解決方案

\frac{# \text{sales with star items} }{# \text{sales}}

  • 比例 z 檢驗

當您有二元結果時,這適用於不同的情況。比例的 z 檢驗比較了這些二元結果的比例。

(下面的一些論點是您將能夠進行 t 檢驗,對於大數而言,這與 z 檢驗大致相同。對於比例,您可以進行 z 檢驗,因為二項式分佈有一個參數來確定方差和均值,與正態分佈不同)

  • 自舉

這將是可能的,但並不是真正必要的,因為 Delta 方法可以更直接地提供您觀察到的統計數據的錯誤。

  • 增量法

您對兩個可能相關的變量的比率感興趣:1. 總銷售額和 2. 明星商品的銷售額。

這些變量可能是漸近正態分佈的,因為它們是來自許多個人的銷售額的總和(測試過程可以被認為是一個過程,例如從個人用戶的銷售額分佈中挑選個人用戶的銷售額樣本)。因此,您可以使用 Delta 方法。

此處描述了使用 Delta 方法估計比率的方法。Delta 方法的這種應用的結果實際上與Hinkley 的結果的近似值一致,這是兩個相關正態分佈變量的比率的精確表達式(Hinkley DV,1969,關於兩個相關正態隨機變量的比率,Biometrica vol. 56第 3 號)。

(旁注:正如西安在評論中所指出的,George Marsaglia 1965 年在JASA 第 60 卷第 309 期中給出了更早的準確表達描述。2006 年在Jstatsoft 第 16 卷第 4 期中給出了簡單的現代描述)

為了 Z=XY[X\Y]N([μx μy],[σ2xρσxσy ρσxσyσ2y])

確切的結果是: f(z)=b(z)d(z)a(z)312πσXσY[Φ(b(z)1ρ2a(z))Φ(b(z)1ρ2a(z))]+1ρ2πσXσYa(z)2exp(c2(1ρ2))
a(z)=(z2σ2X2ρzσXσY+1σ2Y)12 b(z)=μXzσ2Xρ(μX+μYz)σXσY+μYσ2Y c=μ2Xσ2Y2ρμXμY+σXσY+μ2Yσ2Y d(z)=exp(b(z)2ca(z)22(1ρ2)a(z)2)
基於漸近行為的近似值是:(對於 μY/σY ):F(z)Φ(zμX/μYσXσYa(z)/μY)
當您插入近似值時,您最終會得到 Delta 方法的結果 a(z)=a(μX/μY) a(z)σXσY/μYa(μX/μY)σXσY/μY=(μ2Xσ2Yμ4Y2μXρσXσYμ3Y+σ2Xμ2Y)12

的值 μX,μY,σX,σY,ρ 可以從您的觀察中估計,這允許您估計單個用戶的分佈的方差和均值,並且與此相關的是多個用戶總和的樣本分佈的方差和均值。

  • 更改指標

我相信至少對單個用戶的銷售額(而不是比率)分佈進行初步繪圖是很有趣的。最終您可能會遇到 A 組和 B 組中的用戶之間存在差異的情況*,*但是當您考慮比率的單個變量時,它恰好並不顯著(這有點類似於 MANOVA更強大比單一的方差分析測試)。

雖然了解組間差異,但您感興趣的指標沒有顯著差異,可能對您做出決策沒有太大幫助,但它確實有助於您理解基礎理論,並可能在下次設計更好的更改/實驗。


插圖

下面是一個簡單的說明:

讓來自用戶的銷售的假設分佈分佈為分數 a,b,c,d 這表明有多少用戶屬於特定情況(實際上這種分佈會更複雜):

                          star item sales
                        0$ 40$ 

other item sales  0$ a b
10$      c               d

然後是具有 10000 個用戶的組的總數的樣本分佈,使用一種算法a=0.190,b=0.001,c=0.800,d=0.009

和其他算法a=0.170,b=0.001,c=0.820,d=0.009
看起來像:

例子

其中顯示 10000 次運行吸引新用戶併計算銷售額和比率。直方圖用於比率的分佈。這些線是使用來自 Hinkley 的函數進行的計算。

  • 您可以看到兩個總銷售額的分佈近似為多元正態分佈。比率的等值線表明,您可以很好地估計比率作為線性和(如前面提到的/鏈接的線性化 Delta 方法),並且高斯分佈的近似值應該可以很好地工作(然後您可以使用 t-測試大數字就像 z 測試一樣)。
  • 您還可以看到,與僅使用直方圖相比,這樣的散點圖可能會為您提供更多信息和洞察力。

用於計算圖形的 R 代碼:

set.seed(1)
#
# 
# function to sampling hypothetic n users 
# which will buy star items and/or regular items
#
# star item sales
# 0$ 40$ 
# 
# regular item sales 0$ a b
# 10$ c d
#
#
sample_users <- function(n,a,b,c,d) {
 # sampling 
 q <- sample(1:4, n, replace=TRUE, prob=c(a,b,c,d))
 # total dolar value of items
 dri = (sum(q==3)+sum(q==4))*10
 dsi = (sum(q==2)+sum(q==4))*40
 # output
 list(dri=dri,dsi=dsi,dti=dri+dsi, q=q)
}


# 
# function for drawing those blocks for the tilted histogram
#
block <- function(phi=0.045+0.001/2, r=100, col=1) {
 if (col == 1) {
   bgs <- rgb(0,0,1,1/4)
   cols <- rgb(0,0,1,1/4)
 } else {
   bgs <- rgb(1,0,0,1/4)
   cols <- rgb(1,0,0,1/4)
 }
 angle <- c(atan(phi+0.001/2),atan(phi+0.001/2),atan(phi-0.001/2),atan(phi-0.001/2))
 rr <- c(90000,90000+r,90000+r,90000)
 x <- cos(angle)*rr
 y <- sin(angle)*rr
 polygon(x,y,col=cols,bg=bgs)
}
block <- Vectorize(block)


#
# function to compute Hinkley's density formula
#
fw <- function(w,mu1,mu2,sig1,sig2,rho) {
 #several parameters
 aw <- sqrt(w^2/sig1^2 - 2*rho*w/(sig1*sig2) + 1/sig2^2)
 bw <- w*mu1/sig1^2 - rho*(mu1+mu2*w)/(sig1*sig2)+ mu2/sig2^2
 c <- mu1^2/sig1^2 - 2 * rho * mu1 * mu2 / (sig1*sig2) + mu2^2/sig2^2
 dw <- exp((bw^2 - c*aw^2)/(2*(1-rho^2)*aw^2))
 
 # output from Hinkley's density formula
 out <- (bw*dw / ( sqrt(2*pi) * sig1 * sig2 * aw^3)) * (pnorm(bw/aw/sqrt(1-rho^2),0,1) - pnorm(-bw/aw/sqrt(1-rho^2),0,1)) + 
   sqrt(1-rho^2)/(pi*sig1*sig2*aw^2) * exp(-c/(2*(1-rho^2)))
 
 out
}
fw <- Vectorize(fw)

#
# function to compute
# theoretic distribution for sample with parameters (a,b,c,d)
# lazy way to compute the mean and variance of the theoretic distribution
fwusers <- function(na,nb,nc,nd,n=10000) {
 users <- c(rep(1,na),rep(2,nb),rep(3,nc),rep(4,nd))
 dsi <- c(0,40,0,40)[users]
 dri <- c(0,0,10,10)[users]
 dti <- dsi+dri
 
 sig1 <- sqrt(var(dsi))*sqrt(n)
 sig2 <- sqrt(var(dti))*sqrt(n)
 cor <- cor(dti,dsi)
 mu1 <- mean(dsi)*n
 mu2 <- mean(dti)*n
 
 w <- seq(0,1,0.001)
 f <- fw(w,mu1,mu2,sig1,sig2,cor)
 list(w=w,f=f,sig1 = sig1, sig2=sig2, cor = cor, mu1= mu1, mu2 = mu2)
}


# sample many ntr time to display sample distribution of experiment outcome
ntr <- 10^4

# sample A
dsi1 <- rep(0,ntr)
dti1 <- rep(0,ntr)
for (i in 1:ntr) {
 users <- sample_users(10000,0.19,0.001,0.8,0.009)
 dsi1[i] <- users$dsi
 dti1[i] <- users$dti
}

# sample B
dsi2 <- rep(0,ntr)
dti2 <- rep(0,ntr)
for (i in 1:ntr) {
 users <- sample_users(10000,0.19-0.02,0.001,0.8+0.02,0.009)
 dsi2[i] <- users$dsi
 dti2[i] <- users$dti
}


# hiostograms for ratio
ratio1 <- dsi1/dti1
ratio2 <- dsi2/dti2
h1<-hist(ratio1, breaks = seq(0, round(max(ratio2+0.04),2), 0.001))
h2<-hist(ratio2, breaks = seq(0, round(max(ratio2+0.04),2), 0.001))

# plotting

plot(0, 0, 
    xlab = "sum of total sales", ylab = "sum of star item sales",
    xlim = c(82000,92000),
    ylim = c(2500,6000), 
    pch=21, col = rgb(0,0,1,1/10), bg = rgb(0,0,1,1/10))
title("sample distribution for sum of 10 000 users")

# isolines
brks <- seq(0, round(max(ratio2+0.02),2), 0.001)
for (ls in 1:length(brks)) {
 col=rgb(0,0,0,0.25+0.25*(ls%%5==1))
 lines(c(0,10000000),c(0,10000000)*brks[ls],lty=2,col=col)
}

# scatter points
points(dti1, dsi1,
      pch=21, col = rgb(0,0,1,1/10), bg = rgb(0,0,1,1/10))
points(dti2, dsi2,
      pch=21, col = rgb(1,0,0,1/10), bg = rgb(1,0,0,1/10))

# diagonal axis
phi <- atan(h1$breaks)
r <- 90000
lines(cos(phi)*r,sin(phi)*r,col=1)

# histograms
phi <- h1$mids
r <- h1$density*10
block(phi,r,col=1)

phi <- h2$mids
r <- h2$density*10
block(phi,r,col=2)

# labels for histogram axis
phi <- atan(h1$breaks)[1+10*c(1:7)]
r <- 90000
text(cos(phi)*r-130,sin(phi)*r,h1$breaks[1+10*c(1:7)],srt=-87.5,cex=0.9)
text(cos(atan(0.045))*r-400,sin(atan(0.045))*r,"ratio of sum of star items and sum of total items", srt=-87.5,cex=0.9)

# plotting functions for Hinkley densities using variance and means estimated from theoretic samples distribution
wf1 <- fwusers(190,1,800,9,10000)
wf2 <- fwusers(170,1,820,9,10000)
rf1 <- 90000+10*wf1$f
phi1 <- atan(wf1$w)
lines(cos(phi1)*rf1,sin(phi1)*rf1,col=4)
rf2 <- 90000+10*wf2$f
phi2 <- atan(wf2$w)
lines(cos(phi2)*rf2,sin(phi2)*rf2,col=2)

引用自:https://stats.stackexchange.com/questions/398436