大切なものは、見えるところに。

乳がんかな?と疑い始めてからの日々を、振り返りながら、ゆるやかに綴ります。

Python|トピック分析で自分が書いた記事(乳がん検診に行くまで)を可視化する|gensim


wordcloudを作っていると、LDAというトピック分析の手法が目に止まりました。文章を単語による関連性ではなく、トピック(話題)に焦点を当て分類するもので、とても面白そうだとおもいました。

qiita.com


文章内の単語やフレーズの頻度や共起関係を分析し、共通の意味を持つ単語や文書をグループ化します。トピックモデル(例: Latent Dirichlet Allocation)を使用して、文章の主題の抽出や関連性の特定を行います。
また興味深いのは、LDAでは文書上には現れない潜在的共起性も同時に考慮することができるという点です。これはどういうことなのでしょうか?普段の目線では見つからない、埋もれたつながりを見つけることができるのでしょうか?分かりませんがとにかくやってみます。

以前行ったネットワーク分析に続いて、LDAについて理解するというより、ただできるかどうか試してみました。そして皆さんと同じように、分析結果をwordcloudで可視化しました。
LDAの分析には、gensimのldamodelを使用しました。

環境

pip3 show gensim
Name: gensim
Version: 4.2.0



トピック数を自分で決める必要があります。
以前のネットワーク分析で9つのグループに分けられたので、今回はトピック数を9にしてみたいと思います。
yoihinomemo.hateblo.jp
コードは長いので折りたたみました。


ここを押してコードを表示する

from janome.charfilter import *
from janome.analyzer import Analyzer
from janome.tokenizer import Tokenizer
from janome.tokenfilter import *
import gensim
from gensim import corpora, models
import numpy as np
from tqdm import tqdm
from collections import Counter
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from wordcloud import WordCloud
import codecs
import logging
import math
from PIL import Image

my=open("ファイル名.txt").read()

#my_split=my.split("\n")
my_split=my.split("。")

#word=my.replace("_","")

tokenizer = Tokenizer("janome簡易辞書.csv", udic_type="simpledic", udic_enc="utf8")  
#tokenizer = Tokenizer()  
corpus=[]

for my_list in my_split:
    tokens = tokenizer.tokenize(my_list)
    
    corpus.append([token.base_form for token in tokens
                  if token.part_of_speech.split(',')[0]=="名詞" or token.part_of_speech.split(',')[0]=="動詞"or token.part_of_speech.split(',')[0]=="形容詞"])


remove_words = ["'\n","ん","した","たのは","ない","して","ま","ており","てい","う","よう","ついて","おいて","ああ","こと","ている","する","になっ","たん",
               "ござる","もあり","ある","もの","おる","いる","ないと","なる","わかる","おっしゃる","なさる","そう","られる","いく","かける",
                "いう","?","くれる","それ","思う","これ","の","下さる","くださる","いたす","先生","やる","できる","中","見る","言う","さ","れる"]

# for文で一文字ずつ削除
for remove_words in remove_words:
    corpus = [corpus for corpus in corpus if remove_words not in corpus]


dictionary = gensim.corpora.Dictionary(corpus)
corpus0 = [dictionary.doc2bow(text) for text in corpus]
print(corpus0)

tfidf = gensim.models.TfidfModel(corpus0)
corpus_tfidf = tfidf[corpus0]

print(corpus_tfidf)

#Metrics for Topic Models
start = 2
limit = 22
step = 1

coherence_vals = []
perplexity_vals = []

for n_topic in tqdm(range(start, limit, step)):
    lda_model = gensim.models.ldamodel.LdaModel(corpus=corpus0, id2word=dictionary, num_topics=n_topic, random_state=0)
    perplexity_vals.append(np.exp2(-lda_model.log_perplexity(corpus0)))
    coherence_model_lda = gensim.models.CoherenceModel(model=lda_model, texts=corpus, dictionary=dictionary, coherence='c_v')
    coherence_vals.append(coherence_model_lda.get_coherence())

# LDA Model
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
lda_model = gensim.models.ldamodel.LdaModel(corpus=corpus0, id2word=dictionary, num_topics=9, alpha = 'symmetric' , random_state=0)

