เทคนิคที่ 029 · PYTHON / JAVASCRIPT

ฮิสโทแกรมHistogram

ดูรูปร่างของข้อมูล ไม่ใช่แค่ค่าเฉลี่ย

1
พื้นฐานควรรู้ก่อน · อ่านกราฟแท่งเป็น
ลองเล่น

01ภาพรวมของกราฟนี้

Histogram แบ่งช่วงของตัวเลขหนึ่งคอลัมน์ออกเป็นถัง (bin) ที่กว้างเท่ากัน แล้ววาดแท่งสูงตามจำนวนข้อมูลในแต่ละถัง ผลที่ได้คือรูปร่างของการกระจาย ซึ่งบอกอะไรที่ค่าเฉลี่ยกับส่วนเบี่ยงเบนมาตรฐานสองตัวเลขบอกไม่ได้

เปรียบเทียบให้เห็นภาพ

เหมือนเรียงนักเรียนทั้งโรงเรียนตามส่วนสูง แล้วให้คนที่สูงใกล้กันยืนรวมเป็นแถวเดียวกัน แถวไหนคนเยอะแปลว่าช่วงส่วนสูงนั้นพบบ่อย

ใช้ที่ไหนบ้าง — ตรวจว่าข้อมูลเบ้ไปทางไหนก่อนเลือกโมเดล ดูว่าราคาสินค้ากระจุกที่ช่วงไหน หรือหาว่ามีสองกลุ่มปนกันอยู่ในข้อมูลหรือไม่

สร้างอย่างไร

  1. ขั้นที่ 1เลือกคอลัมน์ตัวเลขหนึ่งคอลัมน์
  2. ขั้นที่ 2กำหนดจำนวนหรือความกว้างของ bin
  3. ขั้นที่ 3นับจำนวนข้อมูลในแต่ละ bin
  4. ขั้นที่ 4วาดแท่งสูงตามจำนวน โดยแท่งชิดกัน

ตัวอย่างคลาสสิก · การกระจายของความสูงมนุษย์ และการค้นพบว่ามันมีสองยอด

เมื่อรวมความสูงของผู้ชายและผู้หญิงไว้ในชุดเดียว histogram จะแสดงสองยอด ซึ่งเป็นตัวอย่างคลาสสิกที่สุดของ "ค่าเฉลี่ยเดียวไม่ได้เล่าความจริง" เพราะค่าเฉลี่ยจะตกอยู่ตรงหุบเขาระหว่างยอดทั้งสอง ซึ่งเป็นค่าที่แทบไม่มีใครมีจริง

NIST/SEMATECH e-Handbook · Histogram

02อธิบายภาษาคนเข้าใจง่าย ๆ

สมมติเรามีคะแนนสอบของนักเรียน 500 คน ถ้าบอกแค่ว่าค่าเฉลี่ยคือ 65 คะแนน เรายังไม่รู้เลยว่าเกิดอะไรขึ้นจริง ๆ อาจเป็นว่าเกือบทุกคนได้ราว ๆ 65 หรืออาจเป็นว่าครึ่งหนึ่งได้ 40 อีกครึ่งได้ 90 ซึ่งสองแบบนี้ต้องแก้ปัญหาคนละทางกันโดยสิ้นเชิง

Histogram ตอบคำถามนี้ด้วยวิธีง่ายมาก คือแบ่งช่วงคะแนนเป็นช่วงย่อยที่กว้างเท่ากัน เช่น 0 ถึง 10 ไปจนถึง 90 ถึง 100 แล้วนับว่าแต่ละช่วงมีนักเรียนกี่คน จากนั้นวาดแท่งให้สูงตามจำนวนที่นับได้

สิ่งที่ทำให้ Histogram ต่างจากกราฟแท่งธรรมดาคือแกนนอนของมันเป็นตัวเลขต่อเนื่อง ไม่ใช่หมวดหมู่ แท่งจึงต้องวางชิดกันเพื่อสื่อว่าช่วงค่าต่อเนื่องกันไป ถ้าเว้นช่องว่างระหว่างแท่ง คนอ่านจะเข้าใจผิดว่าเป็นหมวดที่แยกขาดจากกัน

จำนวน bin คือการตัดสินใจที่สำคัญที่สุดและเป็นจุดที่คนพลาดบ่อยที่สุด ถ้า bin น้อยเกินไปรูปร่างจะเรียบจนกลืนรายละเอียดที่สำคัญ เช่น ยอดสองยอดจะรวมเป็นยอดเดียว ถ้า bin มากเกินไปแท่งจะหยักขึ้นลงตามความบังเอิญของข้อมูลจนดูเหมือนมีรูปแบบทั้งที่ไม่มี

