🦁

【自然言語処理】【Python】TF-IDFを使って文書の特徴をつかもう

2022/09/17に公開

はじめに

古くから、自然言語処理の世界では、単語をスコアリングする手法として、TF-IDFというのがあります。
古くからあるにもかかわらず、普通に暮らしていると馴染みのない概念だと思います。かく言う私も、Pythonで自然言語処理を学ぶまでは名前すら聞いたことがありませんでした。
ここでは、そんなTF-IDFについて、はじめて触れる方にも分かるように概念を丁寧に説明するとともに、Pythonを使った実装方法についてご紹介していきたいと思います。
 

🐱TF-IDFとは

一言でいうと、ある文書の含まれる単語の重要度を示す指標です。文書の中で重要な単語をとらえることで、その文書の特徴を知ることができます。
具体的には以下の式で計算します。

単語の重要度というと、パッと思いつくのが、単語の出現頻度ではないでしょうか。それはある意味で正しいのですが、単語の出現頻度だけでみてしまうと、極端にいうと、「です」「ます」みたいな、どの文書にも出現しうるような単語が重要という話になります
そこで、単語の出現頻度だけではなく、他の文書での出現頻度が低い=単語のレア度も見るのがTF-IDFです。
 

🐶具体的なイメージ

A社、B社、C社の3社に会社経営で重視しているものをテーマに文書を書いてもらったとします。話を単純化するため、単語を3つ使って、重複可で選んでもらったところ、以下の通りになりました。
ここから、3社の文書にでてくる単語のTF-IDFを考えてみたいと思います。

 

🌱TF(出現頻度)

TF値の計算式は以下の通りです。

いずれの文書においても、単語の数は3つなので、分母は3になりますね。分子である出現回数は次の通りになります。

TF値は次の通りになりますね。

 

🌲IDF値(レア度)

IDF値の計算式は以下の通りです。logはIDF値が発散しないためのおまじないです。1を足しているのもIDF値を0にしないためのおまじないです。

分子の全文書数は3になりますね。分母である単語ごとの出現社数(文書数)は次のとおりです。

IDF値は次のとおりです。出現社数の少ない従業員のIDF値が高くなっていますね。

 

🌳TF-IDF

TF-IDFは次の通りになります。
A社は利益しかいっていないので、利益が重要語になるのは当たり前ですね。
B社は社会貢献、C社は従業員が最重要単語となります。
TF-IDFは文書を特徴づけるキーワードが何かという観点でも使えると思います。

 

 

🐹文章データの用意

TF-IDFを計算する文章データについては、上場企業2,500社が有価証券報告書で公表している「経営方針」(2022年3月期分)としました。
各企業が経営方針として何を重要と言っているかをTF-IDFで計算するイメージです。

import pandas as pd
df = pd.read_csv("/content/2203有報セット.csv",index_col=0)
df

以下のようなデータフレームを準備しました。

 
以下のリンクで、文章データの取得方法についてご紹介していますので、ご覧ください
https://zenn.dev/robes/articles/4ab5a7f992000d
 

🐰MeCabと辞書(NEologd)のインストール

自然言語処理の第一歩は形態素解析です。TF-IDFは単語を扱うので、優秀な辞書を用意しておくのがよいでしょう。

!apt-get -q -y install sudo file mecab libmecab-dev mecab-ipadic-utf8 git curl python-mecab > /dev/null
!git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git > /dev/null 
!echo yes | mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -n > /dev/null 2>&1
!pip install mecab-python3 > /dev/null

# シンボリックリンクによるエラー回避
!ln -s /etc/mecabrc /usr/local/etc/mecabrc
# 辞書が格納されているパスを確認
!echo `mecab-config --dicdir`"/mecab-ipadic-neologd"

 

🐻形態素解析を行う

企業ごとの経営方針について、TF-IDFが計算できるよう、形態素解析を行います。
そのために、関数を作ったうえで、関数を実行しています。
 

🌴ストップワードの取得

  • 最初から重要な単語と見做さないワードはストップワードとして除去します。ストップワードとして、自ら設定することも可能ですが、ここでは、一般的なストップワードをダウンロードして使っていきたいと思います。
  • !wget urlで簡単に取得できます。
!wget http://svn.sourceforge.jp/svnroot/slothlib/CSharp/Version1/SlothLib/NLP/Filter/StopWord/word/Japanese.txt

 

🌵関数の作成

import re
#stopwordsの指定
with open("/content/Japanese.txt","r") as f:
    stopwords = f.read().split("\n")


