17 September 2012

文章の表記ゆれをなんとなくチェックする

文章の表記ゆれを簡単にそれなりにチェックできないかなと思い mecab で適当にやってみました.

方針

  • 文章を形態素に分ける
  • '読み + 品詞' が同じなのに表記が異なるものを出力する

準備

mecab のインストール

homebrew なりのパッケージマネージャで簡単にインストールできます.

$ brew install mecab mecab-ipadic
python バインディングのインストール

今回は python から使うので python のバインディングも入れます. 以下から一番新しいバージョンの tgz を持ってきて easy_install するだけです.

Google Code Archive - Long-term storage for Google Code Project Hosting.

$ curl http://mecab.googlecode.com/files/mecab-python-0.994.tar.gz > mecab-python-0.994.tar.gz
$ easy_install mecab-python-0.994.tar.gz

virtualenv の環境でインストールするとなお良いとおもいます.

2012.05版 Python開発のお気に入り構成(ポロリもあるよ) - YAMAGUCHI::weblog

コード

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import MeCab
import sys

mecab = MeCab.Tagger('-Ochasen')
result = {}
linenum = 0

for line in sys.stdin:
    linenum += 1
    iter = mecab.parseToNode(line)
    while iter:
        if iter.surface:
            surface = iter.surface
            pos = iter.feature.split(',')[0]
            reading = iter.feature.split(',')[-2]
            if pos != '記号' and reading != '*':
                key = (reading, pos)
                if key not in result:
                    result[key] = {}
                if surface in result[key]:
                    result[key][surface].append(linenum)
                else:
                    result[key][surface] = [linenum]
        iter = iter.next

for (k, v) in result.iteritems():
    (reading, pos) = (k[0], k[1])
    if len(v) > 1:
        counts = ', '.join(map(lambda key: '%s (%d)' % (key,
                           len(v[key])), list(v.keys())))
        linenums = '\t'.join(map(lambda key: '%s: %s' % (key,
                             ', '.join(str(x) for x in v[key])),
                             list(v.keys())))
        print '%s\t%s\t%s' % (reading, counts, linenums)

文章の表記ゆれをなんとなくチェックする ? GitHub

品詞と読みのタプルを辞書のキーにして, その値に表記ごとに出現行番号を突っ込んでいきます. 複数の表記があった場合だけ結果として出力しています. 記号やうまく取れなかった場合は単に無視しています.

結果

Google JavaScript Style Guide の訳文 を食わせてみましたが, なんとなくそれっぽい結果になりました.

出力は '読み \t 表記 (出現頻度), ... \t 表記: 出現行番号, ... \t ...' です.

(dev)[13:31 kosei@mba notation_check]% cat doc/index.rst | python tools/notation_checker.py
ナゼ    なぜ (2), 何故 (1)      なぜ: 117, 373  何故: 1003
ブン    文 (15), 分 (4) 文: 107, 119, 119, 119, 119, 802, 837, 1034, 1203, 1204, 1206, 1221, 1223, 1224, 1302   分: 802, 802, 817, 1198
ツケル  つける (2), 付ける (1)  つける: 37, 39  付ける: 920
ナリ    なり (17), 成り (2)     なり: 39, 140, 204, 204, 237, 284, 304, 535, 535, 677, 689, 926, 1123, 1234, 1352, 1526, 1584   成り: 1083, 1103
ヒト    人 (1), ひと (1)        人: 1584        ひと: 433
オコナッ        行っ (2), 行なっ (1)    行っ: 709, 716  行なっ: 1003
フツウ  普通 (1), ふつう (1)    普通: 101       ふつう: 716
オコナイ        行ない (1), 行い (1)    行ない: 284     行い: 1186
ワカリ  分かり (1), わかり (1)  分かり: 39      わかり: 304
ツケ    付け (5), つけ (1)      付け: 39, 501, 502, 539, 902    つけ: 508
カタ    型 (20), 方 (1) 型: 175, 187, 633, 1003, 1005, 1009, 1009, 1009, 1013, 1013, 1023, 1032, 1034, 1045, 1045, 1123, 1136, 1154, 1154, 1284 方: 414
クダサイ        下さい (1), ください (73)       下さい: 1341    ください: 127, 140, 140, 185, 223, 235, 237, 358, 373, 412, 435, 444, 474, 480, 496, 501, 502, 504, 508, 510, 510, 522, 529, 539, 549, 561, 561, 565, 573, 600, 600, 623, 633, 662, 666, 666, 666, 689, 689, 697, 713, 714, 716, 745, 755, 768, 768, 768, 867, 881, 906, 908, 920, 951, 1009, 1019, 1028, 1103, 1174, 1178, 1180, 1182, 1205, 1207, 1234, 1253, 1266, 1333, 1359, 1391, 1580, 1582, 1584
アワセ  あわせ (2), 合わせ (3)  あわせ: 768, 1224       合わせ: 788, 794, 891
エル    得る (1), える (1)      得る: 1043      える: 1103
イチ    位置 (1), 一 (4)        位置: 1224      一: 237, 768, 976, 996
ナイ    ない (8), 無い (1)      ない: 39, 88, 98, 105, 444, 675, 1316, 1378     無い: 1167
ナラベル        並べる (2), ならべる (2)        並べる: 772, 788        ならべる: 976, 996
セイ    性 (11), せい (1)       性: 12, 39, 140, 237, 389, 444, 474, 549, 565, 580, 1580        せい: 414
ヨビダシ        呼び出し (3), 呼出 (1)  呼び出し: 320, 802, 802 呼出: 284
スコシ  少し (1), すこし (1)    少し: 1582      すこし: 1500
イレ    入れ (7), いれ (1)      入れ: 713, 714, 817, 1182, 1205, 1207, 1582     いれ: 1582
カ      可 (3), 化 (23) 可: 260, 318, 329       化: 91, 140, 169, 404, 489, 535, 580, 672, 679, 683, 683, 707, 709, 716, 720, 728, 837, 1009, 1050, 1068, 1081, 1088, 1186
マワリ  周り (2), まわり (2)    周り: 1582, 1584        まわり: 1582, 1582
ナンラカノ      なんらかの (2), 何らかの (3)    なんらかの: 912, 1068   何らかの: 1056, 1074, 1094
ナヅケ  名づけ (1), 名付け (1)  名づけ: 510     名付け: 43
モッ    持っ (2), もっ (2)      持っ: 204, 433  もっ: 246, 1584
ホウ    ほう (3), 方 (2)        ほう: 529, 908, 1552    方: 262, 1538
ジ      時 (7), 次 (6)  時: 39, 114, 158, 474, 683, 920, 1083   次: 119, 223, 391, 1186, 1491, 1526
コウ    行 (4), 項 (6)  行: 768, 779, 794, 885  項: 879, 879, 1413, 1431, 1442, 1442
トル    とる (2), 取る (1)      とる: 1063, 1123        取る: 510
うまくいかなかったケース
  • 読みも品詞も同じだけど表記が別で問題ないケース
    • ブン (文と分), カタ (型と方), イチ (位置と一) など
  • 活用の考慮
    • 'ツケル' には '付ける' と 'つける' の表記ゆれがあったが, 平仮名表記のほうが多かったので平仮名に統一した. しかし '付けて', '付けない' などの活用を含めると漢字の '付ける' という表記のほうが多かったのでそちらに修正.

まとめと感想