library(tidyverse)
library(sf)
library(geodata)
library(rmapshaper)
library(scales)แผนที่ประเทศไทยแสดงจำนวนประชากรรายจังหวัด ปี 2568
บทความนี้เราจะลองนำข้อมูล Open Data จากภาครัฐที่ทุกคนสามารถเข้าถึงและดาวน์โหลดได้ฟรี มาสร้างเป็นแผนที่แสดงความหนาแน่นของประชากรรายจังหวัด (Choropleth Map) โดยใช้ข้อมูลสถิติจำนวนประชากรและบ้านประจำปี พ.ศ. 2568 จากสำนักบริหารการทะเบียน กรมการปกครอง (DOPA) และเชื่อมโยงเข้ากับข้อมูลขอบเขตแผนที่ประเทศไทยจาก GADM ครับ
1. เตรียมข้อมูลแผนที่ประเทศไทย (Spatial Data)
เริ่มต้นด้วยการโหลดแพ็กเกจที่จำเป็นสำหรับการจัดการข้อมูลเชิงพื้นที่ (sf, geodata, rmapshaper) และชุดเครื่องมือจัดการข้อมูลทั่วไป (tidyverse)
เราจะดึงข้อมูลขอบเขตจังหวัดของประเทศไทย (Level 1) จาก GADM และใช้ ms_simplify() เพื่อลดขนาดพิกัด Geometry ลงเหลือ 10% ซึ่งช่วยให้ไฟล์มีขนาดเล็กลงมากและโหลดบนหน้าเว็บได้อย่างรวดเร็ว
Code
# 1. ดาวน์โหลดข้อมูล Shapefile ระดับจังหวัดของไทย
th_sf_raw <- gadm(country = "THA", level = 1, path = tempdir()) |>
st_as_sf()
# 2. ลดความซับซ้อนของเส้นขอบเขตเพื่อลดขนาดไฟล์
th_sf_simplified <- ms_simplify(th_sf_raw, keep = 0.10, keep_shapes = TRUE)
# 3. เตรียมรหัสจังหวัด 2 หลัก (ดึงจาก ISO_1 และ CC_1) สำหรับใช้เป็น Key ในการ Merge
th_map_ready <- th_sf_simplified |>
mutate(
# ตัวอย่าง: TH-50 -> "50" ส่วน กทม. ที่ ISO_1 เป็น NA จะดึงจาก CC_1 = "10"
province_code = if_else(is.na(ISO_1), CC_1, str_extract(ISO_1, "\\d+"))
) |>
select(
province_code,
province_id = GID_1,
province_en = NAME_1
)ในฐานข้อมูล GADM คอลัมน์ชื่อภาษาไทย (NL_NAME_1) มีข้อผิดพลาดจากต้นทางบางจุด (เช่น กรุงเทพฯ ระบุเป็น “เชียงใหม่” หรือชัยภูมิระบุเป็น “ชัยนาท”) ดังนั้น การสกัดรหัสจังหวัด 2 หลักจาก ISO_1 มาใช้เป็น Key จะตรงกับรหัสจังหวัดมาตรฐานของกระทรวงมหาดไทย 100% และไม่มีปัญหาข้อมูลตกหล่นครับ
2. ดึงและทำความสะอาดข้อมูลประชากรจากกรมการปกครอง (DOPA)
ข้อมูลสถิติจำนวนประชากรและบ้านรายจังหวัด ประจำปี พ.ศ. 2568 เผยแพร่ผ่านทางเว็บไซต์ สำนักบริหารการทะเบียน กรมการปกครอง
ไฟล์ข้อมูลใช้เครื่องหมาย | (Pipe) เป็นตัวคั่น และในปัจจุบันไฟล์ถูกบันทึกด้วยการเข้ารหัสแบบ UTF-8 เรียบร้อยแล้ว ทำให้เราสามารถใช้ readr::read_delim() อ่านตรงจาก URL ได้ทันที
pop68_prov_dopa_url <- "https://stat.bora.dopa.go.th/new_stat/file/68/stat_c68.txt"
# กำหนดชื่อคอลัมน์ตาม Schema มาตรฐานของกรมการปกครอง
col_names <- c(
"yymm",
"cc_code",
"cc_desc",
"rcode_code",
"rcode_desc",
"ccaatt_code",
"ccaatt_desc",
"ccaattmm_code",
"ccaattmm_desc",
"male",
"female",
"total",
"house"
)
# อ่านข้อมูลตรงจาก URL
raw_dopa <- read_delim(
pop68_prov_dopa_url,
delim = "|",
col_names = col_names,
col_types = cols(.default = col_character()),
trim_ws = TRUE
)จากนั้นเราจะกรองเอาเฉพาะแถวที่เป็น ระดับจังหวัด (77 จังหวัด) โดยตัดแถวยอดรวมระดับประเทศออก และแปลงตัวเลขประชากรเป็นตัวเลขจำนวนเต็ม (numeric):
pop68_province <- raw_dopa |>
mutate(cc_desc = str_trim(cc_desc)) |>
# กรองแถวที่ไม่ใช่จังหวัดออก (เช่น ยอดรวมทั่วประเทศ)
filter(
!is.na(cc_desc) & cc_desc != "" & !str_detect(cc_desc, "ยอดรวม|ทั่วประเทศ")
) |>
# กรองเฉพาะระดับจังหวัด (รหัสอำเภอและตำบลเป็นค่าว่างหรือ 0)
filter(
(is.na(ccaatt_code) | ccaatt_code == "" | str_detect(ccaatt_code, "^0+$")) &
(is.na(rcode_code) | rcode_code == "" | str_detect(rcode_code, "^0+$"))
) |>
mutate(
male = parse_number(male),
female = parse_number(female),
total_population = parse_number(total),
total_households = parse_number(house),
province_clean = str_remove(cc_desc, "^จังหวัด") |> str_trim(),
people_per_household = round(total_population / total_households, 2)
) |>
select(
province_code = cc_code,
province_name = cc_desc,
province_clean,
male,
female,
total_population,
total_households,
people_per_household
)
# ตรวจสอบตัวอย่างข้อมูล 5 จังหวัดแรก
pop68_province |>
select(province_code, province_name, total_population, total_households) |>
head(5)# A tibble: 5 × 4
province_code province_name total_population total_households
<chr> <chr> <dbl> <dbl>
1 10 กรุงเทพมหานคร 5422568 3356358
2 11 จังหวัดสมุทรปราการ 1383924 792409
3 12 จังหวัดนนทบุรี 1322280 775919
4 13 จังหวัดปทุมธานี 1246062 728164
5 14 จังหวัดพระนครศรีอยุธยา 822703 366492
3. รวมข้อมูล (Join) และสร้างแผนที่ด้วย ggplot2
เมื่อได้ทั้งข้อมูลแผนที่และข้อมูลประชากรที่มี Key province_code ตรงกันแล้ว เราสามารถสั่ง left_join() แล้ววาดแผนที่ด้วย geom_sf() ได้ทันที
# ทำการ Left Join ข้อมูลประชากรเข้ากับ Shapefile แผนที่
th_pop_map <- th_map_ready |>
left_join(pop68_province, by = "province_code")ggplot(th_pop_map) +
geom_sf(
aes(fill = total_population / 1e6),
color = "white",
linewidth = 0.25
) +
scale_fill_viridis_c(
option = "plasma",
direction = -1,
labels = label_number(suffix = "M", accuracy = 0.5),
name = "ประชากร\n(ล้านคน)"
) +
labs(
title = "จำนวนประชากรรายจังหวัดของประเทศไทย (พ.ศ. 2568)",
subtitle = "ข้อมูลสถิติจากสำนักบริหารการทะเบียน กรมการปกครอง",
caption = "แหล่งข้อมูล: สำนักบริหารการทะเบียน กรมการปกครอง (DOPA) | แผนที่: GADM"
) +
theme_void() +
theme(
plot.title = element_text(
face = "bold",
size = 16,
hjust = 0.5,
margin = margin(b = 6)
),
plot.subtitle = element_text(
size = 12,
color = "gray30",
hjust = 0.5,
margin = margin(b = 15)
),
plot.caption = element_text(
size = 9,
color = "gray50",
hjust = 0.5,
margin = margin(t = 12)
),
legend.position = "right",
legend.title = element_text(size = 11, face = "bold"),
legend.text = element_text(size = 10)
)
บทสรุป
ด้วยการใช้พลังของ tidyverse ร่วมกับ sf และ ggplot2: 1. เราสามารถดึงข้อมูลสถิติภาครัฐจาก DOPA มาคลีนได้ง่ายดายโดยตรงผ่าน URL 2. จัดการกับโครงสร้างข้อมูลเชิงพื้นที่ และแก้ไขประเด็นเรื่อง Key Matching ได้อย่างแม่นยำผ่าน รหัสจังหวัด (province_code) 3. สร้างแผนที่ Choropleth Map ที่สวยงาม แสดงภาพรวมการกระจายตัวของประชากรได้อย่างชัดเจน โดยมีกรุงเทพมหานครและหัวเมืองใหญ่ในแต่ละภูมิภาคเป็นศูนย์กลางประชากรที่หนาแน่นที่สุดครับ