ทางเลือกแทน ANOVA ทางเดียวสำหรับข้อมูล heteroskedastic


36

ฉันมีข้อมูลจากชีวมวลสาหร่าย 3 กลุ่ม ( , , ) ซึ่งมีขนาดตัวอย่างไม่เท่ากัน ( , , ) และฉันต้องการเปรียบเทียบว่ากลุ่มเหล่านี้มาจากประชากรเดียวกันหรือไม่B C n A = 15 n B = 13 n C = 12ABCnA=15nB=13nC=12

การวิเคราะห์ความแปรปรวนแบบทางเดียวจะเป็นวิธีที่แน่นอนอย่างไรก็ตามเมื่อทำการทดสอบความเป็นไปได้ในข้อมูลของฉัน heteroskedascity ดูเหมือนจะเป็นประเด็นหลัก ข้อมูลดิบของฉันโดยไม่มีการแปลงทำให้อัตราส่วนของความแปรปรวน ( ) ซึ่งสูงกว่าค่าวิกฤติมาก ( F _ {\ rm crit} = 4.16 ) ดังนั้นฉันจึงไม่สามารถทำการวิเคราะห์ความแปรปรวนแบบทางเดียวได้ .Fmax=19.1Fcrit=4.16

ฉันพยายามแปลงให้เป็นมาตรฐานของข้อมูล แม้หลังจากการทดลองของการเปลี่ยนแปลงต่าง ๆ (เข้าสู่ระบบรากที่สองสแควร์) ต่ำสุดFmaxผลิตหลังจากการเปลี่ยนแปลงกับlog10เปลี่ยนแปลงเป็น7.16ซึ่งยังคงสูงเมื่อเทียบกับFcritcrit}

ใครที่นี่สามารถให้คำแนะนำฉันเกี่ยวกับสถานที่ที่จะไปจากที่นี่? ฉันไม่สามารถคิดวิธีการอื่นในการแปลงข้อมูลให้เป็นมาตรฐานได้ มีทางเลือกอื่นสำหรับ ANOVA แบบทางเดียวหรือไม่?

PS: ข้อมูลดิบของฉันอยู่ด้านล่าง:

A: 0.178 0.195 0.225 0.294 0.315 0.341 0.36  0.363 0.371 0.398 0.407 0.409 0.432 
   0.494 0.719
B: 0.11  0.111 0.204 0.416 0.417 0.441 0.492 0.965 1.113 1.19  1.233 1.505 1.897
C: 0.106 0.114 0.143 0.435 0.448 0.51  0.576 0.588 0.608 0.64  0.658 0.788 0.958

2
การทดสอบ F แสดงให้เห็นอย่างชัดเจนว่ากลุ่มไม่ได้มาจากประชากรเดียวกัน ขั้นตอนต่อไปคือการตีความผลลัพธ์นี้เริ่มต้นด้วยการสร้างภาพข้อมูลที่ชัดเจนและคำอธิบายเชิงปริมาณของข้อมูลแยกตามกลุ่ม
whuber

นอกจากนี้ยังมีการทดสอบการเปลี่ยนแปลงในแพ็คเกจเหรียญ สำหรับ ANOVA มันจะเป็นฟังก์ชั่น oneway_test ตัวอย่าง Quick-R: statmethods.net/stats/resampling.html
user16263

คำตอบ:


73

มีตัวเลือกจำนวนมากเมื่อจัดการกับข้อมูลที่แตกต่างกัน น่าเสียดายที่ไม่มีการรับประกันว่าจะทำงานได้ตลอดเวลา นี่คือตัวเลือกบางอย่างที่ฉันคุ้นเคย:

  • แปลง
  • เวลช์ ANOVA
  • น้ำหนักน้อยที่สุดกำลังสอง
  • การถดถอยที่แข็งแกร่ง
  • ข้อผิดพลาดมาตรฐานที่สอดคล้องกัน
  • บูต
  • การทดสอบ Kruskal-Wallis
  • การถดถอยโลจิสติกอันดับ

