close

如果感興趣,也可以直達公開課的b站鏈接:https://www.bilibili.com/video/BV1rf4y157ue,文末閱讀原文可以直達學習!

加載開發需要的R包
library(devtools)#>Loadingrequiredpackage:usethispackageVersion("devtools")#>[1]'2.4.3'這篇推文能學到什麼?

使用來自devtools 包的各種函數來體驗下開發一個R包的全部過程,為學習開發R包打下一個基礎

體驗R包開發介紹

開發一個名為regexcite包,並不具備實用功能,只是來體驗下整個R包開發過程

功能:使用正則表達式處理字符

使用Git對R包版本進行控制

R包安裝,書寫包內函數幫助文檔,使用 testthat檢查R包

1. `create_package()`

先打開Rstudio,使用create_package()函數初始化一個新的項目文件夾,用來作為regexcite包的開發環境

#定義路徑:注意The path should not be nested inside another RStudio Project, R package, or Git repo.path<-"../regexcite"#創建文件夾dir.create(path)#初始化路徑create_package(path)

在運行create_package(path)會彈出一個新的Rstudio界面,接下來在這個新的界面操作

在regexcite文件夾內新生成了好多文件,下次可以直接regexcite.Rproj打開

image-202204082121169882.`use_git()`

使用use_git()函數,在新的工作環境初始化Git倉庫,來對R包進行版本控制

library(devtools)#初始化Git倉庫use_git()#>✔InitialisingGitrepo#>✔Adding'.Rhistory','.Rdata','.httr-oauth','.DS_Store'to'.gitignore'

會在工作目錄新增加一個.git文件夾

image-202204082131071963. 寫出R包regexcite的第一個函數

現在定義一個函數

#定義函數strsplit1<-function(x,split){strsplit(x,split=split)[[1]]}#測試函數(x<-"alfa,bravo,charlie,delta")#>[1]"alfa,bravo,charlie,delta"strsplit1(x,split=",")#>[[1]]#>[1]"alfa""bravo""charlie""delta"4. `use_r()`

定義的strsplit1()函數保存在一個.R結尾的文件中,並存放於當工作目錄的R/目錄中,use_r()可以在R/目錄下創建腳本,或者手動去創建也可以

#在R/下創建strsplit1文件use_r("strsplit1")#>•Edit'R/strsplit1.R'#>•Call`use_test()`tocreateamatchingtestfile

可以看到R/下新創建了strsplit1.R文件

image-202204082144222255.`load_all()`

快捷鍵:Ctrl + Shift + L (Windows & Linux) or Cmd + Shift + L (macOS)

為了測試新寫的函數,將所有R/下腳本中的函數加載到當前環境中,類似於source("R/strsplit1.R")。

#加載strsplit1()函數load_all()#>ℹLoadingregexcite

測試下函數是否工作

(x<-"alfa,bravo,charlie,delta")#>[1]"alfa,bravo,charlie,delta"strsplit1(x,split=",")#>[1]"alfa""bravo""charlie""delta"

使用exists()函數查看strsplit1函數是否在當前環境,可以看出來load_all()加載的函數不存在於當前環境

#查看strsplit1函數是否在當前環境exists("strsplit1",where=globalenv(),inherits=FALSE)#>[1]FALSE6. `check()`

快捷鍵:Ctrl + Shift + E (Windows & Linux) or Cmd + Shift + E (macOS)

check()函數查看寫的R包是否正常工作,如果發現有警告或者報錯等,要及時解決

check()#>──RCMDcheckresults───────────────────regexcite0.0.0.9000────#>Duration:24.3s#>#>❯checkingDESCRIPTIONmeta-information...WARNING#>Non-standardlicensespecification:#>`use_mit_license()`,`use_gpl3_license()`orfriendstopicka#>license#>Standardizable:FALSE#>#>0errors✔|1warning✖|0notes✔7. 編輯`DESCRIPTION`文件

查看下DESCRIPTION文件,其包含了R包的元數據,即包名,介紹,還有作者信息等

Package:regexciteTitle:WhatthePackageDoes(OneLine,TitleCase)Version:0.0.0.9000Authors@R:person(given="First",family="Last",role=c("aut","cre"),email="first.last@example.com",comment=c(ORCID="YOUR-ORCID-ID"))Description:Whatthepackagedoes(oneparagraph).License:`use_mit_license()`,`use_gpl3_license()`orfriendstopickalicenseEncoding:UTF-8LazyData:trueRoxygen:list(markdown=TRUE)RoxygenNote:7.1.1

進行編輯

Package:regexciteTitle:MakeRegularExpressionsMoreExcitingVersion:0.0.0.9000Authors@R:person(given="your_given_name",family="your_family_name",role=c("aut","cre"),email="your_email@example.com",comment=c(ORCID="YOUR-ORCID-ID"))Description:onveniencefunctionstomakesomecommontaskswithstringmanipulationandregularexpressionsabiteasier.License:`use_mit_license()`,`use_gpl3_license()`orfriendstopickalicenseEncoding:UTF-8LazyData:trueRoxygen:list(markdown=TRUE)RoxygenNote:7.1.28. `use_mit_license()`

