Writing
RstatsR Packagedevtools

คู่มือสร้าง R Package ฉบับ 2026

คู่มือสร้าง R Package ฉบับ 2026

คู่มือสร้าง R Package ฉบับ 2026

การสร้าง R Package ในปี 2026 เน้นความเร็ว (Performance) และการทำงานร่วมกับ AI บทความนี้สรุปขั้นตอนจากวิดีโอของ Pat Schloss (Riffomonas Project) และปรับปรุงด้วยเครื่องมือสมัยใหม่อย่าง Positron, pak, และ Quarto

info ทำไมต้องสร้าง R Package?

แม้แต่โค้ดที่ใช้งานส่วนตัว การจัด Package ช่วยให้คุณ:

  • แชร์งาน กับทีมหรือชุมชนนักวิจัยได้ง่าย
  • จัดการ Dependencies อย่างเป็นระบบ ไม่มีปัญหา “ใน PC ฉันรันได้นะ”
  • เขียน Documentation ที่ใช้จริงได้ผ่าน Roxygen2 และ Quarto
  • ทดสอบโค้ดอัตโนมัติ ด้วย testthat ก่อนที่คนอื่นจะเจอบัก

🛠️ 0. เตรียมกระเป๋าเครื่องมือ (2026 Essentials)

ในปี 2026 เราเปลี่ยนมาใช้เครื่องมือที่เร็วและฉลาดกว่าเดิม ก่อนเริ่มควรติดตั้งสิ่งเหล่านี้ให้ครบ:

เครื่องมือหน้าที่ทำไมต้องใช้?
Positron หรือ RStudioIDE สำหรับพัฒนา RPositron เร็วกว่ามากสำหรับโปรเจกต์ใหญ่
pakติดตั้ง Packageเร็วกว่า install.packages() หลายเท่า รองรับ parallel install
devtoolsเครื่องมือพัฒนา Packageครอบคลุมทุก Workflow หลัก
usethisสร้างโครงสร้างอัตโนมัติลด Boilerplate ได้ 90%
testthatUnit Testingช่วยให้โค้ดเชื่อถือได้
roxygen2สร้างเอกสารจากโค้ดเขียนครั้งเดียว ได้ Help file อัตโนมัติ
AI Assistantช่วยร่าง docs & testsเพิ่มความเร็วในการพัฒนา
# ติดตั้ง pak ก่อนเป็นอันดับแรก
install.packages("pak")

# จากนั้นใช้ pak ติดตั้งทุกอย่างพร้อมกัน (เร็วมาก)
pak::pkg_install(c("devtools", "usethis", "testthat", "roxygen2", "quarto"))

⚙️ 0.5 ตั้งค่า .Rprofile เพื่อความสะดวกสูงสุด

เพื่อให้ R โหลดเครื่องมือพัฒนาโดยอัตโนมัติทุกครั้งที่เปิดโปรเจกต์ ให้ตั้งค่า .Rprofile ไว้ล่วงหน้าสักครั้ง แล้วจะไม่ต้องรัน library(devtools) เองอีกเลย

เปิดไฟล์ตั้งค่า:

usethis::edit_r_profile()

เพิ่ม Code นี้ลงไป:

if (interactive()) {
  suppressMessages(library(devtools))
  suppressMessages(library(usethis))
}
info ทำไมต้องเช็ค interactive()?

เงื่อนไข if (interactive()) ป้องกันไม่ให้ devtools และ usethis ถูกโหลดเมื่อ R รันแบบอัตโนมัติ (เช่น ใน GitHub Actions หรือ Rscript จาก Terminal) ซึ่งเป็น Best practice สำคัญ — Package เหล่านี้ควรโหลดเฉพาะในเซสชัน interactive ของนักพัฒนาเท่านั้น ไม่ควรรันในสภาพแวดล้อม production

ผลที่ได้: คุณสามารถใช้คีย์ลัดอย่าง Ctrl + Shift + L ได้ทันทีทุกครั้งที่เปิด RStudio หรือ Positron โดยไม่ต้องรัน library() เองก่อน


🏗️ 1. ก่อสร้างโครงสร้างพื้นฐาน (Setup)

สร้าง Package ใหม่

