🌿

RでMeCab(RcppMeCab)を利用して形態素解析する方法

commits12 min read

RcppMeCabとは

RcppMeCabは、Junhewk Kim氏が開発している、MeCabとRcppを利用して形態素解析するためのRパッケージです。

RcppMeCabによる形態素解析の例

形態素解析するための関数として、RcppMeCab::posRcppMeCab::posParallelの2つがあります。両者はまったく同じAPIを提供するものですが、posParallelのほうは形態素解析の処理を内部的にマルチスレッドで回すことができます。posParallelが対応しているOS・プラットフォームならば、基本的にposParallelを使っておくほうが速いです。

渡す引数によって、以下のような出力を得ることができます。

require(RcppMeCab)

sentence <- c("陽が照って鳥が啼き あちこちの楢の林も、けむるとき", "ぎちぎちと鳴る 汚い掌を、おれはこれからもつことになる")

## テキストだけ与える場合、デフォルトの戻り値はnamed list of character vectors.
## リストの各要素は、表層形(surface form)と素性情報の1番目(IPA辞書では「品詞」)を'/'で区切ってつなげた文字列ベクトルになる。
RcppMeCab::pos(sentence)
#> $`陽が照って鳥が啼き あちこちの楢の林も、けむるとき`
#>  [1] "陽/名詞"       "が/助詞"       "照っ/動詞"     "て/助詞"      
#>  [5] "鳥/名詞"       "が/助詞"       "啼き/動詞"     " /記号"      
#>  [9] "あちこち/名詞" "の/助詞"       "楢/名詞"       "の/助詞"      
#> [13] "林/名詞"       "も/助詞"       "、/記号"       "けむる/動詞"  
#> [17] "とき/名詞"    
#> 
#> $`ぎちぎちと鳴る 汚い掌を、おれはこれからもつことになる`
#>  [1] "ぎちぎち/副詞" "と/助詞"       "鳴る/動詞"     " /記号"      
#>  [5] "汚い/形容詞"   "掌/名詞"       "を/助詞"       "、/記号"      
#>  [9] "おれ/名詞"     "は/助詞"       "これから/副詞" "もつ/動詞"    
#> [13] "こと/名詞"     "に/助詞"       "なる/動詞"

## 'join = FALSE'を指定すると、戻り値はnamed list of named character vectorsになる。
## Neologd辞書などでは収録されている語彙そのものに'/'が含まれていることがあるため、使用ケースによって使い分けるとよい。
RcppMeCab::pos(sentence, join = FALSE)
#> $`陽が照って鳥が啼き あちこちの楢の林も、けむるとき`
#>       名詞       助詞       動詞       助詞       名詞       助詞       動詞 
#>       "陽"       "が"     "照っ"       "て"       "鳥"       "が"     "啼き" 
#>       記号       名詞       助詞       名詞       助詞       名詞       助詞 
#>       " " "あちこち"       "の"       "楢"       "の"       "林"       "も" 
#>       記号       動詞       名詞 
#>       "、"   "けむる"     "とき" 
#> 
#> $`ぎちぎちと鳴る 汚い掌を、おれはこれからもつことになる`
#>       副詞       助詞       動詞       記号     形容詞       名詞       助詞 
#> "ぎちぎち"       "と"     "鳴る"       " "     "汚い"       "掌"       "を" 
#>       記号       名詞       助詞       副詞       動詞       名詞       助詞 
#>       "、"     "おれ"       "は" "これから"     "もつ"     "こと"       "に" 
#>       動詞 
#>     "なる"

## 'format = data.frame'にすると、戻り値は以下のようなデータフレームになる。
## pos列・subtype列は素性情報の1~2番目(IPA辞書では「品詞」と「品詞細分類1」)、
## analytic列は素性情報の8番目(IPA辞書の「読み」)だが、
## 未知語で推定されない素性だった場合などには'NA_character_'が含まれることがある。
RcppMeCab::pos(sentence, format = "data.frame") %>%
    head(16L)
