LoginSignup
1

More than 1 year has passed since last update.

SudachiPyで形態素単位に分割|つぶやきをWordcloudで可視化③

Last updated at Posted at 2021-07-14

私事ではありますが、2020年5月にコーギーという中型犬をお迎えしました。
愛犬のブログを運営してますので、見てもらえると嬉しいです。

corgi_komugi

「コーギー関連のツイートをwordcloudで可視化したい!」と思い立ち、以下の4項目を実施しました。

  1. TwitterAPIでツイートを取得
  2. 正規表現を用いてツイートを整形
  3. SudachiPyで形態素単位に分割 ← 今回
  4. WordCloudで可視化

一連のコードと様々なアウトプットの表示

今回は、「SudachiPyで形態素単位に分割」に関して説明していきます。

対象者

  • SudachiPyの特徴を知りたい人
  • SudachiPyでコードを書いてみたい人

SudachiPyの特徴

SudachiPyのメンテナーがこちらのリンクで書いてくれてます。

まとめると、特徴は以下の3つになります。

  • small, core, fullの3タイプから、使用する辞書を選択できる
  • 分割タイプも3タイプから選択できる
  • 表記を正規化してくれる

ざっくりとした要件定義

Input ... 前処理が完了した、DataFrame形式のツイートデータ
Output ... 単語の間をスペースで分割し、すべてのツイートを結合したstring

Inputは、こちらの記事で取得したデータフレーム(以下)になります。

2021-07-13_06h26_12.png

Outputは、テキスト内容を形態素単位に分割し、次のようになります。

2021-07-13_08h57_49.png

コード

コード全体

import pandas as pd
from sudachipy import tokenizer
from sudachipy import dictionary

class SudachiTokenizer():
    def __init__(self, dict_type="core", mode="C", stopwords=None, include_pos=None):

        if dict_type not in ["core", "small", "full"]:
            raise Exception("invalid dict_type. 'core' ,'small' or 'full'")
        self.tokenizer_obj = dictionary.Dictionary(dict_type=dict_type).create()

        if mode not in ["A", "B", "C"]:
            raise Exception("invalid mode. 'A' ,'B' or 'C'")
        self.mode = getattr(tokenizer.Tokenizer.SplitMode, mode)
        print(self.mode )

        if stopwords is None:
            self.stopwords = []
        else:
            self.stopwords = stopwords

        if include_pos is None:
            self.include_pos = ["名詞", "動詞", "形容詞"]
        else:
            self.include_pos = include_pos

    def parser(self, text):
        return self.tokenizer_obj.tokenize(text, self.mode)

    def tokenize(self, text, pos=False):
        res = []
        for m in self.parser(text):
            p = m.part_of_speech()
            base = m.normalized_form() #.dictionary_form()
            #print(base, ": ", p)
            if p[0] in self.include_pos and base not in self.stopwords and p[1] != "数詞":
                if pos:
                    res.append((base, p[0]))
                else:
                    res.append(base)
        return res    

def create_word_chain(col, df, tokenizer):
    word_lists=[]

    for i in range(len(df)):
        text = df.loc[i, col]
        word_list = tokenizer.tokenize(text, pos=False)
        for word in word_list:
            word_lists.append(word)      
    word_chain =' '.join(word_lists)
    return word_chain

include_pos = ["名詞", "動詞", "形容詞"]
stopwords = ["コーギー", "見る","為る", "今日","無い","居る","成る"]
sudachi_tokenizer = SudachiTokenizer(dict_type="core", mode="A", stopwords=stopwords, include_pos=include_pos)
word_chain = create_word_chain('TW_TEXT_mod', df, sudachi_tokenizer)

コード解説

SudachiTokenizer()のinit

SudachiPyで必要なdict_typeとmodeを指定します。
SudachiPyの条件に合わない語句を指定した場合は、エラーが出るようにしています。

