gibasaパッケージについて
gibasaは、RからMeCabを利用して形態素解析をおこなうためのパッケージです。
モチベーションとしては、tidytext::unnest_tokens
を意識した処理をMeCabを利用しつつできるようにしたいということで開発しています。また、gibasaはv0.5.0からMeCabのソースコードをソースパッケージ内に含んでいるため、ビルドするのにMeCabのバイナリファイルを必要としないという長所があります。
インストールの仕方
CRANのほか、r-universeからもインストールできます。
# Install gibasa from r-universe repository
install.packages("gibasa", repos = c("https://paithiov909.r-universe.dev", "https://cloud.r-project.org"))
バイナリパッケージが用意されていない環境では、ソースパッケージをビルドしてインストールします。ビルド時にMECAB_DEFAULT_RC
という環境変数を内部的に指定するため、正しく動作させるにはmecabrcとMeCabの辞書があらかじめ適切な位置に配置されている必要があります。使っているOSのパッケージマネージャなどからインストールしておいてください。
# Sys.setenv(MECAB_DEFAULT_RC = "/fullpath/to/your/mecabrc") # if necessary
remotes::install_github("paithiov909/gibasa")
Windowsの場合、ソースビルドにはRtoolsが必要です。また、インストールの方法によらず、gibasaを使用するにはMeCabの辞書が必要なため、これなどを使ってMeCabをインストールしておいてください。
なお、v0.9.4から、オリジナルのMeCabと同様にmecabrc
ファイルを参照できるようになったため、辞書とmecabrc
ファイルが適切に配置されていれば、MeCabのバイナリががなくても形態素解析が可能になっています。mecabrc
ファイルは、環境変数MECABRC
で指定されたファイルか、~/.mecabrc
というファイルが参照されます。
たとえば、IPA辞書(ipadic)をPyPIからダウンロードして利用するには、ターミナルから次のコマンドを実行します。
$ python3 -m pip install ipadic
$ python3 -c "import ipadic; print('dicdir=' + ipadic.DICDIR);" > ~/.mecabrc
また、gibasaはMeCabのシステム辞書をビルドする機能もラップしているため、辞書のソースファイルを用意できれば、パッケージのインストール後に辞書を配置することによっても使用可能になります。たとえば、kelpbedsというパッケージを利用してIPA辞書を配置して使えるようにするには、次のようにします。
install.packages("kelpbeds", repos = c("https://paithiov909.r-universe.dev", "https://cran.r-project.org"))
dic_dir <- fs::dir_create(file.path(Sys.getenv("HOME"), "ipadic-utf8"))
kelpbeds::prep_ipadic(dic_dir)
gibasa::build_sys_dic(
dic_dir = dic_dir,
out_dir = dic_dir,
encoding = "utf8"
)
readr::write_lines(
paste0("dicdir=", dic_dir),
file.path(Sys.getenv("HOME"), ".mecabrc")
)
基本的な使い方
gibasaは、次にあげる関数を使って、CJKテキストの分かち書きをすることができるというパッケージです。
まず、doc_id
列とtext
列をもつデータフレームについて、gibasa::tokenize
でtidy
textのかたちにできます(以下の例ではIPA辞書を使っています)。ちなみに、元のデータフレームのdoc_id
列とtext
列以外の列は戻り値にも保持されます。
gibasa::ginga[5]
#> [1] " カムパネルラが手をあげました。それから四、五人手をあげました。ジョバンニも手をあげようとして、急いでそのままやめました。たしかにあれがみんな星だと、いつか雑誌で読んだのでしたが、このごろはジョバンニはまるで毎日教室でもねむく、本を読むひまも読む本もないので、なんだかどんなこともよくわからないという気持ちがするのでした。"
dat <- data.frame(
doc_id = seq_along(gibasa::ginga[5:8]),
text = gibasa::ginga[5:8],
meta = c("aaa", "bbb", "ccc", "ddd")
)
res <- gibasa::tokenize(dat, text, doc_id)
head(res)
#> # A tibble: 6 × 6
#> doc_id meta sentence_id token_id token feature
#> <fct> <chr> <int> <int> <chr> <chr>
#> 1 1 aaa 1 1 記号,空白,*,*,*,*, , ,
#> 2 1 aaa 1 2 カムパネルラ 名詞,一般,*,*,*,*,*
#> 3 1 aaa 1 3 が 助詞,格助詞,一般,*,*,*,が,ガ,ガ
#> 4 1 aaa 1 4 手 名詞,一般,*,*,*,*,手,テ,テ
#> 5 1 aaa 1 5 を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
#> 6 1 aaa 1 6 あげ 動詞,自立,*,*,一段,連用形,あげ…
gibasa::prettify
でfeature
列の素性情報をパースして分割できます。このとき、col_select
引数でパースしたい列を指定すると、それらの列だけをパースすることができます。
head(gibasa::prettify(res))
#> # A tibble: 6 × 14
#> doc_id meta sentence_id token_id token POS1 POS2 POS3 POS4 X5StageUse1
#> <fct> <chr> <int> <int> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 1 aaa 1 1 記号 空白 NA NA NA
#> 2 1 aaa 1 2 カムパ… 名詞 一般 NA NA NA
#> 3 1 aaa 1 3 が 助詞 格助詞 一般 NA NA
#> 4 1 aaa 1 4 手 名詞 一般 NA NA NA
#> 5 1 aaa 1 5 を 助詞 格助詞 一般 NA NA
#> 6 1 aaa 1 6 あげ 動詞 自立 NA NA 一段
#> # ℹ 4 more variables: X5StageUse2 <chr>, Original <chr>, Yomi1 <chr>,
#> # Yomi2 <chr>
head(gibasa::prettify(res, col_select = 1:3))
#> # A tibble: 6 × 8
#> doc_id meta sentence_id token_id token POS1 POS2 POS3
#> <fct> <chr> <int> <int> <chr> <chr> <chr> <chr>
#> 1 1 aaa 1 1 記号 空白 NA
#> 2 1 aaa 1 2 カムパネルラ 名詞 一般 NA
#> 3 1 aaa 1 3 が 助詞 格助詞 一般
#> 4 1 aaa 1 4 手 名詞 一般 NA
#> 5 1 aaa 1 5 を 助詞 格助詞 一般
#> 6 1 aaa 1 6 あげ 動詞 自立 NA
head(gibasa::prettify(res, col_select = c(1, 3, 5)))
#> # A tibble: 6 × 8
#> doc_id meta sentence_id token_id token POS1 POS3 X5StageUse1
#> <fct> <chr> <int> <int> <chr> <chr> <chr> <chr>
#> 1 1 aaa 1 1 記号 NA NA
#> 2 1 aaa 1 2 カムパネルラ 名詞 NA NA
#> 3 1 aaa 1 3 が 助詞 一般 NA
#> 4 1 aaa 1 4 手 名詞 NA NA
#> 5 1 aaa 1 5 を 助詞 一般 NA
#> 6 1 aaa 1 6 あげ 動詞 NA 一段
head(gibasa::prettify(res, col_select = c("POS1", "Original")))
#> # A tibble: 6 × 7
#> doc_id meta sentence_id token_id token POS1 Original
#> <fct> <chr> <int> <int> <chr> <chr> <chr>
#> 1 1 aaa 1 1 記号
#> 2 1 aaa 1 2 カムパネルラ 名詞 NA
#> 3 1 aaa 1 3 が 助詞 が
#> 4 1 aaa 1 4 手 名詞 手
#> 5 1 aaa 1 5 を 助詞 を
#> 6 1 aaa 1 6 あげ 動詞 あげる
gibasa::pack
を使うと、pull
引数で指定した列について、いわゆる「分かち書き」にすることができます。デフォルトではtoken
列について分かち書きにします。
res <- gibasa::prettify(res)
gibasa::pack(res)
#> # A tibble: 4 × 2
#> doc_id text
#> <fct> <chr>
#> 1 1 カムパネルラ が 手 を あげ まし た 。 それ から 四 、 五 人 手 を …
#> 2 2 ところが 先生 は 早く も それ を 見つけ た の でし た 。
#> 3 3 「 ジョバンニ さん 。 あなた は わかっ て いる の でしょ う 」
#> 4 4 ジョバンニ は 勢い よく 立ちあがり まし た が 、 立っ て みる と も…
gibasa::pack(res, POS1)
#> # A tibble: 4 × 2
#> doc_id text
#> <fct> <chr>
#> 1 1 記号 名詞 助詞 名詞 助詞 動詞 助動詞 助動詞 記号 名詞 助詞 名詞 記号 …
#> 2 2 記号 接続詞 名詞 助詞 形容詞 助詞 名詞 助詞 動詞 助動詞 名詞 助動詞 助…
#> 3 3 記号 名詞 名詞 記号 名詞 助詞 動詞 助詞 動詞 名詞 助動詞 助動詞 記号
#> 4 4 記号 名詞 助詞 副詞 副詞 動詞 助動詞 助動詞 助詞 記号 動詞 助詞 動詞 …
ベンチマーク
TokyoR #98でのLTのスライドでも紹介しましたが、ある程度の分量がある文字列ベクトルに対して、ごくふつうに形態素解析だけをやるかぎりにおいては、RMeCabよりもgibasaのほうが解析速度が速いと思います(というより、RMeCabでもMeCabを呼んでいる部分はおそらく十分速いのですが、欲しいかたちに加工するためのRの処理に時間がかかることが多いです)。
dat <- data.frame(
doc_id = seq_along(gibasa::ginga),
text = gibasa::ginga
)
gibasa <- function() {
gibasa::tokenize(dat) |>
gibasa::prettify(col_select = "POS1") |>
dplyr::mutate(doc_id = as.integer(doc_id)) |>
dplyr::select(doc_id, token, POS1) |>
as.data.frame()
}
rmecab <- function() {
purrr::imap_dfr(
RMeCab::RMeCabDF(dat, 2),
~ data.frame(doc_id = .y, token = unname(.x), POS1 = names(.x))
)
}
bench <- microbenchmark::microbenchmark(gibasa(),
rmecab(),
times = 20L,
check = "equal")
ggplot2::autoplot(bench)