Update: นี่คือการสาธิตใน R บางวิธีของการปรับตัวแบบเชิงเส้น (เช่น ANOVA หรือการถดถอย) เมื่อคุณมีความแตกต่างของ heteroscedasticity / heterogeneity ของความแปรปรวน

เริ่มต้นด้วยการดูข้อมูลของคุณ เพื่อความสะดวกฉันได้โหลดมันลงในสองเฟรมข้อมูลที่เรียกว่าmy.data(ซึ่งมีโครงสร้างเหมือนด้านบนด้วยหนึ่งคอลัมน์ต่อกลุ่ม) และstacked.data(ซึ่งมีสองคอลัมน์: valuesด้วยตัวเลขและindตัวบ่งชี้กลุ่ม)

เราสามารถทดสอบอย่างเป็นทางการสำหรับheteroscedasticityด้วยการทดสอบของ 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                    

คุณมีความแตกต่างอย่างแน่นอน เราจะตรวจสอบเพื่อดูว่าผลต่างของกลุ่มคืออะไร กฎง่ายๆคือโมเดลเชิงเส้นมีความทนทานต่อความหลากหลายที่แตกต่างกันตราบใดที่ความแปรปรวนสูงสุดไม่เกินคูณมากกว่าความแปรปรวนขั้นต่ำดังนั้นเราจะพบอัตราส่วนนั้นเช่นกัน: 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

ความแปรปรวนของคุณแตกต่างกันอย่างมีนัยสำคัญกับที่ใหญ่ที่สุดBเป็นมีขนาดเล็กที่สุด นี่คือระดับของปัญหาที่แตกต่างกันของปัญหา 19×A

คุณคิดว่าจะใช้การแปลงเช่นล็อกหรือรากที่สองเพื่อสร้างความแปรปรวนให้คงที่ ที่จะใช้งานได้ในบางกรณี แต่การแปลงรูปแบบBox-Cox จะทำให้เกิดความแปรปรวนโดยการบีบอัดข้อมูลแบบไม่สมมาตรบีบทั้งข้อมูลลงด้วยข้อมูลสูงสุดที่บีบมากที่สุดหรือบีบให้สูงที่สุด ดังนั้นคุณต้องการความแปรปรวนของข้อมูลของคุณเพื่อเปลี่ยนแปลงด้วยค่าเฉลี่ยสำหรับสิ่งนี้เพื่อให้ทำงานได้อย่างเหมาะสมที่สุด ข้อมูลของคุณมีความแตกต่างกันมาก แต่มีความแตกต่างกันเล็กน้อยระหว่างค่าเฉลี่ยและค่ามัธยฐานเช่นการแจกแจงส่วนใหญ่ทับซ้อนกัน ในฐานะที่เป็นแบบฝึกหัดการสอนเราสามารถสร้างบางอย่างparallel.universe.dataโดยเพิ่มให้กับค่าทั้งหมดและถึง.72.7B.7Cเพื่อแสดงว่ามันทำงานอย่างไร:

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

การใช้การแปลงแบบสแควร์รูททำให้ข้อมูลเหล่านั้นค่อนข้างคงที่ คุณสามารถเห็นการปรับปรุงสำหรับข้อมูลเอกภพคู่ขนานได้ที่นี่:

ป้อนคำอธิบายรูปภาพที่นี่

แทนที่จะลองเปลี่ยนรูปแบบอื่น ๆ วิธีที่เป็นระบบมากกว่านี้คือการปรับพารามิเตอร์ Box-Cox ให้เหมาะสม (แม้ว่าโดยทั่วไปแล้วจะแนะนำให้ปัดเศษให้เป็นรูปแปรที่ใกล้เคียงที่สุด) ในกรณีของคุณทั้งสแควร์รูทหรือบันทึกสามารถยอมรับได้แม้ว่าจะใช้งานไม่ได้ก็ตาม สำหรับข้อมูลเอกภพคู่ขนานรากที่สองนั้นดีที่สุด: λ = 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) 

