特定秘密保護法案のコメントを簡易テキストマイニング

特定秘密保護法案が先週の金曜日(12/6)可決された。いろいろな問題が指摘されているが、ここでは、ネットで公開されているアンケート結果を元に、簡易的なテキストマイニングを試みてみた。

分析対象

某有名新聞社 A新聞のHPでは、特定秘密保護法案のトピックスの中で、投稿マップというイベントを行っていた。これは、

  • 横に、この法案に賛成 or 反対の軸
  • 縦に、"日本の安全が脅かされていると感じるか"どうか、の軸

のマップがセルボタンで構成されていて、その中で自分の意見に一致する場所をクリックし自分のコメントを残せる、という仕組みである。投稿されたコメントは、すべて閲覧することができるので、マッピングの意見と、それに対応するコメントの傾向を調べることができる。テキストマイニングでは、コメントなり何なりの単語の数を数えたりする。(文の論旨、論理の把握というのはとても難しい)ので、マッピング軸に対応するコメントの議題(論旨ではなく)を、単語の分布傾向によって推し量ってみよう、というのが今回の主旨である。

マッピングはタテヨコに10段階 ... 100個のボックスに意見を記入できる仕組みだったのだが、ここでは、"法案に賛成・反対"の軸(横軸)に絞る。法案に対する意見とそれに対応する"コメントの中の単語分布"がどうなるか? ... 今回調査するデータは、法案成立直前、2013/12/06 20:19 更新時のものを利用する。(特に理由は無く、ちょうどそのタイミングでデータを取ったから。)

ちなみに、僕の意見をあらかじめ言っておく。科学的調査をするにあたっては、客観性が最も重要なので、僕もそのようにおこなうつもりだが、人間である以上、完全に客観的になれるものでもないと思う。だから、調査者は、先に自分の立ち居地について事前に表明していたほうがよいという考えである。僕は、この法案についてどちらかというと慎重論者である。

データ抽出方法

さて、ではどう分析するか?まずデータがなければ始まらない。データを集めて精製するのはそんなに難しくない。

  1. 事前にツール環境を作る。簡易DB ... sqliteを導入
  2. python でHTMLを扱うので、HTML分析用Beautifulsoup導入
  3. 日本語の形態素解析...Mecab を導入 & python-mecabドライバ
  4. A新聞社からコメントのHTMLをファイルにして保存(webブラウザでできる)
  5. Beautifulsoupでコメント抽出(by python)
  6. Mecab分かち書き、単語単位に分ける
  7. sqliteにデータベースとしてぶっこむ。
  8. あとはSQLをたたいて平均なり何なりを確認

以上。これには虎の巻(というかカンペ)があって、

集合知プログラミング

集合知プログラミング

 

 先の手順のうち、2と6の日本語形態素解析関連以外は、この本にやり方が載っているのでお勧め。Mecabまわりについてもそんなに難しくない。たとえばこんな感じ。

import MeCab as mc
stoppos=[u'助詞',u'助動詞',u'副詞',u'接続詞',u'連体詞',u'記号']
stopwrd=[u'する',u'いる',u'ある',u'てる',u'思う',u'こと',u'これ',u'あれ',u'それ',u'れる',u'なる']
LANG_MODE='utf-8'
def getWordsList(text):
    pttrn=re.compile('[a-z]+$')
    tmc=mc.Tagger()
    wl=[ ]
    parse_str=tmc.parse(text)
    if isinstance(parse_str,str):
        for w in parse_str.split('\n'):
            v=w.split(',')
            word=unicode(v[0].split('\t')[0],LANG_MODE)
            pos=unicode(v[0].split('\t')[-1],LANG_MODE)
            if pttrn.match(word)==None and len(v)>=3 and pos not in stoppos:
                k1=unicode(v[1],LANG_MODE)
                k2=unicode(v[2],LANG_MODE)
                uw=unicode(v[-3],LANG_MODE)
                if len(uw)>=2 and k1 not in stoppos and k2 not in stoppos:
                    if (uw,pos) not in wl and uw not in stopwrd:
                        wl.append( (uw,pos) )
    return wl

 textで入力された文を形態素解析して、stopposとstopwrdで調査対象としない品詞と単語を外し、そのtextに含まれていた単語のリストを返す。(同じ単語で違う品詞の場合も有りうるので品詞と組にして返している)

今回の分析では、一つのコメントの中で同一単語の数は数えないことにする。つまり、あるコメントにその単語が含まれるか否か、"有無"をDB化した。コメント自体が短いのと、後の分析する指標の見通しを良くするためである。

分析-評価の方法

さあDBができた。じゃ平均とって見るか...って何の平均を取るんだっけ?

説明し忘れていることがある。対象のコメントは、それぞれ縦と横に10段階、100セルのマッピングで意見が表現されているのであった。このマッピングを数値化しておかねばならない。ここでは単純に、-1~1 までの区間で数値化する。今回は横軸についての調査なので、マップの左側、もっとも反対が-1 、以降0.2ずつ増えて、右側のもっとも賛成が1になる。この数値(インデクスと呼ぼう)で、実際のコメント数の分布状況を確認してみると、

