テキストを、地の文と台詞で、別々にカウントする

###はじめに
山内長承「Ｐｙｔｈｏｎによるテキストマイニング入門」(オーム社)の理解を深めるために、テキストの編集から分析を行うスクリプトを書きました。
上記書と違うのは、地の文と台詞を分けて分析しているところです。

スクリプトでは、
if word[0] == "「" or word[-1:] == "」":
の処理により、台詞であると判断しています。
（今になって考えると、この処理だと、
「台詞１。
　台詞２。
　台詞３」
のような文章だと、真ん中が地の文に分けられてしまうので、後日改善します）
文単位での分け方は、句点の他に、！や？、……の後に続く全角スペースを使用しています。

#地の文の文章の文字数をカウントしてヒストグラムを作り、上位10個の頻出単語を出力するスクリプト
```python
# -*- coding: utf-8 -*-
from collections import Counter
import re
import numpy as np
import matplotlib.pyplot as plt

path_txt = 'text_utf-8.txt'

with open(path_txt, encoding='utf-8') as txt:
    string = txt.read()

#改行を目安にリスト形式へ変換
string = string.splitlines()

#元のテキストに複数改行があるとリストの要素に空が入ってくるので、それを削除
string = [a for a in string if a != '']

#文頭の一文字下げの全角スペースを削除。文中の全角スペースは残して、後で文の分割で使う
string = [a.lstrip("　") for a in string]

#文章を、地の文と台詞に分ける
string_descriptive = [] #地の文
string_quote = [] #台詞

for word in string:
    if word[0] == "「" or word[-1:] == "」":
        string_quote.append(word)
    else:
        string_descriptive.append(word)

#地の文を、さらに一文ごとに分割
string_sentence = [] #一文ずつ入れるリスト
string_sentence_tmp = []

# 句点で分割。句点そのものは残す。要Python3.7以降
for word in string_descriptive:
    string_sentence_tmp.extend(re.split("(?<=。)",word))

# 「！」などに続く全角空白で分割。
for word in string_sentence_tmp:
    string_sentence.extend(word.split("　"))

#上の処理だと、リストの中に空の要素が入ってしまうので、取り除く。
string_sentence = [a for a in string_sentence if a != '']

#各々のリストの要素数を出力
print("地の文の行数は",len(string_descriptive),"　地の文に含まれる一文の数は",len(string_sentence),"　台詞の数は",len(string_quote))

#一文毎に分けたリストの各文章の長さをカウントして上から100個を出力
cnt = Counter([len(x) for x in string_sentence])
print(sorted(cnt.items(), key=lambda x: x[1], reverse=True)[:100])

#ヒストグラムの出力
nstring = np.array([len(x) for x in string_sentence if len(x) < 150])
print('max', nstring.max())
plt.hist(nstring, bins=nstring.max())
plt.show()

#上位10個の頻出単語を出力
cnt_countall = Counter([x for x in string_sentence])
print(sorted(cnt_countall.items(), key=lambda x: x[1], reverse=True)[:10])
```

#台詞の文章の文字数をカウントしてヒストグラムを作り、上位10個の頻出単語を出力するスクリプト（ほぼ上の処理と同じ）
```python
# -*- coding: utf-8 -*-
from collections import Counter
import re
import numpy as np
import matplotlib.pyplot as plt

path_txt = 'text_utf-8.txt'

with open(path_txt, encoding='utf-8') as txt:
    string = txt.read()

#改行を目安にリスト形式へ変換
string = string.splitlines()

#元のテキストに複数改行があるとリストの要素に空が入ってくるので、それを削除
string = [a for a in string if a != '']

#文頭の一文字下げの全角スペースを削除。文中の全角スペースは残して、後で文の分割で使う
string = [a.lstrip("　") for a in string]

#文章を、地の文と台詞に分ける
string_descriptive = [] #地の文
string_quote = [] #台詞

for word in string:
    if word[0] == "「" or word[-1:] == "」":
        string_quote.append(word)
    else:
        string_descriptive.append(word)

#台詞から、鍵括弧を削除
string_quote = [a.lstrip("「") for a in string_quote]
string_quote = [a.rstrip("」") for a in string_quote]


#台詞を、さらに一文ごとに分割
string_quote_line = [] #一文ずつ入れるリスト
string_quote_line_tmp = []

# 句点で分割。要Python3.7以降
for word in string_quote:
    string_quote_line_tmp.extend(re.split("(?<=。)",word))

# 「！」などに続く全角空白で分割。
for word in string_quote_line_tmp:
    string_quote_line.extend(word.split("　"))

#上の処理だと、リストの中に空の要素が入ってしまうので、取り除く。
string_quote_line = [a for a in string_quote_line if a != '']

#一文毎に分けたリストの各文章の長さをカウントして上から100個を出力
cnt = Counter([len(x) for x in string_quote_line])
print(sorted(cnt.items(), key=lambda x: x[1], reverse=True)[:100])

#ヒストグラムの出力
nstring = np.array([len(x) for x in string_quote_line if len(x) < 150])
print('max', nstring.max())
plt.hist(nstring, bins=nstring.max())
plt.show()

cnt_countall = Counter([x for x in string_quote_line])
print(sorted(cnt_countall.items(), key=lambda x: x[1], reverse=True)[:10])
```

###実行
手持ちのラノベをOCRして作ったテキストで試してみました。
１枚目が地の文、２枚目が台詞です。ラノベは、一文が短いと言われます。台詞は、それ以上に短かったです。


![20210704_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684864/7a0a7152-7163-ae79-1ac3-170bf5ce97a3.png)
![20210704_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684864/c30486e1-5d3c-6643-2049-a77a66f3fa88.png)


ちなみに、以下は、地の文と台詞の頻出文の上位１０です。

地の文
[('そう。', 154), ('そして──', 107), ('だが。', 83), ('なるほど。', 81), ('次回予告', 62), ('……………………', 61), ('あらすじ', 61), ('そして。', 58), ('かくて。', 52), ('いや。', 52)]

台詞
[('ふっ！', 365), ('ふっ。', 336), ('ほーっほっほっほ！', 291), ('ええ。', 120), ('ああ。', 103), ('あ。', 100), ('いや。', 100), ('氷の矢！', 97), ('え？', 86), ('そう。', 77)]