วิธีปฏิบัติที่ปลอดภัยคือลองหลายค่าแล้วดูว่าข้อสรุปเปลี่ยนไหม ถ้าเห็นสองยอดที่ bin เท่าไรก็ยังเห็น แปลว่ามีจริง แต่ถ้ายอดที่สองหายไปเมื่อเปลี่ยน bin เล็กน้อย ก็ยังสรุปไม่ได้ Playground ด้านล่างออกแบบมาให้ลองข้อนี้โดยเฉพาะ

03ข้อมูลที่ต้องใช้

รูปทรงของข้อมูลข้อมูลดิบรายแถว (ยังไม่สรุป)
จำนวนมิติที่เข้ารหัสได้2 มิติ
จำนวนแถวที่เหมาะ200–50,000 แถวขั้นต่ำ 30 แถว
เส้นฐานศูนย์ต้องเริ่มที่ศูนย์แกนตั้งคือความถี่ ความสูงของแท่งสื่อจำนวนโดยตรง ถ้าไม่เริ่มที่ศูนย์ ความสูงจะโกหกทันที

คอลัมน์ที่ต้องเตรียม

คอลัมน์ทำหน้าที่ชนิดเข้ารหัสด้วยจำนวนค่าที่เหมาะหมายเหตุ
valueค่าที่ต้องการดูการกระจายตัวเลขต่อเนื่องxต่อเนื่อง★ ต้องเป็นข้อมูลดิบรายแถว ห้ามสรุปมาก่อน — ถ้ามีแต่ค่าเฉลี่ยแล้ว วาด histogram ไม่ได้

ต้องเตรียมข้อมูลอย่างไรก่อนวาด

  1. กรองค่าหายออก และบันทึกไว้ว่ากรองไปกี่แถว
  2. ตัดสินใจเรื่องค่าผิดปกติก่อนวาด: จะคงไว้ จะตัด หรือจะใช้ scale log
  3. ถ้าข้อมูลเบ้ขวาแรง พิจารณา log-transform แล้วบอกในชื่อแกนว่าทำแล้ว

ค่าที่หายไป — ตัดแถวที่ค่าว่างออกจากการนับ และเขียนจำนวนที่ตัดไว้ใน caption เสมอ — ถ้าค่าหายไม่ได้สุ่ม การกระจายที่เห็นจะบิด

สเกลของแกน — แกน x ใช้ linear ตามค่าจริง ถ้าข้อมูลเบ้ขวาแรง เช่น รายได้ ใช้ log บนแกน x และคง bin ให้กว้างเท่ากันในสเกล log

กราฟจะเริ่มพังเมื่อไร

ต่ำกว่า 30 แถว รูปร่างจะเป็นผลของความบังเอิญมากกว่าของจริง — ใช้ strip-plot หรือ dot-plot แทน

หน้าตาไฟล์ที่ต้องเตรียม

value

04อ่านแล้วเห็นอะไร

สิ่งที่เห็นได้จากภาพ

  • ข้อมูลกระจุกอยู่ช่วงไหนมากที่สุด
  • การกระจายมียอดเดียวหรือหลายยอด — หลายยอดมักแปลว่ามีหลายกลุ่มปนกันอยู่
  • เบ้ไปทางไหน — หางยาวทางขวาแปลว่ามีค่าสูงผิดปกติจำนวนน้อย
  • ช่วงที่ไม่มีข้อมูลเลย ซึ่งมักเป็นสัญญาณของการปัดค่าหรือการตัดข้อมูล

สิ่งที่กราฟนี้ตอบไม่ได้

  • บอกไม่ได้ว่าแต่ละแถวเป็นของใคร — ข้อมูลรายตัวหายไปหมดหลังนับรวม
  • เทียบสองกลุ่มโดยตรงไม่ได้ถ้าซ้อนกันบนภาพเดียว ให้ใช้ box-plot หรือ small-multiples แทน
  • ไม่บอกลำดับเวลา แม้ข้อมูลจะเก็บมาตามเวลาก็ตาม

อ่านตามลำดับนี้

  1. ดูตำแหน่งยอดก่อน — ข้อมูลส่วนใหญ่อยู่ตรงไหน
  2. ดูความกว้าง — แคบแปลว่าค่าใกล้เคียงกัน กว้างแปลว่ากระจาย
  3. ดูความสมมาตร — หางข้างไหนยาวกว่า
  4. ดูจำนวนยอด — มากกว่าหนึ่งยอดแปลว่าควรแยกวิเคราะห์

คำถามที่ตอบได้

  • ค่าส่วนใหญ่ตกอยู่ในช่วงไหน
  • ข้อมูลชุดนี้มีแค่กลุ่มเดียวหรือหลายกลุ่ม
  • ใช้ค่าเฉลี่ยเป็นตัวแทนได้ไหม หรือควรใช้มัธยฐาน