print(lda_model.print_topics())
print(lda_model.print_topics(),file=codecs.open('分析結果.txt', 'a', 'utf-8'))

    
#トピック内の単語とその確率を取得
topic_num = 9
for i in range(topic_num):
    items = [(dictionary[t[0]], t[1]) for t in lda_model.get_topic_terms(i, topn = 30)]

    print(f"topic_id = {i},items = {items}")
    #print(f"topic_id = {i},items = {items}",file=codecs.open('/home/mikaishii917/labo/ldaspterms.txt', 'w', 'utf-8'))
    #print(f"topic_id = {i},freq = {topic_freq[i]},items = {items}",file=codecs.open('分析結果.txt', 'w', 'utf-8'))


#文書内の単語がどのトピックへどの程度の確率で該当するか
for i in range(len(corpus0)):
    dts = lda_model.get_document_topics(corpus0[i], per_word_topics = True)

    for dt in dts[2]:
        item = dictionary[dt[0]]
        print(f"corpus = {i}, item = {item}, topic_id = {dt[1]}")
        #print(f"corpus = {i}, item = {item}, topic_id = {dt[1]}",file=codecs.open('分析結果.txt', 'w', 'utf-8'))


    
# save model
#lda_model.save('lda.model')

#print(lda.print_topics())

# test
N = sum(count for doc in corpus0 for id, count in doc)
print("N: ",N)

perplexity = np.exp2(-lda_model.log_perplexity(corpus0))
print("perplexity:", perplexity)


# KL divergence
def calc_topic_distances(m, topic):
    import numpy as np

    def kldiv(p, q):
        distance = np.sum(p * np.log(p / q))
        return distance

    t = m.state.get_lambda()
    for i, p in enumerate(t):
        t[i] = t[i] / t[i].sum()

    base = t[topic]
    distances = [(i_p[0], kldiv(base, i_p[1])) for i_p in enumerate(t) if i_p[0] != topic]
    return distances

def plot_distance_matrix(m):
    import numpy as np
    import matplotlib.pylab as plt

    # make distance matrix
    mt = []
    for i in range(m.num_topics):
        d = calc_topic_distances(m, i)
        d.insert(i, (i, 0))  # distance between same topic
        d = [_d[1] for _d in d]
        mt.append(d)

    mt = np.array(mt)

    # plot matrix
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.set_aspect("equal")
    plt.imshow(mt, interpolation="nearest", cmap=plt.cm.ocean)
    plt.yticks(range(mt.shape[0]))
    plt.xticks(range(mt.shape[1]))
    plt.colorbar()
    plt.savefig("図の作成.png")

plot_distance_matrix(lda_model)
plt.show()

# WordCloud_show
fig, axs = plt.subplots(ncols=2, nrows = math.ceil(lda_model.num_topics/2), figsize=(16,20))
axs = axs.flatten()

def circle(size=300):
    d = size
    r = size // 2
    m = r * 0.86
    x, y = np.ogrid[:d, :d]
    mask = (x - r) ** 2 + (y - r) ** 2 > m ** 2
    return 255 * mask.astype(int)

def color_func(word, font_size, position, orientation, random_state, font_path):
    return 'darkturquoise'

for i, t in enumerate(range(lda_model.num_topics)):

    x = dict(lda_model.show_topic(t,30))
    im = WordCloud(font_path='/フォントの場所.ttf',
        background_color='black',
        mask=circle(size=300),    
        color_func=color_func,
        max_words=4000,
        width=500, height=300,
        random_state=0
    ).generate_from_frequencies(x)
    axs[i].imshow(im.recolor(colormap= 'Paired_r' , random_state=244), alpha=0.98)
    axs[i].axis('off')
    axs[i].set_title('Topic '+str(t))
    plt.savefig('図の作成.png') 

    
# vis
plt.tight_layout()
plt.show()

トピック分析LDAにより、可視化された図





各トピックの単語と内容。



topic 0
(0, '0.057*"痛み" + 0.057*"私" + 0.057*"2020年" + 0.057*"2021年" + 0.057*"乳がん検診" + 0.057*"しれる" + 0.057*"何" + 0.057*"無駄" + 0.057*"乳房" + 0.057*"消える"'),

