jiebaRS

August 6, 2026 · View on GitHub

GitHub
Stars License:
MIT Docs dev
Version Ask
DeepWiki

简体中文 | English

jiebaRSjiebaR 的 Rust 后端替代实现, 用于中文分词、词性标注和关键词提取。它使用 jieba-rs 作为分词引擎, 带来了现代化的性能与可维护性。

多年前,qinwf 基于 cppjieba 创建了 jiebaR,长期以来一直是 R 语言中文分词的首选工具。然而,qinwf 已停止维护 jiebaR,并且 jiebaR 已被 CRAN 移除, 无法直接安装。这给 R 语言的教学与研究带来了不便和困扰。因此,基于功能完善、性能出色、 持续维护的 Rust jieba-rs crate, 我开发了 jiebaRS,为 R 用户提供一个现代化、易用的中文分词工具, 并尽可能保持与原 jiebaR API 的兼容性。

安装

从 CRAN 安装

你可以从 CRAN 安装 jiebaRS 的发布版本:

install.packages("jiebaRS")

如果您使用 Linux,您可以尝试从 P3M 为不同的 Linux 发行版安装预编译的二进制文件,避免从源代码编译。

R-universe / R-multiverse

R-universe 和 R-multiverse 上提供了预编译的二进制包:

install.packages("jiebaRS", repos = "https://yousa-mirage.r-universe.dev")
install.packages("jiebaRS", repos = "https://community.r-multiverse.org")

从源码安装

使用 pak 从源码安装:

pak::pak("Yousa-Mirage/jiebaRS")

注意: 从源码构建需要 Rust 工具链来编译 Rust 后端。

用法

library(jiebaRS)

分词

创建一个 worker 并对文本进行分词:

cutter <- worker()
segment("南京市长江大桥", cutter)
#> [1] "南京市"   "长江大桥"

批量分词支持多个字符串,通过 batch 参数控制聚合方式。当输入多于一个字符串时, 会自动并行分词,速度远超 jiebaR。

texts <- c("南京市长江大桥。", "这是一个测试,小明很聪明。")

# list:每个输入字符串对应一个字符向量
segment_batch(texts, cutter, batch = "list")
#> [[1]]
#> [1] "南京市"   "长江大桥"
#> 
#> [[2]]
#> [1] "这是" "一个" "测试" "小明" "很"   "聪明"

# flatten:所有词元拼接为一个向量
segment_batch(texts, cutter, batch = "flatten")
#> [1] "南京市"   "长江大桥" "这是"     "一个"     "测试"     "小明"     "很"      
#> [8] "聪明"

# data.frame:doc_id + word 两列
segment_batch(texts, cutter, batch = "data.frame")
#>   doc_id     word
#> 1      1   南京市
#> 2      1 长江大桥
#> 3      2     这是
#> 4      2     一个
#> 5      2     测试
#> 6      2     小明
#> 7      2       很
#> 8      2     聪明

如果你想对一段很长的文本进行并行分词,可以先将其拆分为 32~128 个片段, 再使用 segment_batch()

词性标注

使用 tagging() 函数为分词结果标注词性(POS)标签:

tagger <- worker(type = "tag")

# 默认:命名向量(词为名称,标签为值)
tagging("这是一个测试,小明很聪明。", tagger)
#> 这是 一个 测试 小明   很 聪明 
#>  "v"  "m" "vn" "nr" "zg"  "a"

# data.frame:term + tag 两列
tagging("这是一个测试,小明很聪明。", tagger, format = "data.frame")
#>   term tag
#> 1 这是   v
#> 2 一个   m
#> 3 测试  vn
#> 4 小明  nr
#> 5   很  zg
#> 6 聪明   a

# legacy:jiebaR 风格(词为值,标签为名称)
tagging("这是一个测试,小明很聪明。", tagger, format = "legacy")
#>      v      m     vn     nr     zg      a 
#> "这是" "一个" "测试" "小明"   "很" "聪明"

关键词提取

使用 keywords() 函数通过 TF-IDF 提取关键词:

keys <- worker(type = "keywords", topn = 3)

text <- "今天纽约的天气真好啊,京华大酒店的张尧经理吃了一只北京烤鸭。后天纽约的天气不好,昨天纽约的天气也不好,北京烤鸭真好吃。"

# 命名数值向量(关键词 -> 权重)
keywords(text, keys)
#>  北京烤鸭      纽约      天气 
#> 1.2514383 1.0095837 0.9689916

# 含 term + weight 两列的数据框
keywords_df(text, keys)
#>       term    weight
#> 1 北京烤鸭 1.2514383
#> 2     纽约 1.0095837
#> 3     天气 0.9689916

你也可以使用 textrank() 函数基于 TextRank 算法提取关键词。该功能在 Python 的 jieba 库中可用,但 jiebaR 中没有提供该功能。

ranker <- worker(type = "textrank", topn = 3)

textrank(text, ranker)
#>        天气        纽约        不好 
#> 19308397026 19181002755 13770391787

textrank_df(text, ranker)
#>   term      weight
#> 1 天气 19308397026
#> 2 纽约 19181002755
#> 3 不好 13770391787

自定义词典