คำสั่งเดียวจบ — usethis จะสร้างโฟลเดอร์และไฟล์พื้นฐานทั้งหมดให้อัตโนมัติ:

usethis::create_package("path/to/YourPackageName")

หลังรันคำสั่งนี้ คุณจะได้โครงสร้างเริ่มต้นแบบนี้:

YourPackageName/
├── DESCRIPTION      # ข้อมูล Package (ชื่อ, เวอร์ชัน, Dependencies)
├── NAMESPACE        # ควบคุมว่าฟังก์ชันไหนเป็น Public/Private
├── R/               # 📁 โฟลเดอร์สำหรับวางโค้ด R ทุกไฟล์
└── YourPackageName.Rproj

เชื่อมต่อกับ Git & GitHub

ในปี 2026 เราไม่แค่ใช้ Git แต่เราตั้ง CI/CD ให้ทำงานอัตโนมัติทันทีตั้งแต่วันแรก:

usethis::use_git()                              # เริ่มต้น Git repository
usethis::use_github()                           # Push ขึ้น GitHub
usethis::use_github_action_check_standard()    # ตั้ง CI: ตรวจโค้ดอัตโนมัติทุกครั้งที่ Push
info GitHub Actions คืออะไร?

GitHub Actions จะรัน R CMD check บนเซิร์ฟเวอร์ของ GitHub ทุกครั้งที่คุณ Push โค้ดขึ้นไป ช่วยให้คุณรู้ทันทีว่าโค้ดของคุณผ่านมาตรฐาน CRAN หรือไม่ โดยไม่ต้องรอทดสอบเองบนเครื่อง

ใส่ใบอนุญาต (License)

usethis::use_mit_license("Your Name")   # MIT เป็น License ที่นิยมมากสำหรับ Open-source

🔄 2. วงจรการพัฒนา (The Modern Dev Loop)

หัวใจสำคัญของการพัฒนา R Package คือ “The Golden Cycle” ซึ่งในปี 2026 ทำได้ลื่นไหลและเร็วขึ้น:

Edit code → Load All → Test in Console → Repeat

ขั้นตอนที่ 1: สร้างฟังก์ชันใหม่

ใช้ usethis สร้างไฟล์ใน R/ โฟลเดอร์ให้อัตโนมัติ (พร้อมตั้งชื่อไฟล์ให้ถูกต้อง):

usethis::use_r("my_cool_function")

ขั้นตอนที่ 2: โหลดโค้ดเพื่อทดสอบ (load_all)

นี่คือคำสั่งที่คุณจะใช้บ่อยที่สุดตลอดการพัฒนา มันจำลองการ library() ทั้ง Package แต่จากโค้ดในเครื่องของคุณโดยตรง:

devtools::load_all()

# หรือใช้ Shortcut ที่เร็วกว่า:
# Windows: Ctrl + Shift + L
# Mac:     Cmd  + Shift + L

ใน 2026 load_all() ทำงานได้ฉลาดขึ้น — มันจะ “Incremental load” คือโหลดเฉพาะส่วนที่เปลี่ยนแปลงไป ไม่โหลดซ้ำทั้งหมด ทำให้เร็วขึ้นมากสำหรับ Package ขนาดใหญ่


📝 3. การทำคู่มือ (AI-Assisted Documentation)

เรายังคงใช้ Roxygen2 เป็น standard แต่ในปี 2026 AI ช่วยร่างได้เบื้องต้น เหลือแค่ให้เราตรวจสอบและเติมรายละเอียดสำคัญ

โครงสร้าง Roxygen2 ของฟังก์ชัน

#' คำนวณค่าเฉลี่ยรายวัน
#'
#' ฟังก์ชันนี้รับ Vector ตัวเลขและคืนค่าเฉลี่ย โดยสามารถเลือก
#' กำจัด NA ออกก่อนคำนวณได้
#'
#' @param x Numeric vector ที่ต้องการคำนวณ
#' @param na.rm Logical ถ้าเป็น TRUE (ค่าเริ่มต้น) จะกำจัด NA ก่อนคำนวณ
#'
#' @return ค่าเฉลี่ยของ Vector ที่รับเข้ามา (Numeric scalar)
#'
#' @export
#'
#' @examples
#' daily_mean(c(10, 20, 30))
#' daily_mean(c(10, NA, 30), na.rm = TRUE)
daily_mean <- function(x, na.rm = TRUE) {
  mean(x, na.rm = na.rm)
}

