Python|トピック分析で自分が書いた記事(乳がん検診に行くまで)を可視化する|gensim
wordcloudを作っていると、LDAというトピック分析の手法が目に止まりました。文章を単語による関連性ではなく、トピック(話題)に焦点を当て分類するもので、とても面白そうだとおもいました。
文章内の単語やフレーズの頻度や共起関係を分析し、共通の意味を持つ単語や文書をグループ化します。トピックモデル(例: 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にして可視化したのがこちらです。
それなりに、上手く分類できたのではないでしょうか。