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

แผนที่ระบายสีChoropleth Map

เห็นรูปแบบเชิงพื้นที่ที่ตารางซ่อนไว้

2
ต่อยอดโดยตรงควรรู้ก่อน · เข้าใจว่าสีแทนค่า
ลองเล่น

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

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

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

เหมือนแผนที่พยากรณ์อากาศที่ระบายสีจังหวัดตามอุณหภูมิ ซึ่งเห็นได้ทันทีว่าภาคไหนร้อนภาคไหนเย็น

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

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

  1. ขั้นที่ 1★ แปลงจำนวนนับเป็นอัตราส่วนต่อประชากรหรือพื้นที่
  2. ขั้นที่ 2เลือกวิธีแบ่งช่วงค่าและระบุไว้ในคำอธิบาย
  3. ขั้นที่ 3ใช้สเกลสีลำดับเดียวไล่อ่อนไปเข้ม
  4. ขั้นที่ 4ใส่คำอธิบายสีพร้อมหน่วยเสมอ

ตัวอย่างคลาสสิก · แผนที่อหิวาตกโรคของ John Snow ปี 1854

John Snow วาดตำแหน่งผู้เสียชีวิตจากอหิวาตกโรคลงบนแผนที่ย่านโซโหในลอนดอน แล้วเห็นว่าผู้เสียชีวิตกระจุกรอบปั๊มน้ำบนถนน Broad Street ซึ่งเป็นหลักฐานที่ทำให้เขาโน้มน้าวให้ถอดคันโยกปั๊มออก นี่คือตัวอย่างแรก ๆ ที่การวางข้อมูลลงบนแผนที่เผยสาเหตุที่ตารางตัวเลขซ่อนไว้

Wikipedia · 1854 Broad Street cholera outbreak

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

ข้อมูลหลายอย่างผูกกับพื้นที่โดยธรรมชาติ เช่น อัตราการว่างงานรายจังหวัด ซึ่งดูจากตาราง 77 แถวแล้วแทบมองไม่เห็นรูปแบบอะไร

Choropleth map ใช้ประโยชน์จากข้อเท็จจริงว่าคนจำแผนที่ประเทศตัวเองได้ เมื่อระบายสีตามค่า เขตที่มีค่าใกล้กันซึ่งอยู่ติดกันก็จะรวมเป็นบริเวณสีเดียวกันทันที

ผลคือรูปแบบเชิงพื้นที่ เช่น ภาคเหนือทั้งภาคมีค่าสูงกว่าภาคกลาง ปรากฏขึ้นในพริบตา ซึ่งเป็นสิ่งที่ตารางเรียงตามตัวอักษรไม่มีทางแสดงได้

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

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

กับดักที่สามคือวิธีแบ่งช่วงค่า แบ่งเท่ากัน แบ่งตามควอนไทล์ หรือแบ่งด้วย Jenks ให้แผนที่หน้าตาต่างกันมากจากข้อมูลชุดเดียวกัน จึงต้องระบุวิธีที่ใช้เสมอ

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

รูปทรงของข้อมูลGeoJSON + ตารางค่าที่ join ด้วย key
จำนวนมิติที่เข้ารหัสได้2 มิติ
จำนวนแถวที่เหมาะ20–80 เขตขั้นต่ำ 8 แถว
เส้นฐานศูนย์ไม่เกี่ยวข้องค่าเข้ารหัสด้วยสี ไม่มีแกนค่า

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

คอลัมน์ทำหน้าที่ชนิดเข้ารหัสด้วยจำนวนค่าที่เหมาะหมายเหตุ
provinceเขตพื้นที่เชิงพื้นที่shape10–100 เขตต้องตรงกับชื่อในไฟล์ขอบเขต
region
ไม่บังคับ
ภูมิภาคหมวดหมู่row4–8 กลุ่มใช้จัดกลุ่มในตารางประกอบ
rateอัตราส่วนตัวเลขต่อเนื่องcolorต่อเนื่อง★ ต้องเป็นอัตราส่วน ไม่ใช่จำนวนนับ

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

  1. ★ หารด้วยประชากรหรือพื้นที่เพื่อให้เป็นอัตราส่วน
  2. ตรวจว่าชื่อเขตตรงกับไฟล์ขอบเขตทุกตัว
  3. เลือกและบันทึกวิธีแบ่งช่วงค่า

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

