01ภาพรวมของกราฟนี้
Dot density map วางจุดหนึ่งจุดต่อทุก ๆ จำนวนหน่วย ทำให้เห็นความหนาแน่นที่แท้จริงโดยไม่ถูกขอบเขตการปกครองบิดเบือนแบบ choropleth แลกกับการที่อ่านค่าที่แน่นอนไม่ได้เลยและจุดที่เกินราวสองหมื่นจะอิ่มตัวจนบอกอะไรไม่ได้
เปรียบเทียบให้เห็นภาพ
เหมือนภาพถ่ายดาวเทียมตอนกลางคืนที่แสงไฟบอกว่าคนอยู่ตรงไหนจริง ๆ โดยไม่สนใจเส้นแบ่งจังหวัด
ใช้ที่ไหนบ้าง — การกระจายตัวของประชากร ตำแหน่งของเหตุการณ์ที่บันทึกพิกัดไว้ หรือการกระจายของกลุ่มชาติพันธุ์ในเมือง
สร้างอย่างไร
- ขั้นที่ 1กำหนดว่าหนึ่งจุดแทนกี่หน่วย
- ขั้นที่ 2สุ่มตำแหน่งจุดภายในพื้นที่ที่อยู่อาศัยจริง ไม่ใช่ทั่วทั้งเขต
- ขั้นที่ 3ใช้จุดเล็กและโปร่งใสเล็กน้อย
- ขั้นที่ 4ระบุอัตราส่วนจุดต่อหน่วยในคำอธิบาย
ตัวอย่างคลาสสิก · แผนที่จุดแสดงการแบ่งแยกเชื้อชาติในเมืองของอเมริกา
โครงการ Racial Dot Map ของมหาวิทยาลัยเวอร์จิเนียวางจุดหนึ่งจุดต่อประชากรหนึ่งคนจากสำมะโนปี 2010 ภาพที่ได้เผยให้เห็นเส้นแบ่งที่ชัดเจนระหว่างย่านต่าง ๆ ซึ่งตัวเลขระดับเขตกลบไว้หมด และกลายเป็นหลักฐานเชิงภาพที่ทรงพลังในการถกเถียงเรื่องนโยบายที่อยู่อาศัย
02อธิบายภาษาคนเข้าใจง่าย ๆ
Choropleth มีปัญหาที่เรียกว่าปัญหาหน่วยพื้นที่ คือค่าที่เห็นขึ้นกับว่าเราลากเส้นแบ่งเขตอย่างไร ซึ่งเป็นเส้นที่มนุษย์กำหนดขึ้นและมักไม่ตรงกับความจริงทางภูมิศาสตร์
Dot density map เลี่ยงปัญหานี้ด้วยการไม่ใช้เขตเลย แต่วางจุดลงไปตรง ๆ โดยหนึ่งจุดแทนจำนวนหน่วยที่กำหนดไว้
ผลคือความหนาแน่นของจุดบนภาพเท่ากับความหนาแน่นจริงในพื้นที่ ทำให้เห็นว่าคนกระจุกอยู่ตามแนวถนนหรือแม่น้ำอย่างไร ซึ่ง choropleth กลบหมดด้วยการระบายเขตทั้งเขตเป็นสีเดียว
ราคาที่จ่ายอย่างแรกคืออ่านค่าไม่ได้เลย ไม่มีใครนับจุดได้ และไม่ควรพยายาม กราฟนี้ตอบคำถามว่า "หนาแน่นตรงไหน" ไม่ใช่ "มีเท่าไร"
อย่างที่สองคือเมื่อจุดเกินราวสองหมื่นจุด บริเวณที่หนาแน่นจะกลายเป็นก้อนทึบและความต่างภายในก้อนนั้นก็หายไปหมด ซึ่งตอนนั้นต้องเพิ่มจำนวนหน่วยต่อจุดหรือเปลี่ยนไปใช้ hexbin
อย่างที่สามที่คนมักลืมคือตำแหน่งจุดต้องสุ่มภายในพื้นที่ที่อยู่อาศัยจริง ไม่ใช่ทั่วทั้งเขต ไม่อย่างนั้นจะได้ภาพที่มีคนอาศัยกลางทะเลสาบหรือบนยอดเขา
03ข้อมูลที่ต้องใช้
คอลัมน์ที่ต้องเตรียม
| คอลัมน์ | ทำหน้าที่ | ชนิด | เข้ารหัสด้วย | จำนวนค่าที่เหมาะ | หมายเหตุ |
|---|---|---|---|---|---|
lat | ละติจูด | เชิงพื้นที่ | y | ต่อเนื่อง | พิกัดของจุดหรือของเขต |
lon | ลองจิจูด | เชิงพื้นที่ | x | ต่อเนื่อง | ต้องอยู่ในระบบพิกัดเดียวกับแผนที่ฐาน |
value | ปริมาณ | จำนวนนับ | opacity | จำนวนนับ | แปลงเป็นจำนวนจุดด้วยอัตราส่วนที่กำหนด |
ต้องเตรียมข้อมูลอย่างไรก่อนวาด
- ★ เลือกอัตราส่วนจุดต่อหน่วยให้จุดรวมอยู่ในช่วงที่อ่านได้
- สุ่มตำแหน่งภายในพื้นที่ที่อยู่อาศัยจริงเท่านั้น
- ล็อกค่าเริ่มต้นของการสุ่มเพื่อให้ภาพซ้ำได้
ค่าที่หายไป — พื้นที่ที่ไม่มีข้อมูลจะดูเหมือนพื้นที่ที่ไม่มีคน ต้องตีกรอบหรือระบายลายให้ต่างกัน
สเกลของแกน — อัตราส่วนจุดต่อหน่วยคงที่ทั้งแผนที่ และต้องระบุไว้ในคำอธิบายเสมอ
กราฟจะเริ่มพังเมื่อไร
เกิน 20,000 จุด บริเวณหนาแน่นอิ่มตัวเป็นก้อนทึบ
หน้าตาไฟล์ที่ต้องเตรียม
lat,lon,value
13.8013,100.1331,184
18.2209,98.5482,50
…04อ่านแล้วเห็นอะไร
สิ่งที่เห็นได้จากภาพ
- บริเวณที่หนาแน่นจริงโดยไม่ถูกเส้นเขตบิด
- รูปแบบการกระจายตามภูมิประเทศ เช่น ตามแนวถนนหรือแม่น้ำ
- พื้นที่ว่างที่ไม่มีใครอยู่
สิ่งที่กราฟนี้ตอบไม่ได้
- ★ อ่านค่าที่แน่นอนไม่ได้เลย ไม่มีใครนับจุดได้
- เทียบสองบริเวณให้แม่นไม่ได้เมื่อจุดอิ่มตัว
- บอกไม่ได้ว่าจุดหนึ่งจุดคือใครหรือที่ไหนแน่นอน เพราะตำแหน่งถูกสุ่มภายในพื้นที่
อ่านตามลำดับนี้
- อ่านคำอธิบายก่อนว่าหนึ่งจุดแทนกี่หน่วย
- มองหาบริเวณที่จุดหนาแน่นที่สุด
- ดูรูปร่างของการกระจายว่าตามแนวอะไร
คำถามที่ตอบได้
- คนกระจุกอยู่ตรงไหน
- การกระจายตัวเป็นแบบกระจุกหรือกระจาย
- มีพื้นที่ว่างขนาดใหญ่ตรงไหน
สัญญาณว่ามีอะไรผิด
- ไม่ระบุว่าหนึ่งจุดแทนกี่หน่วย
- มีจุดในพื้นที่ที่คนอยู่ไม่ได้ เช่น กลางทะเลสาบ
- บริเวณหนาแน่นทึบจนไม่เห็นความต่างภายใน
05เหมาะกับการนำเสนอแบบไหน
ทำอย่างไรให้คนอ่านได้ข้อสรุปโดยไม่ต้องบรรยาย
- ★ ระบุอัตราส่วนจุดต่อหน่วยไว้ในคำอธิบายให้เด่น
- ตีกรอบพื้นที่ที่พูดถึงในเรื่อง
- ใส่แผนที่ฐานสีอ่อนเพื่อให้รู้ว่าที่นี่คือที่ไหน
รูปแบบชื่อกราฟที่ควรใช้
การกระจายของ <สิ่งที่วัด> โดยหนึ่งจุดแทน <จำนวน> หน่วย
06เอาไปใช้ต่อ
โค้ดทุกชิ้นอ่านไฟล์ dot-density-map-sample.csv ชุดเดียวกับที่ Playground ใช้ และตัวเลขในโค้ดเปลี่ยนตามค่าที่คุณปรับด้านล่าง
# ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*"
# ดาวน์โหลด dot-density-map-sample.csv จากหน้านี้ แล้ววางไว้ข้างสคริปต์
import matplotlib
matplotlib.use("Agg") # รันแบบไม่มีหน้าจอ ถ้ารันในเครื่องตัวเองลบบรรทัดนี้ได้
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
C1, C2, C3 = "#2a78d6", "#eb6834", "#1baf7a" # palette ของคลัง เปลี่ยนเป็นสีองค์กรได้
INK, SURFACE = "#0b0b0b", "#fcfcfb"
df = pd.read_csv("dot-density-map-sample.csv")
df["lat"] = pd.to_numeric(df["lat"], errors="coerce")
df["lon"] = pd.to_numeric(df["lon"], errors="coerce")
df["value"] = pd.to_numeric(df["value"], errors="coerce")
PER = "20" # ← ค่าจาก Playground
fig, ax = plt.subplots(figsize=(7.6, 4.2), facecolor=SURFACE)
ax.set_facecolor(SURFACE)
PER = 20 # หนึ่งจุดแทน 20 หน่วย
rng = np.random.default_rng(42) # ล็อกการสุ่มเพื่อให้ภาพซ้ำได้
xs, ys = [], []
for _, r in df.iterrows():
n = int(r["value"] / PER)
xs += list(r["lon"] + rng.normal(0, 0.08, n)) # สุ่มรอบพิกัดกลาง
ys += list(r["lat"] + rng.normal(0, 0.08, n))
ax.scatter(xs, ys, s=3.5, color=C1, alpha=0.45, linewidths=0)
ax.set_xlabel("ลองจิจูด"); ax.set_ylabel("ละติจูด")
ax.set_title(f"หนึ่งจุด = {PER} หน่วย", loc="left")
ax.spines[["top", "right"]].set_visible(False) # ลดเส้นที่ไม่ได้ให้ข้อมูล
fig.tight_layout()
fig.savefig("dot-density-map.png", dpi=160)
print("เขียน dot-density-map.png แล้ว ·", len(df), "แถว")07ลองเล่นเพื่อเข้าใจ
ตำแหน่งของจุดภายในพื้นที่เป็นการกระจายเพื่อการแสดงผล ไม่ใช่ตำแหน่งจริงของแต่ละหน่วย
ดูข้อมูลเป็นตาราง (24 แถว)
| ละติจูด | ลองจิจูด | ค่า |
|---|---|---|
| 13.801 | 100.133 | 184 |
| 18.221 | 98.548 | 50.0 |
| 7.663 | 99.011 | 80.0 |
| 14.373 | 101.583 | 162 |
| 18.167 | 100.079 | 178 |
| 8.684 | 98.466 | 162 |
| 14.958 | 102.031 | 138 |
| 19.180 | 98.561 | 71.0 |
| 6.854 | 98.738 | 199 |
| 13.264 | 100.423 | 182 |
| 19.617 | 98.267 | 90.0 |
| 7.085 | 98.117 | 141 |
| 12.458 | 99.895 | 84.0 |
| 18.895 | 98.480 | 148 |
| 6.866 | 96.877 | 120 |
| 13.118 | 100.550 | 65.0 |
| 18.923 | 100.168 | 76.0 |
| 7.398 | 98.207 | 150 |
| 13.792 | 100.146 | 126 |
| 18.545 | 98.655 | 134 |
| 7.153 | 98.406 | 96.0 |
| 14.906 | 99.642 | 31.0 |
| 18.063 | 97.913 | 160 |
| 7.676 | 99.105 | 90.0 |
ลองแล้วจะเห็นอะไร
- หนึ่งจุดแทนกี่หน่วย: ลดจำนวนหน่วยต่อจุดแล้วจุดจะเยอะขึ้นจนบริเวณหนาแน่นกลายเป็นก้อนทึบ — ตัวเลขนี้เปลี่ยนข้อสรุปที่ผู้อ่านได้
ข้อควรระวัง
- ไม่ระบุว่าหนึ่งจุดแทนกี่หน่วย ทำให้ภาพอ่านไม่ได้เลย
- สุ่มจุดทั่วทั้งเขตแทนที่จะสุ่มในพื้นที่ที่อยู่อาศัยจริง
- ใส่จุดมากเกินจนบริเวณหนาแน่นทึบและความต่างภายในหายไป
- พยายามใช้ตอบคำถามเชิงตัวเลข ทั้งที่ออกแบบมาให้ดูรูปแบบเท่านั้น
เมื่อไหร่ไม่ควรใช้กราฟนี้
- ต้องอ่านค่าของแต่ละพื้นที่→ใช้แทน: choropleth-map พร้อมตารางประกอบ
- จุดหนาแน่นจนอิ่มตัว→ใช้แทน: hexbin-map หรือ kernel-heat-map
- ข้อมูลมีแค่ระดับเขต ไม่มีพิกัดจริง→ใช้แทน: choropleth-map