ANOVA และการทดสอบภายหลัง
เปรียบเทียบค่าเฉลี่ยระหว่างสามกลุ่มขึ้นไปด้วย ANOVA ทางเดียว และดำเนินการทดสอบ Tukey HSD เพื่อระบุว่าคู่ใดแตกต่างกัน
ANOVA และการทดสอบภายหลัง เป็นบทเรียน Pandas & NumPy Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Pandas & NumPy Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Pandas & NumPy Academy มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดจึงไม่ใช้การทดสอบ t หลายครั้ง
เมื่อเปรียบเทียบค่าเฉลี่ยระหว่างกลุ่มตั้งแต่สามกลุ่มขึ้นไป การทำ t-test หลายครั้งจะทำให้ อัตราความผิดพลาดประเภทที่ 1 (อัตราผลบวกลวง) สูงขึ้น การทดสอบกลุ่ม A เทียบกับ B, A เทียบกับ C และ B เทียบกับ C โดยใช้ α=0.05 ในแต่ละครั้ง ทำให้โอกาสเกิดผลบวกลวงโดยรวมอยู่ที่ประมาณ 14% ไม่ใช่ 5% การวิเคราะห์ความแปรปรวน (ANOVA) แก้ปัญหานี้ด้วยการทดสอบทุกกลุ่มพร้อมกันในการทดสอบครั้งเดียว โดยควบคุมอัตราผลบวกลวงให้อยู่ที่ α อย่างพอดี ANOVA หนึ่งครั้งจึงเข้ามาแทนที่ t-test แบบจับคู่ทั้งหมดสำหรับคำถามโดยรวมว่า “มีกลุ่มใดแตกต่างกันหรือไม่”
ANOVA ทางเดียว: ภาพรวมสำคัญ
ANOVA ทางเดียวใช้ทดสอบว่าค่าเฉลี่ยของกลุ่มตั้งแต่สามกลุ่มขึ้นไปแตกต่างกันอย่างมีนัยสำคัญหรือไม่ โดยแยกความแปรปรวนทั้งหมดออกเป็น ความแปรปรวนระหว่างกลุ่ม (อธิบายได้ด้วยการเป็นสมาชิกกลุ่ม) และ ความแปรปรวนภายในกลุ่ม (สัญญาณรบกวนแบบสุ่ม) ค่าสถิติ F คืออัตราส่วนของความแปรปรวนทั้งสองส่วน: F = (ความแปรปรวนระหว่างกลุ่ม) / (ความแปรปรวนภายในกลุ่ม) ค่า F ที่สูงหมายความว่าความแตกต่างระหว่างกลุ่มมีมากเมื่อเทียบกับสัญญาณรบกวน จึงทำให้ได้ค่า p ขนาดเล็ก สมมติฐานว่างคือ H₀: ค่าเฉลี่ยของทุกกลุ่มเท่ากัน
import numpy as np
from scipy import stats
np.random.seed(0)
# Three product variants with different conversion rates
group_a = np.random.normal(10.0, 2.0, 50) # baseline
group_b = np.random.normal(11.5, 2.0, 50) # slightly better
group_c = np.random.normal(13.0, 2.0, 50) # clearly better
f_stat, p = stats.f_oneway(group_a, group_b, group_c)
print(f'F-statistic: {f_stat:.3f}')
print(f'p-value: {p:.6f}')
print('At least one group differs?', 'Yes' if p < 0.05 else 'No')ANOVA กับ DataFrames ของ Pandas
ในทางปฏิบัติ ข้อมูลของคุณจะอยู่ใน DataFrame รูปแบบยาว โดยคอลัมน์หนึ่งเก็บป้ายกำกับกลุ่ม และอีกคอลัมน์เก็บค่าที่วัดได้ ให้แยกแต่ละกลุ่มออกมาเป็น Series แล้วส่งให้ stats.f_oneway() หรือหากคุณมี DataFrame รูปแบบกว้าง (มีหนึ่งคอลัมน์ต่อหนึ่งกลุ่ม) ก็สามารถส่งแต่ละคอลัมน์เข้าไปโดยตรงได้ ฟังก์ชันนี้รับอาร์กิวเมนต์ตามตำแหน่งได้ไม่จำกัดจำนวน จึงขยายไปใช้กับกลุ่ม 4, 5 กลุ่ม หรือมากกว่านั้นได้ง่าย
import pandas as pd
import numpy as np
from scipy import stats
np.random.seed(1)
df = pd.DataFrame({
'group': ['A']*40 + ['B']*40 + ['C']*40 + ['D']*40,
'score': np.concatenate([
np.random.normal(70, 10, 40),
np.random.normal(75, 10, 40),
np.random.normal(80, 10, 40),
np.random.normal(72, 10, 40)
])
})
groups = [group['score'].values
for _, group in df.groupby('group')]
f, p = stats.f_oneway(*groups)
print(f'F={f:.3f}, p={p:.4f}')
print(df.groupby('group')['score'].mean().round(2))ข้อสมมติของ ANOVA
ANOVA ทางเดียวมีข้อสมมติดังนี้: (1) ความเป็นอิสระ — ข้อมูลสังเกตแต่ละรายการเป็นอิสระต่อกัน; (2) การแจกแจงปกติ — ค่าคลาดเหลือภายในแต่ละกลุ่มมีการแจกแจงใกล้เคียงปกติ (มีความทนทานสำหรับกลุ่มตัวอย่างขนาดใหญ่ผ่าน CLT); (3) ความแปรปรวนเท่ากัน — ทุกกลุ่มมีความแปรปรวนเท่ากัน ให้ทดสอบข้อสมมติเรื่องความแปรปรวนด้วย การทดสอบของ Levene (stats.levene(*groups)) หากความแปรปรวนไม่เท่ากัน ให้ใช้ ANOVA ของ Welch แทน ซึ่งมีให้ใช้ในไลบรารี pingouin หรือคำนวณผ่าน statsmodels ได้
import numpy as np
from scipy import stats
np.random.seed(0)
g1 = np.random.normal(10, 2, 40)
g2 = np.random.normal(12, 2, 40)
g3 = np.random.normal(11, 8, 40) # Much higher variance
# Test equal variances
stat_l, p_l = stats.levene(g1, g2, g3)
print(f'Levene\'s test: stat={stat_l:.3f}, p={p_l:.4f}')
if p_l < 0.05:
print('Warning: Unequal variances! Standard ANOVA may be unreliable.')
print('Consider Welch\'s ANOVA or Kruskal-Wallis.')Kruskal-Wallis: ANOVA แบบไม่อิงพารามิเตอร์
เมื่อข้อสมมติของ ANOVA ไม่เป็นจริง (การแจกแจงไม่เป็นปกติ กลุ่มตัวอย่างขนาดเล็ก หรือความแปรปรวนไม่เท่ากัน) การทดสอบ Kruskal-Wallis คือทางเลือกแบบไม่อิงพารามิเตอร์ การทดสอบนี้ตรวจสอบว่ากลุ่มต่าง ๆ มี การแจกแจง แตกต่างกันหรือไม่ โดยแปลงข้อมูลเป็นอันดับแล้วเปรียบเทียบผลรวมของอันดับ ให้ใช้ scipy.stats.kruskal(*groups) สมมติฐานว่างคือทุกกลุ่มมีการแจกแจงเดียวกัน (เทียบเท่ากับการทดสอบว่าค่ามัธยฐานเท่ากันเมื่อการแจกแจงมีรูปร่างเหมือนกัน) วิธีนี้ใช้ได้เสมอ แต่มีพลังในการทดสอบน้อยกว่า ANOVA เมื่อข้อสมมติเป็นจริง
import numpy as np
from scipy import stats
np.random.seed(0)
# Non-normal data: customer satisfaction scores (1-10 scale)
g1 = np.random.choice(range(1, 11), 50, p=[0.05]*10)
g2 = np.random.choice(range(1, 11), 50, p=[0.02, 0.03, 0.05, 0.08, 0.12, 0.15, 0.20, 0.15, 0.12, 0.08])
g3 = np.random.choice(range(1, 11), 50, p=[0.15, 0.15, 0.15, 0.15, 0.10, 0.10, 0.08, 0.05, 0.04, 0.03])
stat, p = stats.kruskal(g1, g2, g3)
print(f'Kruskal-Wallis: H={stat:.3f}, p={p:.4f}')
print('Groups differ?', 'Yes' if p < 0.05 else 'No')การทดสอบภายหลัง: เหตุใดจึงจำเป็น
ANOVA ที่มีนัยสำคัญบอกคุณได้ว่า มีค่าเฉลี่ยของอย่างน้อยหนึ่งกลุ่มที่แตกต่างกัน แต่ไม่ได้บอกว่าแตกต่างกันที่คู่ใด การทดสอบภายหลังจะเปรียบเทียบทุกคู่พร้อมแก้ไขปัญหาการเปรียบเทียบหลายครั้ง วิธีที่ได้รับความนิยมมากที่สุดคือ ผลต่างที่มีนัยสำคัญอย่างซื่อตรงของ Tukey (Tukey HSD) ซึ่งควบคุมอัตราความผิดพลาดโดยรวมของครอบครัวการทดสอบให้อยู่ที่ α อย่างพอดี โดยมีให้ใช้ใน statsmodels.stats.multicomp.pairwise_tukeyhsd() ให้ทำการทดสอบภายหลังเฉพาะเมื่อ ANOVA มีนัยสำคัญแล้วเท่านั้น — ไม่ใช่ใช้ค้นหาแบบสุ่มเสี่ยงในผลลัพธ์ที่ไม่มีนัยสำคัญ
import numpy as np
import pandas as pd
from scipy import stats
from statsmodels.stats.multicomp import pairwise_tukeyhsd
np.random.seed(0)
data = np.concatenate([
np.random.normal(70, 10, 40),
np.random.normal(80, 10, 40),
np.random.normal(75, 10, 40)
])
groups = ['A']*40 + ['B']*40 + ['C']*40
# First confirm ANOVA is significant
f, p = stats.f_oneway(*[data[i*40:(i+1)*40] for i in range(3)])
print(f'ANOVA p={p:.4f}')
if p < 0.05:
print('Post-hoc Tukey HSD:')
print(pairwise_tukeyhsd(data, groups, alpha=0.05))การอ่านผลลัพธ์ Tukey HSD
ตาราง Tukey HSD มีหนึ่งแถวต่อหนึ่งคู่ของกลุ่ม คอลัมน์ที่ควรให้ความสนใจคือ meandiff (ความแตกต่างของค่าเฉลี่ยระหว่างคู่กลุ่ม), lower และ upper (ช่วงความเชื่อมั่น 95% ของความแตกต่าง) และ reject (เป็น True หากคู่ดังกล่าวแตกต่างกันอย่างมีนัยสำคัญที่ α) ช่วงความเชื่อมั่นที่ไม่มีศูนย์อยู่ภายในหมายความว่าคู่นั้นแตกต่างกันอย่างมีนัยสำคัญ กลุ่มที่ช่วงความเชื่อมั่นทับซ้อนกันไม่ได้แตกต่างกันอย่างมีนัยสำคัญ
import numpy as np
from statsmodels.stats.multicomp import pairwise_tukeyhsd
np.random.seed(42)
data = np.concatenate([
np.random.normal(10, 2, 60), # Group A
np.random.normal(13, 2, 60), # Group B (clearly higher)
np.random.normal(10.5, 2, 60) # Group C (barely different from A)
])
groups = ['A']*60 + ['B']*60 + ['C']*60
result = pairwise_tukeyhsd(data, groups, alpha=0.05)
print(result)
# A-B: reject=True (B is higher)
# A-C: reject=False (barely different)
# B-C: reject=True (B is higher)การแก้ไขแบบ Bonferroni และ Benjamini-Hochberg
นอกจาก Tukey HSD แล้ว คุณสามารถใช้ การแก้ไขแบบ Bonferroni กับ t-test แบบจับคู่ได้ โดยทำ t-test ทั้งหมดก่อน แล้วปรับค่าเกณฑ์เป็น α/k เมื่อ k คือจำนวนการเปรียบเทียบ วิธีนี้ค่อนข้างเข้มงวด จึงอาจพลาดความแตกต่างที่มีอยู่จริง การแก้ไขอัตราการค้นพบผลบวกลวง (FDR) แบบ Benjamini-Hochberg เข้มงวดน้อยกว่า โดยควบคุมสัดส่วนที่คาดหมายของการค้นพบที่เป็นผลบวกลวง ทำให้พบผลบวกจริงได้มากขึ้น แลกกับผลบวกลวงที่เพิ่มขึ้นเล็กน้อย ให้ใช้ statsmodels.stats.multitest.multipletests(p_values, method='fdr_bh')
import numpy as np
from scipy import stats
from statsmodels.stats.multitest import multipletests
np.random.seed(0)
groups = [np.random.normal(10 + i*1.5, 2, 40) for i in range(4)]
names = ['A', 'B', 'C', 'D']
# All pairwise t-tests
p_values = []
pairs = []
for i in range(len(groups)):
for j in range(i+1, len(groups)):
_, p = stats.ttest_ind(groups[i], groups[j])
p_values.append(p)
pairs.append(f'{names[i]}-{names[j]}')
# Benjamini-Hochberg correction
reject, adj_p, _, _ = multipletests(p_values, method='fdr_bh')
for pair, p, ap, r in zip(pairs, p_values, adj_p, reject):
print(f'{pair}: raw_p={p:.4f}, adj_p={ap:.4f}, significant={r}')ขนาดอิทธิพลสำหรับ ANOVA: Eta-Squared
สำหรับ ANOVA ตัววัดที่เทียบเท่ากับ Cohen's d คือ eta-squared (η²): สัดส่วนของความแปรปรวนทั้งหมดที่อธิบายได้ด้วยการเป็นสมาชิกกลุ่ม η² = SS_between / SS_total ค่า < 0.01 ถือว่าเล็ก, 0.06 ถือว่าปานกลาง และ > 0.14 ถือว่าใหญ่ Partial eta-squared (η²_p) พบได้บ่อยกว่าในงานวิจัยที่ตีพิมพ์ โดยหารด้วยผลรวมของความแปรปรวนระหว่างกลุ่มกับความแปรปรวนจากความคลาดเคลื่อนเท่านั้น ไม่ใช่ความแปรปรวนทั้งหมด ควรคำนวณ η² ควบคู่กับค่า p ของ ANOVA เสมอ เพื่อแสดงว่าอิทธิพลนั้นมีความสำคัญในทางปฏิบัติหรือเพียงตรวจพบได้ในทางสถิติเท่านั้น
import numpy as np
from scipy import stats
np.random.seed(0)
groups = [
np.random.normal(10, 2, 50),
np.random.normal(12, 2, 50),
np.random.normal(14, 2, 50)
]
all_data = np.concatenate(groups)
grand_mean = all_data.mean()
ss_between = sum(len(g) * (g.mean() - grand_mean)**2 for g in groups)
ss_total = sum((x - grand_mean)**2 for g in groups for x in g)
eta_sq = ss_between / ss_total
f, p = stats.f_oneway(*groups)
print(f'F={f:.3f}, p={p:.4f}')
print(f'Eta-squared: {eta_sq:.4f} ({eta_sq:.1%} of variance explained)')ภาพรวม ANOVA สองทาง
ANOVA สองทางขยาย ANOVA ทางเดียวให้รองรับปัจจัยเชิงหมวดหมู่สองปัจจัยพร้อมกัน ตัวอย่างเช่น การทดสอบว่าคะแนนสอบแตกต่างกันตามทั้งวิธีการสอนและระดับประสบการณ์ของนักเรียนหรือไม่ นอกจากนี้ยังทดสอบ อิทธิพลปฏิสัมพันธ์ด้วยว่า อิทธิพลของวิธีการสอนขึ้นอยู่กับระดับประสบการณ์หรือไม่ ANOVA สองทางใช้งานผ่าน statsmodels ได้ด้วย ols('score ~ C(method) + C(level) + C(method):C(level)', data=df).fit() และ anova_lm(model) ซึ่งเป็นพื้นฐานสำหรับการออกแบบการทดลองที่ก้าวหน้ายิ่งขึ้น
import pandas as pd
import numpy as np
from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm
np.random.seed(0)
df = pd.DataFrame({
'method': ['trad']*60 + ['active']*60,
'level': ['beginner']*30 + ['advanced']*30 + ['beginner']*30 + ['advanced']*30,
'score': (np.random.normal(70, 8, 30).tolist() +
np.random.normal(80, 8, 30).tolist() +
np.random.normal(75, 8, 30).tolist() +
np.random.normal(88, 8, 30).tolist())
})
model = ols('score ~ C(method) + C(level) + C(method):C(level)', data=df).fit()
print(anova_lm(model, typ=2)[['F', 'PR(>F)']].round(4))ANOVA แบบวัดซ้ำ
ANOVA แบบวัดซ้ำใช้เมื่อวัดตัวอย่างเดิมหลายครั้ง เช่น ทดสอบเวลาตอบสนองที่เวลา 0, 30 และ 60 นาทีหลังได้รับการรักษา วิธีนี้เป็นการขยาย t-test แบบจับคู่ให้รองรับหลายกลุ่ม เมื่อคำนึงถึงความแตกต่างระหว่างบุคคล วิธีนี้จึงมีพลังในการทดสอบมากกว่า ANOVA ทางเดียว ใน Python ให้ใช้ pingouin.rm_anova() หรือ pg.pairwise_tests() สำหรับการวิเคราะห์ภายหลัง เมื่อสมมติฐานทรงกลมไม่เป็นจริง (ทดสอบด้วยการทดสอบของ Mauchly) ให้ใช้ค่า p ที่แก้ไขด้วย Greenhouse-Geisser
import pandas as pd
import numpy as np
# Example structure for repeated measures (pingouin library)
np.random.seed(0)
subjects = list(range(20)) * 3 # 20 subjects, 3 time points
timepoints = ['T0']*20 + ['T1']*20 + ['T2']*20
scores = np.concatenate([
np.random.normal(50, 10, 20), # baseline
np.random.normal(55, 10, 20), # improved
np.random.normal(60, 10, 20) # further improved
])
df_rm = pd.DataFrame({'subject': subjects, 'time': timepoints, 'score': scores})
print(df_rm.groupby('time')['score'].mean().round(2))
# Use pingouin.rm_anova(data=df_rm, dv='score', within='time', subject='subject')ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจแนวคิดการวิเคราะห์ข้อมูลจากบทเรียนนี้
ทบทวนบทเรียน
ในบทเรียนนี้ คุณได้เรียนรู้ว่า scipy.stats.f_oneway()ใช้ทดสอบว่ามีกลุ่มใดมีค่าเฉลี่ยแตกต่างกันหรือไม่ โดยควบคุมอัตราความผิดพลาดประเภทที่ 1 โดยรวม การทดสอบภายหลัง (Tukey HSD)ช่วยระบุว่าคู่ใดแตกต่างกันหลังจาก ANOVA มีนัยสำคัญ และ Kruskal-Wallisเป็นทางเลือกแบบไม่อิงพารามิเตอร์เมื่อข้อสมมติเรื่องการแจกแจงปกติหรือความแปรปรวนเท่ากันไม่เป็นจริง ต่อไปเราจะเริ่มโครงงานสรุปยอด โดยนำทักษะทั้งหมดมาผสานเป็นกระบวนการข้อมูลตั้งแต่ต้นจนจบ
เรียนรู้ Python ด้วย AI tutor — ฟรี
เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป
- คอร์ส
- 30
- บทเรียน
- 120
คำถามที่พบบ่อย
บทเรียน “ANOVA และการทดสอบภายหลัง” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ANOVA และการทดสอบภายหลัง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Pandas & NumPy Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Pandas & NumPy Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ANOVA และการทดสอบภายหลัง”
เปรียบเทียบค่าเฉลี่ยระหว่างสามกลุ่มขึ้นไปด้วย ANOVA ทางเดียว และดำเนินการทดสอบ Tukey HSD เพื่อระบุว่าคู่ใดแตกต่างกัน คุณปฏิบัติ Pandas & NumPy Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Pandas & NumPy Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Pandas & NumPy Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “ANOVA และการทดสอบภายหลัง” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Pandas & NumPy Academy นี้ได้ไหม
ได้ บทเรียน Pandas & NumPy Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- สถิติเชิงพรรณนาและการทดสอบการแจกแจงปกติ
- การทดสอบทีเพื่อเปรียบเทียบค่าเฉลี่ย
- การทดสอบไคสแควร์เพื่อความเป็นอิสระ
- ANOVA และการทดสอบภายหลัง