【Python】LDAトピックモデルを使って文書クラスタリングを行う

##概要
wikipediaより，各大学にまつわる文書集合を作成，作成された文書集合に対しLDAトピックモデルを適用することで大学のクラスタリングを行う．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377231/a0c0bd91-dc2b-b53a-3263-a566e386dbed.png" width=600>


|||
|---|---|
|クエリ|大学|
|文書数|100|
|トピック数|5，10，15で実験してあとはチューニングしていく|
|期待値|体育系，私立，音楽系，文化系など同じような学科や活動のある大学が同じクラスタにまとまるのではないか？|

[**LDAトピックモデル**](https://haripo.com/articles/2016/LDA-for-Pokemon-analysis/)とは文書集合を入力に各文書に対してクラスタリングを行い，出力として各文書に対しトピック（クラスタ）への確率分布を付与する機械学習モデルのこと．LDAは教師なし学習なので教師データを必要としないメリットがある反面，いくつのトピックに分類するかの事前入力が必要になるというデメリット？（結果の精度はチューニング次第）がある．文書集合に対しそのままLDAを適用することはできないため，分かち書き（形態素解析した結果を空白文字で区切ること）した文書に対しLDAを適用する．

LDAトピックモデル

|||
|---|---|
|学習タイプ|教師なし|
|用途|文書のクラスタリング|
|今回使用するモジュール|scikit-learn|
|入力|文書集合およびトピック数|
|出力|（各文書に対して割り当てられる）トピックへの確率分布|

作業の流れ

||内容|入力|ソース|出力|
|---|---|---|---|---|
|1|大学リストを作成|[大学の一覧リンク](https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E3%81%AE%E5%A4%A7%E5%AD%A6%E4%B8%80%E8%A6%A7)|01_get_name.py|university.txt|
|2|大学リストからwikipediaテキストを抽出して文書集合を作成|university.txt|02_wikipedia_api.py|document.txt|
|3|データのクレンジング|university.txt<br>document.txt|03_cleansing.py|university_fix.txt<br>document_fix.txt|
|4|文書集合の分かち書きとBug-Of-Wordsの適用|document_fix.txt|04_mecab.py|mecabed.txt|
|5|LDAトピックモデルを適用|mecabed.txt|05_lda.py|lda_result.csv|
|6|大学名との紐づけと結果のフィルタリング|lda_result.csv<br>university_fix.txt<br>result_of_lda.csv|06_filter.py|lda_fix.csv|
|7|自動化||automation.sh|||
|追加|ポケモンをクラスタリングしてみる||




#1.大学リストを作成
[大学リスト](https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E3%81%AE%E5%A4%A7%E5%AD%A6%E4%B8%80%E8%A6%A7)から大学名リストを取得する．

```python:01_get_name.py
#coding:utf-8
import requests
import json
from bs4 import BeautifulSoup

r = requests.get('https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E3%81%AE%E5%A4%A7%E5%AD%A6%E4%B8%80%E8%A6%A7')
soup = BeautifulSoup(r.text , "html.parser")

b = open("url.csv","w")
flag = 0
for a in soup.find_all('a'):
    if '愛国学園大学' == a.text:
        flag = 1

    if flag == 1 and "大学" in a.text:
        print(a.text,a.attrs['href'])
        b.write(a.text+"\n")

    if '和洋女子大学' == a.text:
        flag = 0
b.close()
```

#2.大学リストからwikipediaテキストを抽出して文書集合を作成

wikipedia apiを使って大学名を入力にその大学のwikipedia文書を返す．
文書を１行にまとめてファイルに保存．

```python:02_wikipedia_api.py
#coding:utf-8
PURPLE  = '\033[35m'
RED     = '\033[31m'
CYAN    = '\033[36m'
GREEN   = '\033[92m'
BLUE    = '\033[94m'
ENDC    = '\033[0m'

import wikipediaapi
from time import sleep


def wiki_api(name):
    wiki_wiki = wikipediaapi.Wikipedia('ja')
    wiki = wiki_wiki.page(name)
    #print (wiki.exists())
    print (GREEN + wiki.title + ENDC)
    print (wiki.summary)
    #print (wiki.fullurl)
    #print (wiki.text)
    return wiki.text.replace("\n","")

a = open("url.csv","r")
b = open("document.csv","w")
for i in a:
    name = i.rstrip()
    text = wiki_api(name)
    b.write(text+"\n")
    sleep(0.3)
a.close()
b.close()
```

#3.データのクレンジング

中身のない行（文書）とそれにひもづく大学名を取り除く

```python:03_cleansing.py
#coding:utf-8

#入力ファイル
a = open("university.txt","r")
b = open("document.txt","r")
#出力ファイル
c = open("university_fix.txt","w")
d = open("document_fix.txt","w")

#中身のない行を検知してその行番号をリスト化
num = 0
delete_row = []
for i in b:
    num += 1
    i = i.rstrip()
    if i ==  "":
        print (num)
        delete_row.append(num)
    else:
        d.write(i+"\n")
print(delete_row)
b.close()
d.close()


#リストに含まれる行番号の大学名を取り除く
num = 0
for i in a:
    num += 1
    i = i.rstrip()
    if num in delete_row:
        print ("hit!!")
    else:
        c.write(i+"\n")
a.close()
c.close()
```

#4.文書集合の分かち書きとBug-Of-Wordsの適用

文書を形態素解析し，単語-全文書中におけるその単語の出現回数の辞書を作成．
上限閾値以上または下限閾値以下の単語を除外単語リスト化．
Bug-of-Wordsの適用．（除外単語リストに含まれる単語を文書から取り除いた分かち書き文書をファイルに逐次出力．）

```python:04_mecab.py
#coding:utf-8

import MeCab


def Mplg(text):
    output_words = []
    output_text  = ''
    m = MeCab.Tagger(' -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd')
    soup = m.parse (text)
    for row in soup.split("\n"):
        word =row.split("\t")[0]
        if word == "EOS":
            break
        else:
            pos = row.split("\t")[1]
            slice = pos.split(",")
            if len(word) > 1:
                if slice[0] == "名詞":
                    output_words.append(word)
                    output_text = output_text + ' ' + word
                elif slice[0] in [ "形容詞" , "動詞", "副詞"]:
                    if slice[5] == "基本形":
                        output_words.append(slice[-3])#活用していない原型を取得
                        output_text = output_text + ' ' + slice[-3]
    return output_words,output_text


# 単語-単語の出現回数の辞書を作成
def word_counter(words):
    for word in words:
        if word in dict_word_count.keys():
            dict_word_count[word] += 1
        else:
            dict_word_count[word] = 1

# 除外単語リストの作成（最大閾値以上および最小閾値以下の出現頻度の単語）
def make_jogai(MAX,MIN):
    for k, v in sorted(dict_word_count.items(), key=lambda x: -x[1]):
        if v >= MAX or v <= MIN:
            #print(str(k) + ": " + str(v))
            jogai.append(k)

# Bug-Of-Wordsの適用（除外単語リストに含まれる単語の除外を各文書に対して行う）
def remove_jogai_from_document(document):
    for jogai_word in jogai:
        for word in document:
            if word == jogai_word:
                document.remove(jogai_word)
    return document
    

######## main funcion #################
a = open("document_fix.txt","r")
b = open("mecabed.txt","w")

dict_word_count = {}
jogai           = []
documents       = []
MAX = 1000
MIN = 10

#単語-出現回数辞書を作成
for i in a:
    text = i.rstrip()
    output_words, output_text = Mplg(text)
    documents.append(output_words)
    word_counter(output_words)
a.close()

#2つの閾値を入力に除外単語リストを作成
make_jogai(MAX,MIN)

#各文書に対しBug-Of-Wordsを適用
for document in documents:
    jogai_document = remove_jogai_from_document(document)
    print(jogai_document)
    text = ""
    for word in jogai_document:
        text = text + word + " "
    b.write(text+"\n")
b.close()
```

#5.LDAトピックモデルを適用


```python:05_lda.py
#coding:utf-8
PURPLE  = '\033[35m'
RED     = '\033[31m'
CYAN    = '\033[36m'
GREEN   = '\033[92m'
BLUE    = '\033[94m'
ENDC    = '\033[0m'

import gensim
from gensim import corpora, models, similarities
from time import sleep

#LDAの結果（各文書における各トピックへの確率分布）から所属トピックとその確率を選定．
def find_max(topics_per_document):
    list_A = []
    list_B = []
    for q in topics_per_document:
        list_A.append(q[0])
        list_B.append(q[1])
    max_prob  = max(list_B)
    max_index = list_B.index(max_prob)
    max_topic = list_A[max_index]
    print(GREEN+str(max_topic)+','+str(max_prob)+ENDC)
    print('___________________________')
    return max_topic,max_prob

# 辞書の作成
def make_dictionary(list3):
    print(GREEN+"辞書の作成を開始します。"+ENDC)
    dictionary = corpora.Dictionary(list3)
    #dictionary.filter_extremes(no_below=1, no_above=1) # 「頻度が１回のものは無視」というのを解除
    dictionary.save_as_text('dict_ittan.txt')
    dictionary = gensim.corpora.Dictionary.load_from_text('dict_ittan.txt')
    return dictionary

# 各トピックを構成するコーパス辞書の作成
def make_corpus(dictionary,list3):
    print(GREEN+"コーパスの作成を開始します。"+ENDC)
    corpus = [dictionary.doc2bow(text) for text in list3]
    corpora.MmCorpus.serialize('cop.mm', corpus)
    corpus = gensim.corpora.TextCorpus('cop.mm')
    return corpus

#ldaの適用
def maketopic_lda(dictionary,topic_N,id_list):
    corpus2 = corpora.MmCorpus('cop.mm')
    # num of topic
    #topic_N = 2
    lda = gensim.models.ldamodel.LdaModel(corpus=corpus2, num_topics=topic_N, id2word=dictionary)
    lda.save("lda.model")
    for i in range(topic_N):
        print('TOPIC:', i, '__', lda.print_topic(i))
    index = 0
    for topics_per_document in lda[corpus2]:
        print(topics_per_document)
        page_id = id_list[index]
        max_topic,max_prob = find_max(topics_per_document)
        b.write(str(max_topic)+','+str(max_prob)+'\n')
        index += 1



#main部分
N = int(input(GREEN+'How many topics do you want?\n-->'+ENDC))
a = open('mecabed.txt','r')
b = open('result_of_lda.csv','w')
id_list = []
url_list = []
suggest_list = []
listB = []
cnt = 0
#各文書をリスト型に追加していく．
for i in a:
    content = i.rstrip()
    CNT     = content.split(' ')
    listB.append(CNT)
    id_list.append(str(cnt))
    cnt += 1
#辞書の作成
dictionary = make_dictionary(listB)
#コーパス辞書の作成
make_corpus(dictionary,listB)
#各文書をldaへ適用
maketopic_lda(dictionary,N,id_list)
a.close()
b.close() 
```
#6.大学名とLDA結果の紐付け
大学リストとladの結果の紐付けを行う．

```python:06_filter.py
#codig:utf-8

a = open("result_of_lda.csv","r")
b = open("university_fix.txt","r")
lda  = []
univ = []
for i in a:
    i = i.rstrip()
    lda.append(i)
for i in b:
    i = i.rstrip()
    univ.append(i)
a.close()
b.close()

c  = open("lda_fix.csv","w")
for i in range(0,len(univ)):
    #大学名とその大学文書のlda結果の紐付け
    c.write(univ[i]+","+lda[i]+"\n")
c.close()
```

#7.自動化
以下のようなシェルを作ることで各ソースの実行そのものを自動化する．

```shell:auto.sh
echo "start program"
python3 01_university.py
python3 02_wikipedia.py
python3 03_cleansing.py
python3 04_mecab.py
python3 05_lda.py
python3 06_filter.py
echo "Terminated all"
```

実行方法

```markdown
$ sh auto.sh
```



#ポケモンをクラスタリングしてみる!
この[ポケモン一覧ページ](https://wiki.xn--rckteqa2e.com/wiki/%E3%83%9D%E3%82%B1%E3%83%A2%E3%83%B3%E4%B8%80%E8%A6%A7)から約800ポケモンの紹介ページの本文を抽出．

- pokemon.txt（ポケモンのリスト）
- document.txt（ポケモン紹介ページの本文のリスト）


```python:01_pokemon_wiki.py
#coding:utf-8
PURPLE  = '\033[35m'
RED     = '\033[31m'
CYAN    = '\033[36m'
GREEN   = '\033[92m'
BLUE    = '\033[94m'
ENDC    = '\033[0m'



import requests
import json
from bs4 import BeautifulSoup
from time import sleep
from extractcontent3 import ExtractContent

##### ExtractContent （本文のみ抽出）########
def extractor(html):
    extractor = ExtractContent()
    opt = {"threshold":50}
    extractor.analyse(html)
    text, title = extractor.as_text()
    html, title = extractor.as_html()
    title = extractor.extract_title(html) 
    return text.replace("\n","")


####main#########
r = requests.get("https://wiki.xn--rckteqa2e.com/wiki/%E3%83%9D%E3%82%B1%E3%83%A2%E3%83%B3%E4%B8%80%E8%A6%A7")
soup = BeautifulSoup(r.text , "html.parser")
b = open("pokemon.txt","w")
c = open("document.txt","w")
flag = 0
num = 0
#ポケモン-出現回数の辞書型
dict_pokemon_num = {}
#ポケモン-ポケモンのページへの相対リンク
dict_pokemon_url = {}


# フシギダネからザルードまでのaタグを抽出して辞書に追加してく
for a in soup.find_all("a"):
    if 'フシギダネ' == a.text:
        flag = 1
    if flag == 1 and a.text not in dict_pokemon_num.keys() and a.attrs["title"] != "フォルム":
        dict_pokemon_num[a.text] = 1
        dict_pokemon_url[a.text] = a.attrs["href"]
        #print (a.text)
    elif flag == 1 and a.text in dict_pokemon_num.keys():
        #print(a.text)
        dict_pokemon_num[a.text] += 1 
    if 'ザルード' == a.text:
        flag = 0
num = 1

#作った辞書で回す
for k,v in dict_pokemon_num.items():
    if v < 2:
        r = requests.get("https://wiki.xn--rckteqa2e.com"+dict_pokemon_url[k])
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        html = r.text
        text = extractor(html)
        if text != "":
            b.write(k+"\n")
            c.write(text+"\n")
        print (CYAN,str(num),GREEN,k,BLUE,"https://wiki.xn--rckteqa2e.com"+dict_pokemon_url[k],ENDC,text)
        num += 1
        sleep(0.5)
c.close()
b.close()
```
生成された2つのファイル```pokemon.txt（ポケモンのリスト）```および```document.txt（ポケモン紹介ページの本文のリスト）```をLDAにかけることでポケモンのクラスタリングをする．以下のように実行する．

```bash
# get pokemon name and document
$ python3 01_pokemon_wiki.py
# check same lines
$ wc -l pokemon.txt
$ wc -l document.txt
# mecab
$ python3 04_mecab.py
# apply to lda with 50 topic (you can change like 20, 40, 100 ...)
$ python3 05_lda.py 50
# linking pokemon name with lda result
$ python3 06_filter.py
```

ここで以下のようにLDA実行と同時に引数としてトピックを入力する形に直します．
そして出力ファイルは入力されたトピック数に応じたファイル名で生成させます．


```python:05_lda.py
#coding:utf-8
PURPLE  = '\033[35m'
RED     = '\033[31m'
CYAN    = '\033[36m'
GREEN   = '\033[92m'
BLUE    = '\033[94m'
ENDC    = '\033[0m'

import gensim
from gensim import corpora, models, similarities
from time import sleep
import sys

def find_max(topics_per_document):
    list_A = []
    list_B = []
    for q in topics_per_document:
        list_A.append(q[0])
        list_B.append(q[1])
    max_prob  = max(list_B)
    max_index = list_B.index(max_prob)
    max_topic = list_A[max_index]
    print(GREEN+str(max_topic)+','+str(max_prob)+ENDC)
    print('___________________________')
    return max_topic,max_prob


def make_dictionary(list3):
    print(GREEN+"辞書の作成を開始します。"+ENDC)
    dictionary = corpora.Dictionary(list3)
    #dictionary.filter_extremes(no_below=1, no_above=1) # 「頻度が１回のものは無視」というのを解除
    dictionary.save_as_text('dict_ittan.txt')
    dictionary = gensim.corpora.Dictionary.load_from_text('dict_ittan.txt')
    return dictionary

def make_corpus(dictionary,list3):
    print(GREEN+"コーパスの作成を開始します。"+ENDC)
    corpus = [dictionary.doc2bow(text) for text in list3]
    corpora.MmCorpus.serialize('cop.mm', corpus)
    corpus = gensim.corpora.TextCorpus('cop.mm')
    return corpus

def maketopic_lda(dictionary,topic_N,id_list):
    corpus2 = corpora.MmCorpus('cop.mm')
    # num of topic
    #topic_N = 2
    lda = gensim.models.ldamodel.LdaModel(corpus=corpus2, num_topics=topic_N, id2word=dictionary)
    lda.save("lda.model")
    for i in range(topic_N):
        print('TOPIC:', i, '__', lda.print_topic(i))
    index = 0
    for topics_per_document in lda[corpus2]:
        print(topics_per_document)
        page_id = id_list[index]
        #url     = url_list[index]
        #suggest = suggest_list[index]
        max_topic,max_prob = find_max(topics_per_document)
        b.write(str(max_topic)+','+str(max_prob)+'\n')
        index += 1

#N = int(input(GREEN+'How many topics do you want?\n-->'+ENDC))
N = int(sys.argv[1])
a = open('mecabed.txt','r')
b = open('lda_topic_'+str(N)+'.csv','w')
id_list = []
url_list = []
suggest_list = []
listB = []
cnt = 0
for i in a:
    content = i.rstrip()
    CNT     = content.split(' ')
    listB.append(CNT)
    id_list.append(str(cnt))
    cnt += 1

dictionary = make_dictionary(listB)
make_corpus(dictionary,listB)
maketopic_lda(dictionary,N,id_list)
a.close()
b.close() 
```

紐付け作業においてldaの結果と紐づける名前のリストを```pokemon.txt```に変更します

```python:06_filter.py
#codig:utf-8
import sys
a = open("lda_topic_"+str(sys.argv[1])+".csv","r")
b = open("pokemon.txt","r")
lda  = []
univ = []
for i in a:
    i = i.rstrip()
    lda.append(i)
for i in b:
    i = i.rstrip()
    univ.append(i)
a.close()
b.close()

c  = open("lda_fix_"+str(sys.argv[1])+".csv","w")
for i in range(0,len(univ)):
    #print(univ[i]+","+lda[i])
    c.write(univ[i]+","+lda[i]+"\n")
c.close()
```

以下のようなシェルを作ることで文書集合の作成からLDA結果の紐付け作業までの工程はもちろん，あらかじめ用意したトピックリストの各トピック数でLDAへの適用，nkfコマンドでwindows用改行文字に直したファイル出力までの全てを自動化できる

```shell
#!/bin/sh
python3 01_pokemon_wiki.py
python3 04_mecab.py
topic=(20 30 40 50 60 70 80)
for i in ${topic[@]}
do
python3 05_lda.py $i
python3 06_filter.py $i
nkf -Lw lda_fix_$i.csv > lda_fix_$i\_windows.txt
done
```


例）トピック数50で実験した結果

```bash
フシギダネ,15,0.7219629
フシギソウ,19,0.9953103
フシギバナ,15,0.6368739
ヒトカゲ,45,0.45993584
リザード,20,0.9959487
リザードン,40,0.9688096
ゼニガメ,14,0.741524
カメール,20,0.9321855
カメックス,0,0.72974116
キャタピー,10,0.79301083
トランセル,40,0.9963009
バタフリー,17,0.7317033
ビードル,13,0.7982853
コクーン,40,0.4774862
スピアー,9,0.70181185
ポッポ,40,0.5321193
ピジョン,20,0.9951229
ピジョット,20,0.7100772
オニスズメ,48,0.9962139
オニドリル,15,0.89664364
アーボ,20,0.9958068
アーボック,31,0.46153808
ピカチュウ,48,0.5868726
ニドラン♀,46,0.4606429
ニドリーナ,48,0.62461305
ニドクイン,46,0.9957182
ニドラン♂,13,0.52524185
ニドリーノ,0,0.57344216
ニドキング,13,0.4955739
ピッピ,17,0.6320646
プリン,10,0.88646644
プクリン,27,0.7837478
ズバット,14,0.53271806
フーディン,12,0.9953096
メノクラゲ,20,0.5298927
ドククラゲ,45,0.7912453
コイル,48,0.9308691
ゲンガー,43,0.5107302
イワーク,19,0.9509926
ビリリダマ,2,0.8390378
サワムラー,39,0.7538181
ベロリンガ,31,0.9967319
サイホーン,40,0.9959124
ヒトデマン,3,0.9920958
ケンタロス,14,0.99652284
コイキング,13,0.99657005
ギャラドス,13,0.99780583
メタモン,10,0.9969629
イーブイ,33,0.56855327
シャワーズ,35,0.8503397
サンダース,32,0.9974726
ブースター,32,0.6798635
ポリゴン,34,0.99526477
オムナイト,13,0.9620492
オムスター,42,0.742761
プテラ,49,0.9955828
カビゴン,26,0.9637021
フリーザー,22,0.99497324
ミニリュウ,37,0.73361796
ハクリュー,40,0.8853342
カイリュー,4,0.44743374
ミュウツー,17,0.34704843
ミュウ,10,0.5251674
マグマラシ,47,0.99354994
オオタチ,48,0.9455403
レディバ,46,0.9957898
レディアン,20,0.96617955
イトマル,22,0.67386144
アリアドス,20,0.9622127
クロバット,3,0.98338825
ピチュー,6,0.8323217
キレイハナ,14,0.83911973
ウソッキー,20,0.99674034
ヤンヤンマ,8,0.9533309
アンノーン,6,0.9945525
ソーナンス,17,0.46323615
キリンリキ,40,0.7138145
ノコッチ,15,0.9949208
ブルー,13,0.5510142
グランブル,13,0.9690133
ハリーセン,20,0.9940946
ハッサム,0,0.7733671
オクタン,13,0.6209819
デリバード,48,0.9966547
ドーブル,44,0.891903
バンギラス,32,0.3585982
ルギア,15,0.99458337
ホウオウ,3,0.8711686
セレビィ,19,0.93274736
キモリ,24,0.98537207
アチャモ,21,0.5262336
ミズゴロウ,32,0.50814164
ラグラージ,14,0.9943324
ケムッソ,6,0.9947268
サーナイト,17,0.9958453
アメタマ,26,0.96620476
キノガッサ,42,0.7286921
ヤルキモノ,18,0.7262381
テッカニン,0,0.9937572
ヌケニン,47,0.81371534
ヤミラミ,18,0.6504367
ホエルコ,15,0.99690664
ホエルオー,5,0.99663085
ナックラー,10,0.8168851
ビブラーバ,33,0.98727167
ユレイドル,22,0.53424084
アノプス,44,0.98184943
カゲボウズ,13,0.99594927
トドゼルガ,16,0.8088447
ダンバル,10,0.9160265
レジロック,8,0.7045759
レジアイス,28,0.99057597
ラティアス,33,0.9786892
レックウザ,15,0.9174846
ジラーチ,49,0.9258955
デオキシス,6,0.66840494
ヒコザル,17,0.98468286
ゴウカザル,21,0.96370155
ポッチャマ,20,0.9934198
コロボーシ,35,0.9623025
コリンク,43,0.9795815
ミツハニー,37,0.9841902
パチリス,6,0.9888609
ミミロル,17,0.96732986
リーシャン,22,0.9683847
ドータクン,46,0.9940926
ミカルゲ,22,0.97720546
ルカリオ,2,0.9607935
グレッグル,6,0.87244475
ベロベルト,31,0.6662545
エレキブル,30,0.43199724
メガヤンマ,40,0.6671086
リーフィア,32,0.7847004
グレイシア,20,0.5182468
ユクシー,2,0.9662031
エムリット,1,0.9807788
アグノム,2,0.59351045
マナフィ,17,0.9956433
ダイケンキ,37,0.9909224
レパルダス,20,0.72597754
ムンナ,6,0.91358566
ムシャーナ,13,0.8132166
タブンネ,10,0.892756
アーケン,37,0.80979604
アーケオス,4,0.66560894
ゾロア,33,0.9533284
ゾロアーク,49,0.9898953
メブキジカ,26,0.98759276
シュバルゴ,46,0.8314548
プルリル,20,0.9946722
シビビール,41,0.6883133
シビルドン,36,0.97608906
オノノクス,14,0.99433136
アギルダー,15,0.75311595
コジョンド,44,0.96499485
サザンドラ,8,0.99203116
ウルガモス,11,0.957388
レシラム,39,0.9954609
ゼクロム,39,0.901257
ランドロス,3,0.995699
キュレム,17,0.64577836
ケルディオ,10,0.9928422
ハリマロン,40,0.55416167
ハリボーグ,46,0.9859963
ブリガロン,32,0.5161448
フォッコ,20,0.9889865
テールナー,20,0.98599607
マフォクシー,36,0.9900995
ケロマツ,46,0.9702205
ゲコガシラ,33,0.9853691
ゲッコウガ,27,0.9902948
ホルード,41,0.9915509
ファイアロー,32,0.99486613
ビビヨン,11,0.869856
メェークル,37,0.77537984
ゴーゴート,29,0.61031944
ニャスパー,6,0.69631577
ウデッポウ,3,0.52186996
アマルルガ,37,0.99299717
ニンフィア,30,0.9960317
ルチャブル,46,0.99509263
デデンネ,6,0.48202303
メレシー,42,0.9923991
ヌメイル,29,0.9458429
バケッチャ,8,0.9932405
パンプジン,47,0.9951234
ゼルネアス,46,0.9937942
イベルタル,29,0.9815082
ジガルデ,18,0.896523
ディアンシー,29,0.99147695
ボルケニオン,10,0.9925167
モクロー,15,0.7050662
フクスロー,4,0.6129972
ジュナイパー,48,0.9952174
ニャビー,48,0.9919648
ニャヒート,40,0.766105
ガオガエン,39,0.92533803
アシレーヌ,48,0.65440434
ツツケラ,18,0.5399727
ケララッパ,27,0.5270585
デカグース,32,0.91468173
アゴジムシ,44,0.53425646
デンヂムシ,34,0.9636924
ケケンカニ,40,0.980781
イワンコ,3,0.99387413
ルガルガン,33,0.97486955
ヨワシ,26,0.9966876
ヒドイデ,40,0.99162155
ドヒドイデ,1,0.95545053
ドロバンコ,20,0.9941973
オニシズクモ,34,0.95332974
ラランテス,5,0.96079546
ヌイコグマ,33,0.98514503
アマージョ,13,0.6474947
ヤレユータン,26,0.983102
ナゲツケサル,36,0.48651266
コソクムシ,17,0.9865712
グソクムシャ,13,0.5196565
シロデスナ,2,0.69976693
ナマコブシ,5,0.9711746
タイプ:ヌル,46,0.80124456
シルヴァディ,15,0.9970553
メテノ,44,0.9872699
ネッコアラ,5,0.9782184
バクガメス,49,0.9760962
ミミッキュ,18,0.6626353
ハギギシリ,1,0.95545083
ジャラランガ,20,0.897408
カプ・コケコ,26,0.9782183
カプ・テテフ,11,0.9836651
コスモウム,46,0.82960135
ソルガレオ,32,0.9920937
ルナアーラ,37,0.48105103
ウツロイド,4,0.6531933
マッシブーン,40,0.6313842
フェローチェ,35,0.40877625
デンジュモク,1,0.9918322
テッカグヤ,20,0.5979422
カミツルギ,1,0.9905755
アクジキング,48,0.99375606
マギアナ,42,0.84285724
ベベノム,30,0.98860246
アーゴヨン,15,0.9980971
ツンデツンデ,4,0.3930683
ズガドーン,8,0.64648956
ゼラオラ,20,0.9907511
メルタン,8,0.49884778
メルメタル,8,0.96526825
サルノリ,13,0.98999625
バチンキー,19,0.985145
ゴリランダー,47,0.9839329
ヒバニー,3,0.65007854
ラビフット,12,0.7778221
エースバーン,46,0.9867527
メッソン,21,0.5107635
ジメレオン,13,0.9815049
インテレオン,41,0.8123586
ホシガリス,12,0.981153
ヨクバリス,38,0.9807821
ココガラ,19,0.98310024
アオガラス,46,0.98392695
アーマーガア,12,0.81915045
サッチムシ,6,0.53771853
レドームシ,46,0.98115116
イオルブ,6,0.9900888
クスネ,7,0.9807834
フォクスライ,25,0.98338866
ヒメンカ,18,0.98922545
ワタシラガ,29,0.9899945
ウールー,23,0.9889878
バイウールー,9,0.835603
カムカメ,29,0.98039883
カジリガメ,13,0.99182975
ワンパチ,38,0.98657143
パルスワン,17,0.9052254
タンドン,37,0.8986008
トロッゴン,34,0.68553525
セキタンザン,41,0.98957306
カジッチュ,6,0.990198
アップリュー,0,0.9899936
タルップル,14,0.9091824
スナヘビ,15,0.52667546
サダイジャ,38,0.98957235
ウッウ,40,0.99169064
サシカマス,37,0.982179
カマスジョー,46,0.9815043
エレズン,46,0.98832726
ストリンダー,28,0.72928894
ヤクデ,31,0.9896812
マルヤクデ,9,0.9877442
タタッコ,35,0.9815039
オトスパス,30,0.9821802
ヤバチャ,4,0.9893459
ポットデス,38,0.993285
ミブリム,38,0.98366344
テブリム,13,0.39960432
ブリムオン,35,0.9896786
ベロバー,38,0.98366266
ギモー,41,0.9824983
オーロンゲ,38,0.99083877
タチフサグマ,38,0.9545473
ニャイキング,38,0.99140185
サニゴーン,1,0.98115236
ネギガナイト,46,0.9859936
バリコオル,27,0.9865731
デスバーン,41,0.9818503
マホミル,20,0.4912323
マホイップ,33,0.98860246
タイレーツ,37,0.9900996
バチンウニ,29,0.9791473
ユキハミ,18,0.45753637
モスノウ,35,0.8924432
イシヘンジン,13,0.98114985
コオリッポ,14,0.65582013
イエッサン,0,0.990574
モルペコ,43,0.46835557
ゾウドウ,17,0.9795796
ダイオウドウ,40,0.9872707
パッチラゴン,37,0.98934656
パッチルドン,25,0.84072286
ウオノラゴン,37,0.98989254
ウオチルドン,42,0.79864275
ジュラルドン,42,0.9916892
ドラメシヤ,1,0.6310524
ドロンチ,47,0.98366416
ドラパルト,46,0.8493541
ムゲンダイナ,20,0.50825346
ザルード,40,0.98280305
```

#参考
- https://developer.smartnews.com/blog/2013/08/19/lda-based-channel-categorization-in-smartnews/
- https://www.ism.ac.jp/~daichi/lectures/H24-TopicModel/ISM-2012-TopicModels-daichi.pdf