LDA(Latent Dirichlet Allocation) でのトピック抽出 でレビュー分析

レビューの分析方法をまとめる。

import os
import glob
import sys
from datetime import (datetime, date, timedelta)
import logging
import re
import shutil
import tempfile

import pandas as pd
import numpy as np
from scipy.sparse.csc import csc_matrix
from scipy.sparse import csr_matrix

from sklearn.metrics.pairwise import (
    cosine_similarity,
    euclidean_distances,
)
from sklearn import preprocessing
from sklearn.decomposition import NMF
from sklearn.cluster import KMeans


from IPython.display import display, HTML
from pandas.tools.plotting import table
import matplotlib.pyplot as plt


from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer
from sklearn.decomposition import NMF, LatentDirichletAllocation


import pickle
import os

import MeCab

n_samples = 500
n_features = 1000
n_components = 5
n_top_words = 20


def print_top_words(model, feature_names, n_top_words):
    for topic_idx, topic in enumerate(model.components_):
        message = "Topic #%d: " % topic_idx
        message += " ".join([feature_names[i]
                             for i in topic.argsort()[:-n_top_words - 1:-1]])
        print(message)
    print()

def lda_print_top_words(components, feature_names, n_top_words):
    for topic_idx, topic in enumerate(components):
        message = "Topic #%d: " % topic_idx
        message += " ".join([feature_names[i]
                             for i in topic.argsort()[:-n_top_words - 1:-1]])
        print(message)
    print()


def is_bigger_than_min_tfidf(term, terms, tfidfs):
    '''
    [term for term in terms if is_bigger_than_min_tfidf(term, terms, tfidfs)]で使う
    list化した、語たちのtfidfの値のなかから、順番に当てる関数。
    tfidfの値がMIN_TFIDFよりも大きければTrueを返す
    '''
    if tfidfs[terms.index(term)] > MIN_TFIDF:
        return True
    return False


def tfidf(values):
    # analyzerは文字列を入れると文字列のlistが返る関数
    vectorizer = TfidfVectorizer(analyzer=stems, min_df=1, max_df=50, max_features=n_features)
    corpus = [v for v in values]

    x = vectorizer.fit_transform(corpus)

    return x, vectorizer  # xはtfidf_resultとしてmainで受け取る


def countvec(values):
    # analyzerは文字列を入れると文字列のlistが返る関数
    vectorizer = CountVectorizer(analyzer=stems, min_df=1, max_df=50, max_features=n_features)
    corpus = [v for v in values]

    x = vectorizer.fit_transform(corpus)

    return x, vectorizer  # xはtfidf_resultとしてmainで受け取る


def _split_to_words(text, to_stem=False):
    """
    入力: 'すべて自分のほうへ'
    出力: tuple(['すべて', '自分', 'の', 'ほう', 'へ'])
    """
    tagger = MeCab.Tagger('mecabrc')  # 別のTaggerを使ってもいい
    mecab_result = tagger.parse(text)
    info_of_words = mecab_result.split('\n')
    words = []
    for info in info_of_words:
        # macabで分けると、文の最後に’’が、その手前に'EOS'が来る
        if info == 'EOS' or info == '':
            break
            # info => 'な\t助詞,終助詞,*,*,*,*,な,ナ,ナ'
        info_elems = info.split(',')
        # 6番目に、無活用系の単語が入る。もし6番目が'*'だったら0番目を入れる
        if info_elems[6] == '*':
            # info_elems[0] => 'ヴァンロッサム\t名詞'
            words.append(info_elems[0][:-3])
            continue
        if to_stem:
            # 語幹に変換
            words.append(info_elems[6])
            continue
        # 語をそのまま
        words.append(info_elems[0][:-3])
    words_set = set(words).difference(stop_words_set)
    return list(words_set)


def words(text):
    words = _split_to_words(text=text, to_stem=False)
    return words


def stems(text):
    stems = _split_to_words(text=text, to_stem=True)
    return stems

