#1.はじめに
プログラミングを学習し始め自然言語処理に興味を持ったので,自然言語処理100本ノック2020を勉強し始めた.第4章:形態素解析を参考にしながら夏目漱石の小説『吾輩は猫である』の文章中の数字を抽出してベンフォードの法則が成り立っているか調べることにした.
#2.目次
1.はじめに
2.目次
3.ベンフォードの法則
4.コード
5.まとめ
参考
#3.ベンフォードの法則
ベンフォードの法則とは住所,株価,電話料金など我々の身の回りにあるものの数字の最初の一桁目の分布についてなり立つ法則である.それらの数字は1〜9の数字のいずれかであり,その分布は一桁目の数字をn,その分布をpとすると
$$ p = \log_{10}\frac{n+1}{n} $$
となる.これを表にすると以下のようになる.
n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
p×100 | 30.1% | 17.6% | 12.5% | 9.7% | 7.9% | 6.7% | 5.8% | 5.1% | 4.6% |
#4.コード
小説中の数字の集合を調べる.
n = []
def parse_line(line):
(surface, attr) = line.split('\t')
arr = attr.split(',')
return {
'surface': surface,
'base': arr[6],
'pos': arr[0],
'pos1': arr[1]
}
"""
mecabでは以下のように読み込まれる
表層形\t品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用形,活用型,原形,読み,発音
・・・
表層形\t品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用形,活用型,原形,読み,発音
EOS
>>> parse_line('吾輩\\t名詞,代名詞,一般,*,*,*,吾輩,ワガハイ,ワガハイ')
{'surface': '吾輩', 'base': '吾輩', 'pos': '名詞', 'pos1': '代名詞'}
"""
def read_mecab_file(filename) -> [[{}]]:
sentence = []
with open(filename, mode='rt', encoding='utf-8') as f:
for line in f:
line = line.rstrip('\n')
if line == 'EOS':
yield sentence
sentence = []
else:
sentence.append(parse_line(line))
sentences = read_mecab_file('neko.txt.mecab')
for sentence in sentences:
for word in sentence:
if word['pos'] == '名詞' and word["pos1"] == "数":
n.append(word['surface'])
print(list(set(n)))
['ひと', '一', '幾', '三', '余', '六', 'いく', '1', '億', '2', '7', '○', '3', '八', '数', '半', '2', '七', '1', '五', '九', '十', '万', '〇', '一返御駄仏', '四', '6', '二', '何', '百', '千']
のように出力される.数字以外のものが混ざっているため,それらをを取り除く.
while "半" in n :
n.remove("半")
while "幾" in n:
n.remove("幾")
while "数" in n:
n.remove("数")
while "いく" in n:
n.remove("いく")
while "〇" in n:
n.remove("〇")
while "何" in n:
n.remove("何")
while "ひと" in n:
n.remove("ひと")
while "一返御駄仏" in n:
n.remove("一返御駄仏")
while "○" in n:
n.remove("○")
while "余" in n:
n.remove("余")
print(list(set(n)))
['一', '三', '六', '1', '億', '2', '7', '3', '八', '2', '七', '1', '五', '九', '十', '万', '四', '6', '二', '百', '千']
ここで,二桁以上ある数字を先頭の数字に変換する.
`for i in range(len(n)):
if n[i] == "1":
n[i] = "一"
if n[i] =="1":
n[i] = "一"
if n[i] == "6":
n[i] = "六"
if n[i] == "百":
n[i] = "一"
if n[i] =="2":
n[i] = "二"
if n[i] =="十":
n[i] = "一"
if n[i] =="万":
n[i] = "一"
if n[i] =="億":
n[i] = "一"
if n[i] =="2":
n[i] = "二"
if n[i] =="千":
n[i] = "一"
if n[i] =="7":
n[i] = "七"
if n[i] =="3":
n[i] = "三"
ヒストグラムを作成する.
from kanjize import int2kanji, kanji2int
from matplotlib import pyplot as plt
import japanize_matplotlib
name = ['一', '二', '三', '四',"五","六","七","八","九"]
value = [n.count("一"), n.count("二"),n.count("三"), n.count("四"),n.count("五"),n.count("六"),n.count("七"),n.count("八"),n.count("九")]
plt.bar(name, value)
plt.show()
それぞれの数字の割合は
p=[]
for i in range(len(value)):
p.append(value[i]/sum(value))
print(p)
[0.45576823590274185, 0.1603724780134506, 0.1660631143300569, 0.05069839627521987, 0.05276771857216762, 0.04449042938437662, 0.02379720641489912, 0.03362648732540093, 0.012415933781686497]
となった.結果を表にまとめる.
n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
p×100 | 30.1% | 17.6% | 12.5% | 9.7% | 7.9% | 6.7% | 5.8% | 5.1% | 4.6% |
結果 | 45.6% | 16.0% | 16.6% | 5.1% | 5.3% | 4.4% | 2.4% | 3.4% | 1.2% |
(数値を四捨五入した) |
#5.まとめ
結果を見るとやはり一が一番多く,数字が大きくなるにつれおおよそ出現頻度は下がっている.理論値とずれた理由としてはサンプル数が1933と少なかった事,もしくは特定の小説には成り立たない等が考えられる.
プログラミング初心者かつベンフォードの法則の数式を詳しくフォローしていないため,ご指摘,コメントなどをいただけたら幸いです.
#参考
https://ja.wikipedia.org/wiki/%E3%83%99%E3%83%B3%E3%83%95%E3%82%A9%E3%83%BC%E3%83%89%E3%81%AE%E6%B3%95%E5%89%87
#6.追記
コメントでのご指摘の通りこの方法では見落としおよび,誤ったカウントがされてしまう事になります.今回の結果が大きく変わることは(おそらく)ないように感じられますが,解決方法などありましたら教えていただけると大変嬉しいです.