迷你課程:R語言-12~R 套件

Quick look

學會在 R/RStudio 中使用 devtools + roxygen2 開發一個完整的 R 套件,並了解每個檔案/資料夾的結構、功能與開發流程。


建立 Package 骨架

install.packages(c("devtools", "roxygen2", "testthat", "usethis"))

devtools

roxygen2

roxygen2 是 R 語言中用來自動產生文件(documentation)的套件,它的目的是:將函數註解與文件寫在一起,然後自動產生符合 R 套件規範的 .Rd 說明檔NAMESPACE

傳統的 R 套件開發,需要你分別:
  1. 寫函數在 R/*.R 裡
  2. 寫說明文件在 man/*.Rd` 檔案(這是繁瑣的手動寫 LaTeX-like 的語法)
  3. 修改 NAMESPACE 檔案手動加上 export() 等宣告

非常容易出錯。

而 roxygen2 解決這些問題,讓你:

  • 只要在每個函數上方寫好 #' 標記的文件區塊 用 devtools::document() 一指令,就會自動幫你產生 .Rd 和 NAMESPACE
常見roxygen2 標籤:
  • @param:
  • @param 說明輸入參數
  • @return 說明回傳值
  • @export 表示這個函數會對外開放使用
  • @examples 給出範例用法
  • @import 匯入其他 package 的函數
  • @seealso 指向相關函數 @details 補充更多詳細說明

testthat

usethis

建立專案與初始結構

usethis::create_package("MyPacakge")

此時會在Rstudio預設的Home自動生成以下基本結構:

MyPackage/
├── DESCRIPTION
├── NAMESPACE
├── R/
├── weatherAPI2.Rproj
├── .Rbuildignore
└── .gitignore

如果沒有指定路徑,套件位置會出現在:

  • macOS / Linux: ~/(home folder)
  • Windows: C:/Users/你的帳號/

如何避免自動跳出 RStudio 專案?

如果你只想生成 package 而不切換 session,可以加參數:

usethis::create_package("~/your/path/MyPacakge", open = FALSE)

每個檔案/資料夾解釋

  • DESCRIPTION: 套件的 metadata(名稱、作者、版本、依賴套件)
  • NAMESPACE: 宣告哪些函數對外暴露(由 roxygen2 自動產生),檔案開頭會顯示: Generated by roxygen2: do not edit by hand
  • R/: 放置你寫的所有 .R 原始程式碼檔案
  • .Rbuildignore: 指定哪些檔案不應被打包,例如 .Rproj, .git/ 等
  • .gitignore: Git 專案忽略的檔案規則
  • MyPacakge.Rproj: RStudio 專案設定檔

撰寫函數與註解

#' Retrieve cancer data for patient and subtype
#'
#' @param pat patient data
#' @param sub cancer subtypes
#' @return A list with cancer patient information
#' @export
get_cancer_data <- function(pat, sub) {
  myurl <- paste0("https://api.cancer.gov/data/", pat, ",", sub)
  cancer_list <- jsonlite::fromJSON(myurl)
  return(cancer_list)
}

@param, @return, @export 是什麼?

  • @param: 描述函數的輸入參數
  • @return: 說明函數回傳的東西
  • @export: 表示這個函數會被放到 NAMESPACE,使用者可以直接呼叫

設定與說明檔案 DESCRIPTION

在創立新的套件位置後,記得改變當前路徑,不然如果要執行usethis會抓不到。

setwd(MyPackage)
usethis::use_description(fields = list(
  Title = "Interact with National Cancer Service API",
  Description = "Functions to access the National Cancer Service API.",
  License = "GPL-2",
  Encoding = "UTF-8",
  LazyData = "true"
))

或手動修改 DESCRIPTION 為:

Package: MyPackage
Type: Package
Title: For teaching purpose
Version: 0.1.0
Authors@R: person("YHD", "Oxford", email = "yhdyhd@gmail.com", role = c("aut", "cre"))
Description: Functions to access the National Weather Service API.
License: GPL-2
Encoding: UTF-8
LazyData: true
Imports:
  httr,
  jsonlite
Suggests:
  rmarkdown,
  knitr,
  testthat (>= 3.0.0)
VignetteBuilder: knitr

使用 devtools 的完整 workflow

加入一個函數

usethis::use_r("my_function")

這會在MyPackage下R這個資料夾裡生成一個檔名為my_function的.R檔。就可以開始自定義函數。

文件註解轉成說明檔

devtools::document()
#Console出現: 
#ℹ Updating MyPackage documentation
#ℹ Loading MyPackage
#Writing NAMESPACE
#Writing get_cancer_data.Rd

此時會發現新增一個資料夾叫man,存放了一個檔案:get_cancer_data.Rd (用於 ?get_cancer_data)。 而NAMESPACE這個檔案也被修正了:

# Generated by roxygen2: do not edit by hand

export(get_cancer_data)

載入開發中 package

devtools::load_all()

檢查 package 是否正確

devtools::check()

建立測試結構

usethis::use_testthat()

新增測試檔案

usethis::use_test("get_cancer_data")

建立範例教學文件

usethis::use_vignette("intro")

安裝本地套件

devtools::install()

加入測試

# 建立測試檔 tests/testthat/test-get_cancer_data.R
test_that("get_cancer_data returns a list", {
  result <- get_geo_data(42.440, -76.497)
  expect_type(result, "list")
})

建立長篇教學 Vignette

usethis::use_vignette("MyPackage-intro")
# 然後在 vignettes/MyPackage-intro.Rmd 撰寫教學

最後:打包與分享你的套件!

devtools::check()
devtools::build()

這會產生 .tar.gz 檔案,可給他人安裝使用:

install.packages("MyPackage_0.1.0.tar.gz", repos = NULL, type = "source")
comments powered by Disqus