GINZA＋国語研長単位モデルでテキストを処理してNLPLOTで共起ネットワークを描いてみる

**2022/10/01**  国語研長単位がVer2.9→3.2にVer.upし、コード記述変更しないとインストールできなくなったのでpip変更／なぜかnlplotの共起ネットワークが以前のコードでは描画されなくなったので、コード変更
**2022/09/02**　nanデータがあるとエラーが出るので、前処理前にドロップを追加

***
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/34688c75-0207-811e-f41d-e3bbceb14e26.png)

## はじめに
自然言語処理は、もっぱら[**mecab**](https://pypi.org/project/mecab/)さんのお世話になっていましたが、[**GiNZA**](https://pypi.org/project/ginza/)推しの **[テキストアナリティクス入門](www.amazon.co.jp/dp/4065274109)** を読んで以降、遅ればせながら「これからは[**GiNZA**](https://pypi.org/project/ginza/)」と心に誓っております。


「[**GiNZA**](https://pypi.org/project/ginza/)」は、巷では以下のように説明されています。

>”自然言語処理ライブラリ「[**spaCy**](https://pypi.org/project/spacy/)」をフレームワークとして・・・オープンソース形態素解析器「[**SudachiPy**](https://pypi.org/project/SudachiPy/)」を内部に組み込み、「[**GiNZA日本語UDモデル**](https://megagonlabs.github.io/ginza/)」も利用可能・・・”

・・・なかなかどうして、ライブラリやモデルがいろいろ出てきて少しややこしいので、説明を見てスルーしてしまっていた方も多いのではないでしょうか？


**[**GiNZA**](https://pypi.org/project/ginza/)って何？** というのは、以下のリクルートの記事を読むと何となく理解できます。

https://www.recruit.co.jp/newsroom/2019/0402_18331.html

ざっくり要約させていただくと、
>- 従来の日本語の自然言語処理は、エンジニア自身が形態素解析・文節係り受け解析など複数機能ライブラリを組み込み、統合＝敷居が高い、日本語の自然言語処理技術＝形態素解析によるアプローチが中心 という課題あった。
>- そこで、自然言語処理の応用の容易化を目指し、Megagon Labsが公開した日本語自然言語処理のソースライブラリが「[**GiNZA**](https://pypi.org/project/ginza/)」。同じく公開されている国立国語研究所との共同研究成果である日本語テキストを高精度で解析できる「GiNZA日本語Universal Dependenciesモデル」も利用できるってことで、使うっきゃない。


　
先に申し上げると、むつかしいことはよくわからなくても大丈夫です。
[**GiNZA**](https://pypi.org/project/ginza/)は、所定のライブラリをインストールすれば、だれでも実行でき **「簡単に使えて、高い精度も得られる」** というものなので、日本語の自然言語処理がしたいという方は、ぜひ[**GiNZA**](https://pypi.org/project/ginza/)を！ということで、この記事では [**GiNZA**](https://pypi.org/project/ginza/)適用事例 をアップしたいと思います。

[**GiNZA**](https://pypi.org/project/ginza/)を使ってみたいと思える内容になっているかはわかりませんが、記事の内容は [**GiNZA**](https://pypi.org/project/ginza/)に「[**国語研長単位モデル**](https://github.com/megagonlabs/UD_Japanese-GSD/releases/tag/r2.9-NE)」を適用し、テキスト処理した単語を「自然言語可視化ライブラリ [**NLPLOT**](https://pypi.org/project/nlplot/)」に与えて、**頻出語グラフと共起ネットワークを描いてみる** という内容です。


## 実行したこと
>- Google Colabで実行しています。
>- テキストデータは、Excel表形式にまとめられたアンケート等を想定し、csvデータとしています。適用データは [前回記事](https://qiita.com/hima2b4/items/7125b15ad76b2f5c38e3) 同様、tweetデータ（csv）です。（※「再エネ」でtweet検索したつぶやきを [ついすぽ](https://chrome.google.com/webstore/detail/%E3%81%A4%E3%81%84%E3%81%99%E3%81%BD-tweet-export/imjojcgefiinokaclmifakpphkfeiddg?hl=ja) でcsvにエクスポートしたデータです。）

https://chrome.google.com/webstore/detail/ついすぽ-tweet-export/imjojcgefiinokaclmifakpphkfeiddg?hl=ja

>- 読込んだcsvデータをデータフレームに格納し、[**GiNZA**](https://pypi.org/project/ginza/)で行単位でテキスト処理した後、分かち書きの結果をデータフレームのカラムに反映しています。
>- 分かち書きした結果を「自然言語可視化ライブラリ [**NLPLOT**](https://pypi.org/project/nlplot/)」で取り込み、頻出語グラフと共起ネットワークを描いています。


## GoogleColabで実行するためのライブラリのインストール
以下のライブラリや辞書をインストールしました。

```Python:sudachipy, GINZAのインストール
!pip install sudachipy sudachidict_core
!pip install "https://github.com/megagonlabs/ginza/releases/download/latest/ginza-latest.tar.gz"
```
```Python:「国語研長単位」モデルのインストール
#!pip install ja_gsdluw deplacy -f https://github.com/megagonlabs/UD_Japanese-GSD/releases/tag/r2.9-NE
!pip install https://github.com/megagonlabs/UD_Japanese-GSD/releases/download/r2.9-NE/ja_gsdluw-3.2.0-py3-none-any.whl
```
```Python:pyyamlのVer.指定インストール
!pip install pyyaml==5.4.1
```
[**pyyaml**](https://pypi.org/project/PyYAML/)も[**GiNZA**](https://pypi.org/project/ginza/)のVer.指定インストールについては、以下の記事を参考にさせていただきました。

https://python-to.hateblo.jp/entry/2022/07/06/000000

```Python:GoogleColabへのGINZAインストール時のランライム再起動を不要にするおまじない
import pkg_resources, imp
imp.reload(pkg_resources)
```
このおまじないがないと、[**pyyaml**](https://pypi.org/project/PyYAML/)も[**GiNZA**](https://pypi.org/project/ginza/)もインストール後にランタイムの再起動が求められますが、このおまじないの実行により、インストールするだけ（＝ランタイムの再起動は不要）になります。

```Python:nlplotインストール
pip install nlplot
```
```Python:pyvisインストール
pip install pyvis
```
```Python:日本語フォントインストール
!apt-get -y install fonts-ipafont-gothic
```

## データ読込み
まず、データの読込みです。
データの読込みにおいては、GoogleColabのフォーム機能を利用し、データセットを選択できるようにしています。
選択肢は2つ。tweetサンプルデータ（tweet_data_sampleを選択）と、任意のcsvファイル（Uploadを選択）です。
※tweet_data_sampleは、Githubにアップしたこの記事の適用データです。

```Python:
#@title Select_Dataset { run: "auto" }
#@markdown  **<font color= "Crimson">注意</font>：かならず 実行する前に 設定してください。**</font>

dataset = 'tweet_data_sample' #@param ['tweet_data_sample','Upload']
```

以下を実行すると、datasetの選択に沿ったデータが読み込まれ、読み込んだデータをデータフレームに反映し、表示します。
```Python:
#@title データ読込み
import pandas as pd
from google.colab import files

# データの読み込み
if dataset =='Upload':
  uploaded = files.upload()#Upload
  target = list(uploaded.keys())[0]
  df = pd.read_csv(target,encoding='utf-8')

elif dataset == 'tweet_data_sample':
  file_url ='https://raw.githubusercontent.com/hima2b4/Natural-language-processing/main/TwExport_20220806_152219.csv'
  df = pd.read_csv(file_url,encoding='utf-8')

display(df)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/8ccfe902-a38c-c48d-6aea-e2e4001c8e57.png)

次に、テキスト処理するカラムを指定します。
これもGoogleColabのフォーム機能を利用し、カラム名を入力することで指定できるようにしています。
今回のデータの場合、「テキスト」カラムが対象となりますので、テキストと入力しています。
```Python:
#@title 意見カラムの名称は？ { run: "auto" }

column_name = 'テキスト' #@param {type:"raw"}
```


## テキスト処理
つぎは、テキスト処理です。
テキストから不要語を取りのぞく前処理を行った後、テキスト処理を実行して単語に分解、指定した品詞の単語を取り出し、stop_wordsに指定した単語があれば除外します。
その後、行毎に取り出した単語をデータフレームに格納しています。
品詞とstop_wordsの指定は、GoogleColabのフォーム機能を利用しています。
フォーム機能を利用するとコード操作が不要となりますので、とても便利です。

```Python:
#@title 使用する品詞とストップワードの指定
#@markdown  ※include_pos：使用する品詞，stopwords：表示させない単語 ← それぞれ任意に追加と削除が可能

include_pos = ('NOUN', 'PROPN', 'VERB', 'ADJ')#@param {type:"raw"}
stop_words = ('する', 'ある', 'ない', 'いう', 'もの', 'こと', 'よう', 'なる', 'ほう', 'いる', 'くる', 'お', 'つ', 'とき','ところ', '為', '他', '物', '時', '中', '方', '目', '回', '事', '点', 'ため') #@param {type:"raw"}
```


```Python:
#@title テキスト処理実行
import spacy
import pandas as pd
import re

# 意見に空行がある場合は削除
df[column_name] = df[column_name].replace('\n+', '\n', regex=True)
df.dropna(subset=[column_name], inplace=True)

# テキストデータの前処理
def text_preprocessing(text):
   # 改行コード、タブ、スペース削除
   text = ''.join(text.split())
   # URLの削除
   text = re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-]+', '', text)
   # メンション除去 
   text = re.sub(r'@([A-Za-z0-9_]+)', '', text) 
   # 記号の削除
   text = re.sub(r'[!"#$%&\'\\\\()*+,-./:;<=>?@[\\]^_`{|}~「」〔〕“”〈〉『』【】＆＊・（）＄＃＠。、？！｀＋￥]', '', text)

   return text

df[column_name] = df[column_name].map(text_preprocessing)

#モデルをja_gsdluw（国語研長単位）に
nlp = spacy.load("ja_gsdluw")

# 行毎に出現する単語をリストに追加
words_list=[]  #行毎の単語リスト
for doc in nlp.pipe(df[column_name]):
    sep_word=[token.lemma_ for token in doc if token.pos_ in include_pos and token.lemma_ not in stop_words]
    words_list.append(sep_word)
    #print(word_list)

#行毎の意見 → 単語に分解し、カラムに格納
df['separate_words']= [s for s in words_list]

# 参考:対象カラムに形態素解析実行、すべての形態素結果をseparateカラムに格納
#df['separate'] = [list(nlp(s)) for s in df[column_name]

```
以下は、テキスト処理を実行した後のデータフレームです。
左端の「テキスト」が元tweet。右端の「separate_words」が分かち書きした結果です。
指定した品詞のみ、長単位で処理された単語をリスト形式で格納しています。

先頭行データ「バカなやつだな・・・高く・・・」⇒「ばか,奴・・・高い・・・」と処理されています。
バカ→ばか、やつ→奴、高く→高い 等、単語抽出だけではなく、表記統一が図られていることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/6bbeb064-2bf0-6ce4-bc8c-1d799f2cc888.png)


## NLPLOTによる頻出語グラフ
次に、頻出単語のグラフ化です。
これは[**NLPLOT**](https://pypi.org/project/nlplot/) で描かせています。
表示する頻出語数はスライドバーで変更できるようにしています。（GoogleColabのフォーム機能）

```Python:
#@title NLPLOTによる頻出語グラフ

Number_of_words_shown = 30 #@param {type:"slider", min:20, max:50, step:1}

import nlplot
npt = nlplot.NLPlot(df, target_col='separate_words')

# top_nで頻出上位単語, min_freqで頻出下位単語を指定できる
#stopwords = npt.get_stopword(top_n=0, min_freq=0)

npt.bar_ngram(
    title='uni-gram',
    xaxis_label='word_count',
    yaxis_label='word',
    ngram=1,
    top_n=Number_of_words_shown,
    #stopwords=stopwords,
)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/98c2b897-f788-d80c-6e4f-fae403d9fe11.png)



## 共起ネットワーク
最後に、共起ネットワークです。
これも[**NLPLOT**](https://pypi.org/project/nlplot/)で描かせています。

[**NLPLOT**](https://pypi.org/project/nlplot/) の共起ネットワークは、min_edge_frequencyでプロットするノードの数を制限することができます。（指定数以下のエッジ(辺)しか存在しないノードはプロット対象から除外することができるとのこと）
GoogleColabのフォーム機能を利用し、min_edge_frequencyをスライドバーで変更できるようにしています。

```Python:
#@title 共起ネットワーク
#@markdown  **<font color= "Crimson">ガイド</font>：ネットワーク表示の濃淡は min_edge_frequency で変更できます。表示にみにくさを感じた場合は値を大きく、より細かく表示したい場合は値を小さくしてください。**

min_edge_frequency = 1 #@param {type:"slider", min:1, max:20, step:1}

import nlplot
import plotly
from plotly.subplots import make_subplots
from plotly.offline import iplot

npt = nlplot.NLPlot(df, target_col='separate_words')

npt.build_graph(
    #stopwords=stopwords,
    min_edge_frequency=min_edge_frequency)
# The number of nodes and edges to which this output is plotted.
# If this number is too large, plotting will take a long time, so adjust the [min_edge_frequency] well.
# >> node_size:70, edge_size:166
fig_co_network = npt.co_network(
    title='Co-occurrence network',
    sizing=100,
    node_size='adjacency_frequency',
    color_palette='hls',
    width=1100,
    height=700,
    save=False
)
iplot(fig_co_network)
```
以下は、min_edge_frequency = 2 で実行したのネットワーク図です。ノードの色は、networkxのcommunitiesで計算したコミュニティを表しているようです。ノードの色・大きさ含め、わかりやすく、視認性・可読性はとてもよいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/7f319718-08e5-f628-ce11-f6a8eab13d6b.png)

以下は同じデータを **[テキストアナリティクス入門](www.amazon.co.jp/dp/4065274109)** で紹介されているコードで実行した結果です。
こちらは、フォントサイズ、共起表示など様々な表示変更が可能です。
好みはあると思いますが、どちらもすばらしいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/d902f0d1-c6c3-647b-9f6a-d25d0c0dea68.png)


## 最後に

[**GiNZA**](https://pypi.org/project/ginza/)、[**国語研長単位モデル**](https://github.com/megagonlabs/UD_Japanese-GSD/releases/tag/r2.9-NE)、 [**NLPLOT**](https://pypi.org/project/nlplot/)、いずれもすばらしいですね。

とくに[**国語研長単位モデル**](https://github.com/megagonlabs/UD_Japanese-GSD/releases/tag/r2.9-NE) を知ってからは、WordCloudや共起ネットワークを見ることが以前よりも楽しくなりました。

日本語の単語の境界は、最小単位、短単位、長単位などの単位が規定されているそうで、いわゆる形態素の単位が短単位となるようです。

[**国語研長単位モデル**](https://github.com/megagonlabs/UD_Japanese-GSD/releases/tag/r2.9-NE) を知るまでは、短単位が当たり前でした。
「日本経済新聞」ならば、日本－経済－新聞 です。つながりが見えていれば分解されてもわかりますが、「日本」だけが独立している場合は「日本経済新聞」を連想しませんし、「経済」だけが独立している場合も「日本経済新聞」は連想しませんので、はじめから「日本経済新聞」という単位で抽出された方がすっきりします。

[**「国語研長単位に基づくUD Japanese」という論文**](https://www.anlp.jp/proceedings/annual_meeting/2022/pdf_dir/PH4-3.pdf) にそれぞれの例が紹介されていました。例をみると違いがよくわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/e4e1b0f5-d408-e84a-f28a-0596871ca7cf.png)
また、
>- **長単位は、文節境界を認定したのちに、文節内の短単位要素の結合により認定される。この文節は日本語において係り受けを付与するのに適した単位。**
>- **長単位はその用法（係り受けなどの文脈）に基づく「用法に基づく品詞」が付与される。品詞の観点からも、短単位よりも長単位のほうが UD が示す「構文的な語」に近い。**

ともありました。

また別の論文では、短単位と長単位が目的とするところについて記載がありました。

>現代日本語書き言葉均衡コーパス（BCCWJ）は「コーパスに基づく用例収集、各ジャンルの言語的特徴の解明に適した単位を設計する」方針の下、日本語研究で幅広く利用できるようにするため、①用例収集を目的とした短単位　と**②言語的特徴の解明を目的とした長単位**　を採用。
短単位は、言語の形態論的側面に着目、１単位あたりの字数は短く（少ない）、短い検索クエリで目的の用例を広く多く集めることに向いている。一方、**長単位は、短単位では捉え難い複合語をカバーすることで、短単位よりも長い特定の語に着目した用例検索に向いている。また文節を自立部と付属部にわけることで認定するため、言語の構文的な機能に着目して規定された言語単位ともいえる。**

目的に応じ、うまく使いわけることが大切な感じです。


このようなモデルが公開され、利用させていただくことができるというのは、本当にありがたいことです。
逆に 利用しない・できないというのは、あまりにもったいないことです。
方法さえわかれば誰もが利用できるものですから、みなで利用し普段の仕事をアップデートしましょうということで、この記事を締めくくりたいと思います。

## 参考

https://www.takapy.work/entry/2020/05/17/192947#co-occurrence-networks

https://srad.jp/~yasuoka/journal/653385/

https://www.anlp.jp/proceedings/annual_meeting/2022/pdf_dir/PH4-3.pdf

https://www.amazon.co.jp/%E5%AE%9F%E8%B7%B5Data-Science%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-Python%E3%81%A7%E3%81%AF%E3%81%98%E3%82%81%E3%82%8B%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%82%A2%E3%83%8A%E3%83%AA%E3%83%86%E3%82%A3%E3%82%AF%E3%82%B9%E5%85%A5%E9%96%80-KS%E6%83%85%E5%A0%B1%E7%A7%91%E5%AD%A6%E5%B0%82%E9%96%80%E6%9B%B8-%E5%89%9B%E5%8F%B2/dp/4065274109/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=&sr=

https://qiita.com/hima2b4/items/5619e617c34f588b418a

https://qiita.com/hima2b4/items/53ae54a7cd5ee08529fb

https://qiita.com/hima2b4/items/97cf372f81bc6aac4359

https://qiita.com/hima2b4/items/7125b15ad76b2f5c38e3

https://www.anlp.jp/proceedings/annual_meeting/2008/pdf_dir/PC3-1.pdf