最近师姐有一个小任务需要我协助帮忙,我觉得收获挺大。如何简单计算每篇分析师访问纪要的情感?
1. 数据
师姐下载了国泰数据库中的“分析师调研纪要”内容,希望能够通过计算出调研纪要中的情感。实验数据存在了百度网盘中,包括部分调研纪要+正面词汇+负面词汇+停止词。图1:国泰安访谈纪要数据
提取码:n3ad图2:数据一览
2. 基本思想
针对每个上市公司的每一篇调研纪要(图一红框),师姐提出了两个需求。
一个是直接统计该篇纪要中出现的正负面词汇的词语,除以该纪要的总字数,得到情感度,即为
第二个是基于所有纪要、计算每个词的tfidf权重值,情感为
其中I是指示函数,weight是利用TFIDf训练得到的权重。
整体流程如下。图3:整体流程
3. 代码
3.1 分词
首先,我们需要将每段访谈纪要去除没有用的词(成为停止词,例如“的”“因为”“所以”),并且将长长的句子拆开。
import pandas as pd
import numpy as np
import jieba
import re
from sklearn.feature_extraction.text import TfidfVectorizer
这里是用到了jieba分词包,和sklearn中的tfidf文字向量化包。
class GetTwoImportantLists:
def __init__(self, record_url, stopword_url, meetings_rec):
self.record_url = record_url
self.stopword_url = stopword_url
# 如果record_url是csv格式,这里改成pd.read_csv
self.record = pd.read_excel(self.record_url)
self.meetings_rec = meetings_rec
# 抽取出纪要列,放在content中。
self.content = self.record[self.meetings_rec]
# 导入停止词,放在stop_word的list中。
self.stop_word = []
#匹配不是中文的字符,例如"\n"这种没有意义的字符。
self.zh = re.compile("[^\u4e00-\u9fa5]")
# 停止词放在了txt中,因此使用open打开。
with open(self.stopword_url, 'r', encoding='utf-8') as f:
for i in f:
# 剔除非中文字符。
i = self.zh.sub('', i)
self.stop_word.append(i)
def segmentWord(self):
c1 = []
for item in self.content:
if type(item) == str and len(item) > 0:
# 将非中文符号删了
string1 = self.zh.sub('', item)
a = list(jieba.cut(string1))
#删去停止词
a1 = [item for item in a if item not in stop_word]
b = ' '.join(a1)
c1.append(b)
else:
c1.append("无内容")
return c1
def save_lists(self, output):
"""为了防止出现数据丢失,可以先把拆好的结果存起来"""
c1 = self.segmentWord()
#c1转成excel存起来
c1 = pd.DataFrame(c1, columns=['seg_words'])
c1.to_excel(output)
print("Save is done.")
生成实例:
gtl1 = GetTwoImportantLists(r'...\调研纪要.xlsx',
r'....\stopwords.txt',
'MeetingMinutes')
同时,停止词如下:
gtl1.stop_word
['一', '一', '一一', '一下', '一个', '一些', '一何', '一切', '一则', '一则通过', '一天', '一定', '一方面', '一旦', '一时', '一来'........]
c1 = gtl1.segmentWord()
c1格式如下:
print(c1)
['公司 发展 战略 经营 业绩 回答 投资者 提问 看待 互联网 金融 银行 冲击 回答 我行 业务 结构 同年 相比 收入 结构 资产 质量 改善 我行 长期 发展 目标 战略 清晰 专业化 集约化 综合 金融 互联网 金融 凸显 公司 零售 投行...承诺书',
'公司 发展 战略 经营 业绩 回答 投资者 提问 收入 强劲 增长 驱动力 未来 可持续性 答中收 增长 短短 两年 半占 不到 增长....承诺书',...]
为了防止拆开的记录丢失的话,c1可以通过excel格式存放。那么直接运行save_lists。
gtl1.save_lists(r'...temp.xlsx')
3.2 训练权重+计算情感
class Weights_and_Senti:
def __init__(self, input_url, pos_word_url, neg_word_url):
# input_url是上面拆词储存的链接,pos_word_url是正面词链接,neg_word_url是负面词链接
self.input_url = input_url
self.pos_word_url = pos_word_url
self.neg_word_url = neg_word_url
pos_word = pd.read_csv(self.pos_word_url, engine = 'python')
self.pos_word = list(pos_word['postive'])
neg_word = pd.read_csv(self.neg_word_url, engine = 'python')
self.neg_word = list(neg_word['negative'])
def read_chars(self):
c1 = pd.read_excel(self.input_url)
c1 = list(c1['seg_words'])
return c1
def get_split_words(self, c1):
# c2区别于c1,c2用于计算情感。
c2 = []
for item in c1:
item = item.split(' ')
c2.append(item)
return c2
def core_weights(self, article):
vectorizer = TfidfVectorizer()
features = vectorizer.fit_transform([article])
word_list = vectorizer.get_feature_names() # 获取词袋模型的所有词
weight_list = features.toarray()
result = pd.DataFrame(features.toarray(),
columns = vectorizer.get_feature_names()).T.reset_index()
result.columns = ['word', 'weight']
ww_dict = dict()
length = len(result)
for i in range(length):
word = result.iloc[i, 0]
weight = result.iloc[i, 1]
ww_dict[word] = weight
for kw in poswords:
if kw not in word_list:
ww_dict[kw] = 0
for kw in negwords:
if kw not in word_list:
ww_dict[kw] = 0
return result, ww_dict
def get_weights(self, c1, temp_url):
# 快速把c1组成一个完整的文本
with open(temp_url, 'w') as fp:
for item in c1:
try:
fp.write(item)
except TypeError:
continue
with open(temp_url, 'r') as fp:
c1 = fp.read()
result, ww_dict = self.core_weights(c1)
return result, ww_dict
def senti_score(self, one_rec, pon, won):
if won == 'weight':
total_s = 0
for w in one_rec:
try:
total_s += ww_dict[w]
except:
total_s += 0
if pon == 'pos':
pos_word_num = 0
for kw in poswords:
pos_word_num += one_rec.count(kw) * ww_dict[kw]
return pos_word_num / total_s
else:
neg_word_num = 0
for kw in negwords:
neg_word_num += one_rec.count(kw) * ww_dict[kw]
return neg_word_num / total_s
else:
if pon == 'pos':
pos_word_num = 0
for kw in poswords:
pos_word_num += one_rec.count(kw)
return pos_word_num / len(one_rec)
else:
neg_word_num = 0
for kw in negwords:
neg_word_num += one_rec.count(kw)
return neg_word_num / len(one_rec)
def schedule(self, temp_url):
c1 = self.read_chars()
print("c1 is done.")
c2 = self.get_split_words(c1)
print("c2 is done.")
result, ww_dict = self.get_weights(c1, temp_url)
print("Dictionary is done.")
df = pd.DataFrame(columns=['pos_senti', 'neg_senti', 'pos_w_senti', 'neg_w_senti'])
pos_list = []
neg_list = []
pos_w_list = []
neg_w_list = []
for cont in c2:
if len(cont) > 0:
pos = self.senti_score(cont, 'pos', 'not')
neg = self.senti_score(cont, 'neg', 'not')
w_pos = self.senti_score(cont, 'pos', 'weight')
w_neg = self.senti_score(cont, 'neg', 'weight')
else:
pos = 0
neg = 0
w_pos = 0
w_neg = 0
pos_list.append(pos)
neg_list.append(neg)
pos_w_list.append(w_pos)
neg_w_list.append(w_neg)
df['pos_senti'] = pos_list
df['neg_senti'] = neg_list
df['pos_w_senti'] = pos_w_list
df['neg_w_senti'] = neg_w_list
return df
解释一下。read_chars是把刚刚分好词的结果c1读入。get_split_words是利用c1生成真正分开的结果c2。
c2的格式如下:
[['公司', '发展', '战略', '经营', '业绩', '回答', '投资者', '提问', '看待', '互联网', '金融', '银行', '冲击', '回答', '我行', '业务', ...., '承诺书'], ['公司', '发展', '战略', '经营', ...., '承诺书'],....,]
c1是将每篇纪要拆开中间使用空格连接、最后每篇纪要作为一个记录放在list中;c2是将每篇纪要拆开,纪要本身是一个list,再存在总体的list中。c1用于训练tfidf词语权重、因此要作为一个完整的整体,c2用于计算情感中的词频、因此每个词都要拆开存。
get_weights是把c1中所有独立的纪要迅速合成一个完整的string,然后放在core_weights中训练词语的权重,得到result和ww_dict分别是dataframe格式的字典和dict格式的字典。
senti_score把某一篇纪要计算出某个类型的情感。如果是选择加权计算情感的方式,那么将词语权重放入计算;如果直接计算情感,那么就是统计正负面情感词频占总字数的比重。
schedule是将整个流程完整的走完。
生成实例:
w1 = Weights_and_Senti(r'...\temp.xlsx',
r'...正面词典.csv',
r'...负面词典.csv')
读取c1:
c1 = w1.read_chars()
利用c1训练权重:
result, ww_dict = w1.get_weights(c1, r'...temp.txt')
result和ww_dict均是词语+权重组成的字典,分别如下:图4:result格式图5:ww_dict格式
以上的步骤,通过schedule一步到位、计算情感:
df = w1.schedule(r'...temp.txt')图6:情感列表
那么可以看到,通过加权得到的结果右边两列,确实能将正负面情绪差异扩大。
最后将该情绪得分和原始的表格进行合并。因为是按照顺序计算的情感,因此直接通过merge、index对应合并,即得到每篇访谈纪要的情感得分。
谢谢~~