উদ্দেশ্য: ANOVA একেবারে শূন্য থেকে প্রো লেভেল পর্যন্ত — কি, কেন, কিভাবে, ম্যানুয়াল হিসাব, CRD/RCBD ডিজাইন, Contrasts & Orthogonal Contrasts, One-Way Fixed-Effect ANOVA, Power/Sample Size — সবকিছু বাংলায় এবং ধাপে-ধাপে।
Note: কোডগুলো বেস R-এ চলবে। যে অংশগুলোতে extra প্যাকেজ দরকার (যেমনcar,multcomp,pwr) — সেগুলো ইনস্টল না থাকলে নীচের নির্দেশনা অনুযায়ী আগে ইনস্টল করে নিন।
ANalysis Of VAriance (ANOVA) = ভ্যারিয়েন্স বিশ্লেষণ।
লক্ষ্য: একাধিক গ্রুপের গড় (means) সমান কি
না পরীক্ষা করা।
- শূন্য অনুমান (H₀): সব গ্রুপের গড় সমান \(\mu_1=\mu_2=\cdots=\mu_k\)
- বিকল্প (H₁): অন্তত একটি গড় আলাদা
কেন “ভ্যারিয়েন্স” দেখে “গড়” বিচার?
গ্রুপগুলোর গড় সত্যিই আলাদা হলে গ্রুপের মধ্যে (Between)
ভ্যারিয়েন্স বাড়ে। আমরা Between ও Within
ভ্যারিয়েন্সের অনুপাত দেখি:
\(F = \dfrac{MS_{Between}}{MS_{Within}} =
\dfrac{SS_B/(k-1)}{SS_W/(N-k)}\)
Assumptions:
1) Observations independent; 2) Errors are normal; 3) Group variances
equal (homoscedastic).
চিত্র: F-বিতরণ ও ক্রিটিকাল অঞ্চল
curve(df(x, df1=2, df2=12), from=0, to=8, xlab="F", ylab="Density", main="F-বিতরণ (df1=2, df2=12)")
abline(v=qf(.95,2,12), lty=2)
text(qf(.95,2,12), 0.05, labels="Critical value (α=0.05)", pos=4, cex=.8)
মডেল:
\(y_{ij}=\mu+\tau_i+\varepsilon_{ij},\quad
\varepsilon_{ij}\sim N(0,\sigma^2)\)
এখানে \(\tau_i\) হলো i-তম ট্রিটমেন্টের
স্থির (fixed) প্রভাব।
Sum of Squares:
- \(SS_T=\sum (y-\bar
y_{\cdot\cdot})^2\) — মোট পরিবর্তন
- \(SS_B=\sum n_i(\bar y_{i\cdot}-\bar
y_{\cdot\cdot})^2\) — গ্রুপের গড়ের মধ্যে পার্থক্য
- \(SS_W=SS_T-SS_B\) — গ্রুপের ভিতরের
এলোমেলোতা
df ও Test: \(df_B=k-1\), \(df_W=N-k\), \(F=MS_B/MS_W\) যেখানে \(MS_B=SS_B/(k-1)\), \(MS_W=SS_W/(N-k)\)।
বেল-কার্ভ (Normal) — রেসিডুয়ালস normal কিনা বোঝার ধারণা
curve(dnorm(x,0,1), -4, 4, xlab="z", ylab="density", main="Normal (Bell) Curve)")
abline(v=qnorm(c(.025,.975)), lty=2)
৩টি স্প্রে (A,B,C); প্রতিটি ৪টি পর্যবেক্ষণ।
dat <- data.frame(
spray = rep(c("A","B","C"), each=4),
count = c(12,15,14,13, 18,19,17,18, 11,10,12,9)
)
dat$spray <- factor(dat$spray)
dat
ধাপ-ধাপে SST/SSB/SSW:
k <- nlevels(dat$spray)
n_i <- table(dat$spray)
N <- nrow(dat)
grand_mean <- mean(dat$count)
means <- tapply(dat$count, dat$spray, mean)
SST <- sum( (dat$count - grand_mean)^2 )
SSB <- sum( n_i * (means - grand_mean)^2 )
SSW <- SST - SSB
dfB <- k - 1
dfW <- N - k
MSB <- SSB/dfB
MSW <- SSW/dfW
Fval <- MSB/MSW
pval <- pf(Fval, dfB, dfW, lower.tail = FALSE)
list(SST=SST, SSB=SSB, SSW=SSW, dfB=dfB, dfW=dfW, MSB=MSB, MSW=MSW, F=Fval, pvalue=pval)
## $SST
## [1] 126
##
## $SSB
## [1] 114
##
## $SSW
## [1] 12
##
## $dfB
## [1] 2
##
## $dfW
## [1] 9
##
## $MSB
## [1] 57
##
## $MSW
## [1] 1.333333
##
## $F
## [1] 42.75
##
## $pvalue
## [1] 2.538915e-05
একই জিনিস aov() দিয়ে:
fit <- aov(count ~ spray, data=dat)
summary(fit)
## Df Sum Sq Mean Sq F value Pr(>F)
## spray 2 114 57.00 42.75 2.54e-05 ***
## Residuals 9 12 1.33
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Assumptions চেক: QQ-plot ও Residual vs Fitted
par(mfrow=c(1,2))
qqnorm(residuals(fit)); qqline(residuals(fit))
plot(fitted(fit), residuals(fit), xlab="Fitted", ylab="Residuals"); abline(h=0,lty=2)
par(mfrow=c(1,1))
সমভ্যারিয়েন্স (Levene’s test) — ইনস্টল থাকলে রান হবে
if (requireNamespace("car", quietly = TRUE)) {
car::leveneTest(count ~ spray, data=dat, center = median)
} else {
cat("Note: 'car' প্যাকেজ নেই। চালাতে চাইলে আগে Console এ install.packages('car') করুন।\n")
}
Post-hoc (Tukey HSD):
TukeyHSD(fit)
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = count ~ spray, data = dat)
##
## $spray
## diff lwr upr p adj
## B-A 4.5 2.220337 6.779663 0.0009765
## C-A -3.0 -5.279663 -0.720337 0.0127984
## C-B -7.5 -9.779663 -5.220337 0.0000193
চিত্র: Already added (F curve)।
একটি ফ্যাক্টর, বহু লেভেল (উপরে দেখানো হয়েছে)।
set.seed(1)
dat2 <- expand.grid(A=c("Low","High"), B=c("Old","New"), rep=1:4)
dat2$y <- with(dat2, 10 + ifelse(A=="High",3,0) + ifelse(B=="New",2,0) +
ifelse(A=="High" & B=="New",2,0) + rnorm(nrow(dat2),0,2))
fit2 <- aov(y ~ A*B, data=dat2)
summary(fit2)
## Df Sum Sq Mean Sq F value Pr(>F)
## A 1 46.07 46.07 15.306 0.00206 **
## B 1 104.72 104.72 34.788 7.27e-05 ***
## A:B 1 12.98 12.98 4.311 0.06002 .
## Residuals 12 36.12 3.01
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
একই সাবজেক্টে ভিন্ন সময়/শর্তে মাপ → correlation থাকে। (উদাহরণ: প্রি/পোস্ট টেস্ট)
কিছু ফ্যাক্টর fixed, কিছু random (যেমন ব্লক random)।
lme4/lmerTest সাধারণত ব্যবহৃত।
set.seed(42)
trt <- factor(rep(LETTERS[1:4], each=6))
y <- 5 + c(0,1,2,3)[as.numeric(trt)] + rnorm(length(trt),0,1.5)
summary(aov(y ~ trt))
## Df Sum Sq Mean Sq F value Pr(>F)
## trt 3 21.39 7.129 2.453 0.0931 .
## Residuals 20 58.13 2.907
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
set.seed(7)
trt <- factor(rep(LETTERS[1:4], times=5)) # 4 treatment, 5 blocks
blk <- factor(rep(1:5, each=4))
y <- 10 + c(0,1,2,3)[as.numeric(trt)] + c(0,-1,0.5,1,-0.5)[as.numeric(blk)] + rnorm(length(trt),0,1)
summary(aov(y ~ trt + blk))
## Df Sum Sq Mean Sq F value Pr(>F)
## trt 3 31.02 10.339 7.010 0.00559 **
## blk 4 38.15 9.538 6.467 0.00516 **
## Residuals 12 17.70 1.475
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
CRD vs RCBD সম্পর্ক: RCBD-তে ব্লক যোগ করলে অপ্রয়োজনীয় variation কাটে → MS_W কমে → Power বাড়ে। তবে ডিজাইনের কনস্ট্রেইন্ট বাড়ে (প্রতি ব্লকে সব ট্রিটমেন্ট থাকতে হবে)।
Contrast: গ্রুপের গড়গুলোর লিনিয়ার কম্বিনেশন \(\sum c_i \mu_i\) যেখানে \(\sum c_i=0\)।
- উদাহরণ: \(\mu_1-\mu_2\), বা \((\mu_1+\mu_2)/2 - \mu_3\)।
Orthogonal: দুটি কনট্রাস্ট \(\mathbf{c},\mathbf{d}\) হলে balanced ক্ষেত্রে
\(\sum c_i d_i=0\) হলে ওরা অরথোগোনাল।
এতে কনট্রাস্টগুলো স্বাধীনভাবে তথ্য ভাগ করে, multiple testing
burden কমে।
# Balanced 4-group উদাহরণ:
C1 <- c( 1,-1, 0, 0) # μ1 vs μ2
C2 <- c( 0, 0, 1,-1) # μ3 vs μ4
C3 <- c( 1, 1,-1,-1) # (μ1+μ2) vs (μ3+μ4)
sum(C1*C2); sum(C1*C3); sum(C2*C3) # 0 => balanced case-এ orthogonal
## [1] 0
## [1] 0
## [1] 0
R-এ কাস্টম কনট্রাস্ট টেস্ট (multcomp::glht) — প্যাকেজ
থাকলে রান করুন
if (requireNamespace("multcomp", quietly = TRUE)) {
library(multcomp)
fit <- aov(count ~ spray, data=dat) # dat আগেই আছে
K <- rbind("A vs B" = c(1,-1,0),
"A vs C" = c(1, 0,-1),
"(A+B)/2 vs C" = c(0.5,0.5,-1))
out <- glht(fit, linfct = mcp(spray = K))
summary(out)
} else {
cat("Note: 'multcomp' প্যাকেজ নেই। চালাতে চাইলে আগে Console এ install.packages('multcomp') করুন।\n")
}
##
## Simultaneous Tests for General Linear Hypotheses
##
## Multiple Comparisons of Means: User-defined Contrasts
##
##
## Fit: aov(formula = count ~ spray, data = dat)
##
## Linear Hypotheses:
## Estimate Std. Error t value Pr(>|t|)
## A vs B == 0 -4.5000 0.8165 -5.511 <0.001 ***
## A vs C == 0 3.0000 0.8165 3.674 0.0116 *
## (A+B)/2 vs C == 0 5.2500 0.7071 7.425 <0.001 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## (Adjusted p values reported -- single-step method)
SST <- sum( (dat$count - mean(dat$count))^2 )
SSB <- sum( table(dat$spray) * (tapply(dat$count, dat$spray, mean) - mean(dat$count))^2 )
eta2 <- SSB/SST
f <- sqrt(eta2/(1-eta2))
c(eta2=eta2, cohen_f=f)
## eta2 cohen_f
## 0.9047619 3.0822070
নীচের কোডটি pwr ইনস্টল থাকলে চলবে।
if (requireNamespace("pwr", quietly = TRUE)) {
library(pwr)
# উদাহরণ: k=3, Cohen's f=0.25, α=0.05, power=0.80
pwr.anova.test(k=3, f=0.25, sig.level=0.05, power=0.80)
} else {
cat("Note: 'pwr' প্যাকেজ নেই। Console এ install.packages('pwr') করে নিন।\n")
}
##
## Balanced one-way analysis of variance power calculation
##
## k = 3
## n = 52.3966
## f = 0.25
## sig.level = 0.05
## power = 0.8
##
## NOTE: n is number in each group
প্রফেসরের স্টাইল ব্যাখ্যা (between.var / within.var থেকে f
নির্ণয়):
ধরি within.var ≈ σ² (প্রতি গ্রুপে একই ভ্যারিয়েন্স), আর গ্রুপ-গড়গুলোর ভ্যারিয়েন্স
between.var। Balanced কেসে \(f^2\) আনুমানিকভাবে
mean-ভ্যারিয়েন্স/within-ভ্যারিয়েন্স অনুপাত থেকে আসে।
k <- 4
f_min <- 1*sqrt(1/(2*k))
f_mid <- .5*sqrt((k+1)/(3*(k-1)))
f_max <- .5
c(f_min=f_min, f_mid=f_mid, f_max=f_max)
## f_min f_mid f_max
## 0.3535534 0.3726780 0.5000000
pairwise.t.test(dat$count, dat$spray, p.adjust.method = "bonferroni")
##
## Pairwise comparisons using t tests with pooled SD
##
## data: dat$count and dat$spray
##
## A B
## B 0.0011 -
## C 0.0154 2.2e-05
##
## P value adjustment method: bonferroni
TukeyHSD(aov(count ~ spray, data=dat))
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = count ~ spray, data = dat)
##
## $spray
## diff lwr upr p adj
## B-A 4.5 2.220337 6.779663 0.0009765
## C-A -3.0 -5.279663 -0.720337 0.0127984
## C-B -7.5 -9.779663 -5.220337 0.0000193
pwr.anova.test()
আউটপুটanova(fit) # মূল ANOVA টেবিল
শেষ কথা: উপরের .Rmd রান করলেই বেসিক→প্রো
সব ধাপ আপনার সামনে টেবিল/গ্রাফসহ চলে আসবে।