01ภาพรวมของกราฟนี้
Graduated symbol map วางวงกลมที่ขนาดแปรตามค่าลงบนแผนที่ จุดแข็งคือแก้ปัญหาที่ choropleth ใช้จำนวนนับไม่ได้ เพราะขนาดวงไม่ขึ้นกับพื้นที่ของเขต แลกกับการที่วงซ้อนกันในบริเวณที่หนาแน่นและอ่านขนาดได้หยาบ
เปรียบเทียบให้เห็นภาพ
เหมือนหมุดที่ปักบนแผนที่โดยหมุดใหญ่แปลว่าเรื่องใหญ่ ซึ่งอ่านง่ายกว่าการดูว่าพื้นที่ไหนถูกระบายเข้ม
ใช้ที่ไหนบ้าง — จำนวนประชากรรายจังหวัด ยอดขายรายสาขา จำนวนผู้ป่วยรายเขต หรือปริมาณสินค้าที่ผ่านแต่ละท่าเรือ
สร้างอย่างไร
- ขั้นที่ 1หาตำแหน่งกึ่งกลางของแต่ละเขต
- ขั้นที่ 2★ คำนวณรัศมีจากรากที่สองของค่า
- ขั้นที่ 3วางวงเรียงจากใหญ่ไปเล็กเพื่อไม่ให้วงเล็กถูกบัง
- ขั้นที่ 4ใส่คำอธิบายขนาดแบบวงซ้อนกันสามขนาด
ตัวอย่างคลาสสิก · แผนที่การเดินทัพของนโปเลียนโดย Charles Minard
ภาพปี 1869 ของ Minard ใช้ความหนาของแถบแทนจำนวนทหารที่เหลือตลอดเส้นทางไปและกลับจากมอสโก ซึ่งเป็นการใช้ขนาดแทนปริมาณบนแผนที่ที่ทรงพลังที่สุดชิ้นหนึ่ง และแสดงให้เห็นว่าการรวมปริมาณเข้ากับตำแหน่งเล่าเรื่องได้ดีกว่าการแยกกันแสดง
02อธิบายภาษาคนเข้าใจง่าย ๆ
Choropleth มีข้อจำกัดที่แก้ไม่ได้ คือมันใช้จำนวนนับไม่ได้เลย เพราะพื้นที่ของเขตเข้าไปปนกับค่าที่อยากสื่อ
Graduated symbol map แก้ด้วยการแยกสองสิ่งนี้ออกจากกัน ตำแหน่งบอกว่าอยู่ที่ไหน และขนาดวงบอกว่ามีเท่าไร โดยขนาดของเขตไม่เกี่ยวข้องเลย
ผลคือจำนวนนับแสดงบนแผนที่ได้อย่างซื่อสัตย์ กรุงเทพที่มีพื้นที่เล็กแต่มีคนเยอะจะได้วงใหญ่ตามความจริง ไม่ใช่แถบสีบาง ๆ ที่มองข้ามได้ง่าย
ราคาที่จ่ายอย่างแรกคือขนาดอ่านได้หยาบกว่าความยาวมาก คนประเมินพื้นที่วงกลมต่ำกว่าความจริงเสมอ จึงต้องมีตารางหรือป้ายค่าประกอบ
อย่างที่สองคือในบริเวณที่จุดหนาแน่น วงจะซ้อนกันจนอ่านไม่ได้ ซึ่งแก้ได้ด้วยการเรียงวาดจากใหญ่ไปเล็กและใส่ความโปร่งใส แต่แก้ได้ไม่หมด
ที่สำคัญที่สุดคือรัศมีต้องแปรตามรากที่สองของค่า ถ้าใช้รัศมีแปรตรงกับค่า วงที่มีค่าเป็นสองเท่าจะดูใหญ่กว่าสี่เท่า ซึ่งเป็นการโกหกที่พบบ่อยมาก
03ข้อมูลที่ต้องใช้
คอลัมน์ที่ต้องเตรียม
| คอลัมน์ | ทำหน้าที่ | ชนิด | เข้ารหัสด้วย | จำนวนค่าที่เหมาะ | หมายเหตุ |
|---|---|---|---|---|---|
province | เขตพื้นที่ | เชิงพื้นที่ | x | 10–80 จุด | ต้องมีพิกัดกึ่งกลาง |
regionไม่บังคับ | ภูมิภาค | หมวดหมู่ | color | 4–8 กลุ่ม | ใช้สีบอกกลุ่มได้ |
rate | ปริมาณ | ตัวเลขต่อเนื่อง | size | บวกเท่านั้น | ★ ใช้จำนวนนับได้ ต่างจาก choropleth |
ต้องเตรียมข้อมูลอย่างไรก่อนวาด
- หาพิกัดกึ่งกลางของแต่ละเขต
- ★ คำนวณรัศมีจากรากที่สองของค่า
- เรียงข้อมูลจากมากไปน้อยเพื่อวาดวงใหญ่ก่อน
ค่าที่หายไป — เขตที่ไม่มีข้อมูลไม่ควรวาดวง และต้องระบุจำนวนที่หายไปในคำอธิบาย
สเกลของแกน — พื้นที่วงแปรตรงกับค่า ซึ่งแปลว่ารัศมีแปรตามรากที่สอง และวงใหญ่ที่สุดไม่ควรกว้างเกินหนึ่งในหกของภาพ
กราฟจะเริ่มพังเมื่อไร
เกิน 100 จุด หรือจุดกระจุกกันจนวงซ้อนทับหมด
หน้าตาไฟล์ที่ต้องเตรียม
province,region,rate
กรุงเทพฯ,เหนือ,26.6
เชียงใหม่,อีสาน,34.7
…04อ่านแล้วเห็นอะไร
สิ่งที่เห็นได้จากภาพ
- จุดที่มีปริมาณมากที่สุด
- การกระจุกตัวของปริมาณในบางภูมิภาค
- บริเวณที่มีวงใหญ่อยู่ใกล้กันหลายวง
สิ่งที่กราฟนี้ตอบไม่ได้
- ★ เทียบขนาดวงให้แม่นไม่ได้ คนประเมินพื้นที่วงกลมต่ำกว่าความจริงเสมอ
- อ่านค่าของวงที่ถูกวงใหญ่บังไม่ได้
อ่านตามลำดับนี้
- อ่านคำอธิบายขนาดก่อนว่าวงขนาดไหนแทนค่าเท่าไร
- มองหาวงใหญ่ที่สุดซึ่งคือจุดที่มีปริมาณมากสุด
- ดูว่าวงใหญ่กระจุกกันที่บริเวณไหน
คำถามที่ตอบได้
- ที่ไหนมีปริมาณมากที่สุด
- ปริมาณกระจุกอยู่ในภูมิภาคไหน
- จุดใหญ่ที่สุดใหญ่กว่าจุดรองแค่ไหน
สัญญาณว่ามีอะไรผิด
- ใช้รัศมีแปรตรงกับค่าแทนรากที่สอง
- ไม่มีคำอธิบายขนาด
- วงซ้อนกันจนวงเล็กหายไปหมด
05เหมาะกับการนำเสนอแบบไหน
ทำอย่างไรให้คนอ่านได้ข้อสรุปโดยไม่ต้องบรรยาย
- ★ ใส่คำอธิบายขนาดแบบวงซ้อนสามขนาดพร้อมค่ากำกับ
- ติดค่าเฉพาะวงที่ใหญ่ที่สุดสามถึงห้าวง
- ใช้ความโปร่งใสประมาณเจ็ดสิบเปอร์เซ็นต์เพื่อให้เห็นวงที่ซ้อน
รูปแบบชื่อกราฟที่ควรใช้
<ตัวชี้วัด> รายจังหวัด โดย <จังหวัด> มากที่สุดที่ <ค่า>
06เอาไปใช้ต่อ
โค้ดทุกชิ้นอ่านไฟล์ graduated-symbol-map-sample.csv ชุดเดียวกับที่ Playground ใช้ และตัวเลขในโค้ดเปลี่ยนตามค่าที่คุณปรับด้านล่าง
# ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*"
# ดาวน์โหลด graduated-symbol-map-sample.csv จากหน้านี้ แล้ววางไว้ข้างสคริปต์
import matplotlib
matplotlib.use("Agg") # รันแบบไม่มีหน้าจอ ถ้ารันในเครื่องตัวเองลบบรรทัดนี้ได้
import matplotlib.pyplot as plt
import pandas as pd
import geopandas as gpd
import numpy as np
C1, C2, C3 = "#2a78d6", "#eb6834", "#1baf7a" # palette ของคลัง เปลี่ยนเป็นสีองค์กรได้
INK, SURFACE = "#0b0b0b", "#fcfcfb"
df = pd.read_csv("graduated-symbol-map-sample.csv")
df["rate"] = pd.to_numeric(df["rate"], errors="coerce")
NOTE = "sqrt" # ← ค่าจาก 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["geometry"] = gdf.centroid
gdf = gdf.sort_values("rate", ascending=False) # วาดวงใหญ่ก่อน
base = gpd.read_file("provinces.geojson")
base.plot(ax=ax, facecolor="#f2f1ed", edgecolor="#e2e0da", linewidth=0.5)
ax.scatter(gdf.geometry.x, gdf.geometry.y,
s=np.sqrt(gdf["rate"]) * 60, # รากที่สอง → พื้นที่แปรตามค่า
color=C1, alpha=0.7, edgecolor="#fcfcfb", linewidth=1.2)
ax.set_axis_off()
ax.spines[["top", "right"]].set_visible(False) # ลดเส้นที่ไม่ได้ให้ข้อมูล
fig.tight_layout()
fig.savefig("graduated-symbol-map.png", dpi=160)
print("เขียน graduated-symbol-map.png แล้ว ·", len(df), "แถว")07ลองเล่นเพื่อเข้าใจ
วงที่อยู่ใกล้กันทับกันได้ — วาดวงเล็กทับวงใหญ่เสมอ ไม่อย่างนั้นข้อมูลจะหาย
ดูข้อมูลเป็นตาราง (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 |
ลองแล้วจะเห็นอะไร
- สังเกตอะไร: วงที่มีค่าเป็นสองเท่ามีพื้นที่เป็นสองเท่า ไม่ใช่รัศมีเป็นสองเท่า — ถ้าทำผิดข้อนี้ ภาพจะโกหกอย่างรุนแรง
ข้อควรระวัง
- ★ ใช้รัศมีแปรตรงกับค่าแทนรากที่สอง ทำให้ความต่างดูรุนแรงเกินจริงหลายเท่า
- ไม่ใส่คำอธิบายขนาด ทำให้ผู้อ่านเดาว่าวงขนาดนี้แทนค่าเท่าไร
- วาดจากเล็กไปใหญ่ ทำให้วงเล็กถูกวงใหญ่บังจนหายไป
- ใช้ในบริเวณที่จุดหนาแน่นมากจนวงซ้อนกันหมด
เมื่อไหร่ไม่ควรใช้กราฟนี้
- ข้อมูลเป็นอัตราส่วนที่ผูกกับเขตอยู่แล้ว→ใช้แทน: choropleth-map
- จุดหนาแน่นจนวงซ้อนกันหมด→ใช้แทน: hexbin-map หรือ kernel-heat-map
- ต้องเทียบค่าให้แม่น→ใช้แทน: horizontal-bar เรียงตามค่า