意見 反対 賛成
数値(インデクス) -1.0 -0.8 -0.6 -0.4 -0.2 0.2 0.4 0.6 0.8 1.0
コメント数合計 2788 121 43 23 39 62 44 100 205 4879

 賛成意見のほうがかなり多いことがわかる。ここで、各コメント数とインデクスを掛け合わせ、インデクスとしての平均を取る。

コメント数合計 インデクスの平均 インデクスの分散
8304 0.26558 0.88586

 賛成意見のほうが多いので、インデクスの平均は正の数になる。0.26という数値は、全体の大体26%増しで賛成意見のが多い、という解釈ができる。(分散はとりあえず気にしないこと)

さて、コメントの単語の有無に対するデータベース化をすでに行っているので、「ある単語が含まれるコメントについてのコメント数合計・インデクス平均・分散」を、上記で全体に対して行ったのと同じように、計算することができる。各単語のインデクス平均を調べて、それが1.0に近ければ、賛成のコメントにより多く使われている単語で、-1.0に近ければ、反対のコメントにより多く使われている単語、というわけである。(単語のインデクス平均は、-1.0~1.0の間を取る)

ただし、この評価の注意点として、単語の数が非常に少ない場合がある。たとえば、一回しか出現しない単語で、その一回が数値インデクス-1.0の反対コメントであれば、その単語のインデクス平均は-1.0で、"反対のコメントに多く使われる単語"となってしまう。これを避けるために、全体の単語の出現数がある一定以上のものを調査対象とする。

結果

さて、後は計算してリスト化するだけだ。単語の出現数の閾値は、ここでは、100コメント以上のものを調査対象にする。(全体コメント数が8000超なのでこれで十分か)

まず「賛成派側のコメントにより多く使われる単語」を20個リストしよう。

単語 品詞 含まれるコメント数 インデクス平均 母集団平均値との差(標準偏差単位)
スパイ 名詞 915 0.856 18.988
必要 名詞 1496 0.672 16.682
賛成 名詞 905 0.783 16.530
マスコミ 名詞 742 0.811 15.771
朝日新聞 名詞 301 0.923 12.117
報道 名詞 589 0.706 11.364
防止 名詞 343 0.821 10.929
天国 名詞 272 0.878 10.730
機密 名詞 581 0.682 10.654
反日 名詞 202 0.950 10.343
当たり前 名詞 193 0.950 10.106
朝日 名詞 214 0.915 10.093
国益 名詞 219 0.847 9.135
無い 形容詞 422 0.671 8.851
偏向 名詞 116 0.972 8.088
中国 名詞 546 0.572 7.611
早い 形容詞 143 0.856 7.501
不思議 名詞 113 0.927 7.475
普通 名詞 176 0.795 7.469
日本人 名詞 262 0.682 7.156

つぎは、「反対派側のコメントにより多く使われる単語」20個。

単語   品詞 含まれるコメント数 インデクス平均 母集団平均値との差(標準偏差単位)
国民 名詞 1666 -0.261 22.820
秘密 名詞 1688 -0.219 21.172
主義 名詞 430 -0.687 20.987
政府 名詞 451 -0.610 19.761
民主 名詞 356 -0.675 18.861
戦前 名詞 261 -0.764 17.672
安倍 名詞 255 -0.740 17.068
政治 名詞 314 -0.612 16.524
憲法 名詞 220 -0.748 15.976
権力 名詞 186 -0.827 15.830
戦争 名詞 348 -0.521 15.595
政権 名詞 420 -0.447 15.509
隠す 動詞 213 -0.716 15.227
しまう 動詞 295 -0.555 14.967
悪法 名詞 137 -0.917 14.704
廃案 名詞 173 -0.754 14.245
自民党 名詞 163 -0.780 14.188
維持 名詞 185 -0.703 13.993
治安 名詞 164 -0.759 13.934
官僚 名詞 147 -0.780 13.464

いかがだろうか?割といろいろな傾向が見えてきていると思う。マスコミ関連の単語が多いのは、このアンケートの主催自体が新聞社だからか...

この結果の細かい分析については、ここでは見送ろうと思う。

補足

※それぞれの順位は、インデクス平均ではなく、"どれだけ母集団(全体のコメント)から偏っているか"の基準を示す"母集団平均値との差*1"で、20個をリストしている。これについての詳細は脚注に記す。

最後に

 せっかくなので、得られた単語のリストを使い、勉強中のD3.jsで単語雲(word-cloud)を作ってみた。言及しているコメントが多いほど文字を大きく、賛成派は赤、反対派は青で表示している。(D3.jsはIE8と古いブラウザでは表示できませんあしからず)

こうゆうことがすぐできるD3.jsすばらしい。

*1:母集団の平均を $ m $ 、母分散を$\sigma^2$であるとする。そこから、ランダムに十分多い$n$個データを取得して標本としたとき、この標本の平均は、平均 $ m $ (母集団と同じ)、分散$\sigma^2/n$で表される。そこで、この標本分散による標準偏差$\sigma/\sqrt{n}$をつかって\[母集団平均値との差=\frac{|m-標本平均|\sqrt{n}}{\sigma} \]として計算している。つまり1で、標準偏差程度の差がある、ということ。