N-gram(2) 形態素解析と組み合わせた版

データの活用

こんにちは。前回は、文字レベルでのN-gramを紹介しました。今回は形態素レベルでのN-gramをやってみます。

プログラム

形態素レベルでのN-gramを出力するプログラムを書きました。

from janome.tokenizer import Tokenizer
from janome.tokenfilter import ExtractAttributeFilter,POSStopFilter
from janome.analyzer import Analyzer
tokenizer = Tokenizer()

def fileRead( path ):
  with open( path, encoding='utf-8' ) as file:
    text = file.read()
  return text

#引数 attribute   base_form 基本形  infl_form 活用形
#                 infl_type 活用型  part_of_speech 品詞
#                 phonetic 発音      reading 読み
#                 surface 表層形

#stopFilterで使える品詞
#名詞・接頭詞・動詞・形容詞・副詞・連体詞・接続詞
#助詞・助動詞・感動詞・記号・フィラー・その他
#(詳細)http://chasen.naist.jp/snapshot/ipadic/ipadic/doc/ipadic-ja.pdf
def nGram2( text, n, attribute='base_form' ):
  stopFilter = [ '記号', 'フィラー', 'その他' ]
  filters = [ POSStopFilter( stopFilter ),
              ExtractAttributeFilter( attribute ) ]
  analyzer = Analyzer( [], tokenizer, filters )
  tokens = analyzer.analyze( text )

  result = []
  last = []

  for token in tokens:
    if len( last ) < n:
      last.append( token )
    else:
      result.append( last )
      last = last[1:]
      last.append( token )

  if len( last ) >= n:
    result.append( last )

  return result

def listToDic( list ):
  result = {}
  for item in list:
    tp = tuple( item )
    if tp not in result:
      result[tp] = 0
    result[tp] += 1
  return dict( sorted( result.items(), key=lambda x: -x[1] ) )


if __name__ == '__main__':
	filename = 'matsutoya.txt'
	text = fileRead( filename )
	print( '【{}】'.format(filename) )
	print( listToDic( nGram2(text, 2) ) )

	filename = 'nakajima.txt'
	text = fileRead( filename )
	print( '【{}】'.format(filename) )
	print( listToDic( nGram2(text, 2) ) )

詳細な説明はしませんが、fileRead関数では、ファイルを読み込んでいます。nGram2関数では、形態素解析をして、N個の形態素のまとまりを作り、リストにまとめています。

ここまでの結果を出力してもよいのですが、そのままこのページに掲載すると、ほとんど歌詞のままを載せることになり著作権法的にまずいことになるので、N-gramの結果の個数を数えて降順にソートしているのがlistToDic関数です。

計算結果

松任谷由実と中島みゆきのそれぞれ10曲ずつをサンプルにbi-gram(2-gram)を実行してみました。10曲ではサンプルとして少なすぎました。ガチでやるならば、提供曲も含めて作詞した曲すべてでやってみるべきです。

とはいえ、結果をすべて載せると長くなりすぎるので、サンプルで得られた結果のうちトップ10を掲載します。

【matsutoya.txt】
{('の', 'こと'): 14,
 ('て', 'ゆく'): 9,
 ('春', 'よ'): 9,
 ('君', 'の'): 9,
 ('て', 'いる'): 8,
 ('て', 'も'): 7,
 ('あの', '子'): 6,
 ('空', 'を'): 6,
 ('全て', 'の'): 6,
 ('あなた', 'は'): 6,
 ('悲しい', 'て'): 6,
【以下、省略】
}

【nakajima.txt】
{('に', 'も'): 21,
 ('て', 'ゆく'): 19,
 ('も', 'ない'): 16,
 ('の', '中'): 12,
 ('だ', 'う'): 12,
 ('バカ', 'だ'): 12,
 ('だ', 'ね'): 12,
 ('ファイト', '!'): 11,
 ('が', '笑う'): 10,
 ('ね', 'バカ'): 10,
【以下、省略】
}

これでも、ファンの人ならばわかる曲があるような気がします。サビの部分は繰り返されるので、それなりの回数出現するのですが曲の印象が強いため、案外まんべんなく出現することなくそこそこの回数にとどまったように思いました。

トップ10だけを見ての印象ですが、松任谷由実の歌詞は「名詞+助詞」が多いのに対し、中島みゆきの歌詞は「も+ない」「だ+う」(元の形は「だろ+う」と推測)のように特定の曲に依存しない組み合わせが上位に上がっている傾向があるように思いました。(たった10曲ずつの結果でしかないのですけれど・・・)

除外する品詞に記号を入れておいたのに(ファイト,!)が混ざってしまったのは、きっと全半角の違いでしょう。データを整形してやり直すべきですが、データ整形をちゃんとやらないと期待した結果が得られない例として、あえて残しておくことにします。

今回も本文は短めですが、今回はこれでおしまい。それでは、また。

Posted by 春日井 優