ป้อนคำอธิบายรูปภาพที่นี่

เนื่องจากกรณีนี้เป็น ANOVA (เช่นไม่มีตัวแปรต่อเนื่อง) วิธีหนึ่งที่จะจัดการกับความแตกต่างคือการใช้การแก้ไข Welchเพื่อองศาอิสระของส่วนในการทดสอบ (NB, ค่าเศษส่วนมากกว่า): Fdf = 19.445df = 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

วิธีการทั่วไปมากขึ้นคือการใช้ถ่วงน้ำหนักน้อยสแควร์ เนื่องจากบางกลุ่ม ( B) กระจายออกไปมากขึ้นข้อมูลในกลุ่มเหล่านั้นจึงให้ข้อมูลเกี่ยวกับตำแหน่งของค่าเฉลี่ยน้อยกว่าข้อมูลในกลุ่มอื่น เราสามารถปล่อยให้โมเดลรวมสิ่งนี้โดยการให้น้ำหนักกับจุดข้อมูลแต่ละจุด ระบบทั่วไปคือการใช้ส่วนกลับของความแปรปรวนกลุ่มเป็นน้ำหนัก:

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                  

อัตราผลตอบแทนนี้แตกต่างกันเล็กน้อยและ value กว่า ANOVA ( , ) ที่ ไม่ได้ถ่วงพีFp4.50890.01749

ป้อนคำอธิบายรูปภาพที่นี่

อย่างไรก็ตามน้ำหนักที่น้อยที่สุดไม่ใช่ยาครอบจักรวาล ข้อเท็จจริงที่น่าอึดอัดใจอย่างหนึ่งคือว่ามันถูกต้องหากว่าน้ำหนักนั้นถูกต้องความหมายและเหนือสิ่งอื่นใด มันไม่ได้อยู่ที่ไม่ใช่บรรทัดฐาน (เช่นเอียง) หรือค่าผิดปกติเช่นกัน ใช้น้ำหนักประมาณจากข้อมูลของคุณมักจะปรับการทำงาน แต่โดยเฉพาะอย่างยิ่งถ้าคุณมีข้อมูลมากพอที่จะประเมินความแปรปรวนที่มีความแม่นยำที่เหมาะสม (นี้จะคล้ายคลึงกับความคิดของการใช้ที่โต๊ะแทนที่จะเป็นโต๊ะเมื่อคุณมีหรือt 50 100 Nzt50100ดีกรีอิสระ) ข้อมูลของคุณเป็นเรื่องปกติและคุณไม่มีค่าผิดปกติใด ๆ น่าเสียดายที่คุณมีข้อมูลค่อนข้างน้อย (13 หรือ 15 ต่อกลุ่ม) ความเบ้และค่าผิดปกติบางอย่าง ฉันไม่แน่ใจว่าสิ่งเหล่านี้ไม่ดีพอที่จะทำให้เป็นเรื่องใหญ่ แต่คุณสามารถผสมกำลังสองน้อยที่สุดกับวิธีที่มีประสิทธิภาพ แทนที่จะใช้ความแปรปรวนเป็นการวัดการแพร่กระจายของคุณ (ซึ่งมีความอ่อนไหวต่อค่าผิดปกติโดยเฉพาะที่มีค่าต่ำ) คุณสามารถใช้ส่วนกลับของช่วงระหว่างควอไทล์ (ซึ่งไม่ได้รับผลกระทบจากค่าผิดปกติสูงสุด 50% ในแต่ละกลุ่ม) น้ำหนักเหล่านี้สามารถรวมกับการถดถอยที่แข็งแกร่งโดยใช้ฟังก์ชันการสูญเสียที่แตกต่างกันเช่น bisquare ของ Tukey: N

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 *