使用use_mit_license()函數為R包添加一個MIT許可證(license),在當前環境下會新增倆個文件:LICENSE和LICENSE.md

use_mit_license()#>✔SettingLicensefieldinDESCRIPTIONto'MIT+fileLICENSE'#>✔Writing'LICENSE'#>✔Writing'LICENSE.md'#>✔Adding'^LICENSE\\.md$'to'.Rbuildignore'

注意:如果你要將R包發布到CRAN,需要 在.Rbuildignore中添加LICENSE.md,但是如果放在Github上不需要添加

9. `document()`

快捷鍵:Ctrl + Shift + D (Windows & Linux) or Cmd + Shift + D (macOS)

為strsplit1寫個幫助文檔,即生成一個man/strsplit1.Rd文件,內容由一種特殊 R標記語言組成,類似於LaTeX。這個幫助文檔不用我們自己去寫,可有由roxygen2 包生成

首先打開 R/strsplit1.R文件,鼠標的光標放在寫的函數上,然後點擊:Code > Insert roxygen skeleton,然後會生成幾行注釋

image-20220411115453766

修改成下面這樣

#'Splitastring#'#'@paramxAcharactervectorwithoneelement.#'@paramsplitWhattospliton.#'#'@returnAcharactervector.#'@export#'#'@examples#'x<-"alfa,bravo,charlie,delta"#'strsplit1(x,split=",")strsplit1<-function(x,split){strsplit(x,split=split)[[1]]}

使用document()函數生成man/strsplit1.Rd文件

document()#>ℹUpdatingregexcitedocumentation#>ℹLoadingregexcite#>WritingNAMESPACE#>Writingstrsplit1.Rd#查看寫的幫助文檔?strsplit1image-2022041111573509911. 查看`NAMESPACE`

基於上面strsplit1()函數注釋中的@export,在使用document()函數後會對 NAMESPACE文件進行更新,裡面的內容看起來會是下面這樣

#Generatedbyroxygen2:donoteditbyhandexport(strsplit1)12. 再一次`check()`check()#>──RCMDcheckresults───────────────────regexcite0.0.0.9000────#>Duration:25.4s#>#>0errors✔|0warnings✔|0notes✔

現在可以看到,通過了檢查:0 errors, 0 warnings, 0 notes。如果有問題的話根據提示去解決

13. `install()`

經過上面的操作,regexcite 包已經可以使用,將這個R包安裝一下

install()*checkingforfile『/tmp/Rtmpde0k7R/regexcite/DESCRIPTION』...OK*preparing『regexcite』:*checkingDESCRIPTIONmeta-information...OK*checkingforLFline-endingsinsourceandmakefilesandshellscripts*checkingforemptyorunneededdirectories*building『regexcite_0.0.0.9000.tar.gz』Running/opt/R/4.1.3/lib/R/bin/RCMDINSTALL\/tmp/Rtmpde0k7R/regexcite_0.0.0.9000.tar.gz--install-tests*installingtolibrary『/home/runner/work/_temp/Library』*installing*source*package『regexcite』...**usingstagedinstallation**R**byte-compileandpreparepackageforlazyloading**help***installinghelpindices**buildingpackageindices**testingifinstalledpackagecanbeloadedfromtemporarylocation**testingifinstalledpackagecanbeloadedfromfinallocation**testingifinstalledpackagekeepsarecordoftemporaryinstallationpath*DONE(regexcite)

現在regexcite包會像其他R包一樣,打開一個新的工作環境來測試一下

library(regexcite)x<-"alfa,bravo,charlie,delta"strsplit1(x,split=",")#>[1]"alfa""bravo""charlie""delta"13. `use_testthat()`

上面通過加載包,來測試並不正規。可以使用 use_testthat()來因引入正規的測試功能,運行後會對一些文件進行如下修改,比如創建新的tests文件夾等,具體看下面的注釋信息

use_testthat()#>✔Adding'testthat'toSuggestsfieldinDESCRIPTION#>✔SettingConfig/testthat/editionfieldinDESCRIPTIONto'3'#>✔Creating'tests/testthat/'#>✔Writing'tests/testthat.R'#>•Call`use_test()`toinitializeabasictestfileandopenitforediting.

現在可以寫一個正式的測試功能來對strsplit1()函數進行測試

use_test("strsplit1")#>✔Writing'tests/testthat/test-strsplit1.R'#>•Edit'tests/testthat/test-strsplit1.R'

運行use_test("strsplit1")後,在創建如下文件tests/testthat/test-strsplit1.R,內容如下:

test_that("multiplicationworks",{expect_equal(2*2,4)})

然後修改下:

test_that("strsplit1()splitsastring",{expect_equal(strsplit1("a,b,c",split=","),c("a","b","c"))})

運行test()查看函數是否正常

