Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
0
Help us understand the problem. What are the problem?
@komugi_papa

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

私事ではありますが、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)

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
0
Help us understand the problem. What are the problem?