#>    doc_id sentence_id token_id    token  pos  subtype analytic
#> 1       1           1        1       陽 名詞     一般       ヒ
#> 2       1           1        2       が 助詞   格助詞       ガ
#> 3       1           1        3     照っ 動詞     自立     テッ
#> 4       1           1        4       て 助詞 接続助詞       テ
#> 5       1           1        5       鳥 名詞     一般     トリ
#> 6       1           1        6       が 助詞   格助詞       ガ
#> 7       1           1        7     啼き 動詞     自立     ナキ
#> 8       1           1        8         記号     空白        
#> 9       1           1        9 あちこち 名詞   代名詞 アチコチ
#> 10      1           1       10       の 助詞   連体化       ノ
#> 11      1           1       11       楢 名詞     一般     ナラ
#> 12      1           1       12       の 助詞   連体化       ノ
#> 13      1           1       13       林 名詞     一般   ハヤシ
#> 14      1           1       14       も 助詞   係助詞       モ
#> 15      1           1       15       、 記号     読点       、
#> 16      1           1       16   けむる 動詞     自立   ケムル

いずれの関数についても、sys_dicuser_dicという引数から任意の辞書を指定できるほか、開発版(v0.0.1.3 or higher?)ではoptionsから次のようにしてシステム辞書を指定することもできます。

options(mecabSysDic = "/home/rstudio-user/.local/mecab-dic/ipadic-utf8")

RcppMeCabはv0.0.1.2までのCRANリリースがありますが、それほどアクティブに開発されているわけではないため、筆者が個人的にforkしたうえで修正したりしています。

このforkでは 'format = data.frame'として得られるデータフレームから、token列についてdoc_idごとの分かち書きへと整形するRcppMeCab::packという関数などを追加しています。RcppMeCab::packは、たとえばquantedaと組み合わせて次のように使用することができます。

require(RcppMeCab)
require(quanteda)

sentence <- c("陽が照って鳥が啼き あちこちの楢の林も、けむるとき", "ぎちぎちと鳴る 汚い掌を、おれはこれからもつことになる")
(df <- RcppMeCab::pos(sentence, format = "data.frame") %>% RcppMeCab::pack())
#>   doc_id                                                                 text
#> 1      1   陽 が 照っ て 鳥 が 啼き   あちこち の 楢 の 林 も 、 けむる とき
#> 2      2 ぎちぎち と 鳴る   汚い 掌 を 、 おれ は これから もつ こと に なる
df %>%
    quanteda::corpus() %>%
    quanteda::tokens(what = "word") %>% ## 厳密に半角スペース区切りにしたい場合、ここで`what = "fastestword"`にする
    quanteda::dfm()
#> Document-feature matrix of: 2 documents, 29 features (48.28% sparse) and 0 docvars.
#>     features
#> docs 陽 が 照 っ て 鳥 啼 き あちこち の
#>    1  1  2  1  1  1  1  1  1        1  2
#>    2  0  0  0  0  0  0  0  0        0  0
#> [ reached max_nfeat ... 19 more features ]

RcppMeCabの使用上の注意点

CRANにある最新リリース(2018-07-04)であるv0.0.1.2の注意点として、64bitのWindows環境ではインストールに失敗します。また、CRANリリースには日本語のIPA辞書などを使用時に未知語があると落ちるバグが残っているため、GitHubにある開発版を利用することをおすすめします。

以下は、いずれも筆者がメンテナンスしているfork(paithiov909/RcppMeCab)についての情報です。