สัญญาณว่ามีอะไรผิด

  • แท่งสูงผิดปกติที่ค่ากลม ๆ เช่น 0, 50, 100 มักเป็นค่าที่คนกรอกมั่วหรือค่า default
  • แท่งเดียวสูงลิ่วที่ขอบขวาสุด มักแปลว่าข้อมูลถูก cap ไว้ที่ค่าสูงสุด
  • รูปร่างเปลี่ยนไปคนละเรื่องเมื่อขยับจำนวน bin นิดเดียว แปลว่าข้อมูลน้อยเกินกว่าจะสรุป

05เหมาะกับการนำเสนอแบบไหน

ผู้ชมที่เหมาะคนที่ทำงานกับข้อมูลโดยตรง หรือผู้อ่านรายงานที่พร้อมใช้เวลากับภาพ
เวลาที่ผู้ชมต้องใช้ไม่กี่วินาที
ขนาดเล็กสุดที่ยังอ่านได้320×200 px
สำรวจข้อมูลด้วยตัวเอง
รายงานเชิงวิเคราะห์
สิ่งพิมพ์
สไลด์ผู้บริหารผู้บริหารต้องการข้อสรุป ไม่ใช่รูปร่างการกระจาย ถ้าจำเป็นให้ใช้ box plot หรือเขียนเป็นประโยค
dashboard ที่อัปเดตตลอดรูปร่างการกระจายไม่ได้เปลี่ยนรายนาที การวางไว้บน dashboard จึงกินพื้นที่โดยไม่ให้ข้อมูลใหม่

ทำอย่างไรให้คนอ่านได้ข้อสรุปโดยไม่ต้องบรรยาย

  1. ลากเส้นแนวตั้งที่ค่าเฉลี่ยและมัธยฐาน แล้วติดป้ายทั้งสอง — ระยะห่างของสองเส้นนี้บอกความเบ้ทันที
  2. เขียนจำนวนแถวทั้งหมดและจำนวนที่ถูกตัดออกไว้ใน caption
  3. ระบุความกว้างของ bin ไว้ใน caption เสมอ เพราะมันเปลี่ยนรูปที่เห็น

รูปแบบชื่อกราฟที่ควรใช้

<ตัวแปร> ของ <ประชากร> กระจุกที่ <ช่วง> และมีหางยาวทาง <ซ้าย/ขวา>

06เอาไปใช้ต่อ

โค้ดทุกชิ้นอ่านไฟล์ histogram-sample.csv ชุดเดียวกับที่ Playground ใช้ และตัวเลขในโค้ดเปลี่ยนตามค่าที่คุณปรับด้านล่าง

# ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*"
# ดาวน์โหลด histogram-sample.csv จากหน้านี้ แล้ววางไว้ข้างสคริปต์
import matplotlib
matplotlib.use("Agg")   # รันแบบไม่มีหน้าจอ ถ้ารันในเครื่องตัวเองลบบรรทัดนี้ได้
import matplotlib.pyplot as plt
import pandas as pd

C1, C2, C3 = "#2a78d6", "#eb6834", "#1baf7a"   # palette ของคลัง เปลี่ยนเป็นสีองค์กรได้
INK, SURFACE = "#0b0b0b", "#fcfcfb"

df = pd.read_csv("histogram-sample.csv")
df["value"] = pd.to_numeric(df["value"], errors="coerce")
BINS = 24
SCALE = "linear"   # ← ค่าจาก Playground

fig, ax = plt.subplots(figsize=(7.6, 4.2), facecolor=SURFACE)
ax.set_facecolor(SURFACE)

values = df["value"].dropna()
dropped = len(df) - len(values)
ax.hist(values, bins=BINS, color=C1, edgecolor=SURFACE, linewidth=1.2)
ax.axvline(values.mean(), color=INK, linewidth=1.2, linestyle="--")     # ค่าเฉลี่ย
ax.axvline(values.median(), color=INK, linewidth=1.2)                    # มัธยฐาน
ax.set_xlabel("ค่าที่วัดได้"); ax.set_ylabel("จำนวนข้อมูล")
ax.set_title(f"การกระจาย · {BINS} bin · n={len(values)} (ตัดค่าว่าง {dropped} แถว)", loc="left")
ax.grid(axis="y", alpha=0.25); ax.set_axisbelow(True)

ax.spines[["top", "right"]].set_visible(False)   # ลดเส้นที่ไม่ได้ให้ข้อมูล
fig.tight_layout()
fig.savefig("histogram.png", dpi=160)
print("เขียน histogram.png แล้ว ·", len(df), "แถว")

ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*" · ทดสอบกับเวอร์ชัน 3.10

