การทดสอบสมมติฐานและ p-value
ประมาณ 30 นาที
เป้าหมายของบท
- เข้าใจ null hypothesis (H0) กับ alternative hypothesis (H1) และตรรกะของการ "ทดสอบเพื่อหักล้าง H0"
- ทำ one-sample t-test เพื่อเทียบค่าเฉลี่ยตัวอย่างกับค่าที่มีคนอ้าง
- ทำ two-sample t-test (Welch's) เพื่อเทียบค่าเฉลี่ยระหว่างสองกลุ่ม
- อ่านและตีความ p-value ให้ถูกต้อง (ไม่ใช่ "ความน่าจะเป็นที่ H0 เป็นจริง")
ทำไมต้องมี “การทดสอบสมมติฐาน”
บทที่แล้วเราเรียนรู้ว่าค่าเฉลี่ยจากตัวอย่างมีความไม่แน่นอน จึงต้องรายงานเป็นช่วง (confidence interval) แทนที่จะเป็นตัวเลขเดียว คำถามต่อไปที่พบบ่อยในงานวิเคราะห์คือ: “มีคนอ้างตัวเลขบางอย่างมา (เช่น ยอดขายเฉลี่ยคือ X บาท) ข้อมูลตัวอย่างที่เรามีขัดแย้ง กับคำกล่าวนั้นไหม” หรือ “สองกลุ่มนี้ค่าเฉลี่ยต่างกันจริงหรือแค่ต่างกันเพราะสุ่มตัวอย่างมาไม่เหมือนกัน” — การทดสอบสมมติฐาน (hypothesis testing) คือกรอบการคิดที่ตอบคำถามแบบนี้อย่างเป็นระบบ
null hypothesis (H0) กับ alternative hypothesis (H1)
ทุกการทดสอบสมมติฐานเริ่มจากตั้งสมมติฐานคู่หนึ่ง:
- H0 (null hypothesis) — สมมติฐานที่บอกว่า “ไม่มีความแตกต่าง / ไม่มีผล” เช่น “ยอดขายเฉลี่ย = 5000 บาท” หรือ “ค่าเฉลี่ย ของสองกลุ่มเท่ากัน” นี่คือสิ่งที่เราพยายามหาหลักฐานมาหักล้าง ไม่ใช่สิ่งที่เราอยากพิสูจน์
- H1 (alternative hypothesis) — สมมติฐานตรงข้าม เช่น “ยอดขายเฉลี่ย ≠ 5000 บาท” หรือ “ค่าเฉลี่ยของสองกลุ่มไม่เท่ากัน”
ตรรกะสำคัญคือ: เราไม่มีทาง “พิสูจน์” ว่า H0 เป็นจริงได้ 100% ทำได้แค่ดูว่าข้อมูลที่มีมีหลักฐานเพียงพอที่จะปฏิเสธ H0 หรือไม่ ถ้าไม่มีหลักฐานเพียงพอ เราจะพูดว่า “fail to reject H0” (ไม่ปฏิเสธ H0) ไม่ใช่ “H0 เป็นจริงแน่นอน”
One-sample t-test — เทียบค่าเฉลี่ยตัวอย่างกับค่าที่มีคนอ้าง
สมมติผู้บริหารอ้างว่า ยอดออเดอร์เฉลี่ยของร้านคือ 5000 บาท ทีมอยากตรวจสอบด้วยตัวอย่าง 200 ออเดอร์ — ใช้ตัวอย่างชุด
เดียวกับบทที่แล้วเป๊ะๆ (seed=42, size=200, replace=False จากยอดขายต่อออเดอร์ที่เสร็จสมบูรณ์) เพื่อให้ผลลัพธ์ตรงกันทุกครั้ง:
import pandas as pd
import numpy as np
from scipy import stats
orders = pd.read_csv("data/orders.csv")
order_items = pd.read_csv("data/order_items.csv")
merged = order_items.merge(orders, on='order_id')
completed = merged[merged['status'] == 'completed'].copy()
completed['line'] = completed['quantity'] * completed['unit_price']
order_rev = completed.groupby('order_id')['line'].sum()
rng = np.random.default_rng(42)
sample = rng.choice(order_rev.values, size=200, replace=False)
t_stat, p_value = stats.ttest_1samp(sample, 5000)
ในที่นี้ H0 คือ “ค่าเฉลี่ยจริง = 5000” ส่วน H1 คือ “ค่าเฉลี่ยจริง ≠ 5000” — ผลที่ได้: t_stat ≈ 1.908, p_value ≈ 0.0578
ที่ระดับนัยสำคัญ (significance level) มาตรฐาน α = 0.05: เพราะ p_value (0.0578) > α (0.05) เราจึง fail to reject H0 —
ข้อมูลตัวอย่างนี้ไม่ได้ขัดแย้งกับคำกล่าวที่ว่ายอดเฉลี่ยคือ 5000 บาทอย่างมีนัยสำคัญ
p-value คืออะไรกันแน่ (และไม่ใช่อะไร)
p-value คือ: ถ้า H0 เป็นจริงจริงๆ โอกาสที่จะสุ่มตัวอย่างมาแล้วได้ผลลัพธ์สุดขั้วเท่านี้หรือมากกว่านี้ คือเท่าไหร่
ในตัวอย่างข้างบน p_value ≈ 0.0578 แปลว่า “ถ้ายอดเฉลี่ยจริงเท่ากับ 5000 บาทจริงๆ โอกาสที่จะสุ่มตัวอย่าง 200 ออเดอร์มาแล้ว
ได้ค่าเฉลี่ยที่ต่างจาก 5000 มากเท่ากับที่เราเห็น (หรือมากกว่า) คือ 5.78%”
ข้อควรระวังที่พบบ่อยที่สุด: อย่าอ่าน
p_value = 0.0578ว่า “มีโอกาส 94.2% ที่คำกล่าวของผู้บริหารเป็นจริง” — p-value ไม่ใช่ ความน่าจะเป็นที่ H0 เป็นจริง มันคือความน่าจะเป็นของข้อมูล ภายใต้สมมติฐานว่า H0 เป็นจริง ต่างหาก คนละคำถามกัน
สรุปการอ่านผลให้ถูก:
| ผลลัพธ์ | อ่านว่าอะไร | อ่านผิดที่พบบ่อย |
|---|---|---|
p < 0.05 (ปฏิเสธ H0) |
มีหลักฐานเพียงพอที่จะบอกว่าข้อมูลขัดแย้งกับ H0 อย่างมีนัยสำคัญ | “พิสูจน์แล้วว่า H1 เป็นจริง 100%” |
p ≥ 0.05 (fail to reject) |
ข้อมูลที่มีไม่พอจะหักล้าง H0 ได้ | “พิสูจน์แล้วว่า H0 เป็นจริง 100%” |
Two-sample t-test — เทียบค่าเฉลี่ยระหว่างสองกลุ่ม
อีกคำถามที่พบบ่อยกว่าคือเทียบค่าเฉลี่ยของ สองกลุ่ม เช่น “ลูกค้าที่มาจากช่องทาง organic กับ ads ใครสั่งซื้อเฉลี่ยต่อ ออเดอร์สูงกว่ากัน” ที่นี่ H0 คือ “ค่าเฉลี่ยของสองกลุ่มเท่ากัน”
import pandas as pd
from scipy import stats
orders = pd.read_csv("data/orders.csv")
order_items = pd.read_csv("data/order_items.csv")
customers = pd.read_csv("data/customers.csv")
completed = orders[orders['status'] == 'completed']
oc = completed.merge(customers[['customer_id', 'channel']], on='customer_id')
items = order_items.merge(oc[['order_id', 'channel']], on='order_id')
items['line'] = items['quantity'] * items['unit_price']
rev_by_order = items.groupby(['order_id', 'channel'])['line'].sum().reset_index()
organic = rev_by_order[rev_by_order['channel'] == 'organic']['line'] # n=3010, mean≈5405.2
ads = rev_by_order[rev_by_order['channel'] == 'ads']['line'] # n=2034, mean≈5015.77
t2, p2 = stats.ttest_ind(organic, ads, equal_var=False)
ผลที่ได้: t2 ≈ 1.8537, p2 ≈ 0.0639 — เพราะ p2 > 0.05 จึง fail to reject H0 เช่นกัน: ไม่มีหลักฐานเพียงพอที่จะบอกว่า
สองช่องทางนี้มียอดสั่งซื้อเฉลี่ยต่างกันอย่างมีนัยสำคัญ แม้ตัวเลขดิบ (5405.2 vs 5015.77) จะดูต่างกันพอสมควรก็ตาม — นี่คือ
เหตุผลที่ต้องทดสอบทางสถิติ ไม่ใช่ดูตัวเลขดิบแล้วสรุปเอง เพราะความต่างขนาดนี้อาจเกิดจากการสุ่มตัวอย่างมาไม่เท่ากันล้วนๆ ก็ได้
ทำไมต้องใช้ equal_var=False (Welch’s t-test)
scipy.stats.ttest_ind มีสองโหมด:
equal_var=True(ค่าเริ่มต้น) — Student’s t-test ดั้งเดิม สมมติว่าความแปรปรวนของสองกลุ่มเท่ากันequal_var=False— Welch’s t-test ไม่สมมติว่าความแปรปรวนเท่ากัน
ในทางปฏิบัติ เรามักไม่รู้ล่วงหน้าว่าความแปรปรวนของสองกลุ่มเท่ากันจริงไหม (และข้อมูลยอดขาย/รายได้มักมีความแปรปรวนต่างกัน
ระหว่างกลุ่มด้วย) จึงแนะนำให้ใช้ equal_var=False เป็นค่าเริ่มต้นเสมอเมื่อไม่แน่ใจ — ปลอดภัยกว่าและแทบไม่มีข้อเสีย
ทำไม α ที่เลือกไว้ล่วงหน้าถึงสำคัญ
α = 0.05 เป็นธรรมเนียมที่ใช้กันแพร่หลาย แต่ไม่ใช่กฎเหล็กทางคณิตศาสตร์ — ถ้าทีมเลือกใช้ α = 0.10 แทน ผลการทดสอบ
two-sample t-test ข้างบน (p2 ≈ 0.0639) จะกลายเป็น “มีนัยสำคัญ” ทันที (0.0639 < 0.10) ทั้งที่ข้อมูลไม่ได้เปลี่ยนเลย
สักตัวเลขเดียว — นี่คือเหตุผลว่าทำไมควรกำหนด α ไว้ล่วงหน้าก่อนดูผล ไม่ใช่เลือก α ทีหลังเพื่อให้ได้ข้อสรุปที่อยากได้
(รูปแบบหนึ่งของ p-hacking) และทำไมผลลัพธ์ที่ p-value อยู่ใกล้เส้นแบ่ง (เช่น 0.049 vs 0.051) ไม่ควรถูกตีความว่าต่างกัน
อย่างมีนัยสำคัญในทางปฏิบัติ
ตัวอย่างแก้โจทย์
กด "ขั้นต่อไป" ทีละขั้น ลองรันและแก้โค้ดในแต่ละขั้นได้
ผู้บริหารอ้างว่ายอดออเดอร์เฉลี่ยคือ 5000 บาท — จริงไหม
ผู้บริหารอ้างว่ายอดออเดอร์เฉลี่ยของร้านอยู่ที่ 5000 บาท ทีมอยากตรวจสอบคำกล่าวนี้ด้วยตัวอย่าง 200 ออเดอร์ (สุ่มแบบเดียวกับบทที่แล้ว) ว่าข้อมูลตัวอย่างขัดแย้งกับคำกล่าวนี้อย่างมีนัยสำคัญหรือไม่
ตั้งสมมติฐาน: H0 คือ "ค่าเฉลี่ยจริงของยอดออเดอร์ = 5000 บาท" (สมมติฐานว่าง ที่เราพยายามหาหลักฐานมาหักล้าง) ส่วน H1 คือ "ค่าเฉลี่ยจริง ≠ 5000 บาท" จากนั้นสร้างตัวอย่างเดียวกับบทที่แล้วเป๊ะๆ (seed 42, ขนาด 200, ไม่คืนที่)
ลูกค้าช่องทาง organic กับ ads ใครสั่งของแพงกว่ากันจริงไหม
ทีมการตลาดสงสัยว่าลูกค้าที่มาจากช่องทาง organic กับ ads มียอดสั่งซื้อเฉลี่ยต่อออเดอร์ต่างกันหรือไม่ เทียบด้วย two-sample t-test ระหว่างสองกลุ่ม
เตรียมยอดขายต่อออเดอร์ (เฉพาะออเดอร์ที่เสร็จสมบูรณ์) พร้อมช่องทางของลูกค้าแต่ละออเดอร์ แล้วแยกเป็นสองกลุ่มตาม channel
เติมช่องว่าง
แทน ___ ด้วยโค้ดที่ถูกต้อง แล้วกดตรวจคำตอบ
ทำ one-sample t-test ทดสอบว่ายอดออเดอร์เฉลี่ยของตัวอย่าง (สุ่มแบบเดียวกับบทที่แล้ว seed 42 ขนาด 200 ไม่คืนที่)
ต่างจากคำกล่าวอ้างที่ว่า "ยอดเฉลี่ยคือ 5000 บาท" อย่างมีนัยสำคัญหรือไม่ เก็บค่าสถิติ t ไว้ในตัวแปร t_stat
และ p-value ไว้ในตัวแปร p_value เติม ___ ให้ครบ
แบบฝึกท้ายบท
บทนี้จะนับว่าเสร็จเมื่อผ่านข้อพื้นฐานและประยุกต์ครบ ข้อท้าทายได้ดาวเพิ่ม
🟢 พื้นฐาน
คำนวณค่าสถิติ t ของ one-sample t-test ที่เทียบค่าเฉลี่ยของตัวอย่าง (สุ่มแบบเดียวกับบทที่แล้ว: seed 42, ขนาด 200,
ไม่คืนที่ จากยอดขายต่อออเดอร์ที่เสร็จสมบูรณ์) กับค่าที่อ้างว่า μ = 5000 เก็บไว้ในตัวแปร t_stat
จาก one-sample t-test เดียวกับข้อที่แล้ว (ตัวอย่างเดิม เทียบกับ μ = 5000) เก็บ p-value ไว้ในตัวแปร p_value
จาก p-value ของข้อที่แล้ว ที่ระดับนัยสำคัญ α=0.05 เราปฏิเสธ H0 (ที่ว่ายอดเฉลี่ย = 5000) หรือไม่
เก็บผลเป็น boolean ไว้ในตัวแปร reject_h0
คำนวณยอดขายเฉลี่ยต่อออเดอร์ (เฉพาะออเดอร์ที่เสร็จสมบูรณ์) ของลูกค้าที่มาจากช่องทาง organic
เก็บไว้ในตัวแปร mean_organic
🟡 ประยุกต์
เหมือนข้อที่แล้ว แต่คำนวณยอดขายเฉลี่ยต่อออเดอร์ของลูกค้าที่มาจากช่องทาง ads แทน เก็บไว้ในตัวแปร mean_ads
ทำ two-sample t-test แบบ Welch's (equal_var=False) เทียบยอดขายเฉลี่ยต่อออเดอร์ระหว่างกลุ่ม organic กับ ads
เก็บค่าสถิติ t ไว้ในตัวแปร t2
จาก two-sample t-test เดียวกับข้อที่แล้ว เก็บ p-value ไว้ในตัวแปร p2
🔴 ท้าทาย
จาก p-value ของ two-sample t-test (ข้อที่แล้ว) ที่ระดับนัยสำคัญ α=0.05 มีหลักฐานเพียงพอไหมว่าลูกค้า organic
สั่งซื้อเฉลี่ยต่อออเดอร์สูงกว่าลูกค้า ads อย่างมีนัยสำคัญ เก็บผลเป็น boolean ไว้ในตัวแปร is_significant
ถ้าทีมเลือกใช้ระดับนัยสำคัญที่หลวมกว่าเดิมคือ α=0.10 (แทนที่จะเป็น 0.05) ผลการทดสอบ two-sample t-test
ข้อ e7 จะกลายเป็น "มีนัยสำคัญ" หรือไม่ (p2 < 0.10) เก็บผลเป็น boolean ไว้ในตัวแปร sig_at_10pct
— ตัวอย่างที่เป็นรูปธรรมว่าทำไมค่า α ที่เลือกไว้ล่วงหน้าถึงสำคัญ
Quiz
สรุป
- null hypothesis (H0) คือสมมติฐาน "ไม่มีผล/ไม่มีความแตกต่าง" ที่เราพยายามหาหลักฐานมาหักล้าง ไม่ใช่สิ่งที่อยากพิสูจน์
- one-sample t-test (
ttest_1samp) เทียบค่าเฉลี่ยตัวอย่างกับค่าที่อ้าง ส่วน two-sample t-test (ttest_ind, แนะนำequal_var=False) เทียบค่าเฉลี่ยระหว่างสองกลุ่ม - p-value คือความน่าจะเป็นที่จะเห็นผลสุดขั้วเท่านี้หรือมากกว่า ถ้า H0 เป็นจริง ไม่ใช่ความน่าจะเป็นที่ H0 เป็นจริง และ fail to reject H0 ไม่ได้แปลว่า H0 ถูกพิสูจน์แล้ว
คำถามสัมภาษณ์
อธิบาย p-value ให้ผู้บริหารที่ไม่ได้เรียนสถิติฟัง โดยไม่ใช้คำว่า "null hypothesis"
อาจอธิบายแบบนี้: "สมมติว่าตัวเลขที่เราอยากตรวจสอบนั้นถูกต้องจริงๆ (เช่น ยอดเฉลี่ยคือ 5000 บาทจริง) ถ้าเราสุ่มตัวอย่างมาดูซ้ำๆ หลายๆ ครั้ง มีโอกาสกี่เปอร์เซ็นต์ที่จะได้ตัวอย่างที่ผลต่างจากตัวเลขนั้นมากเท่ากับหรือมากกว่าที่เราเห็นในครั้งนี้" ถ้าตัวเลขนี้ (p-value) ต่ำมาก แปลว่าสิ่งที่เราสังเกตเห็นไม่น่าจะเกิดจากความบังเอิญเฉยๆ ถ้าตัวเลขไม่ถูกต้องจริง — จุดสำคัญคือ p-value ไม่ได้บอกว่า "ตัวเลขที่อ้างมีโอกาสถูกกี่เปอร์เซ็นต์" มันตอบคำถามกลับด้านกัน
ทำไมการทดสอบทางสถิติที่ไม่ปฏิเสธ H0 ไม่ได้แปลว่าไม่มีความแตกต่างจริง
เพราะ "fail to reject H0" แปลว่าข้อมูลที่มีอยู่ไม่มากพอจะให้หลักฐานที่แข็งแรงพอจะหักล้าง H0 ได้ ไม่ได้แปลว่า H0 ถูกพิสูจน์ ว่าเป็นจริง — สาเหตุที่พบบ่อยคือขนาดตัวอย่างเล็กเกินไป (statistical power ไม่พอ) หรือผลต่างที่แท้จริงมีขนาดเล็กจนตรวจจับ ไม่ได้ด้วยตัวอย่างขนาดนี้ ความแตกต่างจริงอาจยังมีอยู่ แค่ข้อมูลที่เก็บมายังไม่พอจะเห็นมันชัดเจน
ถ้า p-value = 0.049 กับ 0.051 ควรตีความต่างกันมากไหม
ไม่ควรตีความต่างกันมาก — เส้นแบ่ง α=0.05 เป็นธรรมเนียมที่ตกลงกันไว้ล่วงหน้า ไม่ใช่กำแพงที่มีความหมายพิเศษทางสถิติจริงๆ ผลลัพธ์ทั้งสองแทบไม่ต่างกันเลยในแง่ความแข็งแรงของหลักฐาน การตัดสินใจทางธุรกิจที่เปลี่ยนขั้วสุดโต่ง (ทำ/ไม่ทำ) แค่เพราะ p-value ต่างกัน 0.002 จึงเป็นความเสี่ยงที่ควรระวัง ควรดูขนาดของผลต่าง (effect size) และบริบทประกอบด้วยเสมอ ไม่ใช่ยึด เกณฑ์ 0.05 แบบทื่อๆ