jiebaR中文分词——R的灵活,C的效率

本文作者:覃文锋,厦门大学公共卫生学院本科生,研究兴趣为生物统计和数据挖掘。

主页:http://qinwenfeng.com/cn/jiebaR-dev/

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

关于COS编辑部

本账户为COS编辑部公共账户,目前由朱雪宁任主编,由王小宁和张心雨担任副总编,编辑有:蔡占锐,常象宇,邓金涛,邓一硕,丁维悦,范超,冯璟烁,冯凌秉,高涛,郎大为,吕翔,彭晨昱,邱怡轩,施涛,覃文锋,王健桥,魏太云,吴佳萍,谢益辉,熊熹,杨舒仪,于嘉傲,主要负责主站文章的规范化编辑以及相关论文、书籍、手册的整理、编纂、出版等工作。

jiebaR中文分词——R的灵活,C的效率》有51个想法

    1. 挺好的。写这个包主要想自己用一下Rcpp,还有是想让CRAN上有中文分词包,安装起来方便一些,现在CRAN上应该可以安装了。原来有的rmmseg4j好像被存档了。

      1. 超级赞啊!!此外,问下在相同词库的情况下和Rwordseg有速度和结果的比较吗?

  1. 您好,在安装jiebaR的时候,win7 32位系统,R3.1.0,安装了Rtools后,一直出现g++ not find,无法编译这个包,该怎么办?

    1. 可以的,新建引擎的时候设置保留标点符号 cutter = worker(symbol = T),这样符号“ . ”就不会被预处理掉,在用户词典(library/jiebaR/dict/ 目录下的 user.dict.utf8 或者 其他自定义路径)中加入这个词 工业4.0 ,就可以了。

      1. 添加自定义字典,出现这样的错误> cutter=worker(user=’字典.txt’)
        Error: buf.size()<1 DictTrie.hpp : 158 (bad dictionary file)
        如果直接粘贴到library/jiebaR/dict/ 目录下的 user.dict.utf8,也会这样报错,但我最后做分词的结果,好多需要的词都被分开了。另外,Rwordseg包分词后的词频文档矩阵里的词类项,一行有好几个词,是版本问题吗?谢谢~

      1. 看了看,默认的词典中是把简体繁体放一起了,这样会不会影响速度?

      2. > 繁简词库 = worker() # 8M
        > 简体词库 = worker(dict = "sim_only_dict.txt") # 4M
        > 小词库 = worker(dict = "small_dict.txt") # 1.5M
        > microbenchmark(繁简词库["test.txt"],简体词库["test.txt"],小词库["test.txt"],times = 5)
        Unit: seconds
        expr min lq mean median uq max
        繁简词库["test.txt"] 2.447109 2.481220 2.517940 2.486276 2.536294 2.638801
        简体词库["test.txt"] 2.478523 2.493575 2.584182 2.508836 2.513322 2.926652
        小词库["test.txt"] 2.548846 2.567268 2.585118 2.571661 2.609558 2.628259
        neval cld
        5 a
        5 a
        5 a

      3. 实际用起来好像大词库还稍微快一些。初始化分词器,大词库会慢一些,占的内存也会大一些,不过一般也就只用一个分词器。

      4. 一个建议,如果输入的是一个字符向量或者外部文件的话,如果不选择写出去,可否输出list,这样就区分开了每个行或者每个向量。

      5. 多谢!同时发现输出外部文件的话,所有的都输出成了一行。如果能够每一行输入都对应一行输出的话两者就能对的上了。

      6. 嗯嗯,github上啥时候有新的版本呢?这个功能很需要啊~

      7. 你好啊。想请教一下,中文分词完之后,中文矩阵要怎么建立啊,用tmcn包吗?我找遍用法也没看到如何使用的说明。还有jiebaR如何读取类似txt格式的文件?非常感谢

      8. 哦。我想起来了。我已经分好词,但是有新问题:我分完词由自己输出了新的txt,是在读入然后想建立矩阵的时候,首先读入的词全部都变成乱码,(因此建立矩阵的事情就堵在这里),后面可能会遇到的另一个情况,是不是必须把分出来的词都向量化了,才能建立文档矩阵?

      9. 好的。我看了github的说明,不过出来的都还是字符,这后面就能够直接建立矩阵了还是需要我提到的所有都向量化?

      10. jiebaR包完成的是分词的工作,其他统计的工作,可以使用 tm 包,或者其他文本挖掘的包。简单统计词频可以用 table() 函数。

      11. 试了很好。只是我后面需要建立词项文档矩阵,必须向量化所有的分词结果,看来如果不行就只能用词典来统计然后直接作为关键词反映出来。tm包貌似是不能直接用的对于输入的数据操作。

      12. 你好,请问导入文件路径的代码要怎么写?我之前写了“C:/DOCUMENT“““`”这一类,结果就变成直接把路径当成字符串来分了

  2. 请问,windows 下经常会出现Error: std::bad_alloc是什么问题呢?

  3. 您好, 我想问一下这段代码:
    # 或者Pipe一个文件路径
    cutter <= "weibo.txt"
    我用过之后系统没有报错,但是也没有给我任何形式的结果。原目录下没有生成任何新的文件。想问下这是怎么回事

  4. 你好,请问这个添加自定义新词是这个new_user_word(mixseg, new_user_word, wordtype)
    ,那我想删除呢?怎么做呢

发表评论

电子邮件地址不会被公开。 必填项已用*标注