前言
写R包这种高大上的工作,原以为学会怎么写得花个一年半载,学会之后写一个包怎么着也得个把月,谁料想周一上午下了决心,到周二下午就一口气写了4个包,算起来从学到写,只花了一天多时间。这里是个小结。
学写R包,我主要参考了3份资料:
- 谢益辉的开发R程序包之忍者篇,2011年。反复读了好几遍。
- 黄俊文的极简R包建立方法, 2013年。反复读了好几遍。
- Hadley Wickham 的R packages, 2015年。反复扫了好几眼。
有了这3份很好的资料,那我还写本篇帖子做什么?
这是因为,他们都是高手中的高手,只是我觉得他们写得,就好比教授写幼儿园教材,仍然不太合小朋友的胃口,不足以让我等菜鸟快速入门。比如读到roxygen2是多么美妙,我起初完全没体验,不知道“举头望文档,低头思函数”到底说的是什么(what the hell was Yihui talking about? 当然,一天之后我就体会到了静夜思的精妙),再一看Emacs就被吓得河水倒流,当年光一个vim就把我折腾个半死。而极简篇里,我觉得既然是极简,能减掉的步骤尽量减,所以还能继续剪。可能当年是极简,时过境迁,人类进步,现在可以比极简更简。
下面,我试试写个减到不能再减的一剑封喉致简版,只需小改两个文本文件,不出十分钟就写出一个能用的R包。
准备
跟前期准备和后期维护相比,写包本身其实是最简单的环节。假定你已经安装好了R和RStudio,那么,运行R,安装几个包:
install.packages(c("devtools", "roxygen2", "testthat", "knitr"))
然后:
- Windows用户,安装 rtools.
- Linux用户,安装对应的 R 开发包,sudo apt-get install r-base-dev.
- OS X 用户,安装command-line-tools,Terminal 运行 git 或者 xcode-select.
致简四部曲
参照益辉的rmini,我做了个更简单的模板(Update:根据Dong的建议,也可以RStudio - File - New project - New directory - R package),叫做rmickey。点击下载并解压缩,得到个名叫rmickey文件夹。这个文件夹名字可以随便改。
双击
rmickey.Rproj
(这个文件名也可以随便改),就会用RStudio打开这个项目。后续步骤全部在RStudio里操作。点击打开RStudio右下窗口Files下的
DESCRIPTION
文件(这个文件名千万别改)和R/foo.R
文件(这个文件名可以随便改)。DESCRIPTION
里是你的包的描述信息。可以看到包名称,版本,日期,包标题,包作者,维护者,依赖性,描述等信息。把其中每一条冒号后面的信息改成你自己的信息,比如把第一行包的名称改成mymickey,保存。R/foo.R
里是你的包里包含的自定义函数,模板里只列了一条做示例。函数的写法跟平时没区别。而函数前面的几行,就是所谓的说明文档,都以#'
开头,第一行是简要介绍,以@param
开头的行逐个介绍函数的自变量(懒得介绍的话就像示例里的x2那一行一样空着,但x2后面必须有一个空格),@return
开头的行介绍因变量(必填),@export
@examples
两行可以不修改。有更多函数的话,照葫芦画瓢在文件里往下继续写就是了。保存。点击RStudio右上窗口的
Build
标签下的Build & Reload
按钮,包就编译并自动安装好了!
试着运行一下你刚才自定义的函数,记得用tab键补全功能和F1查看帮助信息,体会一下刚才你在foo.R文件里修改的内容是如何在tab和F1里体现出来的,然后就自然知道如何把foo.R写得更好。
下载模板–打开项目–修改模板–编译,这就是R包的9分钟致简开发四步曲。
发布和分享
史书分正史和野史,R包分正包和野包。正包是通过R官方认可并且发布在CRAN网站上的,等于领到了认证和许可,可以用install.package()
直接安装;野包就是自己私下写的,随便放在自己喜欢的地方小范围使用的。很多包转正之前都是野包,经过反复测试和升级后才转的正,而有的野包会永远野下去,永不接受招安。我目前写的几个包都是野包,将来转不转正走着瞧,所以这里只介绍野包的发布和分享方法。
- Github法
熟悉Github的话,建议用这种方法。既然熟悉,我这里就不详说Github的用法了,只做个简单介绍。
你要做的,就是在github.com上申请个账号,电脑里安装个客户端,在客户端新建个项目并起个名字例如叫mymickey,把你新的R包文件夹同步上去,就可以通知用户们来使用你的mymickey包了。
用户要做的就是直接安装,只需在R里运行
devtools::install_github("你的账号/你的项目名称")
就把你的包装到他们的R里了。
举个粒子:比如你刚才下载的rmickey模板,本身就是个R包(我的第四个R包),这个包里只有一个函数foo()
,可以给出宇宙和人生终极问题的答案。安装和运行方法是:
devtools::install_github("pzhaonet/rmickey")
library(rmickey)
foo()
如果你跟我一样,搞了几年都搞不明白Github的用法和逻辑,那我建议你用下一种方法分享。
- 离线法
离线法就是让用户把你的包先下载到他们电脑上,再用R包的离线安装方式。
你的包的压缩文件其实在上面点Build & Reload
的时候已经自动生成了,去看看你电脑上的R包文件夹,跟它并列存放的,有个与包同名的.tar.gz
压缩文件。这就是离线安装包。
你要做的,就是随便以任何方式,不管是email、qq、网盘,还是优盘、手机、移动硬盘,只要让用户能得到你这个压缩文件就行了。
用户要做的,就是拿到文件后在他们的电脑上安装,方法是在RStudio菜单栏点Tools
– Install Packages
– Install from Package Archive File
,选上你的包,就行了。
进阶
欲穷千里目,更上一层楼。
用9分钟致简四步法写出的R包,足够自用了。在这个高度,看到的风景已经够美。你可以在这个水平的观景台休整休整,再考虑是否继续往上攀登。孔子登东山而小鲁,登泰山而小天下。如果想看到更高处的风景,那么就继续读本帖前言部分的3份资料,以及他们介绍的更多资料,把你的包写得更好。
心得
现在,让我们回过头来谈一个问题:我们为什么要写R包?
首先是方便自用。
使用R语言这几年,我逐渐积累的自定义函数越来越多,我把他们通通存到一个.R文件里。现在,这个文件已经有2000行,51个自定义函数(Update: 后来发现更多,我忘在另一个.R文件里了)。每次用时都source()
一下,我觉得调用起来已经很方便了,虽然会导致RStudio右上窗Environment标签下特别冗长,而且经常忘记这些函数的用法,需要查看函数源代码,但总好过每次把函数重写或重拷贝一遍。没写成包之前,这几年都是这么过来的。
昨天,我花了半天时间,把它写成了我的第三个R包mf。这当然是个野包,纯属自用,自己懂就行了,我懒得为别人把这51个函数的说明文档从头到尾写一遍。用起来的爽快程度,当然比source()
强多了,不仅Environment区清爽了,更关键的是,忘了的话就用tab和F1大法,几年积攒的不快一扫而空。就算换个工作单位,换台电脑,花几秒钟安装后照旧调用,处处无家处处家。
其次是改善代码。
写代码对我来说一直是件封闭的事情:自己写,自己用,一旦出错,只有自己知道。计算方法没有经过同行评议,出错的风险很大。但是我又不愿意厚着脸皮强迫别人帮我改代码。两全的办法,就是开源,写成包,放在Github上,接受志愿者挑错,有则改之无则加勉。只有这样,才能把代码错误越来越少,改得越来越好,最终还是自己用起来更爽。至于对别人和社区有没有贡献,我只盼自己没有添乱就行了。
最后是审视自我。
我的第一个R包postr是用R markdown做海报,兴致勃勃地放到Github第一天,就有人留言汇报问题。我知道,这只是个开始,以后会有更多的问题,里面会有赞美也会有谩骂。写出个R包真的不算啥,等待在前面的一个又一个的坑才是让人真正头疼的事情。
这个阶段就要接收考验了。你会重新审视自己:我这么干到底是图个啥?我自用已经够了,却要花大量的时间帮一些跟我八杆子都打不到的人解决他们五花八门层出不穷的问题,我闲得么?花的这工夫和精力,到底是能发表成学术论文呢,还是能变现呢,还是能替我洗碗带孩子呢,还是能帮我赚游戏装备?
开源软件是面镜子,让你从中看到自己。唐太宗说过:
以铜为鉴,可以正衣冠;
以人为鉴,可以知得失;
以史为鉴,可以知兴替:
以R包为鉴,可以知本我、自我和超我。
PS. 星期三下午,我壮着胆子把Emacs装上,战战兢兢试了一下就惊呆了。这个ctrl+coo快捷键(可以读作“control 酷”,太好记了)来维护R包简直不要太逆天。昨天我花了四五个小时来为我那51个函数写文档,要是用Emacs,估计最多一小时就够了。省事的话,可能不到10分钟就能写出个包含这51个函数的能用的致简包。最让我受刺激的还不是这个。自打学了vim之后,我就坚信,我永远用不着,也永远学不会Emacs,而实际上,学会上面这个办法,只用了三五分钟……Emacs我不打算学更多,光这一招就够酷……(Update: 根据 Yihui Xie的留言,RStudio 里 ctrl+shift+alt+r也能同样实现)
到底是什么阻挡了尝试新事物的可能?