読者です 読者をやめる 読者になる 読者になる

日本語の文章を解析するためのクラス

前回作ったプログラムで青空文庫のテキストを普通の日本語の文章にすることに成功したので、それを解析するプログラムを書きました。長いプログラムなのでコピペ用のプログラムは張ってません。

github.com

動かす

demoフォルダに漱石の文章がいくつか入っており、demo.pyを実行するとがこれらの文章が解析されます。

>python demo.py
---------------------こころ.txt----------------------
文字数(空白含まず), 160230
空白文字数, 1241
文数, 5168
文の平均文字数, 31.244389
一番長い文の文字数, 148
一番長い文, "学問をやり始めた時に..."
段落数(見出し除く), 1218
段落の平均文字数, 131
見出しの数, 113
一番長い段落の文字数, 978
一番長い段落, " 私はしばらくそこに..."
--------------------------------------------------
---------------------それから.txt---------------------
文字数(空白含まず), 170294
空白文字数, 1503
文数, 6140
文の平均文字数, 27.979967
一番長い文の文字数, 226
一番長い文, "登山の途中雪崩れに圧..."
段落数(見出し除く), 1800
段落の平均文字数, 94
見出しの数, 110
一番長い段落の文字数, 902
一番長い段落, "「何故働かないつて、..."
--------------------------------------------------
--------------------イズムの功過.txt--------------------
...(略)

またmain.pyに引数を渡すと指定のファイルを解析します。

>python main.py demo/坊ちゃん.txt
---------------------坊ちゃん.txt---------------------
文字数(空白含まず), 88272
空白文字数, 274
文数, 2720
文の平均文字数, 32.553676
一番長い文の文字数, 163
一番長い文, "「取締上不都合だから..."
段落数(見出し除く), 471
段落の平均文字数, 187
見出しの数, 11
一番長い段落の文字数, 2138
一番長い段落, " 清の事を考えながら..."
--------------------------------------------------

フォルダを指定するとフォルダ内のすべてのテキストファイルが対象になります。

問題点

  • 文を正確に切れていないことがある。特に「」が多用されてるとき。
  • 重い
  • 標準入力から文章を受け取れない
  • Mecabを使ってない

そのうち改善します。

コードの構造

プログラムでは文章に施す解析を3種類に分けています。

  • 一段落ごとの解析
  • 一文ごとの解析
  • 一文字ごとの解析

それぞれに応じた基底クラスPerCharacterInfo, PerSentenceInfo, PerParagraphInfoがあり、これらを継承したクラスを作ることで自由に解析を追加できます。

コードを書くうえで解析を追加しやすいように工夫したので、新しく解析を追加するために編集する必要があるのはNihongoInfo.pyだけです。

例えば、10文字以下の短い文がどれだけあるかを数えたいとします。この解析は一文ごとの解析なので、PerSentenceInfoを継承し、以下のようなクラスを作ればOKです。

class ShortSentence(PerSentenceInfo):
    def __init__(self):
        self.count = 0
    
    def analyze(self, sentence):
        if len(sentence) <= 10:
            self.count += 1
    
    def show_result(self):
        print("短い文の数, %s" % self.count)

次にこのクラスへの参照をNihongoInfo.per_sentence_infoに追加すれば解析されるようになります。クラスのインスタンスを追加しないように注意してください。

per_sentence_info = [SentenceCount, LongestSentence, ShortSentence]
>python main.py demo/坊ちゃん.txt
---------------------坊ちゃん.txt---------------------
文字数(空白含まず), 88272
空白文字数, 274
文数, 2720
文の平均文字数, 32.553676
一番長い文の文字数, 163
一番長い文, "「取締上不都合だから..."
短い文の数, 244
段落数(見出し除く), 471
段落の平均文字数, 187
見出しの数, 11
一番長い段落の文字数, 2138
一番長い段落, " 清の事を考えながら..."
--------------------------------------------------

結果によると、『坊ちゃん』は2720文中244文が10文字以下の短い文のようです。

段落を文に分ける。

日本語の文はいつも"。"で終わるわけではなく、"。」"や"。)"、ときには。……"などで終わることがあります。そのため段落を文に切る作業にも少し工夫がいります。pythonのリファレンスを見ながら熟考した後、以下のようにreモジュールのsplit関数を使うことに落ち着きました。

    sentence_separater = re.compile(r"([。][」)]?[….―]*[」)]?)")
    @staticmethod
    def nihongo_split(sentences):
        """ sentencesを文ごとに切って返す """
        results = re.split(NihongoAnalyzer.sentence_separater, sentences)
        if results[-1] != "":
            # 最後が"。"で終わっていない場合の処理
            results.append("")
        return [results[i] + results[i + 1] for i in range(0, len(results) - 1, 2)]

ただこれでも完璧ではありません。例えば"「やあ」「やあ久しぶりじゃないか」と私たちは挨拶をしあった。"*1という会話はnihongo_split()では一文とみなされますが、鍵カッコの中身を一文として全体を複数の文とみなした方がいいような気もします。

ですがそうかと思うと"「戦争」「平和」この矛盾する両者は…“*2のような文章のように、先ほどの会話と同じ構造をしているにもかかわらず複数の文とみなすべきでは無い文章もあります。意味を考えない分割の限界です。

感想

Mecabpythonに対応してるなんて知らなかった。

[2017/1/13追記]

Windowsのpython3にインストールできないとも知らなかった。

*1:架空の文章です。

*2:架空の文章です。