สเกลของแกน — ใช้สเกลสีลำดับเดียวสำหรับค่าที่มีทิศทางเดียว และใช้สเกลสองขั้วพร้อมกลางสีเทาเมื่อมีจุดอ้างอิงที่มีความหมาย

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

เขตเล็กมากจนมองไม่เห็น เช่น เขตเมืองในแผนที่ทั้งประเทศ

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

province,region,rate
กรุงเทพฯ,เหนือ,26.6
เชียงใหม่,อีสาน,34.7

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

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

  • บริเวณที่มีค่าสูงหรือต่ำต่อเนื่องกัน
  • เขตที่ต่างจากเพื่อนบ้านอย่างชัดเจน
  • ความต่างระหว่างภูมิภาค

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

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

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

  1. อ่านคำอธิบายสีและหน่วยก่อนเสมอ
  2. มองหาบริเวณสีเดียวกันที่ต่อเนื่องกัน
  3. ตรวจว่าเขตที่เข้มที่สุดเป็นเพราะค่าสูงจริงหรือเพราะพื้นที่ใหญ่

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

  • ภูมิภาคไหนมีค่าสูงที่สุด
  • ค่าสูงกระจุกอยู่ที่ไหน
  • มีเขตไหนผิดจากเพื่อนบ้านบ้าง

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

  • ใช้จำนวนนับแทนอัตราส่วน
  • ไม่ระบุวิธีแบ่งช่วงค่า
  • ใช้สเกลสีรุ้งซึ่งไม่มีลำดับที่คนอ่านตรงกัน
  • ไม่แยกเขตที่ไม่มีข้อมูลออกจากเขตที่มีค่าต่ำ

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

ผู้ชมที่เหมาะทุกคน เพราะแผนที่เป็นภาพที่คุ้นเคยที่สุดแบบหนึ่ง
เวลาที่ผู้ชมต้องใช้ไม่กี่วินาที
ขนาดเล็กสุดที่ยังอ่านได้360×400 px
สไลด์ผู้บริหาร
สื่อสารกับคนทั่วไป
รายงานเชิงวิเคราะห์
สิ่งพิมพ์
มือถือเขตเล็กกดไม่ได้และคำอธิบายสีเบียด

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

  1. ★ เขียนหน่วยไว้ในคำอธิบายสีเสมอ เช่น ต่อประชากรหมื่นคน
  2. ติดชื่อเฉพาะเขตที่พูดถึงในเรื่อง
  3. ใส่ตารางอันดับสิบอันดับแรกประกอบเพื่อให้เทียบค่าได้แม่น

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

<ตัวชี้วัด> ต่อ <ฐาน> รายจังหวัด โดย <จังหวัด> สูงที่สุดที่ <ค่า>

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

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

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

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

df = pd.read_csv("choropleth-map-sample.csv")
df["rate"] = pd.to_numeric(df["rate"], errors="coerce")
NOTE = "area"   # ← ค่าจาก Playground

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

gdf = gpd.read_file("provinces.geojson").merge(df, on="province")
gdf.plot(column="rate", cmap="Blues", ax=ax, legend=True,
         scheme="quantiles", k=5,                       # ระบุวิธีแบ่งช่วงเสมอ
         edgecolor="#fcfcfb", linewidth=0.6,
         missing_kwds=dict(color="#e8e6e0", hatch="///", label="ไม่มีข้อมูล"))
ax.set_axis_off()
ax.set_title("อัตราต่อประชากรหมื่นคน · แบ่งช่วงแบบควอนไทล์", loc="left")

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

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

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

