Data Analyst › Visualization และ storytelling

วาดกราฟด้วย matplotlib และ seaborn

ประมาณ 30 นาที

เป้าหมายของบท

  • ใช้ matplotlib วาดกราฟพื้นฐาน (plt.plot, plt.bar, plt.hist, plt.scatter)
  • ใช้ seaborn วาดกราฟสถิติทั่วไปด้วย syntax ที่สั้นกว่าและ default styling ที่อ่านง่ายกว่า (sns.lineplot, sns.barplot, sns.histplot, sns.scatterplot, sns.boxplot)
  • ใส่ title และ axis label ให้กราฟสื่อความหมายได้ด้วยตัวเอง ไม่ต้องให้คนอ่านเดาเอง

จากเลือกกราฟถูก ไปสู่การวาดจริง

บทที่แล้วเรียนวิธีเลือกชนิดกราฟให้ตรงกับคำถาม บทนี้มาเรียนวิธีวาดกราฟเหล่านั้นจริงๆ ด้วย Python สองไลบรารีที่ใช้บ่อยที่สุดในงาน DA คือ matplotlib (ไลบรารีพื้นฐาน ควบคุมได้ละเอียดทุกอย่าง) และ seaborn (สร้างอยู่บน matplotlib อีกที เขียนสั้นกว่าและมี default styling ที่อ่านง่ายกว่าสำหรับกราฟสถิติทั่วไป)

matplotlib พื้นฐาน

matplotlib.pyplot (นิยมเรียกย่อว่า plt) มีฟังก์ชันวาดกราฟพื้นฐานที่ครอบคลุมเกือบทุกชนิดกราฟที่เจอในบทที่แล้ว:

ฟังก์ชัน ใช้วาดกราฟแบบไหน
plt.plot(x, y) กราฟเส้น — ดูเทรนด์ตามเวลา
plt.bar(x, y) กราฟแท่ง — เทียบค่าระหว่างหมวดหมู่
plt.hist(data, bins=...) histogram — ดูการกระจายของตัวแปรเดียว
plt.scatter(x, y) scatter plot — ดูความสัมพันธ์ระหว่างตัวแปรตัวเลขสองตัว

ตัวอย่างกราฟเส้นแสดงรายได้รายเดือน:

import pandas as pd
import matplotlib.pyplot as plt

orders = pd.read_csv("data/orders.csv")
items = pd.read_csv("data/order_items.csv")
items['line'] = items['quantity'] * items['unit_price']
merged = items.merge(orders, on='order_id')
completed = merged[merged['status'] == 'completed'].copy()
completed['month'] = pd.to_datetime(completed['order_date']).dt.to_period('M').astype(str)
monthly = completed[completed['month'].str.startswith('2025')].groupby('month')['line'].sum()

plt.plot(monthly.index, monthly.values)
plt.title('รายได้รวมรายเดือน ปี 2025')
plt.xlabel('เดือน')
plt.ylabel('รายได้ (บาท)')
plt.xticks(rotation=45)

สังเกตว่านอกจาก plt.plot(...) แล้ว มีอีก 3 บรรทัดสำหรับ title/xlabel/ylabel — บรรทัดพวกนี้ไม่ได้เปลี่ยนข้อมูลที่แสดงเลย แต่ทำให้คนอ่านรู้ทันทีว่ากราฟนี้กำลังบอกอะไร โดยไม่ต้องเดาหรือถามเพิ่ม

seaborn: ทางลัดสำหรับกราฟสถิติ

seaborn (นิยมเรียกย่อว่า sns) ทำสิ่งเดียวกับ matplotlib ได้ในโค้ดที่สั้นกว่า โดยเฉพาะเมื่อข้อมูลอยู่ในรูป DataFrame อยู่แล้ว — แค่บอกว่าคอลัมน์ไหนไปแกนไหน:

matplotlib seaborn (เทียบเท่ากัน)
plt.plot(x, y) sns.lineplot(data=df, x=..., y=...)
plt.bar(x, y) sns.barplot(data=df, x=..., y=...)
plt.hist(data, bins=...) sns.histplot(data=df, x=..., bins=...)
plt.scatter(x, y) sns.scatterplot(data=df, x=..., y=...)
(ไม่มีฟังก์ชันตรงๆ ใน matplotlib) sns.boxplot(data=df, x=..., y=...) — box plot เทียบหลายกลุ่ม
import pandas as pd
import seaborn as sns

products = pd.read_csv("data/products.csv")

sns.histplot(data=products, x='price', bins=20)

โค้ดข้างบนสั้นกว่า plt.hist(products['price'], bins=20) แค่นิดเดียว แต่ seaborn จะจัดสี ขอบแท่ง และสไตล์ โดยรวมให้ดูเป็นมืออาชีพขึ้นโดยอัตโนมัติ — ยิ่งเห็นชัดขึ้นเมื่อกราฟซับซ้อนขึ้น เช่น การแยกสีตามกลุ่ม (hue=...) หรือ box plot ที่เทียบหลายหมวดหมู่พร้อมกัน

ข้อควรระวัง: import seaborn as sns ต้องมีเสมอก่อนเรียกใช้ sns.xxx() — ถ้าลืม จะได้ NameError ทันที (โค้ดรันไม่ผ่านเลย ไม่ใช่แค่กราฟดูแปลก) เพราะ seaborn ไม่ได้ถูก import มาพร้อมกับ pandas หรือ matplotlib

title และ label ไม่ใช่ของแต่ง

กราฟที่ไม่มี title/label ยังคง “ถูก” ในแง่ข้อมูล แต่ไม่มีประโยชน์ในการสื่อสาร เพราะคนอ่านต้องเดาเองว่าแกน x คืออะไร แกน y คืออะไร หน่วยอะไร — title ที่ดีควรบอกว่ากราฟนี้ตอบคำถามอะไร ไม่ใช่แค่ก็อปปี้ชื่อคอลัมน์มาใส่ เช่น “รายได้รวมรายเดือน ปี 2025” สื่อความหมายได้ดีกว่า “monthly” มาก

ในบทนี้ โจทย์ฝึกจะเน้นที่การเตรียมข้อมูลให้พร้อมสำหรับแต่ละชนิดกราฟ (aggregate ให้ถูกคอลัมน์ ถูกรูปทรง) เพราะนี่คือทักษะที่ตรวจได้จริงและเป็นด่านแรกก่อนจะวาดกราฟได้ถูกต้องเสมอ — ส่วนโค้ดวาดกราฟจริงจะอยู่ในตัวอย่างที่ทำให้ดู

ตัวอย่างแก้โจทย์

กด "ขั้นต่อไป" ทีละขั้น ลองรันและแก้โค้ดในแต่ละขั้นได้

กราฟเส้นรายได้รายเดือน ปี 2025 (matplotlib)

อยากดูว่ารายได้รวมในแต่ละเดือนของปี 2025 เปลี่ยนแปลงยังไง (เฉพาะออเดอร์ที่ completed)

  1. เตรียมข้อมูล monthly ก่อน — merge order_items กับ orders เพื่อคำนวณยอดต่อออเดอร์ กรองเฉพาะ completed แล้ว groupby ตามเดือน

    Python

กราฟแท่งรายได้แยกตามหมวดสินค้า (matplotlib)

อยากเทียบว่าหมวดสินค้าไหนทำรายได้เยอะที่สุด (ทุกปี เฉพาะออเดอร์ completed)

  1. เตรียมข้อมูล cat_rev — merge ให้ถึงตาราง products เพื่อได้คอลัมน์ category แล้ว groupby รวมรายได้ เรียงจากมากไปน้อยก่อนวาด (เหตุผลว่าทำไมต้องเรียง ดูในขั้นตอน pitfall)

    Python

histogram และ scatter ด้วย seaborn