今回の分析では、dict_typeを変更しても、最終的な結果に大きな変化はありませんでした。(もしかすると、うまくdict_typeを変更できてないかもです。)

また、stopwords(形態素解析のあとに、削除するワード)とinclude_pos(形態素解析のあとに、含める品詞)を指定します。

class SudachiTokenizer():
    def __init__(self, dict_type="core", mode="C", stopwords=None, include_pos=None):

        if dict_type not in ["core", "small", "full"]:
            raise Exception("invalid dict_type. 'core' ,'small' or 'full'")
        self.tokenizer_obj = dictionary.Dictionary(dict_type=dict_type).create()

        if mode not in ["A", "B", "C"]:
            raise Exception("invalid mode. 'A' ,'B' or 'C'")
        self.mode = getattr(tokenizer.Tokenizer.SplitMode, mode)
        print(self.mode )

        if stopwords is None:
            self.stopwords = []
        else:
            self.stopwords = stopwords

        if include_pos is None:
            self.include_pos = ["名詞", "動詞", "形容詞"]
        else:
            self.include_pos = include_pos

 SudachiTokenizerクラスのparserとtokenize関数

parser関数でparserを定義します。

結果はMorphemeのリストとなっており、表層形 (surface()) 、品詞 (part_of_speech()) 、読み (reading_form()) 、正規化した表現 (normalized_form()) を取得できます
(引用元:https://ohke.hateblo.jp/entry/2019/03/09/101500)

次に、tokenize関数で条件に合った語句を抽出し、リストに加えます。
今回は、正規化した表現で抽出したいので、normalized_form()を使用しています。

    def parser(self, text):
        return self.tokenizer_obj.tokenize(text, self.mode)

    def tokenize(self, text, pos=False):
        res = []
        for m in self.parser(text):
            p = m.part_of_speech()
            base = m.normalized_form() #.dictionary_form()
            #print(base, ": ", p)
            if p[0] in self.include_pos and base not in self.stopwords and p[1] != "数詞":
                if pos:
                    res.append((base, p[0]))
                else:
                    res.append(base)
        return res

create_word_chain関数

次の章でwordcloudで頻出語を可視化しますので、語句と語句の間をスペースで区切り、ひとつのstringにします。

全てのtweetに対して実施する必要があるので、ツイートごとに先ほど作成した関数で形態素単位に分割し、for文でそれを全ツイート繰り返します。

def create_word_chain(col, df, tokenizer):
    word_lists=[]

    for i in range(len(df)):
        text = df.loc[i, col]
        word_list = tokenizer.tokenize(text, pos=False)
        for word in word_list:
            word_lists.append(word)      
    word_chain =' '.join(word_lists)
    return word_chain

作成した関数の実行

これまで作成した関数を実行し、word_chainを作成します。
最終結果に「居る」や「成る」といったコーギーの特徴を表さない語句が表示されていたので、これらの語句をstopwordsに追加しています。

次の章のwordcloudでもstopwordsを指定でき、そちらで指定したほうが全体の計算量が少ないかもしれません。。
(大変そうですし、今回は計算量は無視しました。)

include_pos = ["名詞", "動詞", "形容詞"]
stopwords = ["コーギー", "見る","為る", "今日","無い","居る","成る"]
sudachi_tokenizer = SudachiTokenizer(dict_type="core", mode="A", stopwords=stopwords, include_pos=include_pos)
word_chain = create_word_chain('TW_TEXT_mod', df, sudachi_tokenizer)

まとめ

この記事では、形態素解析器を用いて、ツイートを形態素に分割しました。

次は、ついにwordcloudで可視化します。
こちらの記事で説明してますので、興味があれば見てもらえると嬉しいです。

corgi_komugi

参考文献

日本語形態素解析器 SudachiPy の 現状と今後について
GitHub - WorksApplications/Sudachipy
形態素解析器比較 Sudachi vs Mecab+Neologd
Pythonで形態素解析器Sudachiを使う (SudachiPy)

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
What you can do with signing up
1