ข้อมูลตัวอย่าง · 16 แถว · seed 331● คำนวณใหม่ในเบราว์เซอร์ทันทีที่ปรับค่า
เขตใหญ่ครองสายตา
กรุงเทพฯ26.6กรุงเทพฯ · 26.6 ต่อประชากรหมื่นคนเชียงใหม34.7เชียงใหม่ · 34.7 ต่อประชากรหมื่นคนขอนแก่น32.7ขอนแก่น · 32.7 ต่อประชากรหมื่นคนชลบุรี34.4ชลบุรี · 34.4 ต่อประชากรหมื่นคนภูเก็ต26.8ภูเก็ต · 26.8 ต่อประชากรหมื่นคนนครราชสี18.3นครราชสีมา · 18.3 ต่อประชากรหมื่นคนสงขลา10.4สงขลา · 10.4 ต่อประชากรหมื่นคนอุดรธานี23.1อุดรธานี · 23.1 ต่อประชากรหมื่นคนสุราษฎร์20.1สุราษฎร์ธานี · 20.1 ต่อประชากรหมื่นคนพิษณุโลก17.9พิษณุโลก · 17.9 ต่อประชากรหมื่นคนระยอง17.4ระยอง · 17.4 ต่อประชากรหมื่นคนนครปฐม10.5นครปฐม · 10.5 ต่อประชากรหมื่นคน10.434.7

ต้องใช้กับอัตราหรือสัดส่วนเท่านั้น ถ้าใช้กับจำนวนดิบ พื้นที่ที่มีประชากรมากจะเข้มเสมอ

สูงสุดเชียงใหม่ · 34.7
ค่าเฉลี่ย22.7
จำนวนพื้นที่12
ดูข้อมูลเป็นตาราง (12 แถว)
แผนที่ 12 พื้นที่ ค่าสูงสุดที่ เชียงใหม่
จังหวัดภูมิภาคค่า (ต่อประชากรหมื่นคน)
กรุงเทพฯเหนือ26.6
เชียงใหม่อีสาน34.7
ขอนแก่นกลาง32.7
ชลบุรีตะวันออก34.4
ภูเก็ตตะวันตก26.8
นครราชสีมาใต้18.3
สงขลาเหนือ10.4
อุดรธานีอีสาน23.1
สุราษฎร์ธานีกลาง20.1
พิษณุโลกตะวันออก17.9
ระยองตะวันตก17.4
นครปฐมใต้10.5

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

  • สังเกตอะไร: สังเกตว่าเขตพื้นที่ใหญ่ดึงความสนใจมากกว่าเขตเล็กเสมอ แม้ค่าจะเท่ากัน — นี่คือเหตุผลที่ cartogram มีอยู่

ข้อควรระวัง

  1. ★ ใช้จำนวนนับแทนอัตราส่วน ทำให้แผนที่แสดงแค่ว่าที่ไหนมีคนเยอะ
  2. ลืมว่าเขตพื้นที่ใหญ่ครองสายตามากกว่าเขตเล็กที่มีคนอยู่มากกว่า
  3. ไม่ระบุวิธีแบ่งช่วงค่า ทั้งที่วิธีต่างกันให้แผนที่หน้าตาต่างกันมาก
  4. ใช้สเกลสีรุ้งซึ่งไม่มีลำดับที่คนอ่านตรงกัน

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

  • ต้องเทียบค่าระหว่างเขตให้แม่นใช้แทน: horizontal-bar เรียงตามค่า
  • ประชากรกระจุกในเขตเล็กไม่กี่เขตใช้แทน: cartogram หรือ tile-grid-map
  • ข้อมูลเป็นจุดพิกัดไม่ใช่รายเขตใช้แทน: dot-density-map หรือ graduated-symbol-map

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

เทคนิคถัดไป · แผนที่วงกลมตามขนาด