10
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

日本語テキストから Bag of Words を作製して頻出単語を表示する

Last updated at Posted at 2021-02-14

####目的
日本語のテキスト文書を対象に、単語の出現回数を表示する。(= Bag-of-words の作成)

####Bag-of-words 作製の手順

  1. テキストファイルの準備

  2. 形態素分析の関数定義
    (日本語は英語と違いスペースが無いため、まず文節ごとに区切る必要がある)

  3. テキストファイル読み込みと形態素分析実施

  4. 形態素を id へ変換

  5. コーパスの作成 
    (テキストファイルの文書を id の並びで表現)

  6. Bag of Words 作成と csv ファイルへの書き出し

(ソースコード github, https://github.com/MomonekoView/Bag-of-words)

####環境
Python 3.4.5
Windows 10 or Linux 16.04

####1. テキストファイルの準備
今回例題として、青空文庫の夏目漱石の「心」を引用した。
https://www.aozora.gr.jp/cards/000148/files/773_14560.html

文章を .txt ファイルに転写し保存。(kokoro.txt)
下記はファイルの中身。
_________________________________
こころ
夏目漱石
+目次
上 先生と私

 私わたくしはその人を常に先生と呼んでいた。だからここでもただ先生と書くだけで本名は打ち明けない。これは世間を憚はばかる遠慮というよりも、その方が私にとって自然だからである。私はその人の記憶を呼び起すごとに、すぐ「先生」といいたくなる。筆を執とっても心持は同じ事である。よそよそしい頭文字かしらもじなどはとても使う気にならない。
(以下略)
_________________________________

####2. 形態素分析の関数定義
解析ソフトとして、janome を使用。インストールは pip install janome で OK。
(Mecab は Windows での環境構築が上手く行かず、使用を断念)

下記ソースで実施。名称 Text_Data の関数として定義。
除去する文字列については、あまり賢いやり方ではないが、マニュアルで個別に指定。

text.py
# coding: utf-8
import sys
import os
import pickle
import numpy as np
from janome.tokenizer import Tokenizer

def Text_Data(file):

    # ファイルの読み込み
    f = open(file)
    text = f.read()
    f.close()
    text = text.split('\n')

    # 文章で分割
    for i in range(len(text)):
        text[i] = text[i].split('')

    # 以下形態素解析(形態素に分割)
    t = Tokenizer()
    new_text = []
    word_list = []

    for i in range(len(text)):
        new_text = new_text + text[i]
    print(new_text)

    # トークナイズしてリストに格納
    for j in range(len(new_text)):
        tokens = t.tokenize(new_text[j])
        for k in range(len(tokens)):
            word_list.append(tokens[k].surface)

    # 除去する文字列を指定
    rems = ['@', ',', '', '', ' ', 'http', 'https','//', '', '×', '.',
            '+', '', 
            '/',', ', '(', ')', '', '', '://', 'RT', ':', ';','', '',
            '','', '', '-', '',  '%', '', '!', '', '#', '',
            '$', '', '=', '', '', '', '^', '{', '}', '~', '', 
            '&', '', '', '?', '??', '', '??',  '<', '', '>', '', '_', '_', 
            'co', 'jp', '(@', '', '', '', '', '', '"', '', 
            ')」', '', '', '', '[', ']',
            'から', 'より', 'こそ', 'でも', 'しか','さえ', 'けれど', 'たり', 'つつ', 'とも',
            'たら', 'ある', 'なら', 'のに', 'です', 'ます', 'する', 'ほど', 'ない', 'くる',
            'なり', 'そう', 'まし', 'その', 'この', 'あの', 'せる', 'どう', 'ため', 'どこ',
            'いる', 'これ', 'それ', 'あれ', 'いい', 'など', 'あっ', 'もう', 'さん', 'じゃ',
            'から', 'あり', 'ので', 'とも', 'ませ', 'でし', 'とき', 'こと', 'なる', 'って',
            'ただ', 'まで', 'もの', 'つか', 'なっ', 'でき', 'もっ', 'けど', 'ほぼ', 'なー',
            'そこ', 'ここ', 'だろ', 'なん', 'だっ', 'なあ', 'っけ', 'せる', 'やっ', 'また',
            'どれ', 'なれ', 'かも', 'いく', 'いけ', 'いう', 'たい', 'あと', 'かも', 'しれ',  
            'こう', 'なく', 'よく', 'だけ', 'れる', 'よう', 'かけ', 'どの', 'てる', 'とか',
            'という', '思う', '思っ', 'として', 'ところ', 'しまう', 'なんか', 'そんな', 
            'でしょ', 'しまい', 'わかり', 'まま'
            ]

    # 除去リストに数字を追加 
    for number in range(10000):
        rems.append(str(number))

    # 除去リストに追加 ― 一文字のひらがなとカタカナ,全角数字,アルファベット大文字・小文字
    hirakana    = [chr(i) for i in range(12353, 12436)]
    katakana    = [chr(i) for i in range(12449, 12533)]
    zenkaku_num = [chr(i) for i in range(65296, 65296+10)]
    alph_L      = [chr(i) for i in range(97, 97+26)]
    alph_S      = [chr(i) for i in range(65, 65+26)]
    for a in range(len(hirakana)):
        rems.append(hirakana[a])
    for b in range(len(katakana)):
        rems.append(katakana[b])
    for c in range(len(zenkaku_num)):
        rems.append(zenkaku_num[c])
    for d in range(len(alph_L)):
        rems.append(alph_L[d])
    for e in range(len(alph_S)):
        rems.append(alph_S[e])

    words = [x for x in word_list if (x in rems) == False]

    return words

####3. テキストファイル読み込みと形態素分析実施

NLP_run.py
# coding: utf-8
import os
import glob
import numpy as np
from text import Text_Data
from NLP_func import *
import pandas as pd

words = Text_Data("kokoro.txt")
NLP_run.py
print(words)

['こころ', '夏目', '漱石', '目次', '上', '\u3000', '先生', '私', '一', '私', 'わたくし', '人', '常に', '先生', '呼ん', 'だから', '先生', '書く', '本名', '打ち明け', '世間', '憚', 'はばかる', '遠慮', '方', '私', 'にとって', '自然', '私', '人', '記憶', '呼び', '起す', 'ごと', 'すぐ', '先生', 'たく', '筆', ・・・・・・・,, '妻', '己', 'おのれ', '過去', 'に対して', 'もつ', '記憶', 'なるべく', '純白', '保存', 'おい', 'やり', '私', '唯一', 'ゆい', '希望', '私', '死ん', '後', '妻', '生き', '以上', 'あなた', '限り', '打ち明け', 'られ', '私', '秘密', 'すべて', '腹の中', 'しまっ', 'おい', '下さい']

全角空白(\u3000)が見られるが、形態素分割はOKとする。
ちなみに、len(words) は 45273 である。
記号や数字、助詞などを除くと、およそ 4 万 5 千個の単語からなる小説ということになる。

####4. 形態素を id へ変換
リスト (words) 内の各単語に id を振る。
(「ゼロから作る Deep Learning ② 自然言語処理編,斎藤 康毅 著,O'REILLY」 の第 2 章を参照した)

NLP_func.py
# coding: utf-8
import numpy as np

def id_preprocess(words, word_to_id, id_to_word):
    for word in words:
        if word not in word_to_id:
            new_id = len(word_to_id)
            word_to_id[word] = new_id
            id_to_word[new_id] = word
    return word_to_id, id_to_word
NLP_run.py
word_to_id = {}
id_to_word = {}
word_to_id, id_to_word = id_preprocess(words, word_to_id, id_to_word)
NLP_run.py
print(word_to_id)

{'こころ': 0, '夏目': 1, '漱石': 2, '目次': 3, '上': 4, '\u3000': 5, '先生': 6, '私': 7, '一': 8, 'わたくし': 9, '人': 10, '常に': 11, '呼ん': 12, 'だから': 13, '書く': 14, '本名': 15, '打ち明け': 16, '世間': 17, '憚': 18, 'はばかる': 19, '遠慮': 20, '方': 21, 'にとって': 22, '自然': 23, '記憶': 24, '呼び': 25, '起す': 26, 'ごと': 27, 'すぐ': 28, 'たく': 29, '筆': 30, ・・・・・・・・, '徒労': 7730, '渡辺': 7731, '華山': 7732, 'なべ': 7733, 'かざん': 7734, '邯鄲': 7735, '画': 7736, '死期': 7737, '繰り延べ': 7738, '先達': 7739, 'せんだって': 7740, '果たす': 7741, '果たし': 7742, '善悪': 7743, '供する': 7744, '保存': 7745, '秘密': 7746}

記号や数字、助詞などを除いて、合計 7746 個の単語からなる模様。

word_to_id を逆にした id_to_word は下記。

NLP_run.py
print(id_to_word)

{0: 'こころ', 1: '夏目', 2: '漱石', 3: '目次', 4: '上', 5: '\u3000', 6: '先生', 7: '私', 8: '一', 9: 'わたくし', 10: '人', 11: '常に', 12: '呼ん', 13: 'だから', 14: '書く', 15: '本名', 16: '打ち明け', 17: '世間', 18: '憚', 19: 'はばかる', 20: '遠慮', 21: '方', 22: 'にとって', 23: '自然', 24: '記憶', 25: '呼び', 26: '起す', 27: 'ごと', 28: 'すぐ', 29: 'たく', 30: '筆', ・・・・・・・,7730: '徒労', 7731: '渡辺', 7732: '華山', 7733: 'なべ', 7734: 'かざん', 7735: '邯鄲', 7736: '画', 7737: '死期', 7738: '繰り延べ', 7739: '先達', 7740: 'せんだって', 7741: '果たす', 7742: '果たし', 7743: '善悪', 7744: '供する', 7745: '保存', 7746: '秘密'}

####5. コーパスの作成

NLP_func.py
def corpus_preprocess(words, word_to_id, id_to_word):
    corpus = np.array([word_to_id[w] for w in words])
    return corpus
NLP_run.py
corpus_dic = {}
corpus_dic = corpus_preprocess(words, word_to_id, id_to_word)
NLP_run.py
print(corpus_dic)

[ 0 1 2 ... 499 694 1260]
これは、元の文章に出現する各単語を id で置換したものに相当する。
len(corpus_dic) = len(words) = 45273 であり、元の小説 45273 語を単語 id で表現したことになる。

####6. Bag of Words 作成と csv ファイルへの書き出し
5 で作成した corpus_dic の id の頻度をカウントする。

NLP_run.py
bag_of_words = {}
vocab_size = len(word_to_id) # = len(id_to_word) 

bag_of_words = {}
corpus_size = len(corpus_dic)
for i in range(vocab_size):
    count = 0
    for j in range(corpus_size):
        if corpus_dic[j] == i: # 元の文書を走査していき,一致していればカウントする。
            count += 1
    bag_of_words[id_to_word[i]] = count
    
# 頻出順にソート
bag_of_words = sorted(bag_of_words.items(), key = lambda x: x[1], reverse=True)

# 単語を ID 順に CSV ファイルへ保存  # BOW を CSV ファイルへ保存
Table = pd.DataFrame(bag_of_words)
Table.to_csv('Table.csv', index=False, encoding="shift_jis")

####結果
出現回数でソートした単語は以下の通り。(上位30単語を表示)
image.png
やはり「先生」が多いのは納得。
親友の K について、text.py の中で、英文字を除去していたが、これは全角文字だから残ったと思われる。
大事な登場人物なので除去されなくて何故かほっとしている。

10
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?