# レビュー分析用
def review_topickeyword_nmf(item_id):
    df_item_id = df_id.loc[df_id['review_item_id'] == item_id]
    if df_item_id['review_item_id'].count() == 0:
        print('None Item')
    print('itemid: {0}, review_count: {1}'.format(item_id, df_item_id['review_item_id'].count()))
    df_test = df_item_id[['review_rating', 'review_title', 'review_comment']]
    df_test[['review_comment']] = df_test[['review_comment']].applymap(lambda x: '{}'.format(x.replace('\\n', '\n')))
    df_test[['review_rating']] = df_test[['review_rating']].astype('int64')
    for num in range(5, 0, -1):
        df_test_part = df_test.loc[df_test['review_rating'] == num]
        print('review_rating: {0} count: {1}'.format(num, df_test_part['review_rating'].count()))
        y = df_test_part.values[:n_samples, 0]
        tfidf_result, tfidf_vectorizer = tfidf(df_test_part.values[:n_samples, 2])
        tf_result, tf_vectorizer = countvec(df_test_part.values[:n_samples, 2])
        nmf = NMF(n_components=n_components, random_state=1,
              beta_loss='kullback-leibler', solver='mu', max_iter=1000, alpha=.1,
              l1_ratio=.5).fit(tfidf_result)
        tfidf_feature_names = tfidf_vectorizer.get_feature_names()
        print_top_words(nmf, tfidf_feature_names, n_top_words)


def review_topickeyword_lda(item_id):
    df_item_id = df_id.loc[df_id['review_item_id'] == item_id]
    if df_item_id['review_item_id'].count() == 0:
        print('None Item')
    print('itemid: {0}, review_count: {1}'.format(item_id, df_item_id['review_item_id'].count()))
    df_test = df_item_id[['review_rating', 'review_title', 'review_comment']]
    df_test[['review_comment']] = df_test[['review_comment']].applymap(lambda x: '{}'.format(x.replace('\\n', '\n')))
    df_test[['review_rating']] = df_test[['review_rating']].astype('int64')
    for num in range(5, 0, -1):
        df_test_part = df_test.loc[df_test['review_rating'] == num]
        print('review_rating: {0} count: {1}'.format(num, df_test_part['review_rating'].count()))
        y = df_test_part.values[:n_samples, 0]
        tf_result, tf_vectorizer = countvec(df_test_part.values[:n_samples, 2])
        lda = LatentDirichletAllocation(n_components=n_components, max_iter=5,
                                    learning_method='batch',
                                    learning_offset=50.,
                                    random_state=0)
        document_topics = lda.fit_transform(tf_result)
        tf_feature_names = tf_vectorizer.get_feature_names()
        print_top_words(lda, tf_feature_names, n_top_words)
# stopwordで精度上がる
stop_words = """
こちら
それなり
なか

手段
かたち


前回

など
まで
すね

まし
0
いや
もう
よそ


ごろ
はじめ



今回
多く
本当




あちら
また





もの
どちら


カ所
6
どこか
いま
未満

せる

思う


ヶ月

時点
ハイ

8



ぜんぶ
ヶ所
そう
年生
みつ





くせ

見る

何人
関係
結局
箇所
5
する



3
自分
ごっちゃ
ある

しかた

ほか

あな

こっち
ため

以後


って
とても
ちゃん



なん


感じ
ひと
場合
ヵ所




これ

かく


すぎる
やつ


同じ
簿

2
向こう
しまう
いろいろ


だめ


下記
カ月
どれ
わけ

まま


7
幾つ

なに
時間
だけ
です
おれ

もと
もん
あなた

られる

新た
てる







確か
ところ

すべて
言う
たい
なかば


のに


4
きた
その後


以上

できる
それぞれ
みなさん

そっち



これら
ほう
いつ
近く
うち
ます
ので

あれ
ぺん

おまえ
かやの
ごと


よう



わたし
ひとつ





まとも
ぶり



いる
とき
自体
毎日
なんて


ない



どっか

さまざま
本当に



いくつ




ほど
みたい

てん


たび

1



ここ
様々

以下

さらい
全部

上記
彼女
ヵ月
箇月
どっち
それ

はるか
以降

でも
あまり
どこ

すか
べつ
こと
がら



そこ
違い

半ば
けど
がい

あっち



あたり

ふく

一つ
たくさん
より
さん


ずつ

はず
れる
なる






9



しよう

我々
おおまか

しか






以前

ちゃ
とおり
あそこ

まさ


名前


あと




特に


へん





扱い

みんな
そちら
みる

から


たち
そで
方法
くらい
"""
stop_words_set = set(stop_words.strip().split('\n'))
# stop_words_list = list(stop_words_list)
# print(stop_words_list)