ぼろぼろ平原

困った

MeCabをparseしやすいLTSVで出力

MeCabの出力はデフォルトで以下のようになるが、 これが意外と扱いづらい。

$ echo 新しいスクエニのゲームを買った。 | mecab
新しい   形容詞,自立,*,*,形容詞・イ段,基本形,新しい,アタラシイ,アタラシイ
スクエニ    名詞,一般,*,*,*,*,*
の 助詞,連体化,*,*,*,*,の,ノ,ノ
ゲーム   名詞,一般,*,*,*,*,ゲーム,ゲーム,ゲーム
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
買っ  動詞,自立,*,*,五段・ワ行促音便,連用タ接続,買う,カッ,カッ
た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
。 記号,句点,*,*,*,*,。,。,。
EOS

扱いづらい理由

扱いづらい理由はいろいろある。

  • まずTABで区切って、さらにカンマで区切らないといけない
  • 未知語だけカンマの数が違う(上の例だと「スクエニ」の行だけカンマが少ない)
  • *が邪魔
  • EOSが邪魔
  • 形態素の原型はインデックス6?それともインデックス7?」みたいにたまに混乱する

LTSV形式での出力

MeCabのオプションを変更すると、以下のようにLTSV形式で出力できる。

$ echo 新しいスクエニのゲームを買った。 | mecab -F "surface:%m\tpos:%f[0]\tpos1:%f[1]\tpos2:%f[2]\tpos3:%f[3]\tcform:%f[4]\tctype:%f[5]\tbase:%f[6]\tread:%f[7]\tpron:%f[8]\n" -U "surface:%m\tpos:%f[0]\tpos1:%f[1]\tpos2:%f[2]\tpos3:%f[3]\tcform:%f[4]\tctype:%f[5]\tbase:%f[6]\tread:\tpron:\n" -E ""
surface:新しい   pos:形容詞   pos1:自立 pos2:   pos3:   cform:形容詞・イ段    ctype:基本形 base:新しい  read:アタラシイ    pron:アタラシイ
surface:スクエニ    pos:名詞  pos1:一般 pos2:   pos3:   cform:  ctype:  base:   read:   pron:
surface:の pos:助詞  pos1:連体化  pos2:   pos3:   cform:  ctype:  base:の    read:ノ    pron:ノ
surface:ゲーム   pos:名詞  pos1:一般 pos2:   pos3:   cform:  ctype:  base:ゲーム  read:ゲーム  pron:ゲーム
surface:を pos:助詞  pos1:格助詞  pos2:一般 pos3:   cform:  ctype:  base:を    read:ヲ    pron:ヲ
surface:買っ  pos:動詞  pos1:自立 pos2:   pos3:   cform:五段・ワ行促音便  ctype:連用タ接続   base:買う read:カッ pron:カッ
surface:た pos:助動詞   pos1:   pos2:   pos3:   cform:特殊・タ  ctype:基本形 base:た    read:タ    pron:タ
surface:。 pos:記号  pos1:句点 pos2:   pos3:   cform:  ctype:  base:。    read:。    pron:。

LTSVのparse

ltsv.orgを見れば分かる通り、LTSV形式は特別なライブラリ無しで簡単にparseできる。

Pythonの場合(-Eに空文字を指定する方法が無いのでEOSを手動で除去してる):

#!/usr/bin/env python

# pip install mecab-python3
import MeCab

mecab = MeCab.Tagger(r'-F surface:%m\tpos:%f[0]\tpos1:%f[1]\tpos2:%f[2]\tpos3:%f[3]\tcform:%f[4]\tctype:%f[5]\tbase:%f[6]\tread:%f[7]\tpron:%f[8]\n -U surface:%m\tpos:%f[0]\tpos1:%f[1]\tpos2:%f[2]\tpos3:%f[3]\tcform:%f[4]\tctype:%f[5]\tbase:%f[6]\tread:\tpron:\n')
s = mecab.parse("新しいスクエニのゲームを買った。").rstrip("EOS\n")
for line in s.splitlines():
    d = {k: v for k, v in [f.split(":", 2) for f in line.rstrip("\n").split("\t")]}
    print(d)

### 出力
# {'surface': '新しい',
#  'pos': '形容詞',
#  'pos1': '自立',
#  'pos2': '',
#  'pos3': '',
#  'cform': '形容詞・イ段',
#  'ctype': '基本形',
#  'base': '新しい',
#  'read': 'アタラシイ',
#  'pron': 'アタラシイ'}
# ...

Rubyの場合:

#!/usr/bin/env ruby

# gem install natto
require "natto"

mecab = Natto::MeCab.new('-F surface:%m\tpos:%f[0]\tpos1:%f[1]\tpos2:%f[2]\tpos3:%f[3]\tcform:%f[4]\tctype:%f[5]\tbase:%f[6]\tread:%f[7]\tpron:%f[8]\n -U surface:%m\tpos:%f[0]\tpos1:%f[1]\tpos2:%f[2]\tpos3:%f[3]\tcform:%f[4]\tctype:%f[5]\tbase:%f[6]\tread:\tpron:\n')

mecab.parse("新しいスクエニのゲームを買った。") do |line|
  unless line.is_eos?
    h = line.feature.chomp.split("\t").map {|f| f.split(":", 2) }.to_h
    p h
  end
end

### 出力
# {"surface"=>"新しい",
#  "pos"=>"形容詞",
#  "pos1"=>"自立",
#  "pos2"=>"",
#  "pos3"=>"",
#  "cform"=>"形容詞・イ段",
#  "ctype"=>"基本形",
#  "base"=>"新しい",
#  "read"=>"アタラシイ",
#  "pron"=>"アタラシイ"}
# ...

keyの名前について

LTSVのkeyにはこういう名前を付けた。

  • surface: 表層形
  • pos: 品詞
  • pos1: 品詞細分類1
  • pos2: 品詞細分類2
  • pos3: 品詞細分類3
  • cform: 活用形
  • ctype: 活用型
  • base: 原形
  • read: 読み
  • pron: 発音