乳がん検診の必要性・意義について、痛みや私の経験を通じて考えています。

topic 1
(1, '0.116*"行く" + 0.116*"乳がん検診" + 0.012*"痛み" + 0.012*"大変" + 0.012*"ブレスト・アウェアネス" + 0.012*"乳がん自覚症状" + 0.012*"健康診断" + 0.012*"自覚症状" + 0.012*"乳がん" + 0.012*"しれる"')

乳がん検診への呼びかけや乳がん自覚症状についての情報、また、健康診断やブレスト・アウェアネスに関する話題も含まれています。

topic 2
(2, '0.130*"健康診断" + 0.013*"痛み" + 0.013*"乳がん自覚症状" + 0.013*"乳がん検診" + 0.013*"大変" + 0.013*"ブレスト・アウェアネス" + 0.013*"乳房" + 0.013*"乳がん" + 0.013*"私" + 0.013*"自覚症状"')

健康診断や乳がん自覚症状についての情報が含まれています。また、乳房や乳がんに関する話題も取り上げられています。

topic 3
(3, '0.067*"痛み" + 0.067*"ます" + 0.067*"胸" + 0.067*"疑う" + 0.067*"乳がん" + 0.067*"みたい" + 0.067*"凹む" + 0.067*"一般的" + 0.067*"伴う" + 0.007*"乳がん自覚症状"')

乳がんに関連する一般的な症状や疑わしい症状について触れられています。特に、胸の痛みや凹みが乳がんの自覚症状として伴う可能性があることが言及されています。

topic 4
(4, '0.065*"乳房" + 0.065*"凹む" + 0.034*"ぼる" + 0.034*"赤茶色" + 0.034*"ひきつれる" + 0.034*"くい" + 0.034*"分泌物" + 0.034*"ただれる" + 0.034*"赤い" + 0.034*"陥没"')

乳房に関連する変化や乳房の異常についての情報が含まれています。具体的には、凹みや分泌物の変化、赤い色などが言及されています。

topic 5
(5, '0.060*"痛み" + 0.060*"私" + 0.060*"感じる" + 0.060*"くい" + 0.060*"症状" + 0.060*"しれる" + 0.060*"ぼる" + 0.060*"無関係" + 0.060*"乳がん検診" + 0.060*"受ける"')

私自身の感覚や症状についての情報が含まれています。私の感じ方や乳がん検診の重要性について考えています。

topic 6
(6, '0.130*"乳がん自覚症状" + 0.013*"痛み" + 0.013*"大変" + 0.013*"乳がん検診" + 0.013*"健康診断" + 0.013*"ブレスト・アウェアネス" + 0.013*"乳房" + 0.013*"私" + 0.013*"引く" + 0.013*"乳がん"')

乳がん自覚症状や健康診断に関する情報が含まれています。乳房や乳がんについても触れられています。

topic 7
(7, '0.054*"乳がん検診" + 0.054*"知る" + 0.054*"乳がん" + 0.054*"予約" + 0.054*"翌日" + 0.054*"自分" + 0.054*"気" + 0.054*"日" + 0.054*"付ける" + 0.054*"取る"')

乳がん検診の予約や個人の経験についての情報が含まれています。自分で乳がんについて知識を深め、検診の予約を取る重要性が語られています。

topic 8
(8, '0.071*"しこり" + 0.071*"他" + 0.071*"自覚症状" + 0.071*"乳がん" + 0.071*"皆さん" + 0.038*"何" + 0.038*"私" + 0.038*"ご存知" + 0.038*"知る" + 0.038*"事"')

乳がんに関連するしこりや自覚症状についての情報が含まれています。また、一般的な知識や個人の経験についても言及されています。


重複している単語があるので、見た目でグループにまとめてみます。




3つのトピックに別れました。
乳がん自覚症状として「痛み」はあまり感じられることはないと一般的には言われています。
しかし私にとっては最も気になった重要な乳がん自覚症状でした。
そのため、結構強調して記事を書いたのかもしれません。


トピック数を3にして可視化したのがこちらです。

それなりに、上手く分類できたのではないでしょうか。