Có một số tùy chọn có sẵn khi xử lý dữ liệu không đồng nhất. Thật không may, không ai trong số họ được đảm bảo luôn luôn làm việc. Dưới đây là một số tùy chọn tôi quen thuộc với:
- biến đổi
- Tiếng Wales
- bình phương nhỏ nhất
- hồi quy mạnh mẽ
- lỗi tiêu chuẩn không đồng nhất
- bootstrap
- Kiểm tra Kruskal-Wallis
- hồi quy logistic
Cập nhật: Dưới đây là một minh họa R
về một số cách phù hợp với mô hình tuyến tính (nghĩa là ANOVA hoặc hồi quy) khi bạn có sự không đồng nhất / không đồng nhất của phương sai.
Hãy bắt đầu bằng cách xem dữ liệu của bạn. Để thuận tiện, tôi đã tải chúng vào hai khung dữ liệu được gọi my.data
(được cấu trúc như trên với một cột cho mỗi nhóm) và stacked.data
(có hai cột: values
với các số và ind
với chỉ báo nhóm).
Chúng tôi có thể chính thức kiểm tra tính không đồng nhất với thử nghiệm của Levene:
library(car)
leveneTest(values~ind, stacked.data)
# Levene's Test for Homogeneity of Variance (center = median)
# Df F value Pr(>F)
# group 2 8.1269 0.001153 **
# 38
Chắc chắn, bạn có sự không đồng nhất. Chúng tôi sẽ kiểm tra xem phương sai của các nhóm là gì. Một nguyên tắc nhỏ là các mô hình tuyến tính khá mạnh đối với sự không đồng nhất của phương sai miễn là phương sai tối đa không nhiều hơn lớn hơn phương sai tối thiểu, vì vậy chúng tôi cũng sẽ tìm thấy tỷ lệ đó: 4×
apply(my.data, 2, function(x){ var(x, na.rm=T) })
# A B C
# 0.01734578 0.33182844 0.06673060
var(my.data$B, na.rm=T) / var(my.data$A, na.rm=T)
# [1] 19.13021
Phương sai của bạn khác nhau đáng kể, với lớn nhất B
, là nhỏ nhất,. Đây là một mức độ không đồng nhất có vấn đề. 19×A
parallel.universe.data
2.7B
.7 vàoC
parallel.universe.data = with(my.data, data.frame(A=A, B=B+2.7, C=C+.7))
apply(parallel.universe.data, 2, function(x){ var(x, na.rm=T) })
# A B C
# 0.01734578 0.33182844 0.06673060
apply(log(parallel.universe.data), 2, function(x){ var(x, na.rm=T) })
# A B C
# 0.12750634 0.02631383 0.05240742
apply(sqrt(parallel.universe.data), 2, function(x){ var(x, na.rm=T) })
# A B C
# 0.01120956 0.02325107 0.01461479
var(sqrt(parallel.universe.data$B), na.rm=T) /
var(sqrt(parallel.universe.data$A), na.rm=T)
# [1] 2.074217
Sử dụng chuyển đổi căn bậc hai ổn định những dữ liệu đó khá tốt. Bạn có thể thấy sự cải thiện cho dữ liệu vũ trụ song song ở đây:
Thay vì chỉ thử các phép biến đổi khác nhau, một cách tiếp cận có hệ thống hơn là tối ưu hóa tham số Box-Cox (mặc dù thường được khuyến nghị làm tròn số đó thành phép biến đổi có thể hiểu được gần nhất). Trong trường hợp của bạn, căn bậc hai, hoặc nhật ký, , đều được chấp nhận, mặc dù không thực sự hoạt động. Đối với dữ liệu vũ trụ song song, căn bậc hai là tốt nhất: λ = 0,5 λ = 0λλ = .5λ = 0
boxcox(values~ind, data=stacked.data, na.action=na.omit)
boxcox(values~ind, data=stacked.pu.data, na.action=na.omit)
Vì trường hợp này là ANOVA (nghĩa là không có biến liên tục), nên một cách để đối phó với tính không đồng nhất là sử dụng hiệu chỉnh tiếng Wales cho mức độ tự do của mẫu số trong -test (nb ,, một giá trị phân số, thay vì ): Fdf = 19.445
df = 38
oneway.test(values~ind, data=stacked.data, na.action=na.omit, var.equal=FALSE)
# One-way analysis of means (not assuming equal variances)
#
# data: values and ind
# F = 4.1769, num df = 2.000, denom df = 19.445, p-value = 0.03097
Một cách tiếp cận tổng quát hơn là sử dụng bình phương tối thiểu có trọng số . Vì một số nhóm ( B
) trải rộng hơn, dữ liệu trong các nhóm đó cung cấp ít thông tin hơn về vị trí của giá trị trung bình so với dữ liệu trong các nhóm khác. Chúng ta có thể để mô hình kết hợp điều này bằng cách cung cấp trọng số với từng điểm dữ liệu. Một hệ thống phổ biến là sử dụng đối ứng của phương sai nhóm làm trọng số:
wl = 1 / apply(my.data, 2, function(x){ var(x, na.rm=T) })
stacked.data$w = with(stacked.data, ifelse(ind=="A", wl[1],
ifelse(ind=="B", wl[2], wl[3])))
w.mod = lm(values~ind, stacked.data, na.action=na.omit, weights=w)
anova(w.mod)
# Response: values
# Df Sum Sq Mean Sq F value Pr(>F)
# ind 2 8.64 4.3201 4.3201 0.02039 *
# Residuals 38 38.00 1.0000
Điều này mang lại giá trị và hơi khác so với ANOVA không có trọng số ( , ), nhưng nó đã giải quyết tốt sự không đồng nhất: pFp4.5089
0.01749
zt50100N
1 / apply(my.data, 2, function(x){ var(x, na.rm=T) })
# A B C
# 57.650907 3.013606 14.985628
1 / apply(my.data, 2, function(x){ IQR(x, na.rm=T) })
# A B C
# 9.661836 1.291990 4.878049
rw = 1 / apply(my.data, 2, function(x){ IQR(x, na.rm=T) })
stacked.data$rw = with(stacked.data, ifelse(ind=="A", rw[1],
ifelse(ind=="B", rw[2], rw[3])))
library(robustbase)
w.r.mod = lmrob(values~ind, stacked.data, na.action=na.omit, weights=rw)
anova(w.r.mod, lmrob(values~1,stacked.data,na.action=na.omit,weights=rw), test="Wald")
# Robust Wald Test Table
#
# Model 1: values ~ ind
# Model 2: values ~ 1
# Largest model fitted by lmrob(), i.e. SM
#
# pseudoDf Test.Stat Df Pr(>chisq)
# 1 38
# 2 40 6.6016 2 0.03685 *
Trọng lượng ở đây không phải là cực đoan. Các phương tiện nhóm dự đoán khác nhau đôi chút ( A
: WLS 0.36673
, mạnh mẽ 0.35722
; B
: WLS 0.77646
, mạnh mẽ 0.70433
; C
: WLS 0.50554
, mạnh mẽ 0.51845
), với các phương tiện B
và C
được ít kéo bởi giá trị cực.
Trong kinh tế lượng, lỗi tiêu chuẩn Huber-White ("sandwich") rất phổ biến. Giống như hiệu chỉnh tiếng Wales, điều này không yêu cầu bạn phải biết phương sai a-prori và không yêu cầu bạn ước tính trọng số từ dữ liệu của bạn và / hoặc phụ thuộc vào một mô hình có thể không chính xác. Mặt khác, tôi không biết cách kết hợp điều này với ANOVA, nghĩa là bạn chỉ lấy chúng để kiểm tra các mã giả riêng lẻ, điều này khiến tôi thấy ít hữu ích hơn trong trường hợp này, nhưng dù sao tôi cũng sẽ chứng minh chúng:
library(sandwich)
mod = lm(values~ind, stacked.data, na.action=na.omit)
sqrt(diag(vcovHC(mod)))
# (Intercept) indB indC
# 0.03519921 0.16997457 0.08246131
2*(1-pt(coef(mod) / sqrt(diag(vcovHC(mod))), df=38))
# (Intercept) indB indC
# 1.078249e-12 2.087484e-02 1.005212e-01
vcovHC
ttt
R
car
white.adjust
p
Anova(mod, white.adjust=TRUE)
# Analysis of Deviance Table (Type II tests)
#
# Response: values
# Df F Pr(>F)
# ind 2 3.9946 0.02663 *
# Residuals 38
# ---
# Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
FFp
mod = lm(values~ind, stacked.data, na.action=na.omit)
F.stat = anova(mod)[1,4]
# create null version of the data
nullA = my.data$A - mean(my.data$A)
nullB = my.data$B - mean(my.data$B, na.rm=T)
nullC = my.data$C - mean(my.data$C, na.rm=T)
set.seed(1)
F.vect = vector(length=10000)
for(i in 1:10000){
A = sample(na.omit(nullA), 15, replace=T)
B = sample(na.omit(nullB), 13, replace=T)
C = sample(na.omit(nullC), 13, replace=T)
boot.dat = stack(list(A=A, B=B, C=C))
boot.mod = lm(values~ind, boot.dat)
F.vect[i] = anova(boot.mod)[1,4]
}
1-mean(F.stat>F.vect)
# [1] 0.0485
n
kruskal.test(values~ind, stacked.data, na.action=na.omit)
# Kruskal-Wallis rank sum test
#
# data: values by ind
# Kruskal-Wallis chi-squared = 5.7705, df = 2, p-value = 0.05584
Mặc dù thử nghiệm Kruskal-Wallis chắc chắn là sự bảo vệ tốt nhất chống lại lỗi loại I, nhưng nó chỉ có thể được sử dụng với một biến phân loại duy nhất (nghĩa là không có dự đoán liên tục hoặc thiết kế giai thừa) và nó có sức mạnh ít nhất trong tất cả các chiến lược được thảo luận. Một cách tiếp cận không tham số khác là sử dụng hồi quy logistic thứ tự . Điều này có vẻ kỳ lạ với nhiều người, nhưng bạn chỉ cần giả định rằng dữ liệu phản hồi của bạn chứa thông tin thứ tự hợp pháp, điều mà họ chắc chắn làm hoặc nếu không thì mọi chiến lược khác ở trên cũng không hợp lệ:
library(rms)
olr.mod = orm(values~ind, stacked.data)
olr.mod
# Model Likelihood Discrimination Rank Discrim.
# Ratio Test Indexes Indexes
# Obs 41 LR chi2 6.63 R2 0.149 rho 0.365
# Unique Y 41 d.f. 2 g 0.829
# Median Y 0.432 Pr(> chi2) 0.0363 gr 2.292
# max |deriv| 2e-04 Score chi2 6.48 |Pr(Y>=median)-0.5| 0.179
# Pr(> chi2) 0.0391
chi2
Discrimination Indexes
p0.0363