อยากดูการกระจายของราคาสินค้า และความสัมพันธ์ระหว่างราคากับต้นทุน — สองคำถามนี้ตอบด้วย seaborn ได้สั้นกว่า matplotlib ดิบๆ

  1. sns.histplot(data=..., x='price', bins=20) วาด histogram ได้ในบรรทัดเดียว โดยไม่ต้องคำนวณ bin เอง (ต่างจาก pd.cut() ที่ต้องทำเองถ้าจะแบ่งช่วงแบบกำหนดเอง)

    Python

เติมช่องว่าง

แทน ___ ด้วยโค้ดที่ถูกต้อง แล้วกดตรวจคำตอบ

ยังไม่ได้ทำ

เติมชื่อคอลัมน์ให้ sns.barplot วาดกราฟแท่งจำนวนลูกค้าตามช่องทางสมัคร (แกน x คือช่องทาง แกน y คือจำนวน) แล้วเก็บตารางข้อมูลที่เตรียมไว้ในตัวแปร result ด้วย เติม ___ ให้ครบ

แบบฝึกท้ายบท

บทนี้จะนับว่าเสร็จเมื่อผ่านข้อพื้นฐานและประยุกต์ครบ ข้อท้าทายได้ดาวเพิ่ม

🟢 พื้นฐาน

🟢 พื้นฐานยังไม่ได้ทำ

เตรียมข้อมูลสำหรับป้อนให้ plt.plot() วาดกราฟเส้นรายได้รวมรายเดือนของปี 2025 (เฉพาะออเดอร์ completed) เก็บไว้ในตัวแปร result เป็น Series ที่ index เป็นเดือนรูปแบบ YYYY-MM

🟢 พื้นฐานยังไม่ได้ทำ

เตรียมข้อมูลสำหรับป้อนให้ plt.bar() วาดกราฟแท่งรายได้รวมแยกตามหมวดสินค้า (ทุกปี เฉพาะออเดอร์ completed) เรียงจากมากไปน้อย เก็บไว้ในตัวแปร result

🟢 พื้นฐานยังไม่ได้ทำ

เตรียมข้อมูลสำหรับป้อนให้ sns.histplot(data=..., x='price') ดูการกระจายของราคาสินค้า — เอาแค่คอลัมน์ price พอ (ไม่ต้องแบ่ง bin เอง seaborn จัดการให้) เก็บไว้ในตัวแปร result

🟢 พื้นฐานยังไม่ได้ทำ

เตรียมข้อมูลสำหรับป้อนให้ sns.scatterplot(data=..., x='price', y='cost') ดูความสัมพันธ์ระหว่างราคากับ ต้นทุน เก็บไว้ในตัวแปร result เป็น DataFrame สองคอลัมน์ price, cost

🟡 ประยุกต์

🟡 ประยุกต์ยังไม่ได้ทำ

เตรียมข้อมูลสำหรับป้อนให้ sns.boxplot เทียบการกระจายราคาสินค้าในแต่ละหมวด — เอาแค่สองคอลัมน์ category กับ price พอ เก็บไว้ในตัวแปร result

🟡 ประยุกต์ยังไม่ได้ทำ

เตรียมข้อมูลสำหรับกราฟแท่งจำนวนลูกค้าตามช่องทางสมัคร เรียงจากมากไปน้อย เก็บไว้ในตัวแปร result

🟡 ประยุกต์ยังไม่ได้ทำ

เขียนข้อความ title ที่ดีสำหรับกราฟเส้นรายได้รายเดือนปี 2025 — ต้องมีคำที่บอกว่ากราฟนี้แสดง "รายได้" หรือ "ยอดขาย" อะไร และต้องมีคำที่บ่งบอกช่วงเวลา (เช่น "เดือน" "ปี" หรือ "2025") เก็บไว้ในตัวแปร chart_title

🔴 ท้าทาย

🔴 ท้าทายยังไม่ได้ทำ