test()#>ℹLoadingregexcite#>ℹTestingregexcite#>✔|FWSOK|Context#>#>⠏|0|strsplit1#>✔|1|strsplit1#>#>══Results═════════════════════════════════════════════════════════#>[FAIL0|WARN0|SKIP0|PASS1]14. `use_package()`

在寫R包的時候,可能會需要用到其他R的函數,use_package()在DESCRIPTION文件中新加了如下內容,也可以手動添加,這樣就可以通過包名::函數名的形式來引用其他R包的函數,當然不止這一種形式來調用其他R包中的函數,這裡不做擴展

use_package("stringr")#>✔Adding'stringr'toImportsfieldinDESCRIPTION#>•Refertofunctionswith`stringr::fun()`image-20220412184727860

下面把strsplit1.R文件中的strsplit1()函數重新修改下:

str_split_one<-function(string,pattern,n=Inf){stopifnot(is.character(string),length(string)<=1)if(length(string)==1){stringr::str_split(string=string,pattern=pattern,n=n)[[1]]}else{character()}}

函數修改了,那文件名也修改下吧,可以使用rename_files()函數修改R文件夾以及tests/testthat文件夾下的腳本名

rename_files("strsplit1","str_split_one")#>✔Moving'R/strsplit1.R'to'R/str_split_one.R'#>✔Moving'tests/testthat/test-strsplit1.R'to'tests/testthat/test-str_split_one.R'

然後在為str_split_one()函數標記語言寫下幫助文檔:

#'Splitastring#'#'@paramstringAcharactervectorwith,atmost,oneelement.#'@inheritParamsstringr::str_split#'#'@returnAcharactervector.#'@export#'#'@examples#'x<-"alfa,bravo,charlie,delta"#'str_split_one(x,pattern=",")#'str_split_one(x,pattern=",",n=2)#'#'y<-"192.168.0.1"#'str_split_one(y,pattern=stringr::fixed("."))str_split_one<-function(string,pattern,n=Inf){stopifnot(is.character(string),length(string)<=1)if(length(string)==1){stringr::str_split(string=string,pattern=pattern,n=n)[[1]]}else{character()}}

也不要忘了修改測試文件,增加兩個新的測試

test_that("str_split_one()splitsastring",{expect_equal(str_split_one("a,b,c",","),c("a","b","c"))})test_that("str_split_one()errorsifinputlength>1",{expect_error(str_split_one(c("a,b","c,d"),","))})test_that("str_split_one()exposesfeaturesofstringr::str_split()",{expect_equal(str_split_one("a,b,c",",",n=2),c("a","b,c"))expect_equal(str_split_one("a.b",stringr::fixed(".")),c("a","b"))})

在完成上面的操作後,再次使用document()函數,還記的它的功能嗎?

將上面特殊的標記語言轉換為你寫的函數的幫助文檔

再次修改 NAMESPACE文件內容

document()#>ℹUpdatingregexcitedocumentation#>ℹLoadingregexcite#>Warninginsetup_ns_exports(path,export_all,export_imports):#>Objectslistedasexports,butnotpresentinnamespace:strsplit1#>WritingNAMESPACE#>WritingNAMESPACE#>Writingstr_split_one.Rd#>Deletingstrsplit1.Rd

把修改好的函數加載下,看看能不能用

load_all()#>ℹLoadingregexcitestr_split_one("a,b,c",pattern=",")#>[1]"a""b""c"15. `use_github()`

如果配置好了Git,use_github()函數會自動在Github創建一個遠程倉庫,將R包放在Github的倉庫里,這裡需要有點Git的基礎,不做擴展

Git官網:https://git-scm.com/

玩轉Git管理代碼(拓展)

16. `use_readme_rmd()`

將R包放在Github上後,可以使用README.md文件來描述你的R包,描述下如何安裝,如何使用等。需要了解Rmarkdown如何使用,不做擴展

use_readme_rmd()#>✔Writing'README.Rmd'#>✔Adding'^README\\.Rmd$'to'.Rbuildignore'#>•Update'README.Rmd'toincludeinstallationinstructions.#>✔Writing'.git/hooks/pre-commit'build_readme()#>ℹInstallingregexciteintemporarylibrary#>ℹBuilding/tmp/Rtmpde0k7R/regexcite/README.Rmd17. 最後:`check()` and `install()`

再次使用 check()函數,檢查寫的R包是否可以使用,沒有報錯警告就可以安裝了

check()#>──RCMDcheckresults───────────────────regexcite0.0.0.9000────#>Duration:27.7s#>#>0errors✔|0warnings✔|0notes✔

安裝

install()

一共17步看了下開發一個R包的過程,當然每一步還有很多擴展的內容,後面在慢慢學習,對R包開發感興趣的可以打開參考資料提供的鏈接深入學習下

參考資料:

https://r-pkgs.org/whole-game.html#whole-game


末友情宣傳

強烈建議你推薦給身邊的博士後以及年輕生物學PI,多一點數據認知,讓他們的科研上一個台階:

數據挖掘(GEO,TCGA,單細胞)2022年5月場,快速了解一些生物信息學應用圖表
生信入門課-2022年5月場,你的生物信息學第一課
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()