Changes

  • RcppMeCab::packなど、いくつかの関数を追加。
  • 'format = data.frame'時のdoc_id列をfactor型にする(v0.0.1.2では見た目は数字だがcharacter型になっている)など、軽微な挙動の修正・リファクタリング。
  • 'format = data.frame'時のsentence_id列の削除。 この変更は元とほぼ同じ挙動に戻しました。
    • デフォルトでは、MeCab側ではなく、R側で文区切り(stringi::stri_split_boundaries(type = "sentence")による)します。
    • options(mecabSplit = FALSE)とすることで、この文区切り処理をスキップできます。
  • OS・プラットフォームを問わず、渡したcharacter vectorをstringi::stri_enc_toutf8でUTF-8に変換するように。
  • Windowsでのソースパッケージからのインストールの改善。
  • 'format = data.frame'時のC++側の処理の高速化。

Known Issues

  • RcppParallelと同様の範囲でTinyThreadにそのうち対応したいです。

Limitations

  • OS・プラットフォームを問わず、MeCabの辞書はUTF-8でコンパイルしたものを使用する前提なので、Shift-JISなどの辞書を使用することはできません。
  • RcppParallelのAPIではなく、Intel Threaded Building Blocks(v.4.3)のAPIを直接触っているため、Windows, OSX, Linux, Solaris(x86 only)以外のプラットフォームではposParallelは動きません。

Performance

あくまで目安ですが、夏目漱石「吾輩は猫である」の全文(2258 elements. メモリ上のサイズで700KB~1.1MB程度?)を同じ環境で解析させると、次のようになります。

require(RMeCab)
require(RcppMeCab)

sentences <- ldccr::NekoText ## paithiov909/ldccr
dplyr::glimpse(sentences)
#>  chr [1:2258] "吾輩は猫である" "夏目漱石" "一" " 吾輩は猫である。名前はまだ無い。" ...

vec <- iconv(sentences, from = "UTF-8", to = "CP932")
df <- data.frame(text = vec)

tm <- microbenchmark::microbenchmark(
  RMeCabC = lapply(vec, function(elem) {
    RMeCabC(elem, mecabrc = "/MeCab/ipadic-shiftjis/mecabrc")
  }),
  RMeCabDF = RMeCabDF(df, 1, mecabrc = "/MeCab/ipadic-shiftjis/mecabrc"),
  pos = pos(sentences),
  posParallel = posParallel(sentences),
  times = 10L
)
summary(tm)
#>          expr       min       lq     mean   median       uq      max neval
#> 1     RMeCabC 7197.2222 7296.917 7369.802 7341.859 7400.890 7680.760    10
#> 2    RMeCabDF 7643.6918 7696.169 7787.620 7728.074 7769.400 8301.763    10
#> 3         pos 1355.6821 1392.607 1442.339 1397.141 1448.497 1693.051    10
#> 4 posParallel  996.5137 1008.699 1028.882 1014.281 1035.060 1144.857    10

ggplot2::autoplot(tm)

autoplot-tm

他の方の記事ですでに紹介されているように、従来のRcppMeCabについてはRMeCabの同等の処理のほうが速いケースがありましたが、MeCabそのものの解析速度には差はないため、RパッケージからMeCabを利用する際にはC/C++部分の書き方によってパフォーマンスに差が出ます。筆者のforkではそのあたりはある程度リファクタリングされているため、単純に文字列ベクトルを形態素解析するかぎりではRcppMeCabのほうが速いです。

MeCabのインストール

Windows

64bit/32bitを問わず、基本的にこれを使うことをオススメします。使っているR/RStudioのアーキテクチャにあわせて以下のいずれかを選び、展開して生成されるファイルをすべてC:/mecabに移動してください。

その後、C:/mecabにパスを通してください。

なお(きちんと確認していませんが)、mecab-ko-msvcも元のMeCabのソースコードをMicrosoft Visual C++でビルドできるように弄った野良ビルドのひとつで、生成されているバイナリとしては他の日本語向けの野良ビルドとほとんど同じものだと思われます。日本語向けと韓国語向けとの主な違いは、デフォルトで見に行く辞書の場所くらいなので、たとえば日本語と韓国語を切り替えたいという場合であっても、後からRcppMeCabパッケージのインストールをし直したりする必要はありません。

