# Autor cf
#!/usr/bin/env Python
# coding=utf-8
'''
1、从csv或xlsx中读数据
2、使用sklearn库
'''
import pyLDAvis.sklearn
import pyLDAvis
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import LatentDirichletAllocation
import pandas as pd
import jieba
import re
import os
# 本地 csv 文件,可以是本地文件,也可以是远程文件
source_path = '舆情.xlsx'
# 文本 csv 文件里面文本所处的列名,注意这里一定要填对,要不然会报错的!
document_column_name = '文本'
# 输出主题词的文件路径
top_words_csv_path = 'top-topic-words.csv'
# 输出各文档所属主题的文件路径
predict_topic_csv_path = 'document-distribution.csv'
# 可视化 html 文件路径
html_path = 'document-lda-visualization-top.html'
# 选定的主题数
n_topics = 3
# 要输出的每个主题的前 n_top_words 个主题词数
n_top_words = 20
# 去除无意义字符的正则表达式
pattern = u'[\\s\\d,.<>/?:;\'\"[\\]{}()\\|~!\t"@#$%^&*\\-_=+a-zA-Z,。\n《》、?:;“”‘’{}【】()…¥!—┄-]+'
url = 'https://raw.githubusercontents.com/Micro-sheep/Share/main/zhihu/answers.csv'
def top_words_data_frame(model: LatentDirichletAllocation,
tf_idf_vectorizer: TfidfVectorizer,
n_top_words: int) -> pd.DataFrame:
'''
求出每个主题的前 n_top_words 个词
Parameters
----------
model : sklearn 的 LatentDirichletAllocation
tf_idf_vectorizer : sklearn 的 TfidfVectorizer
n_top_words :前 n_top_words 个主题词
Return
------
DataFrame: 包含主题词分布情况
'''
rows = []
feature_names = tf_idf_vectorizer.get_feature_names()
for topic in model.components_:
top_words = [feature_names[i]
for i in topic.argsort()[:-n_top_words - 1:-1]]
rows.append(top_words)
columns = [f'topic {i+1}' for i in range(n_top_words)]
df = pd.DataFrame(rows, columns=columns)
return df
def predict_to_data_frame(model: LatentDirichletAllocation, X: np.ndarray) -> pd.DataFrame:
'''
求出文档主题概率分布情况
Parameters
----------
model : sklearn 的 LatentDirichletAllocation
X : 词向量矩阵
Return
------
DataFrame: 包含主题词分布情况
'''
matrix = model.transform(X)
columns = [f'P(topic word {i+1})' for i in range(len(model.components_))]
df = pd.DataFrame(matrix, columns=columns)
return df
def predict_new_text(text_to_predict_list):
#################################### 1、加载训练好的LDA和tf-idf
lda = joblib.load('lda-joblib.pkl')
tf_idf_vectorizer = joblib.load('tf_idf_vectorizer.pkl')
# features = tf_idf_vectorizer.get_feature_names()
# print(features)
# print(len(features))
# todo 模型预测:在主题-词的概率分布不变的前提下,推理文档-主题概率分布
################################### 2、文本预处理:切词
# 文本先预处理,再在词频模型中结构化,然后将结构化的文本list传入LDA主题模型,判断主题分布。
cuts = []
for text in text_to_predict_list:
cut_text = [' '.join(jieba.lcut(text))]
cuts += cut_text
print(cuts)
################################### 3、把文字转tf-idf向量表示
text_tf_idf = tf_idf_vectorizer.transform(cuts).toarray()
print(text_tf_idf.shape)
print(text_tf_idf)
# 打印非零元素index
nonzero = np.nonzero(text_tf_idf)
print(nonzero)
print(np.array(nonzero).ndim)
################################### 4、计算要预测的每个文本的主题概率分布情况
text_predict_df = predict_to_data_frame(lda, text_tf_idf)
print('预测文本的主题概率分布情况:')
print(text_predict_df)
#################################### 0、文件预处理
if not os.path.exists(source_path):
print(f'请用浏览器打开 {url} 并下载该文件(如果没有自动下载,则可以在浏览器中按键盘快捷键 ctrl s 来启动下载)')
os.exit()
df = (
pd.read_excel(source_path)
.drop_duplicates()
.rename(columns={
document_column_name: 'text'
}))
# 去重、去缺失、分词
df['cut'] = (
df['text']
.apply(lambda x: str(x))
.apply(lambda x: re.sub(pattern, ' ', x))
.apply(lambda x: " ".join(jieba.lcut(x)))
)
#################################### 1、构造 tf-idf
tf_idf_vectorizer = TfidfVectorizer()
tf_idf = tf_idf_vectorizer.fit_transform(df['cut'])
lda = LatentDirichletAllocation(
n_components=n_topics,
max_iter=50,
learning_method='online',
learning_offset=50,
random_state=0)
#################################### 2、使用 tf_idf 语料训练 lda 模型
lda.fit(tf_idf)
#################################### 3、计算 n_top_words 个主题词
top_words_df = top_words_data_frame(lda, tf_idf_vectorizer, n_top_words)
# 保存 n_top_words 个主题词到 csv 文件中
top_words_df.to_csv(top_words_csv_path, encoding='utf-8-sig', index=None)
# 转 tf_idf 为数组,以便后面使用它来对文本主题概率分布进行计算
X = tf_idf.toarray()
#################################### 4、计算完毕主题概率分布情况
predict_df = predict_to_data_frame(lda, X)
# 保存文本主题概率分布到 csv 文件中
predict_df.to_csv(predict_topic_csv_path, encoding='utf-8-sig', index=None)
#################################### 5、模型 + 分词向量 保存
from sklearn.externals import joblib
joblib.dump(lda, 'lda-joblib.pkl')
joblib.dump(tf_idf_vectorizer, 'tf_idf_vectorizer.pkl')
# todo 模型预测:在主题-词的概率分布不变的前提下,推理文档-主题概率分布
#################################### 6、新文本预测
# 文本先预处理,再在词频模型中结构化,然后将结构化的文本list传入LDA主题模型,判断主题分布。
text_to_predict_list = ['称交警(016636)因我骑电动车没有走人行道暴力执法,用手拉我,将我按在花坛旁边,报警人不满,请交警核实处理。']
predict_new_text(text_to_predict_list )
#################################### 7、使用 pyLDAvis 进行可视化
data = pyLDAvis.sklearn.prepare(lda, tf_idf, tf_idf_vectorizer)
pyLDAvis.save_html(data, html_path)
# 清屏
os.system('clear')
# 浏览器打开 html 文件以查看可视化结果
os.system(f'start {html_path}')
print('本次生成了文件:',
top_words_csv_path,
predict_topic_csv_path,
html_path)
#################################### 8、打印模型的收敛效果
print(lda.perplexity(tf_idf)) # 收敛效果