#トークナイザー(関数)
def mecab_tokenizer(text):

    replaced_text = text.lower()
    replaced_text = re.sub(r'[【】]', ' ', replaced_text)       # 【】の除去
    replaced_text = re.sub(r'[()()]', ' ', replaced_text)     # ()の除去
    replaced_text = re.sub(r'[[]\[\]]', ' ', replaced_text)   # []の除去
    replaced_text = re.sub(r'[@@]\w+', '', replaced_text)  # メンションの除去
    replaced_text = re.sub(r'\d+\.*\d*', '', replaced_text) #数字を0にする

    path = "-d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd"
    mecab = MeCab.Tagger(path)
    parsed_lines = mecab.parse(replaced_text).split("\n")[:-2]
    
    #表層形を取得
    # surfaces = [l.split('\t')[0] for l in parsed_lines]
    #原形を取得
    token_list = [l.split("\t")[1].split(",")[6] for l in parsed_lines]
    #品詞を取得
    pos = [l.split('\t')[1].split(",")[0] for l in parsed_lines]
    # 名詞,動詞,形容詞のみに絞り込み
    target_pos = ["名詞","動詞","形容詞"]
    token_list = [t for t, p in zip(token_list, pos) if p in target_pos]
    
    # stopwordsの除去
    token_list = [t for t in token_list if t  not in stopwords]
    
    # ひらがなのみの単語を除く
    kana_re = re.compile("^[ぁ-ゖ]+$")
    token_list = [t for t in token_list if not kana_re.match(t)]
    
    return ' '.join(token_list)

#df全体に対してmecav_tokenizerを適用し、形態素解析を行なったリストを返す関数
def make_docs(df,column_number):
    docs=[]
    for i in range(len(df)):
        text = df.iloc[i,column_number]
        docs.append(mecab_tokenizer(text))
    return docs

 

🌻関数の実行

dfの経営方針に対して関数を実行し、計算結果をdocs_keiei_2203のリストに格納します。

docs_keiei_2203 = make_docs(df,2)

 

🐷TF-IDFの計算

文書全体のTF-IDF値とデータフレームを作成します


from sklearn.feature_extraction.text import TfidfVectorizer

def tf_idf(docs):
    vectorizer = TfidfVectorizer(smooth_idf=False)
    X = vectorizer.fit_transform(docs)

    values = X.toarray()
    feature_names = vectorizer.get_feature_names()
    tfidf_df = pd.DataFrame(values, columns = feature_names)

    return values,feature_names,tfidf_df

# valuesはTF-IDF値、feature_namesは単語、tfidfはデータフレームになります。
values, feature_names, tfidf = tf_idf(docs_keiei_2203)

 

🐼企業ごとにTF-IDFを見てみよう

トヨタ自動車、みずほ銀行、伊藤忠商事のTF-IDFを見てみましょう。
それぞれの企業でTF-IDF値の上位25単語を抽出します。

index =df.query('会社名=="トヨタ自動車株式会社"').index[0]
dft = pd.DataFrame(tfidf.T[index].sort_values(ascending=False)[:25]).rename(columns={index:df.iloc[index,4]})
dft

それぞれの企業を特徴づける単語が抽出されていますね。

 

🐨ワードクラウドで可視化してみよう

TF-IDFに基づいて、ワードクラウドで可視化してみましょう。単語数は50まで表示します。
 

🌸ライブラリーのインストール

!pip install -q japanize-matplotlib
!apt-get -yq install fonts-ipafont-gothic
!ls /usr/share/fonts/opentype/ipafont-gothic

 

🌼ライブラリーのインポート

from wordcloud import WordCloud
import japanize_matplotlib
import matplotlib.pyplot as plt
from PIL import Image

 

🌷ワードクラウド作成

#wordcloud用のvecs_dicを作成する
def vecs_dic(feature_names,values):
    words = feature_names
    vecs = values.tolist()
    temp_dic = {}
    vecs_dic = []
    for vec in vecs:
        for i in range(len(vec)):
            temp_dic[words[i]] = vec[i] 
        vecs_dic.append(temp_dic)
        temp_dic = {} 
    return vecs_dic

#wordcloudを作成する
def wordcloud(vecs_dic,index):
       #フォントはGoogleフォントを使用
    font_path = '/content/NotoSansJP-Bold.otf'
    #maskを有効にすることで、ワードクラウドの形を変えられる。
    #mask = np.array(Image.open("/content/phpYSbfIJ.png"))

    fig = plt.figure(figsize=(15,15))
    wordcloud = WordCloud(
        background_color='Black', 
        width=2500, 
        height=1500,
        font_path=font_path,
        max_words=50,
        colormap="PuBu",
        #mask = mask
    )
    wordcloud.generate_from_frequencies(vecs_dic[index])
    plt.imshow(wordcloud, interpolation='bilinear')
    plt.axis("off")
    plt.figure()
    plt.show()
 
 #関数の実行
 wordcloud(vecs_dic,index)

 
それぞれで、colormapやフォントを変えてみました。

トヨタ自動車


 

みずほ銀行


 

伊藤忠商事

 

さいごに

いかがでしたでしょうか。
TF-IDFは自然言語処理の中では割と古典的な手法ではありますが、説明のしやすさ、わかりやすさの観点からは、ディープラーニングが発展した今でも十分使える存在だと思います。

Discussion