Tags ที่สำคัญต้องรู้:

Tagความหมาย
@paramอธิบาย argument แต่ละตัว
@returnอธิบายสิ่งที่ฟังก์ชันส่งกลับ
@exportทำให้ผู้ใช้เรียกฟังก์ชันนี้ได้จากภายนอก
@examplesใส่โค้ดตัวอย่าง — สำคัญมาก ถ้าส่ง CRAN
@importระบุ Package อื่นที่ฟังก์ชันนี้ต้องการทั้ง Package
@importFromระบุเฉพาะฟังก์ชันที่ต้องการจาก Package อื่น

สร้างไฟล์ Help (.Rd files)

หลังเขียน Roxygen comment แล้ว รันคำสั่งนี้เพื่อสร้างไฟล์ Help จริงๆ:

devtools::document()

# Shortcut: Ctrl + Shift + D (Windows) / Cmd + Shift + D (Mac)

📦 4. การจัดการ Dependencies (Fast Path)

กฎทองที่ต้องจำ: ห้ามใช้ library() ในโค้ด Package เด็ดขาด!

เหตุผลที่ห้ามใช้ library() ในโค้ดของ Package คือมันจะ บังคับโหลด Package เข้า Search path ของผู้ใช้ ซึ่งอาจทับฟังก์ชันชื่อเดียวกันที่ผู้ใช้กำลังใช้งานอยู่ได้

วิธีที่ถูกต้อง:

ขั้นตอนที่ 1: ลงทะเบียน Dependency ใน DESCRIPTION file

# ลงทะเบียนว่า Package ของคุณต้องการ dplyr
usethis::use_package("dplyr")

ขั้นตอนที่ 2: เรียกใช้ฟังก์ชันด้วยการระบุ Namespace ชัดเจน

# ✅ วิธีที่ถูกต้อง — ระบุ Package ที่มาอย่างชัดเจน
my_function <- function(df) {
  dplyr::mutate(df, new_col = value * 2)
}
info 💡 Tip: ใช้ `use_import_from` เพื่อความเร็ว

หากคุณเรียกใช้ฟังก์ชันเดิมซ้ำๆ หลายร้อยครั้งในโค้ด สามารถ import เข้ามาตรงๆ เพื่อเพิ่มความเร็วและลดการพิมพ์ได้:

usethis::use_import_from("dplyr", "mutate")

การทำแบบนี้จะเพิ่ม @importFrom dplyr mutate เข้า NAMESPACE ซึ่งถือว่าเป็น Best practice สำหรับฟังก์ชันที่ใช้บ่อยมากๆ ครับ


✅ 5. การตรวจสอบคุณภาพ (The Final Boss)

เขียน Unit Test (ยุคใหม่)

ปี 2026 เน้น Test-Driven Development (TDD) — หมายความว่าเขียน Test ก่อน แล้วค่อยเขียน Code ให้ผ่าน Test

ขั้นตอนที่ 1: สร้างไฟล์ Test

usethis::use_test("my_cool_function")
# จะสร้างไฟล์ tests/testthat/test-my_cool_function.R

ขั้นตอนที่ 2: เขียน Test ด้วย testthat

# ไฟล์: tests/testthat/test-daily_mean.R

test_that("daily_mean คำนวณค่าเฉลี่ยได้ถูกต้อง", {
  expect_equal(daily_mean(c(10, 20, 30)), 20)
})

test_that("daily_mean จัดการ NA ได้", {
  expect_equal(daily_mean(c(10, NA, 30), na.rm = TRUE), 20)
  expect_true(is.na(daily_mean(c(10, NA, 30), na.rm = FALSE)))
})

test_that("daily_mean รับได้เฉพาะตัวเลข", {
  expect_error(daily_mean("ข้อความ"))
})

ขั้นตอนที่ 3: รัน Test ทั้งหมด

devtools::test()

# Shortcut: Ctrl + Shift + T (Windows) / Cmd + Shift + T (Mac)
info ใช้ AI ช่วยหา Edge Case