เตรียมข้อมูลสรุปสำหรับดู 2 มุมมองพร้อมกัน: รายได้รวม และจำนวนออเดอร์ ของแต่ละหมวดสินค้า (เฉพาะออเดอร์ completed) เก็บไว้ในตัวแปร result เป็น DataFrame ที่ index เป็น category มีคอลัมน์ revenue กับ n_orders

🔴 ท้าทายยังไม่ได้ทำ

เตรียมข้อมูลรายได้รายเดือนของปี 2024 และ 2025 รวมกัน ในรูปแบบ long format พร้อมสำหรับ sns.lineplot(data=result, x='month', y='revenue', hue='year') เก็บไว้ในตัวแปร result เป็น DataFrame มีคอลัมน์ year, month (เลข 1-12), revenue

Quiz

1. seaborn สร้างอยู่บนไลบรารีอะไร
2. ข้อดีหลักของ seaborn เทียบกับ matplotlib ดิบๆ คืออะไร
3. กราฟที่ไม่มี title/labels มีปัญหาอะไร
4. sns.histplot กับ plt.hist ต่างกันยังไงโดยหลักการ

สรุป

  • matplotlib คือไลบรารีพื้นฐานสำหรับวาดกราฟ (plt.plot/bar/hist/scatter) ส่วน seaborn เป็น wrapper ที่สร้างอยู่บน matplotlib อีกที ทำให้กราฟสถิติทั่วไปเขียนสั้นลงและมี default styling ที่ดีกว่า
  • กราฟที่ดีต้องมี title และ axis label ที่บอกว่ากำลังแสดงอะไร ไม่ใช่แค่ชื่อคอลัมน์ดิบๆ
  • โจทย์ในบทนี้ตรวจแค่ข้อมูลที่เตรียมไว้ป้อนกราฟ (data prep) เพราะทักษะที่สำคัญที่สุดคือการสรุปข้อมูลให้ถูกต้องก่อนวาดกราฟ

คำถามสัมภาษณ์

ทำไมการใส่ title/axis labels ที่ชัดเจนถึงสำคัญพอๆ กับตัวข้อมูลที่ถูกต้อง

ข้อมูลที่ถูกต้องแต่สื่อสารไม่ได้ ก็เหมือนไม่มีประโยชน์ในทางปฏิบัติ — คนอ่านกราฟ (เช่นผู้บริหาร) มักไม่มีเวลา ไล่ดูโค้ดหรือถามที่มาของแต่ละแกน ถ้ากราฟไม่มี title/label ที่ชัดเจน คนอ่านต้องเดาเอง หรือกลับมาถามคนทำอีกที ซึ่งทำให้ insight ที่ควรจะสื่อสารได้เร็วกลับช้าลงหรือถูกตีความผิด title/label ที่ดีทำให้กราฟ "ยืนได้ด้วยตัวเอง" แม้จะถูกส่งต่อไปโดยไม่มีคนอธิบายประกอบ

ถ้าต้องเลือกระหว่าง matplotlib กับ seaborn สำหรับงานสำรวจข้อมูลเร็วๆ (exploratory analysis) จะเลือกอะไรและทำไม

สำหรับงานสำรวจข้อมูลเร็วๆ ที่ข้อมูลอยู่ในรูป DataFrame อยู่แล้ว seaborn มักเป็นตัวเลือกที่เร็วกว่า เพราะเขียน โค้ดสั้นกว่าและได้กราฟที่อ่านง่ายโดยไม่ต้องปรับสไตล์เอง (เช่น sns.boxplot เทียบหลายกลุ่ม หรือ sns.histplot ที่จัดการ bin ให้อัตโนมัติ) แต่ถ้าต้องการควบคุมรายละเอียดที่ seaborn ไม่รองรับตรงๆ หรือทำกราฟที่ไม่ธรรมดา (custom layout ซับซ้อน) ก็ยังต้องพึ่ง matplotlib โดยตรง — สองไลบรารีนี้ใช้ร่วมกันได้เสมอ ไม่ต้องเลือกอย่างใดอย่างหนึ่งตลอดไป