OSX

homebrew経由でインストールできます。

brew install mecab
brew install mecab-ipadic

M1/arm64の場合、homebrewからインストールできるバイナリがarm64向けビルドでないらしく、正しく動かないことが報告されています(未確認)。その場合は、自分でMeCabをソースからビルドしてインストールすると動かせるらしいです。

Linux

リポジトリからインストールする場合

apt(apt-get)などからインストールできます。

sudo apt install mecab
sudo apt install libmecab-dev
sudo apt install mecab-ipadic-utf8

RStudio Cloudなど、sudoできない環境で自分でビルドする場合

MeCabを公式で案内されている手順にしたがってビルド・インストールしようとするとsudoが必要になりますが、以下の記事などで案内されているように、makeするときにprefixを指定すれば書き込み権限がある場所にインストールできるため、たとえばRStudio Cloudなどでも、実はMeCabを使うことは可能です。

例として、RStudio CloudでMeCabを利用する場合、Terminalペインから次のようにしてインストールします。

wget "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7cENtOXlicTFaRUE" -O mecab-0.996.tar.gz
tar xvf mecab-0.996.tar.gz
cd mecab-0.996
./configure --prefix=$HOME/.local --with-charset=utf8 --enable-utf8-only
make
make install

この後で、Rコンソール側からパスを追記します。また、/home/rstudio-user/.local/lib/libmecab.so.2を明示的に読み込んでおかないと後でパッケージの読み込みに失敗するため、ここで読み込んでおきます。なお、この手順はRのセッションを立ち上げるたびごとに行う必要があります。

Sys.setenv(PATH = paste(Sys.getenv("PATH"), "/home/rstudio-user/.local/bin", sep = ":"))
dyn.load("/home/rstudio-user/.local/lib/libmecab.so.2")

UTF-8の辞書もあわせてビルドします。あらかじめ、Terminalでダウンロードして展開しておきます。

wget "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7MWVlSDBCSXZMTXM" -O mecab-ipadic-2.7.0-20070801.tar.gz
tar xvzf mecab-ipadic-2.7.0-20070801.tar.gz

これで辞書がビルドできます。こちらもTerminalからでもできますが、mecab-configにパスが通っていないといけないので、Rコンソール側でやってしまうと便利です。

Rコンソールからやる場合、たとえば次のようにします。

setwd("./mecab-ipadic-2.7.0-20070801")
system("bash ./configure --with-charset=utf8 --with-dicdir=/home/rstudio-user/.local/dic/ipadic-utf8")
system("make")
system("make install") ## `sys_dic`に辞書のパスを指定して呼ぶので本当はインストールは不要ですが、いちおう
setwd("../")
## この場合、たとえば以下のようにシステム辞書のパスを指定して使う
# options(mecabSysDic = file.path("/home/rstudio-user/.local/dic/ipadic-utf8"))
# RcppMeCab::pos(c("この木なんの木", "気になる木", "見たこともない木ですから"))

RcppMeCabのインストール

OSXとLinuxでは、以下でインストールすることができます。

# Sys.setenv(MECAB_LANG = "ja") ## Windowsの場合
remotes::install_github(
  "junhewk/RcppMeCab"
  # , INSTALL_opts = "--no-multiarch" ## Windowsの場合
)

筆者がメンテナンスしているforkからは次のようにしてインストールできます。

# Sys.setenv(MECAB_LIBS = "/mecab/path/that/has/libmecab") ## Windowsで特定の場所にあるlibmecab.dllを参照させたい場合
# Sys.setenv(MECAB_LANG = "ja") ## WindowsでMECAB_LIBSを指定しない場合
remotes::install_github(
  "paithiov909/RcppMeCab@release"
  , INSTALL_opts = "--no-multiarch" ## Windowsの場合
)
GitHubで編集を提案

Discussion

ログインするとコメントできます