先看一则新闻的文本,涉及两个词“奔驰”和“道歉”
名词“奔驰”的词频为:正文10次,标题1次
动词“道歉”的词频为:正文1次,标题1次
这则新闻的意思就是“奔驰道歉”,但这两个词的词频表现出完全不同的情况。
再来看算法部分,文本挖掘算法有很多,但同一个算法,理论上是没有办法同时处理上面的例子,高词频特征和低词频特征在上面的例子中,同样重要。
进一步分析:高词频在上例中体现的是“主体”特征,词性为名词
低词频在上例中体现的是“意图”特征,词性为动词
结合上例理解就是,“意图”我们通常不需要反复强调,只需要表达出来即可,如果通篇充斥着“道歉”字样,这篇文章就很难看了,就算是强调,也只会说“再一次诚挚道歉”,这件事就到顶了,再说就不诚恳了。而“主体”通常会多次出现,因为事件的描述、事件的解决办法等等文字,都需要“主体”名词承担主语角色。
如果采用调用python包的做法的话,这里就需要用到“词频-正向文本频率”和“词频-逆向文本频率”两种算法逻辑。
这里,我没有采用调包的做法,函数实现如下:
def mergeTopic(self, strDate):
day_baiduDF = self.getPredayData(strDate)
day_topicList = list(set(day_baiduDF['topic'].tolist()))
topicGroup = []
for topic in day_topicList:
item = {}
topicDF = day_baiduDF.loc[day_baiduDF['topic'] == topic]
topic_v_list = self.seg_vWord(topic)
topic_n_list = self.seg_nWord(topic)
topic_words_n_v = []
for title in topicDF['title'].tolist():
title_v_list = self.seg_vWord(title)
title_n_list = self.seg_nWord(title)
topic_v_list = topic_v_list + title_v_list
topic_n_list = topic_n_list + title_n_list
topic_words_n_v = topic_n_list + topic_v_list
dict_topic_words = dict(Counter(topic_words_n_v))
topic_v_count = dict(Counter(topic_v_list))
topic_n_count = dict(Counter(topic_n_list))
sort_v_list = sorted(topic_v_count.items(), key=lambda d: d[1], reverse=True)
sort_n_list = sorted(topic_n_count.items(), key=lambda d: d[1], reverse=True)
in_list = []
iv_list = []
for i_n in sort_n_list[0:3]:
if len(i_n[0]) > 1:
in_list.append(i_n[0])
for i_v in sort_v_list[0:5]:
# if len(i_v[0]) > 1:
iv_list.append(i_v[0])
item['topic'] = topic
item['n_list'] = in_list
item['v_list'] = iv_list
item['word_dict'] = dict_topic_words
print(item)
topicGroup.append(item)
topic_result_DF = pd.DataFrame(topicGroup).set_index('topic')
eventGroup = []
_day_topicList = []
for topic in day_topicList:
item_event = {}
event_list = []
event_list.append(topic)
n_list = topic_result_DF.loc[topic]['n_list']
v_list = topic_result_DF.loc[topic]['v_list']
dict_topic_words = topic_result_DF.loc[topic]['word_dict']
print(event_list, n_list, v_list)
for _topic in day_topicList:
if topic == _topic:
continue
else:
_n_list = topic_result_DF.loc[_topic]['n_list']
_v_list = topic_result_DF.loc[_topic]['v_list']
n = [n for n in n_list if n in _n_list]
v = [v for v in v_list if v in _v_list]
if len(n) > 0 and len(v) > 0:
event_list.append(_topic)
item_event['topic'] = topic
item_event['event_list'] = event_list
item_event['word_dict'] = dict_topic_words
x_list = [x for x in event_list if x in _day_topicList]
if len(x_list) == 0:
eventGroup.append(item_event)
_day_topicList = _day_topicList + event_list
else:
continue
event_day_DF = pd.DataFrame(eventGroup).set_index('topic')
event_file = 'event_' + strDate
self.disk.SaveData(event_day_DF, event_file)
return event_day_DF
另外,停用词和用户词典需要精细化处理,需要停用大多数语气词或文本来源等与文本意义无关的词汇,如需提高分词识别率,还需要强化用户词典,这个做法,也是实际自然语言处理工作中经常采用的做法。
速度问题:
如果采用常规的聚类算法,在数据量较大的文本处理工作中,确定类别数量的计算量将会特别大,上面函数采用的方法是,每2小时定时任务中执行一次聚类,但该聚类算法只对一天的数据进行处理,而且不计算类别数量,而只采用较为粗暴,类似文本编辑距离的方法进行处理,但有个技巧就是把名词和动词分别进行了处理,实际效果不比调python包差,但速度快很多。
缺点是,只对一天数据聚类后,保存下来就不再动了,以后计算排名时,仅对已聚类好的话题进行增量文本的实时计算,有可能会漏掉同一事件中新增的话题。这是在实时监控中的妥协。如果是做历史事件分析时,就不能采用这种做法,但历史事件分析对速度没有要求,可以承受较为复杂的计算。