วิธีง่ายๆ คือวางโค้ดฟังก์ชันของคุณให้ AI แล้วถามว่า “ช่วยหา Edge cases และเขียน testthat tests ให้หน่อยได้ไหม?” AI มักจะนึกถึงกรณีที่เรามักมองข้ามได้ดี เช่น ค่าว่าง (NULL), vector ความยาวเป็น 0, หรือค่าติดลบ

การตรวจสอบสุดท้าย (The Full Check)

นี่คือขั้นตอนสำคัญที่สุดก่อน Release — devtools::check() จะรันกระบวนการทดสอบทั้งหมด (R CMD check) เหมือนกับที่ CRAN ใช้จริงๆ:

devtools::check()

# Shortcut: Ctrl + Shift + E (Windows) / Cmd + Shift + E (Mac)

เป้าหมาย: ต้องให้ได้ผลลัพธ์นี้ก่อนปล่อย Version ใหม่ทุกครั้ง:

── R CMD check results ─────────────────────────────────
Duration: 23.4s

0 errors ✔ | 0 warnings ✔ | 0 notes ✔

R CMD check succeeded

ปัญหาที่พบอยู่จากประสบการณ์ เมื่อมีการติดตั้งหรือมีภาษาไทย ใน package และ เมื่อ upload แล้ว

.github/workflows/R-CMD-check.yaml

จะเป็นการ R CMD check ในระบบ CI/CD ของ GitHub ซึ่งจะช่วยให้เราสามารถตรวจสอบ package ของเราได้ว่ามีปัญหาอะไรบ้าง

วิธีแก้สำหรับการทำ Package ให้ผ่าน: แนะนำให้เอา \donttest ไปครอบตัวอย่างของฟังก์ชัน get_production_index_month() และ ทุกฟังก์ชันที่มีการเรียกใช้ API จริงๆ เพื่อไม่ให้ระบบต้องมารอโหลด API ตอนตรวจแพ็กเกจครับ

#' @examples
#' \donttest{
#' get_production_index_month(year_th = 2568, month = 12)
#' }

วิธีแก้: เราต้องบอกระบบว่าให้ข้ามโฟลเดอร์นี้ไปตอนสร้างแพ็กเกจ พิมพ์คำสั่งนี้ใน R Console ได้เลยครับ:

usethis::use_build_ignore(".claude")

(คำสั่งนี้จะไปเติมชื่อโฟลเดอร์ลงในไฟล์ .Rbuildignore ให้อัตโนมัติครับ)

📄 6. README และ Vignettes ด้วย Quarto

ในปี 2026 Quarto (.qmd) ได้เข้ามาแทนที่ R Markdown อย่างเต็มตัวใน Workflow การพัฒนา Package:

สร้าง README

# สร้าง README.qmd (แนะนำ) หรือ README.Rmd แบบเก่า
usethis::use_readme_qmd()

README ที่ดีควรมีส่วนเหล่านี้:

สร้าง Vignette (คู่มือเจาะลึก)

usethis::use_vignette("getting-started")
# จะสร้างไฟล์ vignettes/getting-started.qmd

Quarto มีข้อดีสำคัญเหนือ R Markdown สำหรับ Package documentation:


🌐 8. สร้างเว็บ Documentation ด้วย pkgdown

ในปี 2026 ไม่ให้ Package ใดไม่มี Documentation Website pkgdown คือเครื่องมือมาตรฐานที่จะแปลง Roxygen comments, Vignettes และ README ของคุณให้กลายเป็นเว็บไซต์ที่สวยงาม เหมือนที่คุณเห็นใน Package ชั้นนำทุกตัวบน CRAN

ทำไมต้อง pkgdown?

ประเด็นไม่มี pkgdownมี pkgdown
การค้นหาผู้ใช้ต้องรัน ?function() บน RGoogle แล้วเจอหน้าเว็บสวยๆ ทันที
ภาพรวมต้องดีบักไฟล์ .Rd ที่ยากมี Function reference แบบค้นหาได้
TutorialVignettes แยกกระจัดกระจายรวมหน้าเว็บเดียวจบ พร้อม Navbar
บำรุงรักษาผู้ใช้ดู Help ยากLink มาจาก CRAN/GitHub อัตโนมัติ

