R是什么?

记得刚接触R的时候,有一种莫名的抵触,A、B、C、D、E那么多种语言了,为什么又多冒出来一个R?为了时间序列的课程,我又要多记忆一大堆乱七八糟的语法。当发现居然有dd <- 23333 23333 -> ee 这样的语法时,更瞬间奠定了R语言在我心中的逗比地位。

因为老师没有专门教授R的相关细节,毕竟课程的主题不是那个,加之R的语法与众不同,这导致我的R语言相关作业的绝大部分时间一般都在百度、谷歌各种R语言的表达、实现方法中度过。

记得有位哲人说过:“人并没有真正喜欢吃的东西,只是吃的次数多了,就‘喜欢’了。”

我对R语言的看法也差不多。随着对R了解的深入,我才发现,丰富的可视化工具、可重复性研究、匿名函数、延迟求值、元编程,还有6000+的CRAN包等等特性,都是R赫赫的闪光点。

R是一门统计学用的语言,这是这门语言给我的第一印象。看了 John Chambers 在 USER!2014 的视频,以及他对R的定义“a software interface into the best algorithms.” 的时候,我感受到了R的“最初的价值”。

magrittr让我们更欢乐地操纵各种命令,knitr让统计报告和编程文学化,dplyr更方便地处理数据,R还有shiny让你轻松地构建动态内容。我很难想象没有R,让我用其他语言来完成完成这些事情需要多少的工作量。

灵活而高效的接口

有人说R慢,只能说这些人应该不够“本质”,效率和灵活性总是需要平衡的。用C和FORTRAN来实现算法,用R(S)来解决问题,这是S诞生的初衷之一。英语渣渣的理解,不对请轻轻地喷。R的底层C接口对初学者有些复杂,Rcpp的出现很大程度上降低了写出高效率R包和代码的成本。

之前因为对文本挖掘比较感兴趣,所以打算用R来做一些分析,但是发现在R上,文本挖掘最基本的中文分词的模块还没有较好的实现。R是开源的,开源的意义不只是Free使用,还有贡献社区这一层,于是jiebaR诞生了。

jiebaR是“结巴”中文分词(Python)的R语言版本,支持最大概率法(Maximum Probability),隐式马尔科夫模型(Hidden Markov Model),索引模型(QuerySegment),混合模型(MixSegment)共四种分词模式,同时有词性标注,关键词提取,文本Simhash相似度比较等功能。项目使用了Rcpp和CppJieba进行开发。目前托管在GitHub上。安装很简单,你可以下载Windows的二进制包或者:

library(devtools)
install_github("qinwf/jiebaR")

是的,然后你就可以开始分词了,再也没有rJava那头痛的Path设置。

jiebaR使用了Rcpp,用Rcpp可以很容易地把C++的逻辑整合到R里。比如,在R里,你很难实现构建一棵Trie树,写出有向无环图等数据结构,同时进行动态规划算法,这些是最大概率法(MPSegment)—— jiebaR分词的核心算法之一。就算实现了,在R里有for遍历的速度,你猜猜就知道是多么的压力山大。

Rcpp是一个很神奇的包,特别是当你试过使用Rcpp Modules以后,jiebaR使用Rcpp Modules实现了worker的概念,把静态的C++面向对象的模型带到R中动态实现。

常用的分词包有两种加载词库的方法,就是加载包时读取默认的词典和数据模型,或者在分词前加载词典和模型数据。在早期的版本中,jiebaR也使用过这两种方式进行加载。第一种方式,就像一个铁笼子,加载包时一次性加载了词库,封装在一起。第二种方式灵活,可以动态地加载词库和模型数据,适时进行修改,但是每次分词前,加载词库都十分耗费时间,对于小的任务不合适。

有了Rcpp Modules,jiebaR可以把C++中的分词类映射到R语言中的RC类,把这样原本C++中静态的类的操作,带到了R里面,可以动态地运行。在jiebaR里,你可以动态地生成分词器,使用不同的分词器,对不同类型的文本进行操作,分词就像切菜时选不同的菜刀一样。

