8. テキストデータ処理 (1)¶
第 8 回では、テキストデータ処理の基本について学びます。
8.1 テキストデータ処理とは¶
テキストデータ処理は、情報をテキスト形式で保存、編集、分析するための手法であり、多くの分野で頻繁に利用されています。テキストデータ処理には、以下のような操作が含まれます。
- テキストの基本操作:文字列の結合や分割、抽出など
- テキストのクリーニング:不要なデータやノイズ除去、欠損データ補完など
- テキストの変換:文字コードの変換や正規化
- テキストの分析:テキスト内容の理解やパターン・傾向の抽出
8.2 基本的な文字列操作¶
Python には、文字列を操作するための便利な関数がさまざま用意されています。以下ではその代表例を紹介します。
8.2.1 文字列の結合¶
文字列の結合を行うには、join 関数を用います。[区切り文字].join([文字列のリスト]) のようにすることで、文字列のリスト(または他のイテラブル)を区切り文字で連結し、一つの文字列にすることができます。
8.2.2 文字列の分割¶
文字列を分割したい場合は、split 関数を用います。[文字列].split([区切り文字]) のようにすることで、文字列を指定した区切り文字で分割し、リストにすることができます。区切り文字を省略した場合は、スペースで分割されます。
['Python is simple', ' powerful', ' and widely used.']
['Python', 'is', 'simple,', 'powerful,', 'and', 'widely', 'used.']
8.2.3 文字列の置換¶
特定の文字列を別の文字列に置換したり、除去したりしたい場合は、replace 関数を用います。[文字列].replace([置換前の文字列], [置換後の文字列]) のようにすることで、文字列の置換を行うことができます。
replace による文字列の除去
置換後の文字列を空文字列 ("") にすることで、指定した文字列を取り除くことができます。
8.2.4 文字列の出現回数¶
文字列の中に特定の文字列がいくつ含まれているかを数えるには、count 関数を用います。[文字列].count([数えたい文字列]) のようにすることで、指定した文字列が出現する回数を求めることができます。
8.2.5 テキストデータの読み込み¶
テキストファイル (.txt) を開いてデータを読み込むには、with 文とともに open() 関数・read() 関数を用います。例えば、Rapunzel.txt というファイルを読み込んで text という変数に格納するには、以下のようにします。
There once lived a man and his wife, who had long wished for a child, but in vain. Now there was at
ここで "r" は、ファイルを「読み取り専用」モード (read mode) で開こうとしていることを意味します。その他にも「書き込み」「追記」「新規作成」などいくつかのモードが存在しますが、本講義では扱いませんので、興味のある方は各自で調べるようにしてください。
8.3 形態素解析¶
形態素解析とは、文を意味を持つ最小単位の単語(形態素)に分割する操作のことです。日本語の文章に対して自然言語処理を適用する場合、文章を単語単位に分割する分かち書き(形態素解析)が必要になります。形態素解析器には、MeCab や Juman++、Janome などがよく使用されます。
例えば、「吾輩は猫である」という文は、「吾輩」「は」「猫」「で」「ある」に分割されます。
8.3.1 MeCab と辞書データのインストール¶
形態素解析を実際に実行するには、形態素解析器と辞書データをインストールする必要があります。例えば、Google Colaboratory などで以下のコマンドを実行することで、高性能な形態素解析器である MeCab と、軽量の辞書データ unidic-lite をインストールすることができます。
インストール後、以下のようにインポートすることで、MeCab を使えるようになります。
CHIKUWA Editor における形態素解析器と辞書データについて
CHIKUWA Editor には、形態素解析器として「MeCab」が、日本語辞書として「mecab-ipadic-utf8」が標準でインストールされており、これらを用いて形態素解析の練習ができるようになっています。
8.3.2 MeCab による形態素解析の基礎¶
MeCab を用いた形態素解析の基本的な流れを見ていきましょう。
まずは、MeCab の解析器(Tagger)のインスタンスを作成します。日本語の文章を解析してくれる、実際の道具を準備する(工具箱から工具を取り出す)イメージです。
そして、Tagger のインスタンスから parse() 関数を呼び出すことで、形態素解析が実行されます。
例えば、「すもももももももものうち」という文を MeCab で形態素解析する一連の流れは以下のようになります。
import MeCab
# MeCab のインスタンス生成
tagger = MeCab.Tagger()
# 解析するテキスト
text = "すもももももももものうち"
# 形態素解析を実行し、結果を表示
result = tagger.parse(text)
print(result)
すもも 名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
うち 名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS
8.3.3 形態素解析のオプション¶
MeCab では、MeCab.Tagger() にオプションの文字列を渡すことで、形態素解析の出力結果を細かく制御することができます。
分かち書き (-Owakati)¶
-Owakati を指定することで、単語ごとにスペースで区切った単純な分かち書きの結果を出力することができます。
単語の読み (-Oyomi)¶
-Oyomi を指定することで、単語の読み(カタカナ)だけを出力することができます。
その他の形態素解析オプション
その他にも、詳細な内部データをすべて出力する -Odump や、読みとシンプルな品詞を含む -Ochasen (別の形態素解析器である ChaSen 互換の出力フォーマット)などのオプションを指定することができます。
8.3.4 特定の品詞の抽出¶
形態素解析ができたら、特定の品詞を抽出してみましょう。文書の解析でよく利用される重要な品詞は、「名詞」「形容詞」「動詞」です。
まずは、parseToNode() 関数を用いて、形態素解析の結果をノードに変換します。ノードとは、数珠つなぎの要素のようなイメージで、next や prev によって隣り合うノードを順番に見て回ることができる仕組みを指します。
次に、while 文を使用し、ノード化された形態素解析の結果を一つ一つ参照していきます。このとき、ノードの surface 属性には単語が、feature 属性には単語の特徴に関する情報が入っています。形態素解析をした後、単語のリストと品詞のリストを作成するプログラムは以下のようになります。
import MeCab
# MeCab のインスタンス生成
tagger = MeCab.Tagger()
# 解析するテキスト
text = "すもももももももものうち"
# 形態素解析を実行し、ノードに変換
node = tagger.parseToNode(text)
# 単語と品詞を格納するためのリスト
words = []
types = []
# ノードがなくなるまで解析結果を表示
while node:
if node.surface: # surface 属性(単語)が存在する場合
words.append(node.surface)
types.append(node.feature.split(",")[0])
node = node.next # 次のノードに移動
print("単語:", words)
print("品詞:", types)
node.feature の中には、"名詞,一般,*,*,*,*,すもも,スモモ,スモモ" のように、複数の情報がまとめて入っています。ここから "名詞" という情報だけ抽出したい場合には、上のコードで示しているように、split 関数でテキストをカンマで区切り、生成されたリストの先頭を参照すれば良いというわけです。
feature の構造について
本講義で利用することを想定している IPA 辞書を利用する場合、feature は以下の 9 つの要素で構成されます。
品詞, 品詞細分類1, 品詞細分類2, 品詞細分類3, 活用形, 活用型, 原形, 読み, 発音
本項のサンプルコードでは surface を用いてテキスト中の単語そのものを取得していますが、実際の分析においては語の活用の影響を排除するため、「原形」を使うことが望ましいケースが多いことを覚えておいてください。なお、相当する要素が存在しない場合には * が入ります。
品詞が「名詞」の単語のみを抽出してリスト化するには、以下のようにループの中で if 文を使用します。
import MeCab
# MeCab のインスタンス生成
tagger = MeCab.Tagger()
# 解析するテキスト
text = "すもももももももものうち"
# 形態素解析を実行し、ノードに変換
node = tagger.parseToNode(text)
# 単語を格納するためのリスト
nouns = []
# ノードがなくなるまで解析結果を表示
while node:
pos = node.feature.split(",")[0] # 品詞を取得
if pos == "名詞": # 品詞が「名詞」であった場合
nouns.append(node.surface)
node = node.next # 次のノードに移動
print("名詞:", nouns)
8.4 WordCloud¶
形態素解析ができるようになったら、WordCloud を使用して文章の可視化をしてみましょう。WordCloud では、文章の中で出現頻度の高い単語を大きく、あまり出ない単語を小さく表示します。
8.4.1 WordCloud のインストールとインポート¶
WordCloud を使用するには、wordcloud ライブラリをインストールする必要があります。インストールがまだの場合は、以下のコマンドでインストールしてください。
インストール後、wordcloud ライブラリの中から WordCloud クラスをインポートします。
8.4.2 WordCloud の生成¶
WordCloud を生成するには、WordCloud クラスの generate() 関数に対し、スペースで区切られたテキストを渡します。また、WordCloud のインスタンスを生成する際は、width(画像の幅)や height(画像の高さ)、background_color(背景色)、random_state(乱数のシード値)などを指定することができます(詳細はこちら)。
以下は、英語のテキストに対して WordCloud を生成する基本的なコードの例です。
from wordcloud import WordCloud
import matplotlib.pyplot as plt
# テキスト(「Rapunzel」の冒頭)
text = "There once lived a man and his wife, who had long wished for a child, but in vain. Now there was at the back of their house a little window which overlooked a beautiful garden full of the finest vegetables and flowers; but there was a high wall all round it, and no one ventured into it, for it belonged to a witch of great might, and of whom all the world was afraid."
# WordCloud の生成
wordcloud = WordCloud(
width=600,
height=400,
background_color="white",
random_state=42
).generate(text)
# WordCloud の表示
plt.imshow(wordcloud)
plt.axis("off")
plt.tight_layout()
plt.show()
乱数のシード値 (randam_state) について
WordCloud ではデフォルトでランダムに単語の配置を行うため、実行するたびに結果が変わってしまいます。これを防ぎ、結果の再現性を担保するためには、上記コードのように乱数のシード値 (randam_state) を設定します。
英文のストップワードについて
英語のテキストに対する WordCloud の結果を見て気が付いた人もいるかもしれませんが、出現頻度が高いはずの a, the, of などの単語は一切表示されていません。これは、WordCloud では、あまりにも一般的な英単語がストップワードとして、デフォルトで除外されるようになっているためです。どのような英単語がストップワードとして登録されているかは、以下のコードで確認することができます。
WordCloud をストップワードなしで生成したい場合には、インスタンス生成のパラメータで stopwords=set() を指定します。逆にストップワードを追加したい場合には、union 関数を用いて stopwords=STOPWORDS.union({"man", "wife"}) のように書きます。
8.4.3 日本語文章の WordCloud¶
英語の場合は元から単語がスペースで区切られているため、そのまま WordCloud を生成することができましたが、日本語の場合はそうはいきません。まずは形態素解析を行い、単語をスペースで区切った形に変換する必要があります。以下は、MeCab を用いて形態素解析(分かち書き)を行い、日本語の WordCloud を生成するコードの例です。
日本語を表示するには、日本語に対応したフォントのパスを WordCloud の font_path に指定する必要があります。ここでは、japanize_matplotlib ライブラリに同梱されている日本語フォント(ipaexg.ttf)を利用します。japanize_matplotlib がインストールされていない場合は、pip install japanize_matplotlib でインストールしてください。
import os
import MeCab
import japanize_matplotlib
from wordcloud import WordCloud
import matplotlib.pyplot as plt
# MeCab のインスタンス生成(分かち書き)
tagger = MeCab.Tagger("-Owakati")
# 解析するテキスト(「吾輩は猫である」の冒頭)
text = "吾輩は猫である。名前はまだ無い。どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た。"
# 形態素解析を実行
words = tagger.parse(text)
# 日本語フォントのパス(japanize_matplotlib 同梱の ipaexg.ttf)
font_path = os.path.join(
os.path.dirname(japanize_matplotlib.__file__), "fonts", "ipaexg.ttf"
)
# WordCloud の生成
wordcloud = WordCloud(
font_path=font_path,
width=600,
height=400,
background_color="white",
random_state=42
).generate(words)
# WordCloud の表示
plt.imshow(wordcloud)
plt.axis("off")
plt.tight_layout()
plt.show()
日本語の WordCloud を作成することができました。しかし、「は」や「で」など、それ自体は意味を持たない「助詞」が図の大半を占めており、有意義な WordCloud になっているとは言えません。より有意義な WordCloud にするためにはどのようにすればよいか、考えてみましょう。
非自立の名詞を除く操作
「名詞」だけを抽出しても、「の」「こと」「もの」など、それ自体はあまり意味を持たない語が残ることがあります。これらは「非自立」(単独では使われにくい)の名詞であり、feature の 2 番目の要素(品詞細分類1)が "非自立" になっています。これを利用して、次のように「名詞」かつ「非自立ではない」語に絞ると、より意味のある WordCloud になります。
feature = node.feature.split(",")
pos = feature[0] # 品詞(名詞・助詞 など)
sub = feature[1] # 品詞細分類1(一般・非自立 など)
if pos == "名詞" and sub != "非自立":
nouns.append(node.surface)
さらに絞り込みたい場合は、sub in ["一般", "固有名詞", "サ変接続"] のように、残したい種類だけを指定する方法もあります。
演習¶
演習で使うテキストファイル
以下の演習では、外部のテキストファイルを読み込んで使用します。下のリンクからダウンロードし、CHIKUWA Editor の作業フォルダまたはノートブックと同じフォルダに置いてから実行してください。
- 英語のテキスト:Rapunzel.txt(グリム童話「ラプンツェル」。8.2.5 で使用したものと同じファイル)
- 日本語のテキスト:Wagahai.txt(夏目漱石『吾輩は猫である』の冒頭部分)
なお、日本語のファイルは文字化けを防ぐため、encoding="utf-8" を指定して読み込みます。
いずれも著作権の切れたパブリックドメインの作品です。出典は次のとおりです。
Rapunzel.txt:Project Gutenberg, Household Stories by the Brothers Grimm(Lucy Crane 訳)Wagahai.txt:青空文庫(夏目漱石『吾輩は猫である』)
演習 8-1(初級):単語数と出現回数
英語のテキストファイル Rapunzel.txt を読み込んだうえで、次の処理を行ってください。
splitで単語に分割し、単語数を表示する- テキスト中に
"Rapunzel"が何回出現するかをcount関数で数えて表示する
演習 8-2(初級):英語テキストの WordCloud
英語のテキストファイル Rapunzel.txt に対し、不要な単語を除去してから WordCloud を作成してください。
replaceを使って、テキストから不要な単語を 1 つ以上除去する- 加工後のテキストから WordCloud を生成する(
width・height・background_color・random_stateを設定) - WordCloud を表示する(軸は非表示にする)
演習 8-3(中級):名詞の抽出
日本語のテキストファイル Wagahai.txt を形態素解析し、品詞が「名詞」の単語だけを抽出してください。
parseToNodeで日本語テキストをノードに変換するwhileループで各ノードをたどり、品詞が「名詞」の単語だけをリストに集める- 名詞のリストを表示する
演習 8-4(中級):日本語テキストの WordCloud
日本語のテキストファイル Wagahai.txt から「名詞」だけを取り出して、WordCloud を作成してください。
- 形態素解析を行い、「名詞」の単語だけを抽出する(演習 8-3 を参考にする)
- 抽出した名詞を半角スペースで連結して 1 つの文字列にする
- 日本語フォントを指定して WordCloud を生成し、表示する
import os
import MeCab
import japanize_matplotlib
from wordcloud import WordCloud
import matplotlib.pyplot as plt
tagger = MeCab.Tagger()
with open("Wagahai.txt", "r", encoding="utf-8") as f:
text = f.read()
# 1. 形態素解析して「名詞」だけを抽出
node = tagger.parseToNode(text)
nouns = []
while node:
node = node.next
# 2. 名詞を半角スペースで連結
# 日本語フォントのパス(japanize_matplotlib 同梱の ipaexg.ttf)
font_path = os.path.join(
os.path.dirname(japanize_matplotlib.__file__), "fonts", "ipaexg.ttf"
)
# 3. WordCloud を生成して表示
plt.tight_layout()
plt.show()