ติดตั้งและตั้งค่าพื้นฐาน

# ติดตั้ง pkgdown
pak::pkg_install("pkgdown")

# สร้างไฟล์ตั้งค่าพื้นฐาน
pkgdown::init()

คำสั่ง pkgdown::init() จะสร้างไฟล์ _pkgdown.yml ให้คุณ:

url: https://yourusername.github.io/YourPackageName
title: YourPackageName

template:
  bootstrap: 5

reference:
- title: "ฟังก์ชันหลัก"
  contents:
  - daily_mean
  - my_cool_function

articles:
- title: "คู่มือ"
  contents:
  - getting-started

Build เว็บทดสอบ

# Build และ Preview เว็บบนเครื่อง
pkgdown::build_site()

หลังจาก Build คุณจะได้โฟลเดอร์ docs/ ซึ่งเป็นไฟล์ HTML ทั้งหมด สามารถเปิดดูได้ทันที

Deploy อัตโนมัติไป GitHub Pages

วิธีที่ง่ายที่สุดคือใช้ GitHub Actions — push ครั้งเดียว เว็บ Deploy เอง:

usethis::use_pkgdown_github_pages()

คำสั่งนี้จะ:

  1. สร้าง workflow ใน .github/workflows/pkgdown.yaml
  2. Set up GitHub Pages ให้ point ไปที่ gh-pages branch
  3. Deploy อัตโนมัติทุกครั้งที่คุณ push ไป branch main หรือ master

Custom Theme ให้เว็บสวยขึ้น

ปี 2026 มี Theme Template สวยๆ ให้เลือกเยอะครับ แก้ใน _pkgdown.yml:

template:
  bootstrap: 5
  bootswatch: flatly    # เลือก theme: cerulean, cosmo, flatly, journal, และอื่นๆ
  bslib:
    pkgdown-nav-height: 70px
    primary: "#2c3e50"   # เปลี่ยนสีหลักตาม Brand ของคุณ

เพิ่ม Search Functionality

เว็บ pkgdown มี search engine ในตัว (ใช้ lunr.js) ทำให้ผู้ใช้ค้นหาฟังก์ชันได้ทันที ไม่ต้อง browse ทีละหน้า

เพิ่ม Logo และ Favicon

home:
  title: "YourPackageName"
  description: |
    คำอธิบายสั้นๆ ว่า Package นี้ทำอะไร
  strip_header: true

template:
  logo: path/to/logo.png    # รูป logo (แนะนำ 200x200px)
  favicon: path/to/favicon.ico

ประโยชน์เพิ่มเติมของ pkgdown


🚀 9. สรุปคีย์ลัด (2026 Quick Reference)

เก็บตารางนี้ไว้เป็น Cheat Sheet ในการพัฒนา:

งานที่ต้องทำShortcut (Windows)Shortcut (Mac)คำสั่ง R
โหลดโค้ดใหม่Ctrl + Shift + LCmd + Shift + Ldevtools::load_all()
อัปเดตคู่มือCtrl + Shift + DCmd + Shift + Ddevtools::document()
รัน TestsCtrl + Shift + TCmd + Shift + Tdevtools::test()
Check มาตรฐานCtrl + Shift + ECmd + Shift + Edevtools::check()
Build & InstallCtrl + Shift + BCmd + Shift + Bdevtools::install()

🗺️ สรุปภาพรวม Workflow

usethis::create_package()       # 1. สร้าง Package

usethis::use_git() + use_github() # 2. เชื่อม Git & GitHub

usethis::use_r("func_name")     # 3. สร้างไฟล์ฟังก์ชัน

devtools::load_all()  ←──────── # 4. (วนซ้ำ) โหลด → แก้ไข → โหลด

roxygen2 + devtools::document() # 5. เขียนและสร้างคู่มือ

usethis::use_test() + devtools::test() # 6. เขียนและรัน Tests

devtools::check()               # 7. Check ก่อน Release

devtools::install() / pak::pkg_install() # 8. ติดตั้งใช้งานจริง

pkgdown::build_site()           # 9. Build เว็บ documentation (เสร็จแล้ว Deploy อัตโนมัติ)

info อ้างอิง