library(jiebaR)加载包时,没有启动任何分词引擎,启动引擎很简单,就是一句赋值语句就可以了。

cutter = worker()

软件默认设定非常重要,jiebaR默认参数为绝大多数任务调整到了最好的状态(哈哈,我的自我感觉)。初始化分词简单,分词就更简单了。为了让大家少一些待在电脑前的时间,多一些陪家人和朋友的时间,少敲一些键盘,jiebaR重载了<=这个不太常用的符号。分词就是一个类似赋值的过程,足够简单粗暴:

cutter <= "江州市长江大桥,参加了长江大桥的通车仪式。" 

# [1] "江州"     "市长"     "江大桥"   "参加"     "了"       "长江大桥" "的"       "通车"     "仪式"  

# 或者Pipe一个文件路径

cutter <= "weibo.txt"

当然,如果你喜欢打字,也可以使用segment()函数。正如之前说的,可以同时初始化和使用多个分词器。可以添加一些参数来初始化,可用参数列表很长很长,但是一般你不会全用到它们,具体可以参考帮助文档?worker():

cutter2 = worker( user = 某个用户词库路径) ### 初始化第二个引擎

ShowDictPath()  ### 可以显示默认词典路径

这时R的环境里同时有两个加载了不同词库的分词引擎。如果需要了解这两个不同的引擎的区别只需要print一下就可以了。

cutter

# Worker Type:  Mix Segment
# 
# Detect Encoding :  TRUE
# Default Encoding:  UTF-8
# Keep Symbols    :  FALSE
# Output Path     :  
# Write File      :  TRUE
# Max Read Lines  :  1e+05
# 
# Fixed Model Components:  
# 
# $dict
# [1] "C:/Users/user/R/win-library/3.1/jiebaR/dict/jieba.dict.utf8"
# 
# $hmm
# [1] "C:/Users/user/R/win-library/3.1/jiebaR/dict/hmm_model.utf8"
# 
# $user
# [1] "C:/Users/user/R/win-library/3.1/jiebaR/dict/user.dict.utf8"
# 
# $detect $encoding $symbol $output $write $lines can be reset.

哈哈,暴露了我是一个Windows党,每个worker都有一些参数设置,如cutter中的$detect参数决定了引擎是否自动判断输入文件的编码,在引擎加载时可以通过worker(detect = F )进行参数设置,也可以在加载后通过cutter$detect = F进行设置。其实 worker()函数返回的是一个环境(environment),里面封装了真正的分词引擎,你可以通过cutter$worker来查看真正的“引擎”。

cutter$worker

# C++ object <0000000014C98780> of class 'mixseg' <0000000014CA4680>

cutter$workercutter都是环境,在传递时是传址,而不是传值,效率是比较高的。jiebaR的分词速度是其他R语言分词包的5-20倍。

jiebaR除了分词,还提供了词性标注、关键词提取、文本相似度比较等功能,具体的内容可以参考GitHub里的项目介绍。这些功能的用法都差不多。

分词结束后,对于不需要的引擎只需要用rm()进行删除,R有自动的垃圾回收机制,为你解决内存管理的后顾之忧。

分词已经分好,统计分析才是最重要的任务。剃刀已经磨砺,接下来就可以用R来处理中文字符了。

目前该包还有很多需要完善的地方,大家感兴趣的可以参与jiebaR或者CppJieba的开发中,一个pull request,来一发开源的精神。

JOHN CHAMBERS:http://datascience.la/john-chambers-user-2014-keynote/

GitHub:https://github.com/qinwf/jiebaR

二进制包:https://github.com/qinwf/jiebaR/releases

Cppjieba:https://github.com/aszxqw/cppjieba

“结巴”中文分词:https://github.com/fxsjy/jieba

发表/查看评论