น้ำหนักที่นี่ไม่มากนัก กลุ่มที่คาดการณ์หมายถึงความแตกต่างเล็กน้อย ( A: WLS 0.36673, แข็งแกร่ง0.35722; B: WLS 0.77646, แข็งแกร่ง0.70433; C: WLS 0.50554, แข็งแกร่ง0.51845) ด้วยวิธีการBและCถูกดึงโดยค่าสุดขีดน้อย

ในทางเศรษฐมิติข้อผิดพลาดมาตรฐานของฮูเบอร์ - ไวท์ ("แซนวิช")เป็นที่นิยมมาก เช่นเดียวกับการแก้ไข Welch สิ่งนี้ไม่ต้องการให้คุณทราบความแปรปรวน a-Priori และไม่ต้องการให้คุณประเมินน้ำหนักจากข้อมูลของคุณและ / หรือเกิดขึ้นกับแบบจำลองที่อาจไม่ถูกต้อง ในทางกลับกันฉันไม่ทราบวิธีรวมสิ่งนี้กับ ANOVA ซึ่งหมายความว่าคุณได้รับพวกเขาสำหรับการทดสอบของรหัสจำลองแต่ละตัวเท่านั้นซึ่งทำให้ฉันมีประโยชน์น้อยลงในกรณีนี้ แต่ฉันจะแสดงให้พวกเขาเห็น:

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คำนวณเมทริกซ์ความแปรปรวนร่วม - แปรปรวนร่วมแบบแปรผันที่สม่ำเสมอสำหรับ Betas ของคุณ (รหัสจำลองของคุณ) ซึ่งเป็นตัวอักษรในฟังก์ชั่นการโทร ในการรับข้อผิดพลาดมาตรฐานคุณจะแยกเส้นทแยงมุมหลักและนำสแควร์รูท ในการรับ test สำหรับ betas ของคุณคุณแบ่งการประมาณค่าสัมประสิทธิ์ของคุณโดย SEs และเปรียบเทียบผลลัพธ์กับ -distribution ที่เหมาะสม(นั่นคือ -distribution กับองศาอิสระที่เหลือของคุณ) t tttt

สำหรับRผู้ใช้โดยเฉพาะ @TomWenseleers บันทึกในความคิดเห็นด้านล่างว่าฟังก์ชั่น? Anovaในcarแพคเกจสามารถยอมรับการwhite.adjustโต้แย้งที่จะได้รับสำหรับปัจจัยที่ใช้ข้อผิดพลาดที่สอดคล้องกัน heteroscedasticity 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

คุณสามารถลองประเมินเชิงประจักษ์ว่าการกระจายตัวตัวอย่างที่แท้จริงของสถิติการทดสอบของคุณนั้นเป็นอย่างไรด้วยการบูตสแตรป ขั้นแรกให้คุณสร้างค่าว่างที่แท้จริงโดยการทำให้ทุกกลุ่มมีความหมายเท่ากันทุกประการ จากนั้นคุณลองสุ่มใหม่พร้อมเปลี่ยนและคำนวณสถิติการทดสอบ ( ) ของคุณในแต่ละตัวอย่างรองเท้าเพื่อรับการประเมินเชิงประจักษ์เกี่ยวกับการกระจายตัวตัวอย่างของภายใต้ค่า Null กับข้อมูลของคุณไม่ว่าสถานะของพวกเขาจะเป็นอย่างไร สัดส่วนของการแจกแจงการสุ่มตัวอย่างที่มากหรือมากเกินกว่าสถิติการทดสอบที่คุณสังเกตเห็นคือค่า : F หน้า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

ในบางวิธีการบูตสแตรปปิ้งเป็นวิธีการลดสมมติฐานขั้นสุดท้ายเพื่อทำการวิเคราะห์พารามิเตอร์ (เช่นหมายถึง) แต่มันจะถือว่าข้อมูลของคุณเป็นตัวแทนที่ดีของประชากรซึ่งหมายความว่าคุณมีขนาดตัวอย่างที่สมเหตุสมผล เนื่องจากของคุณมีขนาดเล็กจึงอาจเชื่อถือได้น้อยลง อาจเป็นการป้องกันที่ดีที่สุดต่อการไม่เป็นมาตรฐานและความแตกต่างคือการใช้การทดสอบที่ไม่ใช่พารามิเตอร์ ANOVA ที่ไม่ใช่พารามิเตอร์พื้นฐานของ ANOVA คือการทดสอบ Kruskal-Wallis : 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