加载自定义主词典dict — 替换内置词典)或用户词典user — 追加到主词典)。两个文件都使用如下行格式:word [freq] [tag]

# 用户词典:向默认词典添加新词
user_file <- withr::local_tempfile()
writeLines(c("量子机器狗 1000 n", "超导量子比特 1000"), user_file, useBytes = TRUE)

cutter2 <- worker(user = user_file)
segment("量子机器狗和超导量子比特", cutter2)
#> [1] "量子机器狗"   "和"           "超导量子比特"

使用 new_user_word()(别名:add_word())动态添加词语:

cutter3 <- worker()
segment("量子机器狗和超导量子比特", cutter3)
#> [1] "量子" "机器" "狗"   "和"   "超导" "量子" "比特"

new_user_word(cutter3, "量子机器狗", tags = "n")
add_word(cutter3, "超导量子比特", tags = "n")  # 别名
segment("量子机器狗和超导量子比特", cutter3)
#> [1] "量子机器狗"   "和"           "超导量子比特"

也可以直接导入输入法词库。支持 .scel.qcel.qpyd.bdict.bcd 格式,函数会根据文件扩展名自动选择解析器。词库中的词会直接添加到 现有 worker;默认不使用输入法词库中的编码和权重,也不设置词性标签。

cutter_cidian <- worker()
import_cidian(cutter_cidian, "dictionary.scel")
segment("词库中的词语", cutter_cidian)

# 也可以为导入的词统一设置词性标签
import_cidian(cutter_cidian, "dictionary.qcel", tag = "n")

# 直接读取词库中的词和编码
dictionary <- read_cidian("dictionary.scel")
head(dictionary)

停用词

通过 stop_word 参数以字符向量形式提供停用词,或通过 stop_word_file 参数 以文件路径形式提供。停用词会从分词和关键词提取结果中过滤掉。

cutter4 <- worker(stop_word = c("这是", "一个"))
segment("这是一个测试", cutter4)
#> [1] "测试"

自定义 IDF + get_idf()

从你自己的已分词语料库计算 IDF 词典,然后用于 TF-IDF 关键词提取:

docs <- list(
  c("北京", "烤鸭", "纽约"),
  c("北京", "天气"),
  c("纽约", "天气")
)

idf_file <- withr::local_tempfile()
get_idf(docs, path = idf_file)

keys_custom <- worker(type = "keywords", topn = 3, idf = idf_file)
keywords(text, keys_custom)
#>       天气       纽约       不好 
#> 0.06081977 0.06081977 0.04054651

词频与 N-gram

提供两个小巧实用的函数用于词频统计和 n-gram 计数:

tokens <- segment("南京市长江大桥南京市", worker())

# 词频
freq(tokens)
#>       char freq
#> 1   南京市    2
#> 2 长江大桥    1

# 按词频降序排列
freq(tokens, sort = TRUE)
#>       char freq
#> 1   南京市    2
#> 2 长江大桥    1

# N-gram 计数(默认:二元组)
count_ngrams(tokens, n = 2)
#>              term n count
#> 1 南京市 长江大桥 2     1
#> 2 长江大桥 南京市 2     1

# 多个 n 值,以命名向量返回
count_ngrams(tokens, n = 1:2, format = "vector")
#>          南京市        长江大桥 南京市 长江大桥 长江大桥 南京市 
#>               2               1               1               1

与 jiebaR 的比较

jiebaRS 与 jiebaR 都基于 jieba 的分词算法,但在 Rust 后端(jieba-rs)和 C++ 后端 (cppjieba)的实现上不可避免地存在细微差异。以下是使用《围城》和《红楼梦》全文作为测试语料的实测结果。

分词结果相似度

语料字符数jiebaRS 词元数jiebaR 词元数jiebaRS 词条数jiebaR 词条数词表 Jaccard 相似度
《围城》246,871128,985129,56018,37518,7940.929
《红楼梦》860,933451,792451,22844,63445,5960.865

两份分词结果的词元总数接近(差异在 0.2% 以内),词条表重叠度较高。主要差异来自部分专有名词的切分粒度不同(如 jiebaRS 将主角名 “鸿渐” 合并为一个词,jiebaR 则切为 “鸿” + “渐”),以及未登录词的 HMM 推断边界存在少量差异。

分词速度

语料输入模式jiebaRS(秒)jiebaR(秒)加速比
《围城》单长文本0.0390.0651.66x
《围城》10 万句并行0.2462.93011.91x
《红楼梦》单长文本0.1290.2461.91x
《红楼梦》10 万句并行0.6394.6017.20x

单文本分词时 jiebaRS 约快 1.7 ~ 1.9 倍;当输入为大量短句并并行切分时, jiebaRS 可达 7 ~ 12 倍加速。

致谢

jiebaRS 构建于以下开源项目的基础之上:

  • jieba-rs:messense 及贡献者对 Jieba 引擎的 Rust 移植。
  • jiebaR:qinwf 创建的原始 R 包, jiebaRS 即为其替代实现。
  • extendr:extendr 团队让 Rust 与 R 的互操作变得切实可行。
  • 更广泛的 RustR 社区。

没有这些项目,jiebaRS 就不会存在。

许可证

MIT license