现在对R感兴趣的人越来越多,很多人都想快速的掌握R语言,然而,由于目前大部分高校都没有开设R语言课程,这就导致很多人不知道如何着手学习R语言。
对于初学R语言的人,最常见的方式是:遇到不会的地方,就跑到论坛上吼一嗓子,然后欣然or悲伤的离去,一直到遇到下一个问题再回来。当然,这不是最好的学习方式,最好的方式是——看书。目前,市面上介绍R语言的书籍很多,中文英文都有。那么,众多书籍中,一个生手应该从哪一本着手呢?入门之后如何才能把自己练就成某个方面的高手呢?相信这是很多人心中的疑问。有这种疑问的人有福了,因为笔者将根据自己的经历总结一下R语言书籍的学习路线图以使Ruser少走些弯路。
本文分为6个部分,分别介绍初级入门,高级入门,绘图与可视化,计量经济学,时间序列分析,金融等。
1.初级入门
《An Introduction to R》,这是官方的入门小册子。其有中文版,由丁国徽翻译,译名为《R导论》。《R4Beginners》,这本小册子有中文版应该叫《R入门》。除此之外,还可以去读刘思喆的《153分钟学会R》。这本书收集了R初学者提问频率最高的153个问题。为什么叫153分钟呢?因为最初作者写了153个问题,阅读一个问题花费1分钟时间,全局下来也就是153分钟了。有了这些基础之后,要去读一些经典书籍比较全面的入门书籍,比如《统计建模与R软件》,国外还有《R Cookbook》和《R in action》,本人没有看过,因此不便评论。
最后推荐,《R in a Nutshell》。对,“果壳里面的R”!当然,是开玩笑的,in a Nutshell是俚语,意思大致是“简单的说”。目前,我们正在翻译这本书的中文版,大概明年三月份交稿!这本书很不错,大家可以从现在开始期待,并广而告知一下!
2.高级入门
读了上述书籍之后,你就可以去高级入门阶段了。这时候要读的书有两本很经典的。《Statistics with R》和《The R book》。之所以说这两本书高级,是因为这两本书已经不再限于R基础了,而是结合了数据分析的各种常见方法来写就的,比较系统的介绍了R在线性回归、方差分析、多元统计、R绘图、时间序列分析、数据挖掘等各方面的内容,看完之后你会发现,哇,原来R能做的事情这么多,而且做起来是那么简洁。读到这里已经差不多了,剩下的估计就是你要专门攻读的某个方面内容了。下面大致说一说。
3.绘图与可视化
亚里斯多德说,“较其他感觉而言,人类更喜欢观看”。因此,绘图和可视化得到很多人的关注和重视。那么,如何学习R画图和数据可视化呢?再简单些,如何画直方图?如何往直方图上添加密度曲线呢?我想读完下面这几本书你就大致会明白了。
首先,画图入门可以读《R Graphics》,个人认为这本是比较经典的,全面介绍了R中绘图系统。该书对应的有一个网站,google之就可以了。更深入的可以读《Lattice:Multivariate Data Visualization with R》。上面这些都是比较普通的。当然,有比较文艺和优雅的——ggplot2系统,看《ggplot2:Elegant Graphics for Data Analysis》。还有数据挖掘方面的书:《Data Mining with Rattle and R》,主要是用Rattle软件,个人比较喜欢Rattle!当然,Rattle不是最好的,Rweka也很棒!再有就是交互图形的书了,著名的交互系统是ggobi,这个我已经喜欢两年多了,关于ggobi的书有《Interactive and Dynamic Graphics for Data Analysis With R and GGobi》,不过,也只是适宜入门,更多更全面的还是去ggobi的主页吧,上面有各种资料以及包的更新信息!
特别推荐一下,中文版绘图书籍有谢益辉的《现代统计图形》。
4.计量经济学
关于计量经济学,首先推荐一本很薄的小册子:《Econometrics In R》,做入门用。然后,是《Applied Econometrics with R》,该书对应的R包是AER,可以安装之后配合使用,效果甚佳。计量经济学中很大一部分是关于时间序列分析的,这一块内容在下面的地方说。
5.时间序列分析
时间序列书籍的书籍分两类,一种是比较普适的书籍,典型的代表是:《Time Series Analysis and Its Applications :with R examples》。该书介绍了各种时间序列分析的经典方法及实现各种经典方法的R代码,该书有中文版。如果不想买的话,建议去作者主页直接下载,英文版其实读起来很简单。时间序列分析中有一大块儿是关于金融时间序列分析的。这方面比较流行的书有两本《Analysis of financial time series》,这本书的最初是用的S-plus代码,不过新版已经以R代码为主了。这本书适合有时间序列分析基础和金融基础的人来看,因为书中关于时间序列分析的理论以及各种金融知识讲解的不是特别清楚,将极值理论计算VaR的部分就比较难看懂。另外一个比较有意思的是Rmetrics推出的《TimeSeriesFAQ》,这本书是金融时间序列入门的东西,讲的很基础,但是很难懂。对应的中文版有《金融时间序列分析常见问题集》,当然,目前还没有发出来。经济领域的时间序列有一种特殊的情况叫协整,很多人很关注这方面的理论,关心这个的可以看《Analysis of Integrated and Cointegrated Time Series with R》。最后,比较高级的一本书是关于小波分析的,看《Wavelet Methods in Statistics with R》。附加一点,关于时间序列聚类的书籍目前比较少见,是一个处女地,有志之士可以开垦之!
6.金融
金融的领域很广泛,如果是大金融的话,保险也要被纳入此间。用R做金融更多地需要掌握的是金融知识,只会数据分析技术意义寥寥。我觉得这些书对于懂金融、不同数据分析技术的人比较有用,只懂数据分析技术而不动金融知识的人看起来肯定如雾里看花,甚至有人会觉得金融分析比较低级。这方面比较经典的书籍有:《Advanced Topics in Analysis of Economic and Financial Data Using R》以及《Modelling Financial Time Series With S-plus》。金融产品定价之类的常常要用到随机微分方程,有一本叫《Simulation Inference Stochastic Differential Equations:with R examples》的书是关于这方面的内容的,有实例,内容还算详实!此外,是风险度量与管理类。比较经典的有《Simulation Techniques in Financial Risk Management》、《Modern Actuarial Risk Theory Using R》和《Quantitative Risk Management:Concepts, Techniques and Tools》。投资组合分析类和期权定价类可以分别看《Portfolio Optimization with R》和《Option Pricing and Estimation of Financial Models with R》。
7.数据挖掘
这方面的书不多,只有《Data Mining with R:learing with case studies》。不过,R中数据挖掘方面的包已经足够多了,参考包中的帮助文档就足够了。
8.附注
出于版权等事宜的考虑,我无法告知你说在“新浪爱问”等地方可以直接免费下载到上面提到的这些书,但是,我想你可以发挥自己的聪明才智去体悟!
http://yihui.name/cn/2012/09/equal-and-arrow/
http://www.zhihu.com/question/19611094
R是一门古怪的语言,这一点没什么好否认的。它的古怪有好有坏,在不同人眼中也可能是好事或坏事。R是受S语言影响发展起来的,S语言诞生于贝尔实验室。后来AT&T被分拆的时候,这个实验室被拆为今天的贝尔实验室(已经没什么名气了)和AT&T实验室。如前文所说,这个夏天在AT&T实验室呆着,S语言的一位作者Rick是我的办公室邻居,另一位作者Allan暑假里退休了,第三位作者是John Chambers,他早已离开实验室去高校了。S诞生在(文本)数据堆里,而R诞生之后很快走向了合作开发,这一系列历史给它带来了一些看似古怪的特征。
赋值
赋值符号在绝大多数语言中都没什么好讨论的,因为就是一个等号而已。在R社区,这一点却被讨论来讨论去,主要原因是箭头赋值符号<-
的存在。箭头来源于贝尔实验室早年的某台古董机器上有一个下划线的键,但打出来显示的是箭头。S祖先们认为箭头是一个很形象的赋值符号,于是下划线被采纳下来,甚至后来衍生出右箭头(->
)这样更奇怪的赋值符号(表示把左边的值赋给右边的变量),还有双箭头(<<-
)表示给上一层环境中的变量赋值。
最初S代码中,下划线本身就是赋值符号,例如x_1
表示把1赋值给x。我大三的时候还用S-Plus写了不少下划线赋值的S代码。后来R core们做了一个艰难的决定,允许等号作为赋值符号,废弃了下划线的赋值功能,但下划线的传统仍然无处不在,例如Emacs / ESS中默认情况下敲下划线会被替代为左箭头(为了得到真的下划线需要连续按两次下划线),尽管下划线本身已经不能直接显示为箭头,ESS仍然想昨日重现,通过软件方式强行替代之。
我不止一次地说过,我是坚定的等号党。因为等号有赋值功能,大多数语言都用等号(没见其它语言的程序员抱怨等号不形象),等号对我来说不存在歧义,让我的代码更安全。反对党(也就是箭头党)的理由通常是等号存在二义性:它既可以赋值,也可以传递函数参数,如:
x = 1:10
length(x = 1:10)
可是几乎所有语言都这样,也没有见那些语言的程序员对此有抱怨。规则很简单:如果等号出现在单独的环境中,它就是赋值;如果写在函数参数位置,它就是传参数。但R的古怪让这个简单规则也可以变得很难判断,例如:
length((x = 1:10))
length({x = 1:10})
因为()
和{}
将表达式与外面的环境隔离开来,它们被解析的时候先单独运行,所以实际上x在自己的环境里被赋值了;然后()
和{}
会返回整个表达式的结果,这个结果再传给length()
函数。
箭头在任何地方的意思都是赋值(在当前环境下),它可以被写在任何地方,包括函数参数的位置,如:
length(x <- 1:10)
这句代码先对x赋值,然后把整个表达式的值传递给length()
。我们可以把它写得更晕:
length(x = y <- 1:10)
此时等号表示传参数,而y无论如何都会被赋值。在函数参数位置上赋值通常有一举两得的效果,也就是把两件事情写在一行上,之所以能一举两得,主要是利用了箭头的副作用;如果是初学者,这种写法最好避免,首先追求代码的清晰性,避免产生副作用的代码。因为箭头无论何地都可以赋值,要是用错了也不会报错,这种错误往往难以意识到。例如我们创建一个向量,元素为1和2,元素名字分别为a和b,如果不小心写成这样:
c(a = 1, b <- 2) # 本来应该是c(a = 1, b = 2)
你可能不会意识到第二个元素是没有名字的,并且这句话带来一个副作用,就是悄悄给b赋了值。
如果你像我一样有时候写代码不爱打空格,那么还有一个更可怕的潜在错误,要是不小心犯了的话可能很久都查不出来。我们写几个逻辑表达式:x大于5,x小于3,x小于-3。你可能想,这个太简单了,操起键盘就写:
x>5
x<3
x<-3
因为你的懒惰,小于号和负号悄磨叽走到了一起,不小心形成了具有强大法力的赋值箭头,你并没有完成x与-3的逻辑大小比较,而是给x赋值为3。当这几行代码在这里摆着的时候你可能觉得很容易看出来,可是当你玩了两个小时数据之后,想随手看一下df数据框中x变量小于-1的值有多少个,你可能会写出sum(df$x<-1)
这样的语句,相应的结果是,你没得到df$x
小于-1的总数,而是把df数据里x这一列给修改为1了。如果df是一个很大的数据,或你辛辛苦苦处理了半天才得来的数据,你就哭吧。我对这个诡异的案例印象深刻,是因为我亲眼看见过两例别人的错误,在那之后我明知有这样的危险,但自己还是傻不愣登毁了一次自己的数据。
对右箭头赋值,我的想象是这样:某天某祖先写了一长段代码,但没有事先写上把这段代码的结果赋值保留下来,悔得肠子都青了,只好敲回车任凭程序在那儿跑,跑完了得不到返回值,于是该祖先发明了一个右箭头,这样即使先写了一段代码也不用怕,因为可以最后加上-> x
就把前面的返回值赋给x了。我不习惯阅读这种事后赋值的代码,就像读侦探小说似的,到最后才发现代码创建了一个变量。
这种“后悔”的想象还可以继续:R中有一个特殊的变量叫.Last.value
,它总是保存最近一次运行的最后一个结果,即使你上一条代码没有赋值保存,你仍然可以通过.Last.value
去获取。这也意味着,无论你跑什么程序,R都会随时盯着你的返回结果,把它赋值给.Last.value
。
命名风格
R core的主要命名风格是以点分隔词,例如t.test
,这与早年时下划线有赋值含义有关,另外我猜想也是懒惰,因为点只需要按键一次,而其它命名法都需要按Shift键,如camelCase。不过这个也不绝对,R里面仍然有些下划线命名的函数如seq_along
,或驼峰命名如summaryRprof
。这里面有多人合作时的个人风格,更重要的可能是S3泛型函数的影响。S3函数的特点就是“主函数.类名”,如summary.lm
,它根据传递进来的对象的类来匹配具体的子函数。因为在泛型函数中,点是有特殊意义的,所以我们要小心点(这里注意断句),为了安全起见,最好干脆避免点,免得跟泛型函数扯上关系,尤其是包的作者在写函数时。
在其它很多语言如JavaScript中,点通常表示取一个对象的子元素或者应用方法,如x.toString()
。R的点不存在这个问题(要达到同样的效果,一般用$
),它除了可能有S3的意思,没有其它特殊含义。考虑到其它语言以及S3两个原因,我最终投奔了下划线命名法(foo_bar
),次要原因是我觉得下划线把两个单词分得更开,比fooBar
易读。
考虑R中有成千上万的函数命名,某些对象命名可以理解,但仍然透露出某种不规范的痕迹。比如seq()
是S3泛型函数,而同时又存在seq.int
和seq_along
这两种风格的函数,并且前者并不是seq()
应用在int
类上的函数!
每一门语言都有一些历史糟粕,R作为一群统计学家维护的语言,从规范来说槽点很多,但事情的另一面是,他可以让什么最小惊讶原则见鬼去,老夫今天就是要写一个函数把混合效应模型中的随机效应算完打印出来。他的随意对应用统计者来说,可能恰好也是好事。没有这看似乱糟糟的各种贡献,R的发展也许会慢很多。无论如何,对如今已经趋于成熟的R,我们作为用户还是应该尽量朝规范的方向走。
语法
以for循环为例,很多语言都是教条式的for (i=0; i<10; i++)
循环,而R是for (i in x)
,这个x可以是很多种对象,例如1:10
,或10:1
,或c('a', 'c', 'b')
,或list(a = 1, b = 'fgh')
,等等。这种让循环变量在一组对象中循环的做法,我猜想可能借鉴自bash脚本的语法,如
for i in `ls *.csv`
do
echo $i
done
它为啥要参照bash脚本的循环语法而不是C语言的语法,可能跟贝尔实验室的数据处理传统有关。至今AT&T实验室仍然跑着大量的bash脚本,处理大量的文本数据(循环逐个处理每个文件),这一点我在那里感触太深了。同样诞生于贝尔实验室的Awk,其循环语法也借鉴了bash的语法(C语法也保留着),可以在一个数组中循环。
自动扩展和匹配
别的语言一般都不能计算猫加狗这样的表达式,但R可以算1:10 + 1:2
,两个长度不一样的对象也可以做计算,原因是R总是把短对象自动扩展到长对象的长度再计算;这种扩展有时候很难想象,如matrix(1:10, 5) + 1:2
(一个矩阵加一个向量)。向量一般来说看作列向量,也就是n x 1的矩阵,但你可以看见以下表达式都可以正常计算:
matrix(1:10, 5) %*% 1:2 # 5x2乘以2x1,没问题
1:2 %*% matrix(1:10, 1) # 2x1乘以1x10,没问题
1:2 %*% matrix(1:10, 2) # 你到底是2x1还是1x2?
这实在让人防不胜防。除非你事先小心实验,否则这种矩阵乘法出错了都不知道。但这问题其实也来源于作者的懒惰,只要把向量转化为严格的矩阵(不要让R去自动猜测调整),一切问题都解决了。
这些“自动”特征给数据分析其实带来了不少好处,例如在回归设计阵中加一列给截距项的1,你不必写一串1,只要X = cbind(X, 1)
就可以了,R会自动把1扩展为X的行数;又如你想让散点图中的点按照数据顺序依次用红色、蓝色、红色、蓝色……那么plot(x, y, col = c('red', 'blue'))
就够了,而不必把颜色向量写完整了。对数据分析者来说,那些计算机的严格规则最好是匿得越远越好。
有时候自动扩展悄无声息带来的问题会很难查找,例如在各种巧合之下,kuanguang坛霸问的这个问题下面掩盖了一个极大的阴谋,初学者可能看不出里面的门道,楼主的代码运行表面上看起来成功了,但实际上完全是错误的代码。本来这是个很好的例子,只是这家伙碎碎念实在太多了,一天到晚问题不断,我也来一次小心眼,装没看见好了。
引号
R的懒惰是别的语言打死都想不通的,比如把一个不存在的对象转化为字符,这么说有点抽象,我们可以考虑一下library()
这个函数。
library(fun)
这样一句话是什么意思呢?fun
不是一个R对象,它根本不存在,但为什么library(fun)
就可以加载一个名叫fun
的包?主要原因就是懒,因为懒得打引号:
library("fun")
正常来说,这个函数的第一个参数应该是R包的名字,也就是说应该是字符串。在函数内部,最终需要的也是一个字符串。R之所以能把这件事情搞得这么奇葩,也是与它强大的“基于语言的计算”(Computing on the Language)能力有关,参见手册“R Language Definition”第6节。所谓基于语言的计算,就是把代码拿来作计算,各种魔法parse()
、deparse()
、substitute()
、eval()
、match.call()
等等,极大增强了R的语言功能,所以说它是一门统计计算语言实在太低估了它。
例如这里是一个简单的函数,把输入的合法的R符号转化为字符串:
f = function(x) deparse(substitute(x))
f(asdf)
f(hahaha)
这种懒惰在一些Linux工具中也可以看见身影,例如tar
,我们可以按标准写上减号-
以传参数,也可以省略减号让tar
把第一个参数当作参数,后面的参数当文件名:
tar -x -z -f R-2.15.1.tar.gz
tar -xzf R-2.15.1.tar.gz
tar xzf R-2.15.1.tar.gz
这就是“多打一个字符会死星人”。
岔开话题回到library()
这个函数,我印象中R core一直后悔这个函数的命名,想把它改成use()
。因为library()
的存在且高频使用,让很多用户称R包为library(例如I'm using the rpart library),这曾经让某R core(M.M.)极度不爽,因为library在R中的概念是“库”,而不是单个的包,一个库可能是多个包的集合,单个包叫package。啥时侯你意识到函数命名可能比写函数本身还难时,就表明你的码农功力又上一层楼了。
LaTeX
那个年代科学计算类都和LaTeX能扯上关系,这年头都奔HTML去了,谁还去打印大部头的手册啊。R的文档就是一种伪LaTeX文档,R自身也拼命模仿一些LaTeX程序,例如texi2dvi()
函数。这种伪TeX文档带来的就是新的解析工作,参见parse_Rd()
魔法,于是各种规矩铺天盖地而来……
值传递与引用传递
R一向没有引用传递,但这说法不太严格,我们可以把一个环境当作参数传递来去,环境里的对象可以在任何地方被改变。
z = new.env()
z$x = 1
f = function(env) {
env$x = 2
}
f(z)
z$x # 变成了2
近两年Chambers大人继历史上推出S3、S4之后,又推出了引用类(reference classes),应该算是补缺吧。值传递虽然有点低效,但更安全一些,不会冷不丁不小心就修改了一个变量。
这么写下去没完没了,不写了,还是写书更重要,就是这样(波波头的微博禅)。
R,不仅仅是一种语言
本文原载于《程序员》杂志2010年第8期,因篇幅所限,有所删减,这里刊登的是全文。
简介:R是什么
转帖:来自《程序员》上的一篇文章,希望对大家有帮助
工欲善其事,必先利其器,作为一个战斗在IT界第一线的工程师,C/C++、java、perl、python、ruby、php、javascript、erlang等等等等,你手中总有一把使用自如的刀,帮助你披荆斩棘。
应用场景决定知识的储备与工具的选择,反过来,无论你选择了什么样的工具,你一定会努力地把它改造成符合自己应用场景所需的那个样子。从这个道理来说,我选择了R[1]作为数据挖掘人员手中攻城陷池的那把云梯,并努力地把它改造成自己希望的那个样子。
关于R的一个比较准确的描述是:R是一门用于统计计算和作图的语言,它不单是一门语言,更是一个数据计算与分析的环境。统计计算领域有三大工具:SAS、SPSS、S,R正是受S语言和Scheme语言影响发展而来。其最主要的特点是免费、开源、各种各样的模块十分齐全,在R的综合档案网络CRAN中,提供了大量的第三方功能包,其内容涵盖了从统计计算到机器学习,从金融分析到生物信息,从社会网络分析到自然语言处理,从各种数据库各种语言接口到高性能计算模型,可以说无所不包,无所不容,这也是为什么R正在获得越来越多各行各业的从业人员喜爱的一个重要原因。
从R的普及来看,国外的普及度要明显好于国内,跟盗版windows的泛滥会影响linux在中国的普及一样的道理,破解的matlab与SPSS的存在也影响了R在中国的使用人群。但在国外高校的统计系,R几乎是一门必修的语言,具有统治性的地位。在工业界,作为互联网公司翘楚的google内部也有不少的工程使用R进行数据分析工作,这里[2]有一个google campus的讲课视频,内容就是用R作为工具来讲述数据挖掘的概念与算法。
随着近年来R使用者的增加,关于R的报道也屡有见于报端,如2009年初美国纽约时报就有一篇很好的报道:Data Analysts Captivated by R’s Power[3]。报道中述说了R的发展历史以及由于数据挖掘需求的增长而日益普及的现状,它虽源于S但其发展却远远地超过了S,已经成为高校毕业学生所选用的第二大工具语言,google与Pfizer的员工也介绍了R在自己公司中的应用。此外,报道中google首席经济学家Hal Varian说:R的最让人惊艳之处在于你可以通过修改它来做所有的事情,而你已经拥有大量可用的工具包,这无疑让你是站在巨人的肩膀上工作。
以下就R的几个主要应用场景以及我在实践中的经验对这个并不算主流的编程语言作一些介绍。
统计计算:R之最强项
R从它出生的第一天就是为了做统计计算的,那时它被定义为一个统计计算与作图的工具,虽然发展到现在它已经被赋予了越来越强大的功能,但现在R的开发人员里,还是以各个高校统计系的老师与学生为主,他们自然最了解自己最需要的是什么。
在统计计算中,我们常常需要根据样本数据作线性回归,得到一定的规律性,R中实现这个功能十分简单,以下是一个一元线性回归的例子:
x <- 1:10
y <- x+rnorm(10, 0, 1)
fit <- lm(y ~ x)
summary(fit)
注明一下,R里的“<-”符号意义为赋值,大多数情况下它可以用“=”号来代替,但某些特殊的场合不可以,本文会遵循“<-”这种官方使用的写法。这个例子的前两行准备了两列数据:自变量x与因变量y,第三行的函数lm即根据提供的样本数据进行线性回归计算,得到的模型结果可以用第四行打印出来。函数lm除了可以做这种简单的一元线性回归,还可以做多元线性回归,同时返回模型的各种统计量。
做统计的往往免不了要做各种各样的图形,R的另一个基本特点就是对图形的强大支持,以下代码展示了一个箱线图的作法,代码来自boxplot函数的manual,该图显示了几列数据的分位数、中值、均值、奇异点等信息及其对比位置。更详细的关于R的作图功能可以参看[4]。
boxplot(mpg~cyl,data=mtcars, main=”Car Milage Data”, xlab=”Number of Cylinders”, ylab=”Miles Per Gallon”)
机器学习:让你的数据发挥它应有的作用
机器学习、数据挖掘领域面临着一些抽象自大量现实生活的问题,比如关联规则挖掘、聚类、分类这三大问题。作为一个完备的工程计算包,R毫无疑问对它们都提供了足够的支持。
关联规则问题源于“买了这件商品的顾客还买了什么”这个问题,现在已经广泛应用于客户行为分析以及互联网用户行为分析中。关联规则挖掘领域最经典的算法为apriori,R的第三方包arules[5],就是专门用于做关联规则挖掘的。以下例子需要你已经安装了arules包。
library(arules)
data <- paste(“item1,item2″,”item1″,”item2,item3″, sep=”\n”)
write(data, file = “demo_basket”)
tr <- read.transactions(“demo_basket”, format = “basket”, sep=”,”)
data(“Adult”)
rules <- apriori(Adult, parameter = list(supp = 0.5, conf = 0.9, target = “rules”))
最后一行的apriori函数接受一个transaction对象的输入,输出关联规则对象rules,为方便起见,这里用于计算的transaction对象Adult是通过第5行从arules包中现成载入进来的,第2~4行说明了怎么从一个文本文件中读入数据并生成一个transaction对象。
聚类算法使用最广泛的高效算法无疑是kmeans,R在其默认载入的stats包中就包含了这个函数,以下是一个来自kmean说明文档的例子:
x <- rbind(matrix(rnorm(100, sd = 0.3), ncol = 2), matrix(rnorm(100, mean = 1, sd = 0.3), ncol = 2))
cl <- kmeans(x, 2)
plot(x, col = cl$cluster)
points(cl$centers, col = 1:2, pch = 8, cex=2)
代码第1行生成两组两维的正态分布的数据,第一组均值为0,第二组均值为1,两组数据方差都为0.3。第2行对该数据进行聚类,第3和第4行把聚类结果画出来。
分类器是模式识别领域的研究主题,也是人类认知活动的中心。多年来的学术研究积累下来很多种类型的分类器,而其中比较靠谱的分类器基本都能在R中找到对应的实现。诸多分类器中以svm最为著名,它也被一些人称为是单分类器的王道。以下是一个利用svm对著名的iris数据集进行分类的过程,运行该例子需要你已经安装了e1071这个包[6]。
library(e1071)
data(iris)
x <- subset(iris, select = -Species)
y <- iris$Species
model <- svm(x, y)
summary(model)
pred <- predict(model, x)
table(pred, y)
第5行代码调用svm函数,计算由x作为特征y作为类别标签的分类器模型,第7行把模型应用于原数据进行预测。
以上例子的演示并非想让各位读者当场学会各个不同领域中这些功能函数的用法,而是一方面展示一些实际的R代码以及它解决问题的方式,另一方面说明了R在这些常见的机器学习领域的积累。在R帮助下去解决这些或许不是我们专业的问题,可以省去我们大量重复造*的精力,写出来的代码也足够的短小精悍,节省时间之余也让你对自己算法逻辑的全局一览无余。
高性能计算:向量化与并行/分布计算
作为现代数据挖掘人员从业者,可能第一个需要关心的是所使用工具的可伸缩性(scalability),具体来说就是在面对大数据量场景时的计算能力。
一个拥有高性能计算能力的计算包,首先它必须能充分利用历史上积累下来的那些著名的数值计算包,比如blas、lapack;另一方面,它必须具有良好的可扩展性,即它必须方便开发人员并行化自己的算法,很幸运这些特性R都具备了。
类似于R、scilab与matlab那样的工程计算包,通常都会以向量化计算(Vectorization)作为其基本的计算特点(即使python的numpy包也是如此),因为向量化的处理方式是现代大型计算机的基本特性,在计算机领域,无论硬件还是软件,都提供了对向量化的支持,硬件上如Intel的MMX, SSE等指令集都提供了对向量化的支持,更多可以看到wikipedia上的介绍[7]。软件上如blas等著名的计算包,天然地就可以对向量化的命令自动实施并行计算。
所谓向量化,是一种特殊的并行计算的方式,相比于一般程序在同一时间只执行一个操作的方式,它可以在同一时间执行多次操作,通常是对不同的数据执行同样的一个或一批指令,或者说把指令应用于一个数组/向量。以下列出R中经常使用几种向量化运算,都是十分稀松平常的操作,但它们本质上都是同时对一批数据应用相同的操作,所以都可以经过向量化处理方式的改造:
- 向量取值,如:V[1:10]
- 向量赋值,如:V[1:10] <- seq(1,10)
- lapply,类似于python里的map函数:lapply(A, mean)
- 矩阵运算:A + B;A %*% B
向量化因其在计算过程中数据的前后不依赖的特点,是并行计算的天然先驱,一个用向量化实现的算法,必定是一个可以高度并行化的算法。正因为这个原因,在利用R写脚本的时候,都要尽量利用向量化的思想来设计自己的算法,尽可能少地使用循环结构。一旦你的程序都是或大都是基于向量化的,除了当时获得来自于计算机软硬件上的优化外,将来某一天数据量膨胀使得计算成为瓶颈时,你就可以极为方便地把原来的算法并行化。
正如我们所知,CRAN包括了各种你能想像得到的工具包,当然也有不少并行计算的包,这些包被归纳在R高性能计算相关的包列表中[8]。
关于R的向量化及并行计算更详细的内容可以参考我的一篇博客[9]。
编写接口与工具包:最有用的包必定是你写的那一个
一个开源软件的最强大之处在于大量从业人员的贡献,R最让人激奋,进而选择它作为工作平台的一个重要原因则是庞大而无所不包的的CRAN,在那里几乎能找到所有你能想像得到的与分析研究相关的工具包,可以说丝毫不逊色于perl的CPAN。之所以拥有一个如此强大的第三方支持,一方面在于R本身在统计计算与计算能力方面的支持,另一方面则在于开发一个R扩展是如此地容易,以致于每一个使用R作为自己常用工具的人,都会按捺不住强烈的冲动要写一个自己的包,以满足工作需要。如果自己的这个包感觉写得不错,又为很多人所需要,就可以提交到CRAN。这是造成CRAN如此庞大的原因,但同时也造成了CRAN的软件包良萎不全。但大多数情况下,这些包都会是你的得力助手,特别是那些著名而广为使用的包,如果觉得它们不满足你的需要,那么放心地对它们进行修改吧,因为它们都是开源的。
下面展示一个简单的R扩展包的制作过程:
1、生成包结构:新建一个目录mypkg,同时作为包名,在mypkg中新建几个目录与文件,mypkg的目录结构如下图所示。R自带的函数package.skeleton可以自动帮你生成这些目录,但它需要一些现成的函数对象或文件作启动,为了顺序说明整个过程,这里没有使用。
2、目录说明:必需的是DESCRIPTION文件、man目录和R目录,剩下的都是可选的。DESCRIPTION文件描述包的meta信息;R目录下面存放R脚本文件,里面的函数可导出作为包函数库提供给外部使用;如果要在包里放一些试验数据,可以放在data目录里,常用是以csv格式存放,在R终端里data(***)可以载入,这里留空;man目录是R的帮助文档,有一定的格式要求,这里也留空,生成包时会有一些警告,可以不用管;src存放c/c++/fortran源代码,必须同时放置Makefile或Makevars文件指导编译程序工作,这里留空;zzz.R可以在载入包时做一些事情,这里也留空。
3、添加功能:DESCRIPTION文件的内容可以参考任意一个R包对应文件的写法,依样把信息修改成自己相应的信息即可。以下只写一个简单的R函数作为说明,在R目录下添加一个名为helloword.R的文件,文件内容如下:
helloword <- function(x, y)
{
return(x*y)
}
4、安装:在命令行中运行R CMD build mypkg,会编译生成一个mypkg_0.1.tar.gz安装包,其中的数字是我在DESCRIPTION里写的版本号;运行R CMD INSTALL mypkg,就可以把包安装到系统里。
5、试验:运行R,进入R终端;library(mypkg),载入刚制作的包;search(),可以看到mypkg包已经被载入;在R终端运行helloworld(2,3),返回6,试验成功。
一个具有一定功能的包就这样做好了,是不是很简单。如果有其它需要,只要往R目录或src目录添加文件,然后重新生成并安装就可以了。R与c/c++之间的接口调用也十分方便,限于篇幅,无法更仔细地说明,更详细的内容可以参考我的几篇博客[10-13]。
R在中国的发展
R在中国的普及现在并不十分地广泛,主要还是学校及研究机构在使用,但近年来随着R的声名鹊起,也已经有越来越多各个领域的工业界从业人员选择R作为自己的工作平台,其中统计之都[14]是一个国内R用户的聚焦地。今年的6月份在人民大学举行了第3届R语言会议,从前三届会议的人员组成来看,R的中国用户群一直呈现较大的增长趋势,用户分布的领域也越来越丰富。第三届R语言会议参会者人来源可以从会议纪要中看到[15]。相信随着数据挖掘广为各个公司接受,R也会走近工业界的各行各业中。
R在豆瓣中的应用
有一段时间,我一直在寻找介乎于matlab与系统语言(如C, Fortran)的中间物,希望它既能拥有系统语言的高性能,又能方便数据挖掘人员的日常工作,于是我找到了R,这不仅是一门语言,它更是一个理想的计算环境。它一方面方便我对新算法原型的构建、调试、评测,另一方面并没有让我失去系统级语言的计算优势,甚至在实现并行计算方面拥有了更多的选择。现在我使用R编写我们自己的工具包,进行算法原型构造、矩阵运算、并行算法等离线应用,为相似性计算、推荐系统等上层应用提供底层的支持。
一个R写的协同过滤推荐的例子
最后用一个R实现的协同过滤推荐的例子来结束本文,协同过滤是推荐系统中一个基本的算法,详细内容可以参考这里[16]。由于大量地采用了向量化的计算方式(包括各种矩阵运算),所以算法的实现相当简洁,有可能是史上代码最少的协同过滤推荐引擎 data <- read.table(‘data.dat’, sep=’,', header=TRUE)
user <- unique(data$user_id)
subject <- unique(data$subject)
uidx <- match(data$user, user)
iidx <- match(data$subject, subject)
M <- matrix(0, length(user), length(subject))
i <- cbind(uidx, iidx)
M[i] <- 1
mod <- colSums(M^2)^0.5
MM <- M %*% diag(1/mod)
S <- crossprod(MM)
R <- M %*% S
R <- apply(R, 1, FUN=sort, decreasing=TRUE, index.return=TRUE)
k <- 5
res <- lapply(R, FUN=function(r)return(subject[r$ix[1:k]]))
write.table(paste(user, res, sep=’:'), file=’result.dat’,
quote=FALSE, row.name=FALSE, col.name=FALSE)
代码我就不细加注释了,有兴趣了解其原理的同学可以看这里[16]。
参考:
[1] R官方网站
[2] Youtube上的GoogleTechTalks,通过R来教学数据挖掘:Statistical Aspects of Data Mining (Stats 202)
[3] 纽约时报的报道Data Analysts Captivated by R’s Power
[4] R Graphics
[5] arules package
[6] e1072 package
[7] 向量化
[8] R高性能计算相关的包
[9] 向量化与并行计算
[10] R package的最简单制作
[11] R与C的接口:从R调用C的共享库
[12] R对象结构,用.Call的调用方式更灵活地写R-package
[13] 编写R包C扩展的核心指引
[14] 统计之都
[15] 第三届R语言会议(北京会场)会议纪要
[16] 可能是史上代码最少的协同过滤推荐引擎