แม้ว่าการทดสอบ Kruskal-Wallis เป็นการป้องกันที่ดีที่สุดต่อความผิดพลาดประเภท I แต่สามารถใช้กับตัวแปรเด็ดขาดเพียงชุดเดียวเท่านั้น (เช่นไม่มีตัวทำนายต่อเนื่องหรือการออกแบบแบบแฟคทอเรียล) และมีอำนาจน้อยที่สุดของกลยุทธ์ทั้งหมดที่กล่าวถึง อีกวิธีที่ไม่ใช่ตัวแปรคือการใช้การถดถอยโลจิสติค ดูเหมือนว่าจะเป็นเรื่องแปลกสำหรับคนจำนวนมาก แต่คุณเพียง แต่ต้องสมมติว่าข้อมูลการตอบกลับของคุณมีข้อมูลลำดับที่ถูกต้องซึ่งพวกเขาทำอย่างอื่นทุกกลยุทธ์ด้านบนนั้นไม่ถูกต้องเช่นกัน:

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มีการระบุไว้สองเวอร์ชันการทดสอบอัตราส่วนความน่าจะเป็นและการทดสอบคะแนน การทดสอบอัตราส่วนความน่าจะเป็นโดยทั่วไปถือว่าดีที่สุด มันมีผลเป็น -value ของ p0.0363


1
รักคำตอบของคุณ! ตอนนี้มีแวบของบ้านที่จะจัดการกับข้อมูลของฉัน! :) จากแนวทางทั้งหมดนี้คุณจะขอแนะนำให้ใช้ข้อมูลประเภทใดของฉันโดยเฉพาะอย่างยิ่งเงื่อนไขการให้กำลังทางสถิติที่เพียงพอ? ฉันไม่กระตือรือร้นที่จะใช้วิธีที่ไม่ใช่พารามิเตอร์เช่นการทดสอบ Kruskal-Wallis ซึ่งหมายความว่าฉันมีแนวโน้มที่จะยอมรับข้อผิดพลาด Type I หรือไม่ ช่วยแก้ให้ด้วยนะถ้าฉันผิด.
Rick L.

2
ฉันจะหาข้อมูลเพิ่มเติมที่นี่เมื่อฉันมีโอกาส แต่ไม่การทดสอบ Kruskal-Wallis อาจช่วยป้องกันข้อผิดพลาดประเภท I ได้ดีที่สุด โอ้มันอาจไม่ใช่ตัวเลือกที่ทรงพลังที่สุดที่คุณมี
gung - Reinstate Monica

4
โปรดทราบว่าในไลบรารีcarยังมีตัวเลือกในwhite.adjust=Tการจัดการกับความแตกต่างโดยใช้ข้อผิดพลาดมาตรฐานที่ได้รับการปรับแบบขาวเพื่อแก้ไขข้อผิดพลาดมาตรฐาน
Tom Wenseleers

3
ใช่มันเพียง แต่กล่าวถึงมันlm's แต่ก็ยังดูเหมือนว่าจะทำงานให้aov' s (ตัวเลือกสำหรับการwhite.adjustจะwhite.adjust=c(FALSE, TRUE, "hc3", "hc0", "hc1", "hc2", "hc4")- สำหรับข้อมูลเพิ่มเติมโปรดดูที่?hccm)
ทอม Wenseleers

1
@ แนกซ์ไม่ไม่ว่าฉันรู้ มันเป็นกฎง่ายๆ มันไม่น่าจะเป็นบทความในวรรณคดีเชิงสถิติ มันอาจจะอยู่ในตำราเรียนเบื้องต้น
gung - Reinstate Monica
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.