07ลองเล่นเพื่อเข้าใจ

ข้อมูลตัวอย่าง · 600 แถว · seed 42● คำนวณใหม่ในเบราว์เซอร์ทันทีที่ปรับค่า
จำนวนถังที่ใช้แบ่งช่วงค่า
เปลี่ยนวิธีวางค่าบนแกนนอน
0.0020.040.060.080.0จำนวนข้อมูล50.060.070.080.0ค่าที่วัดได้43.3–45.1 · 13 ค่า45.1–46.8 · 16 ค่า46.8–48.5 · 41 ค่า48.5–50.3 · 52 ค่า50.3–52.0 · 62 ค่า52.0–53.7 · 58 ค่า53.7–55.5 · 47 ค่า55.5–57.2 · 32 ค่า57.2–58.9 · 29 ค่า58.9–60.6 · 9 ค่า60.6–62.4 · 0 ค่า62.4–64.1 · 1 ค่า64.1–65.8 · 0 ค่า65.8–67.6 · 0 ค่า67.6–69.3 · 0 ค่า69.3–71.0 · 0 ค่า71.0–72.8 · 8 ค่า72.8–74.5 · 16 ค่า74.5–76.2 · 28 ค่า76.2–77.9 · 50 ค่า77.9–79.7 · 61 ค่า79.7–81.4 · 39 ค่า81.4–83.1 · 28 ค่า83.1–84.9 · 10 ค่ามัธยฐาน 56.0เฉลี่ย 62.5
ความกว้างของ bin1.73
จำนวนแถวที่ใช้600
ความเบ้ (skewness)0.32
ยอดที่นับได้2
ดูข้อมูลเป็นตาราง (24 แถว)
ฮิสโทแกรม 24 bin จาก 600 ค่า มัธยฐาน 56.0 ความเบ้ 0.32
ช่วงจำนวน
43.3–45.113
45.1–46.816
46.8–48.541
48.5–50.352
50.3–52.062
52.0–53.758
53.7–55.547
55.5–57.232
57.2–58.929
58.9–60.69
60.6–62.40
62.4–64.11
64.1–65.80
65.8–67.60
67.6–69.30
69.3–71.00
71.0–72.88
72.8–74.516
74.5–76.228
76.2–77.950
77.9–79.761
79.7–81.439
81.4–83.128
83.1–84.910

ลองแล้วจะเห็นอะไร

  • จำนวน bin: ลากไปซ้ายสุดจะเห็นยอดเดียว ลากไปขวาสุดจะเห็นแท่งหยักเป็นฟันปลา — ข้อมูลชุดนี้มีสองยอดจริง และจะเห็นชัดที่สุดราว 20 ถึง 30 bin
  • สเกลแกน x: กับข้อมูลที่เบ้ขวา การใช้ log ทำให้เห็นโครงสร้างที่ linear บีบจนแบน

ข้อควรระวัง

  1. ใช้จำนวน bin ค่า default ของไลบรารีแล้วสรุปเลย — ต้องลองหลายค่าเสมอก่อนสรุปรูปร่าง
  2. เว้นช่องว่างระหว่างแท่ง ทำให้ดูเหมือนกราฟแท่งหมวดหมู่ ทั้งที่แกนเป็นค่าต่อเนื่อง
  3. เอา histogram ของสองกลุ่มมาซ้อนทับกันจนอ่านไม่ออก — ใช้ small multiples หรือ density แทน
  4. แกนตั้งไม่เริ่มที่ศูนย์ ทำให้สัดส่วนความสูงโกหก
  5. วาด histogram จากข้อมูลที่สรุปมาแล้ว เช่น ค่าเฉลี่ยรายจังหวัด ทั้งที่ควรใช้ข้อมูลดิบ
  6. ไม่บอกว่าตัดค่าผิดปกติออกไปแล้ว ทำให้คนอ่านเห็นการกระจายที่ไม่ตรงความจริง

เมื่อไหร่ไม่ควรใช้กราฟนี้

  • ข้อมูลน้อยกว่า 30 แถวใช้แทน: strip-plot หรือ dot-plot ที่แสดงทุกจุดจริง
  • ต้องเทียบการกระจายมากกว่า 3 กลุ่มใช้แทน: box-plot หรือ ridgeline-plot
  • ตัวแปรเป็นหมวดหมู่ไม่ใช่ตัวเลขต่อเนื่องใช้แทน: bar-chart
  • ต้องการอ่านว่าสัดส่วนเท่าไรที่ต่ำกว่าค่าหนึ่งใช้แทน: ecdf-plot ซึ่งอ่านค่านั้นได้ตรง ๆ

กราฟที่เกี่ยวข้อง

เทคนิคถัดไป · กราฟความหนาแน่น