文本挖掘 订阅
《文本挖掘(英文版)》是 2009年8月人民邮电出版社出版的图书,作者是费尔德曼。该书中涵盖了核心文本挖掘操作、文本挖掘预处理技术、分类、聚类、信息提取、信息提取的概率模型、预处理应用、可视化方法、链接分析、文本挖掘应用等内容,很好地结合了文本挖掘的理论和实践。 [1] 展开全文
《文本挖掘(英文版)》是 2009年8月人民邮电出版社出版的图书,作者是费尔德曼。该书中涵盖了核心文本挖掘操作、文本挖掘预处理技术、分类、聚类、信息提取、信息提取的概率模型、预处理应用、可视化方法、链接分析、文本挖掘应用等内容,很好地结合了文本挖掘的理论和实践。 [1]
信息
ISBN
9787115205353
作    者
费尔德曼
定    价
69元
书    名
文本挖掘(英文版)
出版时间
2009年08月
开    本
16
出版社
人民邮电出版社
文本挖掘(英文版)内容简介
《文本挖掘(英文版)》是一部文本挖掘领域名著,作者为世界知名的权威学者。《文本挖掘(英文版)》非常适合文本挖掘、信息检索领域的研究人员和实践者阅读,也适合作为高等院校计算机及相关专业研究生的数据挖掘和知识发现等课程的教材。
收起全文
精华内容
参与话题
问答
  • 文本挖掘

    2019-09-22 00:42:47
    文本挖掘 1. 文本挖掘 1.1. 什么是文本挖掘 文本挖掘指的是从文本数据中获取有价值的信息和知识,它是数据挖掘中的一种方法。文本挖掘中最重要最基本的应用是实现文本的分类和聚类,前者是有监督的挖掘算法,后...

     文本挖掘

     

    1.      文本挖掘

    1.1.    什么是文本挖掘

    文本挖掘指的是从文本数据中获取有价值的信息和知识,它是数据挖掘中的一种方法。文本挖掘中最重要最基本的应用是实现文本的分类和聚类,前者是有监督的挖掘算法,后者是无监督的挖掘算法。

    文本挖掘是一个多学科混杂的领域,涵盖了多种技术,包括数据挖掘技术、信息抽取、信息检索,机器学习、自然语言处理、计算语言学、统计数据分析、线性几何、概率理论甚至还有图论。

     

    1.2.    文本挖掘有什么用

    1、文本分类

    文本分类是一种典型的机器学习方法,一般分为训练和分类两个阶段。文本分类一般采用统计方法或机器学习来实现。

     

    2、文本聚类

    文本聚类是一种典型的无监督式机器学习方法,聚类方法的选择取决于数据类型。

    首先,文档聚类可以发现与某文档相似的一批文档,帮助知识工作者发现相关知识;其次,文档聚类可以将一类文档聚类成若干个类,提供一种组织文档集合的方法;再次,文档聚类还可以生成分类器以对文档进行分类。

    文本挖掘中的聚类可用于:提供大规模文档内容总括;识别隐藏的文档间的相似度;减轻浏览相关、相似信息的过程。

     

    3、信息检索

    主要是利用计算机系统的快速计算能力,从海量文档中寻找用户需要的相关文档。

     

    4、信息抽取

    信息抽取是把文本里包含的信息进行结构化处理,变成表格一样的组织形式。输入信息抽取系统的是原始文本,输出的是固定格式的信息。

     

    5、自动文摘

    利用计算机自动的从原始文档中提取出文档的主要内容。互联网上的文本信息、机构内部的文档及数据库的内容都在成指数级的速度增长,用户在检索信息的时候,可以得到成千上万篇的返回结果,其中许多是与其信息需求无关或关系不大的,如果要剔除这些文档,则必须阅读完全文,这要求用户付出很多劳动,而且效果不好。

    自动文摘能够生成简短的关于文档内容的指示性信息,将文档的主要内容呈现给用户,以决定是否要阅读文档的原文,这样能够节省大量的浏览时间。简单地说自动文摘就是利用计算机自动地从原始文档中提取全面准确地反映该文档中心内容的简单连贯的短文。

    自动文摘具有以下特点:

    (1)自动文摘应能将原文的主题思想或中心内容自动提取出来。

    (2)文摘应具有概况性、客观性、可理解性和可读性。

    (3)可适用于任意领域。

    按照生成文摘的句子来源,自动文摘方法可以分成两类,一类是完全使用原文中的句子来生成文摘,另一类是可以自动生成句子来表达文档的内容。后者的功能更强大,但在实现的时候,自动生成句子是一个比较复杂的问题,经常出现产生的新句子不能被理解的情况,因此目前大多用的是抽取生成法。

     

    6、自动问答

    自动问答是指对于用户提出的问题,计算机可以自动的从相关资料中求解答案并作出相应的回答。自动问答系统一般包括 3 个组成部分:问题分析、信息检索和答案抽取。

     

    7、机器翻译

    利用计算机将一种源语言转变为另一种源语言的过程。

     

    8、信息过滤

    指计算机系统可以自动的进行过滤操作,将满足条件的信息保留,将不满足条件的文档过滤掉。信息过滤技术主要用于信息安全领域。

     

    9、自动语音识别

    自动语音识别就是将输入计算机的自然语言转换成文本表示的书面语。

     

    1.3.    文本挖掘操作步骤

    1、获取文本

    现有数据导入,或者爬虫获取网络文本。

     

    2、文本预处理

    指剔除噪声文档以改进挖掘精度,或者在文档数量过多时仅选取一部分样本以提高挖掘效率。

    例如网页中存在很多不必要的信息,比如说一些广告,导航栏,html、js代码,注释等等并不需要的信息,可以删除掉。如果是需要正文提取,可以利用标签用途、标签密度判定、数据挖掘思想、视觉网页块分析技术等等策略抽取出正文。

     

    3、文本的语言学处理

    (1)分词

    接下来的步骤就是分词。

    (2)词性标注

    同时也可以使用词性标注。通过很多分词工具分出来的出会出现一个词,外加该词的词性。比如说啊是语气助词。

     

    (3)去除停用词

    比如说句号、是、的等词,没有什么实际的意义。然而这些词在所有的文章中都大量存在,并不能反应出文本的意思,可以处理掉。当然针对不同的应用还有很多其他词性也是可以去掉的,比如形容词等。

     

    4、文本的数学处理-特征提取

    我们希望获取到的词汇,既能保留文本的信息,同时又能反映它们的相对重要性。如果对所有词语都保留,维度会特别高,矩阵将会变得特别稀疏,严重影响到挖掘结果。所以这就需要特征提取。

    特征选取的方式有4种:

    (1)用映射或变换的方法把原始特征变换为较少的新特征;

    (2)从原始特征中挑选出一些最具代表性的特征;

    (3)根据专家的知识挑选最有影响的特征;

    (4)用数学的方法进行选取,找出最具分类信息的特征,这种方法是一种比较精确的方法,人为因素的干扰较少,尤其适合于文本自动分类挖掘系统的应用。

     

    5、分类聚类

    经过上面的步骤之后,我们就可以把文本集转化成一个矩阵。我们能够利用各种算法进行挖掘,比如说如果要对文本集进行分类,分类常用的方法有:简单贝叶斯分类法,矩阵变换法、K-最近邻参照分类算法以及支持向量机分类方法等。

     

    聚类方法通常有:层次聚类法、平面划分法、简单贝叶斯聚类法、K-最近邻聚类法、分级聚类法等。

    6、数据可视化

    最后一步当然就是数据结构的可视化展示,通过合适的可视化图形生动形象展示,让读者听众更容易理解你所要表达的信息。

    文本可视化最常用的图形就是词云。

     

    四、文本挖掘工具

    1、python语言jieba、gensim、sklearn、WordCloud和matplotlib包

    2、R语言jieba、tm、tmcn、Rwordseg和wordcloud包

    3、SAS text miner

    4、SPSS Text Mining

     

    转载于:https://www.cnblogs.com/wodeboke-y/p/11562856.html

    展开全文
  • 文本挖掘、中文文本、R语言、语料库、统计模型、
  • Python大数据分析系列博客,包括网络爬虫、可视化分析、GIS地图显示、情感分析...这篇文章将抓取微博话题及评论信息,采用SnowNLP进行简单的情感分析及文本挖掘,包括随时间的情感分布。希望这篇基础性文章对您有所帮助

    思来想去,虽然很忙,但还是挤时间针对这次肺炎疫情写个Python大数据分析系列博客,包括网络爬虫、可视化分析、GIS地图显示、情感分析、舆情分析、主题挖掘、威胁情报溯源、知识图谱、预测预警及AI和NLP应用等。希望该系列线上远程教学对您有所帮助,也希望早点战胜病毒,武汉加油、湖北加油、全国加油。待到疫情结束樱花盛开,这座英雄的城市等你们来。

    首先说声抱歉,最近一直忙着学习安全知识,其他系列文章更新较慢,已经有一些人催更了。言归正传,前文分享了疫情相关新闻数据爬取,并进行中文分词处理及文本聚类、LDA主题模型分析。这篇文章将抓取微博话题及评论信息,采用SnowNLP进行简单的情感分析及文本挖掘,包括随时间的情感分布。希望这篇基础性文章对您有所帮助,也非常感谢参考文献中老师的分享,一起加油,战胜疫情!如果您有想学习的知识或建议,可以给作者留言~

    在这里插入图片描述

    在这里插入图片描述

    代码下载地址:https://github.com/eastmountyxz/Wuhan-data-analysis
    CSDN下载地址:https://download.csdn.net/download/Eastmount/12239638

    同时推荐前面作者另外五个Python系列文章。从2014年开始,作者主要写了三个Python系列文章,分别是基础知识、网络爬虫和数据分析。2018年陆续增加了Python图像识别和Python人工智能专栏。

    在这里插入图片描述

    前文阅读:
    [Pyhon疫情大数据分析] 一.腾讯实时数据爬取、Matplotlib和Seaborn可视化分析全国各地区、某省各城市、新增趋势
    [Pyhon疫情大数据分析] 二.PyEcharts绘制全国各地区、某省各城市疫情地图及可视化分析
    [Pyhon疫情大数据分析] 三.新闻信息抓取及词云可视化、文本聚类和LDA主题模型文本挖掘



    一.微博话题数据抓取

    该部分内容参考及修改我的学生兼朋友“杨友”的文章,也推荐博友们阅读他的博客,给予支持。作为老师,最开心的事就是看到学生成长和收获。他的博客地址:python爬虫爬取微博之战疫情用户评论及详情

    微博网址: https://m.weibo.cn/


    1.爬虫解析

    第一步,进入微博审查元素,定位评论对应节点,后续抓取评论信息。
    进入微博后,点击《战疫情》主题下,并随便选择一个动态进行分析,我就选择了“央视新闻网”的一条动态“https://m.weibo.cn/detail/4471652190688865”进行分析。

    在这里插入图片描述

    我们刚打开该话题的时候,它显示的是187条评论,但是在审查时可以看到文章中的20个div,并且每个div中装载一条评论,每个页面原始就只能显示20条评论。

    在这里插入图片描述

    当我们把鼠标不断向下滑动的过程中,网页元素中的div也不断随评论的增加而增加,当活动到底部时,所有评论都加载出来了。初步判断该网页属于ajax加载类型,所以先就不要考虑用requests请求服务器了。


    第二步,获取Ajax加载的动态链接数据,通过发布id定位每条话题。
    这些数据都是通过Ajax动态加载的,点击到《战疫情》主题,发现它的URL并没有变化,具体浏览几篇文章后发现,它的的部分URL都是统一的,文章链接 = ‘https://m.weibo.cn/detail/’+发布时的id,可以通过刚找到的 id 在浏览器中拼接试试。

    在这里插入图片描述

    比如下图所示的微博内容。比如:https://m.weibo.cn/detail/4472846740547511

    在这里插入图片描述


    第三步,下拉网页动态刷新数据,发现获取多个page的规律。
    接下来是获取它下一个加载数据的通道,同样是通过抓包的方式获取,不断的下拉网页,加载出其他的Ajax数据传输通道,再进行对比。可以很明显的看出,它的当前链接就只是带上了 “&page=当前数字” 的标签,并且每次加载出18篇动态文章。

    在这里插入图片描述

    查看元素信息如下图所示,每个page显示18个微博话题。

    在这里插入图片描述

    第四步,调用json.loads()函数或在线网站解析Json数据。
    拿到的数据是json格式,再提取信息前需要把str文本转化为json数据,进行查找,可以使用json库查看它的结构 ,也可以在线json解析查看它的结构,更推荐在线解析,方法结构比较清晰。

    在线解析后的结果,简单的给它打上标签,每一个等级为一块,一级包括二级和三级,二级包括三级… 然后通过前面的标签进行迭代输出,索引出来。在线网站:https://www.json.cn/

    在这里插入图片描述


    第五步,获取每条微博的ID值。
    调用方法如下,然后把拿到的id加在https://m.weibo.cn/detail/ 的后面就可以访问具体的文章了。

    import requests
    api_url = 'https://m.weibo.cn/api/feed/trendtop?containerid=102803_ctg1_600059_-_ctg1_600059'
    reponse = requests.get(api_url)
    for json in reponse.json()['data']['statuses']:
        comment_ID = json['id']
        print (comment_ID)
    

    在这里插入图片描述

    此时提取所有链接代码如下:

    import requests,time
    from fake_useragent import UserAgent
    
    comment_urls = []
    def get_title_id():
        '''爬取战疫情首页的每个主题的ID'''
        for page in range(1,3):# 这是控制ajax通道的量
            headers = {
                "User-Agent" : UserAgent().chrome #chrome浏览器随机代理
            }
            time.sleep(2)
            # 该链接通过抓包获得
            api_url = 'https://m.weibo.cn/api/feed/trendtop?containerid=102803_ctg1_600059_-_ctg1_600059&page=' + str(page)
            print (api_url)
            rep = requests.get(url=api_url, headers=headers)
            for json in rep.json()['data']['statuses']:
                comment_url = 'https://m.weibo.cn/detail/' + json['id']
                print (comment_url)
                comment_urls.append(comment_url)
    get_title_id()
    

    输出结果如下:

    https://m.weibo.cn/api/feed/trendtop?containerid=102803_ctg1_600059_-_ctg1_600059&page=1
    https://m.weibo.cn/detail/4472725286834498
    https://m.weibo.cn/detail/4472896510211624
    https://m.weibo.cn/detail/4472846892243445
    https://m.weibo.cn/detail/4472901455185821
    https://m.weibo.cn/detail/4472856669039437
    https://m.weibo.cn/detail/4472897055545751
    https://m.weibo.cn/detail/4472891342667233
    https://m.weibo.cn/detail/4472879381479272
    https://m.weibo.cn/detail/4472889565122923
    https://m.weibo.cn/detail/4472884950738226
    https://m.weibo.cn/detail/4472883461527008
    https://m.weibo.cn/detail/4472904014106917
    ......
    

    第六步,调用requests ajax 爬取更多信息。
    现在需要获取更多的信息,如用户id、性别之类的,这不是selenium可以完成的操作了,还得使用ajax的方式获取json数据,提取详细的信息。这里有个字段是max_id, 我们需要在上一个json文件底部找到该值。

    目标:话题链接、话题内容、楼主ID、楼主昵称、楼主性别、发布日期、发布时间、转发量、评论量、点赞量、评论者ID、评论者昵称、评论者性别、评论日期、评论时间、评论内容

    • 第一个通道

    在这里插入图片描述

    • 现在可以预测下一个max_id
      成功的通过上一个通道拿到了下一个通道的max_id,现在就可以使用ajax加载数据了。
      在这里插入图片描述


    2.爬虫完整代码

    # -*- coding: utf-8 -*-
    import requests,random,re
    import time
    import os
    import csv
    import sys
    import json
    import importlib
    from fake_useragent import UserAgent
    from lxml import etree
    
    importlib.reload(sys)
    startTime = time.time() #记录起始时间
    
    #--------------------------------------------文件存储-----------------------------------------------------
    path = os.getcwd() + "/weiboComments.csv"
    csvfile = open(path, 'a', newline='', encoding = 'utf-8-sig')
    writer = csv.writer(csvfile)
    #csv头部
    writer.writerow(('话题链接','话题内容','楼主ID', '楼主昵称', '楼主性别','发布日期',
                     '发布时间', '转发量','评论量','点赞量', '评论者ID', '评论者昵称',
                     '评论者性别', '评论日期', '评论时间','评论内容')) 
    
    #设置heades
    headers = {
        'Cookie': '_T_WM=22822641575; H5_wentry=H5; backURL=https%3A%2F%2Fm.weibo.cn%2F; ALF=1584226439; MLOGIN=1; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9W5RJaVYrb.BEuOvUQ8Ca2OO5JpX5K-hUgL.FoqESh-7eKzpShM2dJLoIp7LxKML1KBLBKnLxKqL1hnLBoMceoBfeh2EeKBN; SCF=AnRSOFp6QbWzfH1BqL4HB8my8eWNC5C33KhDq4Ko43RUIzs6rjJC49kIvz5_RcOJV2pVAQKvK2UbAd1Uh6j0pyo.; SUB=_2A25zQaQBDeRhGeBM71cR8SzNzzuIHXVQzcxJrDV6PUJbktAKLXD-kW1NRPYJXhsrLRnku_WvhsXi81eY0FM2oTtt; SUHB=0mxU9Kb_Ce6s6S; SSOLoginState=1581634641; WEIBOCN_FROM=1110106030; XSRF-TOKEN=dc7c27; M_WEIBOCN_PARAMS=oid%3D4471980021481431%26luicode%3D20000061%26lfid%3D4471980021481431%26uicode%3D20000061%26fid%3D4471980021481431',
        'Referer': 'https://m.weibo.cn/detail/4312409864846621',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.100 Safari/537.36',
        'X-Requested-With': 'XMLHttpRequest'
    }
    
    #-----------------------------------爬取战疫情首页的每个主题的ID------------------------------------------
    comments_ID = []
    def get_title_id():
        for page in range(1,21):  #每个页面大约有18个话题
            headers = {
                "User-Agent" : UserAgent().chrome #chrome浏览器随机代理
            }
            time.sleep(1)
            #该链接通过抓包获得
            api_url = 'https://m.weibo.cn/api/feed/trendtop?containerid=102803_ctg1_600059_-_ctg1_600059&page=' + str(page)
            print(api_url)
            rep = requests.get(url=api_url, headers=headers)
            #获取ID值并写入列表comment_ID中
            for json in rep.json()['data']['statuses']:
                comment_ID = json['id'] 
                comments_ID.append(comment_ID)
    
    #-----------------------------------爬取战疫情每个主题的详情页面------------------------------------------         
    def spider_title(comment_ID):
        try:
            article_url = 'https://m.weibo.cn/detail/'+ comment_ID
            print ("article_url = ", article_url)
            html_text = requests.get(url=article_url, headers=headers).text
            #话题内容
            find_title = re.findall('.*?"text": "(.*?)",.*?', html_text)[0]
            title_text = re.sub('<(S*?)[^>]*>.*?|<.*? />', '', find_title) #正则匹配掉html标签
            print ("title_text = ", title_text)
            #楼主ID
            title_user_id = re.findall('.*?"id": (.*?),.*?', html_text)[1]
            print ("title_user_id = ", title_user_id)
            #楼主昵称
            title_user_NicName = re.findall('.*?"screen_name": "(.*?)",.*?', html_text)[0]
            print ("title_user_NicName = ", title_user_NicName)
            #楼主性别
            title_user_gender = re.findall('.*?"gender": "(.*?)",.*?', html_text)[0]
            print ("title_user_gender = ", title_user_gender)
            #发布时间
            created_title_time = re.findall('.*?"created_at": "(.*?)".*?', html_text)[0].split(' ')
            #日期
            if 'Mar' in created_title_time:
                title_created_YMD = "{}/{}/{}".format(created_title_time[-1], '03', created_title_time[2])
            elif 'Feb' in created_title_time:
                title_created_YMD = "{}/{}/{}".format(created_title_time[-1], '02', created_title_time[2])
            elif 'Jan' in created_title_time:
                title_created_YMD = "{}/{}/{}".format(created_title_time[-1], '01', created_title_time[2])
            else:
                print ('该时间不在疫情范围内,估计数据有误!URL = ')
                pass
            print ("title_created_YMD = ", title_created_YMD)
            #发布时间
            add_title_time = created_title_time[3]
            print ("add_title_time = ", add_title_time)
            #转发量
            reposts_count = re.findall('.*?"reposts_count": (.*?),.*?', html_text)[0]
            print ("reposts_count = ", reposts_count)
            #评论量
            comments_count = re.findall('.*?"comments_count": (.*?),.*?', html_text)[0]
            print ("comments_count = ", comments_count)
            #点赞量
            attitudes_count = re.findall('.*?"attitudes_count": (.*?),.*?', html_text)[0]
            print ("attitudes_count = ", attitudes_count)   
            comment_count = int(int(comments_count) / 20) #每个ajax一次加载20条数据
            position1 = (article_url, title_text, title_user_id, title_user_NicName,title_user_gender, title_created_YMD, add_title_time, reposts_count, comments_count, attitudes_count, " ", " ", " ", " "," ", " ")
            #写入数据
            writer.writerow((position1))
            return comment_count
        except:
            pass
    
    
    #-------------------------------------------------抓取评论信息---------------------------------------------------
    #comment_ID话题编号
    def get_page(comment_ID, max_id, id_type):
        params = {
            'max_id': max_id,
            'max_id_type': id_type
        }
        url = ' https://m.weibo.cn/comments/hotflow?id={}&mid={}&max_id'.format(comment_ID, comment_ID)
        try:
            r = requests.get(url, params=params, headers=headers)
            if r.status_code == 200:
                return r.json()
        except requests.ConnectionError as e:
            print('error', e.args)
            pass
    
    #-------------------------------------------------抓取评论item最大值---------------------------------------------------
    def parse_page(jsondata):
        if jsondata:
            items = jsondata.get('data')
            item_max_id = {}
            item_max_id['max_id'] = items['max_id']
            item_max_id['max_id_type'] = items['max_id_type']
            return item_max_id
    
    #-------------------------------------------------抓取评论信息---------------------------------------------------
    def write_csv(jsondata):
        for json in jsondata['data']['data']:
            #用户ID
            user_id = json['user']['id']
            # 用户昵称
            user_name = json['user']['screen_name']
            # 用户性别,m表示男性,表示女性
            user_gender = json['user']['gender']
            #获取评论
            comments_text = json['text']
            comment_text = re.sub('<(S*?)[^>]*>.*?|<.*? />', '', comments_text) #正则匹配掉html标签
            # 评论时间
            created_times = json['created_at'].split(' ')
            if 'Feb' in created_times:
                created_YMD = "{}/{}/{}".format(created_times[-1], '02', created_times[2])
            elif 'Jan' in created_times:
                created_YMD = "{}/{}/{}".format(created_times[-1], '01', created_times[2])
            else:
                print ('该时间不在疫情范围内,估计数据有误!')
                pass
            created_time = created_times[3] #评论时间时分秒
            #if len(comment_text) != 0:
            position2 = (" ", " ", " ", " "," ", " ", " ", " ", " ", " ", user_id, user_name, user_gender, created_YMD, created_time, comment_text)
            writer.writerow((position2))#写入数据
            #print (user_id, user_name, user_gender, created_YMD, created_time)    
    
    
    #-------------------------------------------------主函数---------------------------------------------------
    def main():
        count_title = len(comments_ID)
        for count, comment_ID in enumerate(comments_ID):
            print ("正在爬取第%s个话题,一共找到个%s话题需要爬取"%(count+1, count_title))
            #maxPage获取返回的最大评论数量
            maxPage = spider_title(comment_ID)
            print ('maxPage = ', maxPage)
            m_id = 0
            id_type = 0
            if maxPage != 0: #小于20条评论的不需要循环
                try:
                    #用评论数量控制循环
                    for page in range(0, maxPage):
                        #自定义函数-抓取网页评论信息
                        jsondata = get_page(comment_ID, m_id, id_type)
                        
                        #自定义函数-写入CSV文件
                        write_csv(jsondata)
                        
                        #自定义函数-获取评论item最大值
                        results = parse_page(jsondata)
                        time.sleep(1)
                        m_id = results['max_id']
                        id_type = results['max_id_type']              
                except:
                    pass
            print ("--------------------------分隔符---------------------------")
        csvfile.close() 
        
    if __name__ == '__main__':
        
        #获取话题ID
        get_title_id()
        
        #主函数操作
        main()
        
        #计算使用时间
        endTime = time.time()
        useTime = (endTime-startTime) / 60
        print("该次所获的信息一共使用%s分钟"%useTime)
    

    保存数据截图如下图所示:

    在这里插入图片描述

    下图时抓取的话题页面网址,每个页面包括18个话题。

    在这里插入图片描述

    接着抓取每个话题的内容,如下所示:

    正在爬取第1个话题,一共找到个361话题需要爬取
    article_url =  https://m.weibo.cn/detail/4484575189181757
    title_text =#国家卫健委回应健康码互通互认#】国家卫生健康委规划司司长毛群安:目前全国低风险县域已占98%,各省份正在按照统一的数据格式标准和内容要求,加快向全国一体化平台汇聚本地区防疫健康信息的目录。截至目前,#全国绝大多数健康码可实现一码通行#。 人民日报的微博视频 
    title_user_id =  2803301701
    title_user_NicName =  人民日报
    title_user_gender =  m
    该时间不在疫情范围内,估计数据有误!URL = 
    maxPage =  None
    --------------------------分隔符---------------------------
    正在爬取第2个话题,一共找到个361话题需要爬取
    article_url =  https://m.weibo.cn/detail/4484288164243251
    title_text =  法国网友自称自己成了长发公主,度过了居家隔离后的第三天.....#全球疫情##法国疫情# 法国囧事的微博视频 
    title_user_id =  2981906842
    title_user_NicName =  法国囧事
    title_user_gender =  m
    该时间不在疫情范围内,估计数据有误!URL = 
    maxPage =  None
    --------------------------分隔符---------------------------
    正在爬取第3个话题,一共找到个361话题需要爬取
    article_url =  https://m.weibo.cn/detail/4484492666507389
    title_text =  #全球疫情# #意大利疫情# #意大利# “罗马还有其他四处的药店都遭到了抢劫。我们遭受到的是持械抢劫。“这是一位罗马药店药剂师的陈述。她说,在当前疫情的危机情况下,我们处在两难困境之中:受到抢劫和疾病的双重威胁。疫情之下,意大利口罩告急,价格飙高。市民认为是药店不卖,而真实情况是药店真的没有,而供货商又抬高了价格。药店处在两难境地。这位药剂师道出了自己的苦衷,冒着危险还在工作,与医护人员一样,都是奋斗在一线做出牺牲的人。呼吁民众理解,也请求大家的帮助。 Nita大呵呵的微博视频
    title_user_id =  6476189426
    title_user_NicName =  Nita大呵呵
    title_user_gender =  f
    该时间不在疫情范围内,估计数据有误!URL = 
    maxPage =  None
    

    最终抓取360个疫情话题内容。

    在这里插入图片描述


    注意:该爬虫评论写入功能需要改进下,且只能抓取当天的“战疫情”话题及评论,如果想针对某个突发事件进行一段时间的分析,建议每天定时运行该程序,从而形成所需的数据集。也可以根据需求修改为热点话题的抓取,增加搜索功能等。

    作者前文:
    [python爬虫] Selenium爬取新浪微博内容及用户信息
    [Python爬虫] Selenium爬取新浪微博客户端用户信息、热点话题及评论 (上)
    [Python爬虫] Selenium爬取新浪微博移动端热点话题及评论 (下)



    二.微博话题词云分析

    首先,我们对文本进行简单的词云可视化分析。

    1.基本用法

    词云分析主要包括两种方法:

    • 调用WordCloud扩展包画图(兼容性极强,之前介绍过)
    • 调用PyEcharts中的WordCloud子包画图(本文推荐新方法)

    PyEcharts绘制词云的基础代码如下:

    # coding=utf-8
    from pyecharts import options as opts
    from pyecharts.charts import WordCloud
    from pyecharts.globals import SymbolType
    
    # 数据
    words = [
        ('背包问题', 10000),
        ('大整数', 6181),
        ('Karatsuba乘法算法', 4386),
        ('穷举搜索', 4055),
        ('傅里叶变换', 2467),
        ('状态树遍历', 2244),
        ('剪枝', 1868),
        ('Gale-shapley', 1484),
        ('最大匹配与匈牙利算法', 1112),
        ('线索模型', 865),
        ('关键路径算法', 847),
        ('最小二乘法曲线拟合', 582),
        ('二分逼近法', 555),
        ('牛顿迭代法', 550),
        ('Bresenham算法', 462),
        ('粒子群优化', 366),
        ('Dijkstra', 360),
        ('A*算法', 282),
        ('负极大极搜索算法', 273),
        ('估值函数', 265)
    ]
    
    # 渲染图
    def wordcloud_base() -> WordCloud:
        c = (
            WordCloud()
            .add("", words, word_size_range=[20, 100], shape='diamond')  # SymbolType.ROUND_RECT
            .set_global_opts(title_opts=opts.TitleOpts(title='WordCloud词云'))
        )
        return c
    
    # 生成图
    wordcloud_base().render('词云图.html')
    

    输出结果如下图所示,出现词频越高显示越大。

    在这里插入图片描述

    核心代码为:
    add(name, attr, value, shape=“circle”, word_gap=20, word_size_range=None, rotate_step=45)

    • name -> str: 图例名称
    • attr -> list: 属性名称
    • value -> list: 属性所对应的值
    • shape -> list: 词云图轮廓,有’circle’, ‘cardioid’, ‘diamond’, ‘triangleforward’, ‘triangle’, ‘pentagon’, ‘star’可选
    • word_gap -> int: 单词间隔,默认为20
    • word_size_range -> list: 单词字体大小范围,默认为[12,60]
    • rotate_step -> int: 旋转单词角度,默认为45


    2.疫情词云

    接着我们将3月20日疫情内容复制至“data.txt”文本,经过中文分词后显示前1000个高频词的词云。代码如下:

    # coding=utf-8
    import jieba
    import re
    import time
    from collections import Counter
    
    #------------------------------------中文分词------------------------------------
    cut_words = ""
    all_words = ""
    f = open('C-class-fenci.txt', 'w')
    for line in open('C-class.txt', encoding='utf-8'):
        line.strip('\n')
        seg_list = jieba.cut(line,cut_all=False)
        # print(" ".join(seg_list))
        cut_words = (" ".join(seg_list))
        f.write(cut_words)
        all_words += cut_words
    else:
        f.close()
    
    # 输出结果
    all_words = all_words.split()
    print(all_words)
    
    # 词频统计
    c = Counter()
    for x in all_words:
        if len(x)>1 and x != '\r\n':
            c[x] += 1
    
    # 输出词频最高的前10个词
    print('\n词频统计结果:')
    for (k,v) in c.most_common(10):
        print("%s:%d"%(k,v))
    
    # 存储数据
    name = time.strftime("%Y-%m-%d") + "-fc.csv"
    fw = open(name, 'w', encoding='utf-8')
    i = 1
    for (k,v) in c.most_common(len(c)):
        fw.write(str(i)+','+str(k)+','+str(v)+'\n')
        i = i + 1
    else:
        print("Over write file!")
        fw.close()
    
    #------------------------------------词云分析------------------------------------
    from pyecharts import options as opts
    from pyecharts.charts import WordCloud
    from pyecharts.globals import SymbolType
    
    # 生成数据 word = [('A',10), ('B',9), ('C',8)] 列表+Tuple
    words = []
    for (k,v) in c.most_common(1000):
        # print(k, v)
        words.append((k,v))
    
    # 渲染图
    def wordcloud_base() -> WordCloud:
        c = (
            WordCloud()
            .add("", words, word_size_range=[20, 100], shape=SymbolType.ROUND_RECT)
            .set_global_opts(title_opts=opts.TitleOpts(title='全国新型冠状病毒疫情词云图'))
        )
        return c
    
    # 生成图
    wordcloud_base().render('疫情词云图.html')
    

    输出结果如下图所示,仅3月20日的热点话题内容。

    在这里插入图片描述

    在这里插入图片描述



    3.WordCloud

    另一种方法的代码如下:

    # coding=utf-8
    import jieba
    import re
    import sys
    import time
    from collections import Counter
    import matplotlib.pyplot as plt
    from wordcloud import WordCloud
    
    #------------------------------------中文分词------------------------------------
    cut_words = ""
    all_words = ""
    f = open('data-fenci.txt', 'w')
    for line in open('data.txt', encoding='utf-8'):
        line.strip('\n')
        seg_list = jieba.cut(line,cut_all=False)
        # print(" ".join(seg_list))
        cut_words = (" ".join(seg_list))
        f.write(cut_words)
        all_words += cut_words
    else:
        f.close()
    
    # 输出结果
    all_words = all_words.split()
    print(all_words)
    
    # 词频统计
    c = Counter()
    for x in all_words:
        if len(x)>1 and x != '\r\n':
            c[x] += 1
    
    # 输出词频最高的前10个词
    print('\n词频统计结果:')
    for (k,v) in c.most_common(10):
        print("%s:%d"%(k,v))
    
    # 存储数据
    name = time.strftime("%Y-%m-%d") + "-fc.csv"
    fw = open(name, 'w', encoding='utf-8')
    i = 1
    for (k,v) in c.most_common(len(c)):
        fw.write(str(i)+','+str(k)+','+str(v)+'\n')
        i = i + 1
    else:
        print("Over write file!")
        fw.close()
    
    #------------------------------------词云分析------------------------------------
    #打开本体TXT文件
    text = open('data.txt').read()
     
    #结巴分词 cut_all=True 设置为精准模式 
    wordlist = jieba.cut(text, cut_all = False)
     
    #使用空格连接 进行中文分词
    wl_space_split = " ".join(wordlist)
    #print(wl_space_split)
     
    #对分词后的文本生成词云
    my_wordcloud = WordCloud().generate(wl_space_split)
     
    #显示词云图
    plt.imshow(my_wordcloud)
    #是否显示x轴、y轴下标
    plt.axis("off")
    plt.show()
    


    三.SnowNLP情感分析用法

    情感分析的基本流程如下图所示,通常包括:

    • 自定义爬虫抓取文本信息;
    • 使用Jieba工具进行中文分词、词性标注;
    • 定义情感词典提取每行文本的情感词;
    • 通过情感词构建情感矩阵,并计算情感分数;
    • 结果评估,包括将情感分数置于0.5到-0.5之间,并可视化显示。

    在这里插入图片描述

    1.SnowNLP

    SnowNLP是一个常用的Python文本分析库,是受到TextBlob启发而发明的。由于当前自然语言处理库基本都是针对英文的,而中文没有空格分割特征词,Python做中文文本挖掘较难,后续开发了一些针对中文处理的库,例如SnowNLP、Jieba、BosonNLP等。注意SnowNLP处理的是unicode编码,所以使用时请自行decode成unicode。

    Snownlp主要功能包括:

    • 中文分词(算法是Character-Based Generative Model)
    • 词性标注(原理是TnT、3-gram 隐马)
    • 情感分析
    • 文本分类(原理是朴素贝叶斯)
    • 转换拼音、繁体转简体
    • 提取文本关键词(原理是TextRank)
    • 提取摘要(原理是TextRank)、分割句子
    • 文本相似(原理是BM25)

    推荐官网给大家学习。
    安装和其他库一样,使用pip安装即可。

    pip install snownlp
    

    在这里插入图片描述



    2.中文分词

    下面是最简单的实例,使用SnowNLP进行中文分词,同时比较了SnowNLP和Jieba库的分词效果。

    # -*- coding: utf-8 -*-
    from snownlp import SnowNLP
    s1 = SnowNLP(u"这本书质量真不太好!")
    print("SnowNLP:")
    print(" ".join(s1.words))
    
    import jieba
    s2 = jieba.cut(u"这本书质量真不太好!", cut_all=False)
    print("jieba:")
    print(" ".join(s2))
    

    输出结果如下所示:

    总体感觉是SnowNLP分词速度比较慢,准确度较低,比如“不太好”这个词组,但也不影响我们后续的情感分析。


    3.常见功能

    代码如下:

    # -*- coding: utf-8 -*-
    from snownlp import SnowNLP
    s = SnowNLP(u"这本书质量真不太好!")
    
    print(u"\n中文分词:")
    print( " ".join(s.words))
    
    print(u"\n词性标注:")
    print(s.tags)
    for k in s.tags:
        print(k)
    
    print(u"\n情感分数:")
    print(s.sentiments)
    
    print(u"\n转换拼音:")
    print(s.pinyin)
    
    print(u"\n输出前4个关键词:")
    print(s.keywords(4))
    for k in s.keywords(4):
        print(k)
    
    print(u"\n输出关键句子:")
    print(s.summary(1))
    for k in s.summary(1):
        print(k)
    
    print(u"\n输出tf和idf:")
    print(s.tf)
    print(s.idf)
    
    n = SnowNLP(u'「繁體字」「繁體中文」的叫法在臺灣亦很常見。')
    print(u"\n繁简体转换:")
    print(n.han)
    

    s.words 输出分词后的结果,词性标注主要通过 s.tags,s.sentiments 计算情感分数,s.pinyin 转换为拼音,s.keywords(4) 提取4个关键词,s.summary(1) 输出一个关键句子,s.tf 计算TF值(频率),s.idf 计算IDF值(倒文档)。

    输出结果如下所示:

    >>> 
    
    中文分词:
    这 本书 质量 真 不 太 好 !
    
    词性标注:
    [(u'\u8fd9', u'r'), (u'\u672c\u4e66', u'r'), (u'\u8d28\u91cf', u'n'), 
    (u'\u771f', u'd'), (u'\u4e0d', u'd'), (u'\u592a', u'd'), 
    (u'\u597d', u'a'), (u'\uff01', u'w')]
    (u'\u8fd9', u'r')
    (u'\u672c\u4e66', u'r')
    (u'\u8d28\u91cf', u'n')
    (u'\u771f', u'd')
    (u'\u4e0d', u'd')
    (u'\u592a', u'd')
    (u'\u597d', u'a')
    (u'\uff01', u'w')
    
    情感分数:
    0.420002029202
    
    转换拼音:
    [u'zhe', u'ben', u'shu', u'zhi', u'liang', u'zhen', u'bu', u'tai', u'hao', u'\uff01']
    
    输出前4个关键词:
    [u'\u592a', u'\u4e0d', u'\u8d28\u91cf', u'\u771f']
    太
    不
    质量
    真
    
    输出关键句子:
    [u'\u8fd9\u672c\u4e66\u8d28\u91cf\u771f\u4e0d\u592a\u597d']
    这本书质量真不太好
    
    输出tf和idf:
    [{u'\u8fd9': 1}, {u'\u672c': 1}, {u'\u4e66': 1}, 
    {u'\u8d28': 1}, {u'\u91cf': 1}, {u'\u771f': 1}, 
    {u'\u4e0d': 1}, {u'\u592a': 1}, {u'\u597d': 1}, {u'\uff01': 1}]
    {u'\uff01': 1.845826690498331, u'\u4e66': 1.845826690498331, u'\u8d28': 1.845826690498331, 
    u'\u592a': 1.845826690498331, u'\u4e0d': 1.845826690498331, u'\u672c': 1.845826690498331, 
    u'\u91cf': 1.845826690498331, u'\u8fd9': 1.845826690498331, u'\u597d': 1.845826690498331, u'\u771f': 1.845826690498331}
    
    繁简体转换:
    「繁体字」「繁体中文」的叫法在台湾亦很常见。
    >>> 
    

    同样可以进行文本相似度计算,代码参考下图所示:


    4.情感分析

    SnowNLP情感分析也是基于情感词典实现的,其简单的将文本分为两类,积极和消极,返回值为情绪的概率,越接近1为积极,接近0为消极。其原理参考zhiyong_will大神和邓旭东老师的文章,也强烈推荐大家学习。地址:
    情感分析——深入snownlp原理和实践
    自然语言处理库之snowNLP

    下面简单给出一个情感分析的例子:

    # -*- coding: utf-8 -*-
    from snownlp import SnowNLP
    s1 = SnowNLP(u"我今天很开心")
    print(u"s1情感分数:")
    print(s1.sentiments)
    
    s2 = SnowNLP(u"我今天很沮丧")
    print(u"s2情感分数:")
    print(s2.sentiments)
    
    s3 = SnowNLP(u"大傻瓜,你脾气真差,动不动就打人")
    print(u"s3情感分数:")
    print(s3.sentiments)
    

    输出结果如下所示,当负面情感特征词越多,比如“傻瓜”、“差”、“打人”等,分数就会很低,同样当正免情感词多分数就高。

    s1情感分数:
    0.84204018979
    s2情感分数:
    0.648537121839
    s3情感分数:
    0.0533215596706
    

    而在真实项目中,通常需要根据实际的数据重新训练情感分析的模型,导入正面样本和负面样本,再训练新模型。

    • sentiment.train(’./neg.txt’, ‘./pos.txt’)
    • sentiment.save(‘sentiment.marshal’)


    四.SnowNLP微博情感分析实例

    下面的代码是对爬取的疫情话题进行情感分析。本文将抓取的356条(其中4条仅图片)微博疫情话题信息复制至TXT文件中 ,每一行为一条话题,再对其进行中文分词处理。注意,这里仅仅获取序号1-356的情感分数,而其他情感分析可以进行时间对比、主题对比等,其方法和此篇文章类似,希望读者学会举一反三。

    在这里插入图片描述


    1.情感各分数段出现频率

    首先统计各情感分数段出现的评率并绘制对应的柱状图,代码如下:

    # -*- coding: utf-8 -*-
    from snownlp import SnowNLP
    import codecs
    import os
    
    source = open("data.txt","r", encoding='utf-8')
    line = source.readlines()
    sentimentslist = []
    for i in line:
        s = SnowNLP(i)
        print(s.sentiments)
        sentimentslist.append(s.sentiments)
    
    import matplotlib.pyplot as plt
    import numpy as np
    plt.hist(sentimentslist, bins = np.arange(0, 1, 0.01), facecolor = 'g')
    plt.xlabel('Sentiments Probability')
    plt.ylabel('Quantity')
    plt.title('Analysis of Sentiments')
    plt.show()
    

    输出结果如下图所示,可以看到

    对应的分数如下:

    >>>
    4.440892098500626e-16
    0.49055395607520824
    0.9999999999972635
    0.9999998677093149
    0.9979627586368516
    0.9999999990959509
    0.9999830199233769
    0.9998699310812647
    0.9999954477924106
    ...
    

    2.情感波动分析

    接下来分析每条评论的波动情况,代码如下所示:

    # -*- coding: utf-8 -*-
    from snownlp import SnowNLP
    import codecs
    import os
    
    source = open("data.txt","r", encoding='utf-8')
    line = source.readlines()
    sentimentslist = []
    for i in line:
        s = SnowNLP(i)
        print(s.sentiments)
        sentimentslist.append(s.sentiments)
    
    import matplotlib.pyplot as plt
    import numpy as np
    plt.plot(np.arange(0, 356, 1), sentimentslist, 'k-')
    plt.xlabel('Number')
    plt.ylabel('Sentiment')
    plt.title('Analysis of Sentiments')
    plt.show()
    

    输出结果如下所示,呈现一条曲线,因为抓取的评论基本都是好评,所以分数基本接近于1.0,而真实分析过程中存在好评、中评和差评,曲线更加规律。

    同时,在做情感分析的时候,我看到很多论文都是将情感区间从[0, 1.0]转换为[-0.5, 0.5],这样的曲线更加好看,位于0以上的是积极评论,反之消极评论。修改代码如下:

    # -*- coding: utf-8 -*-
    from snownlp import SnowNLP
    import codecs
    import os
    
    #获取情感分数
    source = open("data.txt","r", encoding='utf-8')
    line = source.readlines()
    sentimentslist = []
    for i in line:
        s = SnowNLP(i)
        print(s.sentiments)
        sentimentslist.append(s.sentiments)
    
    #区间转换为[-0.5, 0.5]
    result = []
    i = 0
    while i<len(sentimentslist):
        result.append(sentimentslist[i]-0.5)
        i = i + 1
    
    #可视化画图
    import matplotlib.pyplot as plt
    import numpy as np
    plt.plot(np.arange(0, 356, 1), result, 'k-')
    plt.xlabel('Number')
    plt.ylabel('Sentiment')
    plt.title('Analysis of Sentiments')
    plt.show()
    

    绘制图形如下所示:



    3.情感时间分布

    最后补充随时间分布的情感分数相关建议,读者可能也发现抓取的博客存在重复、时间不均衡等现象。微博数据还是非常不好抓取,数据卡住了很多人,也请读者深入分析下。

    (1) 情感分析通常需要和评论时间结合起来,并进行舆情预测等,建议读者尝试将时间结合。比如王树义老师的文章《基于情感分类的竞争企业新闻文本主题挖掘》。

    (2) 情感分析也是可以进行评价的,我们前面抓取的分为5星评分,假设0-0.2位一星,0.2-0.4位二星,0.4-0.6为三星,0.6-0.8为四星,0.8-1.0为五星,这样我们可以计算它的准确率,召回率,F值,从而评论我的算法好坏。

    (3) 作者还有很多情感分析结合幂率分布的知识,因为需要写文章,这里暂时不进行分享,但是这篇基础文章对初学者仍然有一定的帮助。

    (4) BosonNLP也是一个比较不错的情感分析包,建议感兴趣的读者学习,它提供了相关的词典,如下:https://bosonnlp.com/dev/resource。

    (5) 读者如果不太擅长写代码,可以尝试使用情感分析系统。http://ictclas.nlpir.org/nlpir/


    五.总结

    写到这里,第四篇疫情分析的文章就讲解完毕,希望对您有所帮助,尤其是想写文本挖掘论文的读者。后续还会分享舆情分析、威胁情报溯源、知识图谱、预测预警及AI和NLP应用等。如果文章对您有所帮助,将是我写作的最大动力。作者将源代码上传至github,大家可以直接下载。你们的支持就是我撰写的最大动力,加油~

    同时,向钟院士致敬,向一线工作者致敬。侠之大者,为国为民。咱们中国人一生的最高追求,为天地立心,为生民立命,为往圣继绝学,为万世开太平。以一人之力系万民康乐,以一身犯险保大业安全。他们真是做到了,武汉加油,中国加油!

    在这里插入图片描述

    (By:Eastmount 2020-03-21 中午12点于贵阳 http://blog.csdn.net/eastmount/)



    参考文献:
    [1] [python数据挖掘课程] 十三.WordCloud词云配置过程及词频分析 - Eastmount
    [2] python爬虫爬取微博之战疫情用户评论及详情
    [3] [python爬虫] Selenium爬取新浪微博内容及用户信息
    [4] [Python爬虫] Selenium爬取新浪微博客户端用户信息、热点话题及评论 (上)
    [5] [Python爬虫] Selenium爬取新浪微博移动端热点话题及评论 (下)
    [6] 用pyecharts绘制词云WordCloud - pennyyangpei
    [7] 情感分析——深入snownlp原理和实践
    [8] 自然语言处理库之snowNLP
    [9] 王树义老师的文章《基于情感分类的竞争企业新闻文本主题挖掘》

    展开全文
  • 文本挖掘python

    2018-01-03 18:55:52
    这是用python做的文本挖掘,挺详细的,请大家放心下载
  • 文本挖掘概述与方法

    2018-07-30 14:39:11
    文本挖掘概述与方法,文本挖掘概述与方法,文本挖掘概述与方法,文本挖掘概述与方法,文本挖掘概述与方法,
  • 文本挖掘原理

    2018-03-11 15:22:58
    文本挖掘原理》 全本,非2.74M的15页预览版。由于没有书可卖了,上传供学习参考!
  • R语言文本挖掘

    2019-02-25 07:57:16
    R 语言环境下的文本挖掘文本挖掘被描述为 “自动化或半自动化处理文本的过程”,包含了文档聚类、文档分类、自 然语言处理、文体变化分析及网络挖掘等领域内容。 对于文本处理过程首先要拥有分析的语料(text ...
  • 文本挖掘 情感分析In this tutorial, I will explore some text mining techniques for sentiment analysis. We'll look at how to prepare textual data. After that we will try two different classifiers to ...

    文本挖掘 情感分析

    In this tutorial, I will explore some text mining techniques for sentiment analysis. We'll look at how to prepare textual data. After that we will try two different classifiers to infer the tweets' sentiment. We will tune the hyperparameters of both classifiers with grid search. Finally, we evaluate the performance on a set of metrics like precision, recall and the F1 score.

    在本教程中,我将探讨一些用于情感分析的文本挖掘技术。 我们将研究如何准备文本数据。 之后,我们将尝试使用两个不同的分类器来推断推文的情绪。 我们将使用网格搜索调整两个分类器的超参数。 最后,我们根据一组指标(如准确性,召回率和F1得分)评估性能。

    For this project, we'll be working with the Twitter US Airline Sentiment data set on Kaggle. It contains the tweet’s text and one variable with three possible sentiment values. Let's start by importing the packages and configuring some settings.

    对于此项目,我们将使用Kaggle上Twitter美国航空情绪数据集 。 它包含推文的文本和一个带有三个可能的情感值的变量。 让我们首先导入软件包并配置一些设置。

    import numpy as np 
    import pandas as pd 
    pd.set_option('display.max_colwidth', -1)
    from time import time
    import re
    import string
    import os
    import emoji
    from pprint import pprint
    import collections
    import matplotlib.pyplot as plt
    import seaborn as sns
    sns.set(style="darkgrid")
    sns.set(font_scale=1.3)
    from sklearn.base import BaseEstimator, TransformerMixin
    from sklearn.feature_extraction.text import CountVectorizer
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.model_selection import GridSearchCV
    from sklearn.model_selection import train_test_split
    from sklearn.pipeline import Pipeline, FeatureUnion
    from sklearn.metrics import classification_report
    from sklearn.naive_bayes import MultinomialNB
    from sklearn.linear_model import LogisticRegression
    from sklearn.externals import joblib
    import gensim
    from nltk.corpus import stopwords
    from nltk.stem import PorterStemmer
    from nltk.tokenize import word_tokenize
    import warnings
    warnings.filterwarnings('ignore')
    np.random.seed(37)

    加载数据 (Loading the data)

    We read in the comma separated file we downloaded from the Kaggle Datasets. We shuffle the data frame in case the classes are sorted. Applying the reindex method on the permutation of the original indices is good for that. In this notebook, we will work with the text variable and the airline_sentiment variable.

    我们读取从Kaggle数据集下载的逗号分隔文件。 如果对类进行排序,我们会重新整理数据框。 将reindex方法应用于原始索引的permutation对此很有好处。 在此笔记本中,我们将使用text变量和airline_sentiment变量。

    df = pd.read_csv('../input/Tweets.csv')
    df = df.reindex(np.random.permutation(df.index))
    df = df[['text', 'airline_sentiment']]

    探索性数据分析 (Exploratory Data Analysis)

    目标变量 (Target variable)

    There are three class labels we will predict: negative, neutral or positive.

    我们将预测三种类别的标签:负面,中性或正面。

    The class labels are imbalanced as we can see below in the chart. This is something that we should keep in mind during the model training phase. With the factorplot of the seaborn package, we can visualize the distribution of the target variable.

    类别标签不平衡,如下图所示。 在模型训练阶段,我们应该牢记这一点。 随着factorplot的seaborn包,我们可以直观的目标变量的分布。

    sns.factorplot(x="airline_sentiment", data=df, kind="count", size=6, aspect=1.5, palette="PuBuGn_d")
    plt.show();

    输入变量 (Input variable)

    To analyze the text variable we create a class TextCounts. In this class we compute some basic statistics on the text variable.

    为了分析text变量,我们创建了一个TextCounts类。 在此类中,我们计算有关文本变量的一些基本统计信息。

    • count_words: number of words in the tweet

      count_words :鸣叫中的单词数

    • count_mentions: referrals to other Twitter accounts start with a @

      count_mentions :对其他Twitter帐户的引荐以@开头

    • count_hashtags: number of tag words, preceded by a #

      count_hashtags :标记词的数量, count_hashtags

    • count_capital_words: number of uppercase words are sometimes used to “shout” and express (negative) emotions

      count_capital_words :大写单词的数量有时用于“喊”和表达(负面)情绪

    • count_excl_quest_marks: number of question or exclamation marks

      count_excl_quest_marks :问题或感叹号的数量

    • count_urls: number of links in the tweet, preceded by http(s)

      count_urls :推文中的链接数,以http(s) count_urls

    • count_emojis: number of emoji, which might be a good sign of the sentiment

      count_emojis :表情符号的数量,这可能是情绪的好兆头

    class TextCounts(BaseEstimator, TransformerMixin):
        
        def count_regex(self, pattern, tweet):
            return len(re.findall(pattern, tweet))
        
        def fit(self, X, y=None, **fit_params):
            # fit method is used when specific operations need to be done on the train data, but not on the test data
            return self
        
        def transform(self, X, **transform_params):
            count_words = X.apply(lambda x: self.count_regex(r'\w+', x)) 
            count_mentions = X.apply(lambda x: self.count_regex(r'@\w+', x))
            count_hashtags = X.apply(lambda x: self.count_regex(r'#\w+', x))
            count_capital_words = X.apply(lambda x: self.count_regex(r'\b[A-Z]{2,}\b', x))
            count_excl_quest_marks = X.apply(lambda x: self.count_regex(r'!|\?', x))
            count_urls = X.apply(lambda x: self.count_regex(r'http.?://[^\s]+[\s]?', x))
            # We will replace the emoji symbols with a description, which makes using a regex for counting easier
            # Moreover, it will result in having more words in the tweet
            count_emojis = X.apply(lambda x: emoji.demojize(x)).apply(lambda x: self.count_regex(r':[a-z_&]+:', x))
            
            df = pd.DataFrame({'count_words': count_words
                               , 'count_mentions': count_mentions
                               , 'count_hashtags': count_hashtags
                               , 'count_capital_words': count_capital_words
                               , 'count_excl_quest_marks': count_excl_quest_marks
                               , 'count_urls': count_urls
                               , 'count_emojis': count_emojis
                              })
            
            return df
    tc = TextCounts()
    df_eda = tc.fit_transform(df.text)
    df_eda['airline_sentiment'] = df.airline_sentiment

    It could be interesting to see how the TextStats variables relate to the class variable. So we write a function show_dist that provides descriptive statistics and a plot per target class.

    看看TextStats变量与类变量之间的关系可能会很有趣。 因此,我们编写了一个函数show_dist ,该函数提供描述性统计信息和每个目标类的图表。

    def show_dist(df, col):
        print('Descriptive stats for {}'.format(col))
        print('-'*(len(col)+22))
        print(df.groupby('airline_sentiment')[col].describe())
        bins = np.arange(df[col].min(), df[col].max() + 1)
        g = sns.FacetGrid(df, col='airline_sentiment', size=5, hue='airline_sentiment', palette="PuBuGn_d")
        g = g.map(sns.distplot, col, kde=False, norm_hist=True, bins=bins)
        plt.show()

    Below you can find the distribution of the number of words in a tweet per target class. For brevity, we will limit us to only this variable. The charts for all TextCounts variables are in the notebook on Github.

    在下面,您可以找到每个目标类别的推文中单词数的分布。 为简便起见,我们将限于此变量。 所有TextCounts变量的图表都在Github笔记本中

    • The number of words used in the tweets is rather low. The largest number of words is 36 and there are even tweets with only 2 words. So we’ll have to be careful during data cleaning not to remove too many words. But the text processing will be faster. Negative tweets contain more words than neutral or positive tweets.

      推文中使用的单词数量很少。 单词数量最多,为36个,甚至还有只有2个单词的推文。 因此,在数据清理过程中,我们必须注意不要删除过多的单词。 但是文本处理会更快。 负面推文比中立或正面推文包含更多的单词。
    • All tweets have at least one mention. This is the result of extracting the tweets based on mentions in the Twitter data. There seems to be no difference in the number of mentions with regard to the sentiment.

      所有推文至少都有一处提及。 这是根据Twitter数据中的提及提取推文的结果。 在情感方面,提及的次数似乎没有差异。
    • Most of the tweets do not contain hash tags. So this variable will not be retained during model training. Again, no difference in the number of hash tags with regard to the sentiment.

      大多数推文不包含哈希标签。 因此,在模型训练期间将不会保留此变量。 再次,关于情感,哈希标签的数量没有差异。
    • Most of the tweets do not contain capitalized words and we do not see a difference in distribution between the sentiments.

      大多数推文不包含大写单词,我们在情感之间的分布上也看不到差异。
    • The positive tweets seem to be using a bit more exclamation or question marks.

      积极的推文似乎使用了更多的感叹号或问号。
    • Most tweets do not contain a URL.

      大多数推文不包含URL。
    • Most tweets do not use emojis.

      大多数推文不使用表情符号。

    文字清理 (Text Cleaning)

    Before we start using the tweets’ text we need to clean it. We’ll do the this in the class CleanText. With this class we’ll perform the following actions:

    在开始使用推文之前,我们需要先对其进行清理。 我们将在类CleanText执行此CleanText 在此类中,我们将执行以下操作:

    • remove the mentions, as we want to generalize to tweets of other airline companies too.

      删除提及,因为我们也希望将其推广到其他航空公司的推文中。
    • remove the hash tag sign (#) but not the actual tag as this may contain information

      删除哈希标签符号(#),但不要删除实际标签,因为它可能包含信息
    • set all words to lowercase

      将所有单词设置为小写
    • remove all punctuations, including the question and exclamation marks

      删除所有标点符号,包括问号和感叹号
    • remove the URLs as they do not contain useful information. We did not notice a difference in the number of URLs used between the sentiment classes

      删除网址,因为它们不包含有用的信息。 我们没有发现情感类别之间使用的URL数量有所不同
    • make sure to convert the emojis into one word.

      确保将表情符号转换为一个单词。
    • remove digits

      删除数字
    • remove stopwords

      删除停用词
    • apply the PorterStemmer to keep the stem of the words

      应用PorterStemmer保持词干

    class CleanText(BaseEstimator, TransformerMixin):
        def remove_mentions(self, input_text):
            return re.sub(r'@\w+', '', input_text)
        
        def remove_urls(self, input_text):
            return re.sub(r'http.?://[^\s]+[\s]?', '', input_text)
        
        def emoji_oneword(self, input_text):
            # By compressing the underscore, the emoji is kept as one word
            return input_text.replace('_','')
        
        def remove_punctuation(self, input_text):
            # Make translation table
            punct = string.punctuation
            trantab = str.maketrans(punct, len(punct)*' ')  # Every punctuation symbol will be replaced by a space
            return input_text.translate(trantab)
        def remove_digits(self, input_text):
            return re.sub('\d+', '', input_text)
        
        def to_lower(self, input_text):
            return input_text.lower()
        
        def remove_stopwords(self, input_text):
            stopwords_list = stopwords.words('english')
            # Some words which might indicate a certain sentiment are kept via a whitelist
            whitelist = ["n't", "not", "no"]
            words = input_text.split() 
            clean_words = [word for word in words if (word not in stopwords_list or word in whitelist) and len(word) > 1] 
            return " ".join(clean_words) 
        
        def stemming(self, input_text):
            porter = PorterStemmer()
            words = input_text.split() 
            stemmed_words = [porter.stem(word) for word in words]
            return " ".join(stemmed_words)
        
        def fit(self, X, y=None, **fit_params):
            return self
        
        def transform(self, X, **transform_params):
            clean_X = X.apply(self.remove_mentions).apply(self.remove_urls).apply(self.emoji_oneword).apply(self.remove_punctuation).apply(self.remove_digits).apply(self.to_lower).apply(self.remove_stopwords).apply(self.stemming)
            return clean_X

    To show how the cleaned text variable will look like, here’s a sample.

    为了显示清除后的文本变量的外观,这是一个示例。

    ct = CleanText()
    sr_clean = ct.fit_transform(df.text)
    sr_clean.sample(5)

    glad rt bet bird wish flown south winter

    高兴rt投注鸟希望飞过南冬天

    glad rt bet bird wish flown south winterpoint upc code check baggag tell luggag vacat day tri swimsuit

    高兴rt赌注鸟希望飞行南冬季 点upc代码检查baggag告诉行李vacat日三泳衣

    glad rt bet bird wish flown south winterpoint upc code check baggag tell luggag vacat day tri swimsuitvx jfk la dirti plane not standard

    高兴rt赌注鸟希望飞行南冬季 点upc代码检查baggag告诉行李vacat日三泳衣 vx肯尼迪洛杉矶肮脏飞机不标

    glad rt bet bird wish flown south winterpoint upc code check baggag tell luggag vacat day tri swimsuitvx jfk la dirti plane not standardtell mean work need estim time arriv pleas need laptop work thank

    高兴rt赌注鸟希望飞行南冬天 点upc代码检查baggag告诉行李vacat日三泳衣 vx jfk ladirti飞机不标准 告诉意思是工作需要估计时间到达pleas需要笔记本工作

    glad rt bet bird wish flown south winterpoint upc code check baggag tell luggag vacat day tri swimsuitvx jfk la dirti plane not standardtell mean work need estim time arriv pleas need laptop work thanksure busi go els airlin travel name kathryn sotelo

    高兴rt赌注鸟希望飞行南冬天 点upc代码检查baggag告诉行李vacat日三泳衣 vx jfk ladirti飞机不标准 告诉意思是工作需要估计时间到达pleas需要笔记本工作,感谢 确定busi go els airlin旅行名称凯瑟琳索特洛

    One side-effect of text cleaning is that some rows do not have any words left in their text. For the CountVectorizer and TfIdfVectorizer this does not pose a problem. Yet, for the Word2Vec algorithm this causes an error. There are different strategies to deal with these missing values.

    清除文本的一个副作用是某些行的文本中没有剩余单词。 对于CountVectorizerTfIdfVectorizer这不会造成问题。 但是,对于Word2Vec算法,这会导致错误。 有不同的策略来应对这些缺失的价值观。

    • Remove the complete row, but in a production environment this is not desirable.

      删除完整的行,但是在生产环境中,这是不希望的。
    • Impute the missing value with some placeholder text like *[no_text]*

      用一些占位符文本(例如* [no_text] *)来估算缺失值
    • When applying Word2Vec: use the average of all vectors

      应用Word2Vec时:使用所有向量的平均值

    Here we will impute with placeholder text.

    在这里,我们将使用占位符文本进行插补。

    empty_clean = sr_clean == ''
    print('{} records have no words left after text cleaning'.format(sr_clean[empty_clean].count()))
    sr_clean.loc[empty_clean] = '[no_text]'

    Now that we have the cleaned text of the tweets, we can have a look at what are the most frequent words. Below we’ll show the top 20 words. The most frequent word is “flight”.

    既然我们已经清除了推文的文本,我们就可以看看最常用的词是什么。 下面我们将显示前20个字。 最常见的词是“飞行”。

    cv = CountVectorizer()
    bow = cv.fit_transform(sr_clean)
    word_freq = dict(zip(cv.get_feature_names(), np.asarray(bow.sum(axis=0)).ravel()))
    word_counter = collections.Counter(word_freq)
    word_counter_df = pd.DataFrame(word_counter.most_common(20), columns = ['word', 'freq'])
    fig, ax = plt.subplots(figsize=(12, 10))
    sns.barplot(x="word", y="freq", data=word_counter_df, palette="PuBuGn_d", ax=ax)
    plt.show();

    创建测试数据 (Creating test data)

    To check the performance of the models we’ll need a test set. Evaluating on the train data would not be correct. You should not test on the same data used for training the model.

    要检查模型的性能,我们需要测试集。 评估火车数据是不正确的。 您不应在用于训练模型的相同数据上进行测试。

    First, we combine the TextCounts variables with the CleanText variable. Initially, I made the mistake to execute TextCounts and CleanText in the GridSearchCV. This took too long as it applies these functions each run of the GridSearch. It suffices to run them only once.

    首先,我们将TextCounts变量与CleanText变量结合在一起。 最初,我在GridSearchCV执行TextCounts和CleanText时犯了一个错误。 只要它在GridSearch的每次运行中应用这些功能,就需要花费很长时间。 只运行一次就足够了。

    df_model = df_eda
    df_model['clean_text'] = sr_clean
    df_model.columns.tolist()

    So df_model now contains several variables. But our vectorizers (see below) will only need the clean_text variable. The TextCountsvariables can be added as such. To select columns, I wrote the class ColumnExtractor below.

    因此df_model现在包含几个变量。 但是我们的矢量化程序(见下文)将只需要clean_text变量。 可以这样添加TextCounts变量。 为了选择列,我在下面编写了ColumnExtractor类。

    class ColumnExtractor(TransformerMixin, BaseEstimator):
        def __init__(self, cols):
            self.cols = cols
        def transform(self, X, **transform_params):
            return X[self.cols]
        def fit(self, X, y=None, **fit_params):
            return self
    X_train, X_test, y_train, y_test = train_test_split(df_model.drop('airline_sentiment', axis=1), df_model.airline_sentiment, test_size=0.1, random_state=37)

    超参数调整和交叉验证 (Hyperparameter tuning and cross-validation)

    As we will see below, the vectorizers and classifiers all have configurable parameters. To choose the best parameters, we need to test on a separate validation set. This validation set was not used during the training. Yet, using only one validation set may not produce reliable validation results. Due to chance, you might have a good model performance on the validation set. If you would split the data otherwise, you might end up with other results. To get a more accurate estimation, we perform cross-validation.

    正如我们将在下面看到的,矢量化器和分类器都具有可配置的参数。 为了选择最佳参数,我们需要在单独的验证集上进行测试。 训练期间未使用此验证集。 但是,仅使用一个验证集可能不会产生可靠的验证结果。 由于偶然的原因,您可能在验证集中具有良好的模型性能。 如果以其他方式拆分数据,则可能会导致其他结果。 为了获得更准确的估计,我们执行交叉验证。

    With cross-validation we split the data into a train and validation set many times. The evaluation metric is then averaged over the different folds. Luckily, GridSearchCV applies cross-validation out-of-the-box.

    通过交叉验证,我们将数据多次拆分为训练和验证集。 然后,将评估指标在不同折数上取平均值。 幸运的是,GridSearchCV开箱即用地应用了交叉验证。

    To find the best parameters for both a vectorizer and classifier, we create a Pipeline.

    为了找到矢量化器和分类器的最佳参数,我们创建了Pipeline

    评估指标 (Evaluation metrics)

    By default GridSearchCV uses the default scorer to compute the best_score_. For both the MultiNomialNb and LogisticRegression this default scoring metric is accuracy.

    默认情况下,GridSearchCV使用默认best_score_来计算best_score_ 。 对于MultiNomialNbLogisticRegression此默认评分指标均为准确性。

    In our function grid_vectwe additionally generate the classification_report on the test data. This provides some interesting metrics per target class. This might be more appropriate here. These metrics are the precision, recall and F1 score.

    在我们的函数grid_vect我们还根据测试数据生成了classification_report报告。 这为每个目标类别提供了一些有趣的指标。 这在这里可能更合适。 这些指标是精度,召回率和F1得分

    • Precision: Of all rows we predicted to be a certain class, how many did we correctly predict?

      精度在我们预测为某一类的所有行中,我们正确预测了几行?

    • Recall: Of all rows of a certain class, how many did we correctly predict?

      回想一下在某个类的所有行中,我们正确预测了多少行?

    • F1 score: Harmonic mean of Precision and Recall.

      F1得分精确度和召回率的谐波平均值。

    With the elements of the confusion matrix we can calculate Precision and Recall.

    使用混淆矩阵的元素,我们可以计算精度和召回率。

    # Based on http://scikit-learn.org/stable/auto_examples/model_selection/grid_search_text_feature_extraction.html
    def grid_vect(clf, parameters_clf, X_train, X_test, parameters_text=None, vect=None, is_w2v=False):
        
        textcountscols = ['count_capital_words','count_emojis','count_excl_quest_marks','count_hashtags'
                          ,'count_mentions','count_urls','count_words']
        
        if is_w2v:
            w2vcols = []
            for i in range(SIZE):
                w2vcols.append(i)
            features = FeatureUnion([('textcounts', ColumnExtractor(cols=textcountscols))
                                     , ('w2v', ColumnExtractor(cols=w2vcols))]
                                    , n_jobs=-1)
        else:
            features = FeatureUnion([('textcounts', ColumnExtractor(cols=textcountscols))
                                     , ('pipe', Pipeline([('cleantext', ColumnExtractor(cols='clean_text')), ('vect', vect)]))]
                                    , n_jobs=-1)
        
        pipeline = Pipeline([
            ('features', features)
            , ('clf', clf)
        ])
        
        # Join the parameters dictionaries together
        parameters = dict()
        if parameters_text:
            parameters.update(parameters_text)
        parameters.update(parameters_clf)
        # Make sure you have scikit-learn version 0.19 or higher to use multiple scoring metrics
        grid_search = GridSearchCV(pipeline, parameters, n_jobs=-1, verbose=1, cv=5)
        
        print("Performing grid search...")
        print("pipeline:", [name for name, _ in pipeline.steps])
        print("parameters:")
        pprint(parameters)
        t0 = time()
        grid_search.fit(X_train, y_train)
        print("done in %0.3fs" % (time() - t0))
        print()
        print("Best CV score: %0.3f" % grid_search.best_score_)
        print("Best parameters set:")
        best_parameters = grid_search.best_estimator_.get_params()
        for param_name in sorted(parameters.keys()):
            print("\t%s: %r" % (param_name, best_parameters[param_name]))
            
        print("Test score with best_estimator_: %0.3f" % grid_search.best_estimator_.score(X_test, y_test))
        print("\n")
        print("Classification Report Test Data")
        print(classification_report(y_test, grid_search.best_estimator_.predict(X_test)))
                            
        return grid_search

    GridSearchCV的参数网格 (Parameter grids for GridSearchCV)

    In the grid search, we will investigate the performance of the classifier. The set of parameters used to test the performance are specified below.

    在网格搜索中,我们将研究分类器的性能。 下面指定了用于测试性能的参数集。

    # Parameter grid settings for the vectorizers (Count and TFIDF)
    parameters_vect = {
        'features__pipe__vect__max_df': (0.25, 0.5, 0.75),
        'features__pipe__vect__ngram_range': ((1, 1), (1, 2)),
        'features__pipe__vect__min_df': (1,2)
    }
    
    # Parameter grid settings for MultinomialNB
    parameters_mnb = {
        'clf__alpha': (0.25, 0.5, 0.75)
    }
    
    # Parameter grid settings for LogisticRegression
    parameters_logreg = {
        'clf__C': (0.25, 0.5, 1.0),
        'clf__penalty': ('l1', 'l2')
    }

    分类器 (Classifiers)

    Here we will compare the performance of a MultinomialNBand LogisticRegression.

    在这里,我们将比较MultinomialNBLogisticRegression的性能。

    mnb = MultinomialNB()
    logreg = LogisticRegression()

    CountVectorizer (CountVectorizer)

    To use words in a classifier, we need to convert the words to numbers. Sklearn’s CountVectorizer takes all words in all tweets, assigns an ID and counts the frequency of the word per tweet. We then use this bag of words as input for a classifier. This bag of words is a sparse data set. This means that each record will have many zeroes for the words not occurring in the tweet.

    要在分类器中使用单词,我们需要将单词转换为数字。 Sklearn的CountVectorizer接收所有推文中的所有单词,分配一个ID并计算每条推文中单词的出现频率。 然后,我们将这袋单词用作分类器的输入。 这个词袋是一个稀疏的数据集。 这意味着对于未在推文中出现的单词,每个记录将具有多个零。

    countvect = CountVectorizer()
    # MultinomialNB
    best_mnb_countvect = grid_vect(mnb, parameters_mnb, X_train, X_test, parameters_text=parameters_vect, vect=countvect)
    joblib.dump(best_mnb_countvect, '../output/best_mnb_countvect.pkl')
    # LogisticRegression
    best_logreg_countvect = grid_vect(logreg, parameters_logreg, X_train, X_test, parameters_text=parameters_vect, vect=countvect)
    joblib.dump(best_logreg_countvect, '../output/best_logreg_countvect.pkl')

    TF-IDF矢量化器 (TF-IDF Vectorizer)

    One issue with CountVectorizer is that there might be words that occur frequently. These words might not have discriminatory information. Thus they can be removed. TF-IDF (term frequency — inverse document frequency)can be used to down-weight these frequent words.

    CountVectorizer的一个问题是可能经常出现单词。 这些词可能没有歧视性信息。 因此可以将它们删除。 TF-IDF(术语频率-逆文档频率)可用于降低这些常用单词的权重。

    tfidfvect = TfidfVectorizer()
    # MultinomialNB
    best_mnb_tfidf = grid_vect(mnb, parameters_mnb, X_train, X_test, parameters_text=parameters_vect, vect=tfidfvect)
    joblib.dump(best_mnb_tfidf, '../output/best_mnb_tfidf.pkl')
    # LogisticRegression
    best_logreg_tfidf = grid_vect(logreg, parameters_mnb, X_train, X_test, parameters_text=parameters_vect, vect=tfidfvect)
    joblib.dump(best_logreg_tfidf, '../output/best_logreg_tfidf.pkl')

    Word2Vec (Word2Vec)

    Another way of converting the words to numerical values is to use Word2Vec. Word2Vec maps each word in a multi-dimensional space. It does this by taking into account the context in which a word appears in the tweets. As a result, words that are similar are also close to each other in the multi-dimensional space.

    将单词转换为数值的另一种方法是使用Word2Vec 。 Word2Vec映射多维空间中的每个单词。 它通过考虑单词在推文中出现的上下文来做到这一点。 结果,相似的词在多维空间中也彼此接近。

    The Word2Vec algorithm is part of the gensim package.

    Word2Vec算法是gensim程序包的一部分。

    The Word2Vec algorithm uses lists of words as input. For that purpose, we use the word_tokenize method of the the nltk package.

    Word2Vec算法使用单词列表作为输入。 为此,我们使用nltk包的word_tokenize方法。

    SIZE = 50
    X_train['clean_text_wordlist'] = X_train.clean_text.apply(lambda x : word_tokenize(x))
    X_test['clean_text_wordlist'] = X_test.clean_text.apply(lambda x : word_tokenize(x))
    model = gensim.models.Word2Vec(X_train.clean_text_wordlist
    , min_count=1
    , size=SIZE
    , window=5
    , workers=4)
    model.most_similar('plane', topn=3)

    The Word2Vec model provides a vocabulary of the words in all the tweets. For each word you also have its vector values. The number of vector values is equal to the chosen size. These are the dimensions on which each word is mapped in the multi-dimensional space. Words with an occurrence less than min_count are not kept in the vocabulary.

    Word2Vec模型提供所有推文中的单词词汇。 对于每个单词,您还具有其向量值。 向量值的数量等于所选的大小。 这些是每个单词在多维空间中所映射的维度。 出现次数少于min_count的单词不会保留在词汇表中。

    A side effect of the min_count parameter is that some tweets could have no vector values. This is would be the case when the word(s) in the tweet occur in less than min_count tweets. Due to the small corpus of tweets, there is a risk of this happening in our case. Thus we set the min_count value equal to 1.

    min_count参数的副作用是某些推文可能没有向量值。 如果tweet中的单词少于min_count的情况就是这种情况 鸣叫。 由于推文的语料很少,因此在我们的案例中有发生这种情况的风险。 因此,我们将min_count值设置为等于1。

    The tweets can have a different number of vectors, depending on the number of words it contains. To use this output for modeling we will calculate the average of all vectors per tweet. As such we will have the same number (i.e. size) of input variables per tweet.

    这些推文可以具有不同数量的向量,具体取决于它包含的单词数。 要使用此输出进行建模,我们将计算每条推文所有向量的平均值。 这样,每条推文我们将具有相同数量(即大小)的输入变量。

    We do this with the function compute_avg_w2v_vector. In this function we also check whether the words in the tweet occur in the vocabulary of the Word2Vec model. If not, a list filled with 0.0 is returned. Else the average of the word vectors.

    我们使用函数compute_avg_w2v_vector进行此compute_avg_w2v_vector 。 在此功能中,我们还将检查tweet中的单词是否出现在Word2Vec模型的词汇表中。 如果不是,则返回填充为0.0的列表。 否则,单词向量的平均值。

    def compute_avg_w2v_vector(w2v_dict, tweet):
        list_of_word_vectors = [w2v_dict[w] for w in tweet if w in w2v_dict.vocab.keys()]
        
        if len(list_of_word_vectors) == 0:
            result = [0.0]*SIZE
        else:
            result = np.sum(list_of_word_vectors, axis=0) / len(list_of_word_vectors)
            
        return result
    X_train_w2v = X_train['clean_text_wordlist'].apply(lambda x: compute_avg_w2v_vector(model.wv, x))
    X_test_w2v = X_test['clean_text_wordlist'].apply(lambda x: compute_avg_w2v_vector(model.wv, x))

    This gives us a Series with a vector of dimension equal to SIZE. Now we will split this vector and create a DataFrame with each vector value in a separate column. That way we can concatenate the Word2Vec variables to the other TextCounts variables. We need to reuse the index of X_train and X_test. Otherwise this will give issues (duplicates) in the concatenation later on.

    这给我们一个序列,其向量的尺寸等于SIZE 。 现在,我们将分割此向量,并使用单独列中的每个向量值创建一个DataFrame。 这样,我们可以将Word2Vec变量连接到其他TextCounts变量。 我们需要重用X_trainX_test的索引。 否则,这将在以后的连接中产生问题(重复)。

    X_train_w2v = pd.DataFrame(X_train_w2v.values.tolist(), index= X_train.index)
    X_test_w2v = pd.DataFrame(X_test_w2v.values.tolist(), index= X_test.index)
    # Concatenate with the TextCounts variables
    X_train_w2v = pd.concat([X_train_w2v, X_train.drop(['clean_text', 'clean_text_wordlist'], axis=1)], axis=1)
    X_test_w2v = pd.concat([X_test_w2v, X_test.drop(['clean_text', 'clean_text_wordlist'], axis=1)], axis=1)

    We only consider LogisticRegression as we have negative values in the Word2Vec vectors. MultinomialNB assumes that the variables have a multinomial distribution. So they cannot contain negative values.

    我们仅考虑LogisticRegression,因为在Word2Vec向量中具有负值。 MultinomialNB假定变量具有多项式分布 。 因此它们不能包含负值。

    best_logreg_w2v = grid_vect(logreg, parameters_logreg, X_train_w2v, X_test_w2v, is_w2v=True)
    joblib.dump(best_logreg_w2v, '../output/best_logreg_w2v.pkl')

    结论 (Conclusion)

    • Both classifiers achieve the best results when using the features of the CountVectorizer

      使用CountVectorizer的功能时,两个分类器均能获得最佳结果
    • Logistic Regression outperforms the Multinomial Naive Bayes classifier

      Logistic回归优于多项式朴素贝叶斯分类器
    • The best performance on the test set comes from the LogisticRegression with features from CountVectorizer.

      测试集上的最佳性能来自LogisticRegression和CountVectorizer的功能。

    最佳参数 (Best parameters)

    • C value of 1

      C值1
    • L2 regularization

      L2正则化
    • max_df: 0.5 or maximum document frequency of 50%.

      max_df:0.5或最大文档频率为50%。
    • min_df: 1 or the words need to appear in at least 2 tweets

      min_df:1或单词需要出现在至少2条推文中
    • ngram_range: (1, 2), both single words as bi-grams are used

      ngram_range:(1,2),两个单词都作为双字母组使用

    评估指标 (Evaluation metrics)

    • A test accuracy of 81,3%. This is better than a baseline performance of predicting the majority class (here a negative sentiment) for all observations. The baseline would give 63% accuracy.

      测试精度为81.3%。 这好于预测所有观察结果的多数类别(此处为负面情绪)的基准性能。 基线将给出63%的准确性。
    • The Precision is rather high for all three classes. For instance, of all cases that we predict as negative, 80% is negative.

      这三个类别的精度都很高。 例如,在我们预测为负面的所有情况中,80%为负面。
    • The Recall for the neutral class is low. Of all neutral cases in our test data, we only predict 48% as being neutral.

      中立类别的召回率很低。 在我们的测试数据中,所有中性案例中,我们仅预测48%为中性。

    在新推文上应用最佳模型 (Apply the best model on new tweets)

    For the fun, we will use the best model and apply it to some new tweets that contain @VirginAmerica. I selected 3 negative and 3 positive tweets by hand.

    为了好玩,我们将使用最佳模型并将其应用于包含@VirginAmerica的一些新推文中。 我手动选择了3条负面和3条正面的推文。

    Thanks to the GridSearchCV, we now know what are the best hyperparameters. So now we can train the best model on all training data, including the test data that we split off before.

    多亏了GridSearchCV,我们现在知道了最好的超参数。 因此,现在我们可以在所有训练数据(包括我们之前拆分的测试数据)上训练最佳模型。

    textcountscols = ['count_capital_words','count_emojis','count_excl_quest_marks','count_hashtags'
    ,'count_mentions','count_urls','count_words']
    features = FeatureUnion([('textcounts', ColumnExtractor(cols=textcountscols))
    , ('pipe', Pipeline([('cleantext', ColumnExtractor(cols='clean_text'))
    , ('vect', CountVectorizer(max_df=0.5, min_df=1, ngram_range=(1,2)))]))]
    , n_jobs=-1)
    pipeline = Pipeline([
    ('features', features)
    , ('clf', LogisticRegression(C=1.0, penalty='l2'))
    ])
    best_model = pipeline.fit(df_model.drop('airline_sentiment', axis=1), df_model.airline_sentiment)
    # Applying on new positive tweets
    new_positive_tweets = pd.Series(["Thank you @VirginAmerica for you amazing customer support team on Tuesday 11/28 at @EWRairport and returning my lost bag in less than 24h! #efficiencyiskey #virginamerica"
    ,"Love flying with you guys ask these years. Sad that this will be the last trip ? @VirginAmerica #LuxuryTravel"
    ,"Wow @VirginAmerica main cabin select is the way to fly!! This plane is nice and clean & I have tons of legroom! Wahoo! NYC bound! ✈️"])
    df_counts_pos = tc.transform(new_positive_tweets)
    df_clean_pos = ct.transform(new_positive_tweets)
    df_model_pos = df_counts_pos
    df_model_pos['clean_text'] = df_clean_pos
    best_model.predict(df_model_pos).tolist()
    # Applying on new negative tweets
    new_negative_tweets = pd.Series(["@VirginAmerica shocked my initially with the service, but then went on to shock me further with no response to what my complaint was. #unacceptable @Delta @richardbranson"
    ,"@VirginAmerica this morning I was forced to repack a suitcase w a medical device because it was barely overweight - wasn't even given an option to pay extra. My spouses suitcase then burst at the seam with the added device and had to be taped shut. Awful experience so far!"
    ,"Board airplane home. Computer issue. Get off plane, traverse airport to gate on opp side. Get on new plane hour later. Plane too heavy. 8 volunteers get off plane. Ohhh the adventure of travel ✈️ @VirginAmerica"])
    df_counts_neg = tc.transform(new_negative_tweets)
    df_clean_neg = ct.transform(new_negative_tweets)
    df_model_neg = df_counts_neg
    df_model_neg['clean_text'] = df_clean_neg
    best_model.predict(df_model_neg).tolist()

    The model classifies all tweets correctly. A larger test set should be used to assess the model’s performance. But on this small data set it does what we are aiming for. I hope you enjoyed reading this story. If you did, feel free to share it.

    该模型将所有推文正确分类。 应该使用更大的测试集来评估模型的性能。 但是,在这个小的数据集上,它确实可以实现我们的目标。 希望您喜欢阅读这个故事。 如果您愿意,可以随时分享。

    翻译自: https://www.freecodecamp.org/news/sentiment-analysis-with-text-mining/

    文本挖掘 情感分析

    展开全文
  • 采用基于文本挖掘技术的风险管理软件可以显着提高降低风险的能力,实现数千个来源的文本文档的完整管理。l知识管理管理大量文本文档时,一个很大的问题就是——无法快速地找到重要的信息。例如,对于医疗行业来说,...

    l

    风险管理

    无论是哪个行业,风险分析不足通常都是失败的主要原因,在金融行业尤其如此。采用基于文本挖掘技术的风险管理软件可以显着提高降低风险的能力,实现数千个来源的文本文档的完整管理。

    l

    知识管理

    管理大量文本文档时,一个很大的问题就是——无法快速地找到重要的信息。例如,对于医疗行业来说,研发一个新的产品可能同时需要近十年的基因组学和分子技术研究报告。此时,基于文本挖掘的知识管理软件为此种“信息过剩”情况提供了有效的解决方案。

    l

    网络犯罪预防

    如今,互联网的匿名性和网络交流的便利性使得网络犯罪的数量大大提升。但是,文本挖掘情报和反犯罪应用的发展让政府能更好地预防此类案件的发生。

    l

    客户服务

    文本挖掘和自然语言处理是在客户服务领域常被使用的技术。如今,利用调查、故障单、用户反馈等有效信息,文本挖掘技术可以用来改善客户体验,为客户提供快速高效的解决方案,以期减少客户对帮助中心的依赖程度。

    l

    通过索赔调查进行欺诈检测

    对任何采集信息作为文本的行业来说,文本挖掘是必不可少的技术之一。保险公司正在利用文本挖掘技术,将文本分析结果与结构化数据相结合,以防止欺诈和迅速处理索赔。

    l

    情境广告

    数字广告是文本挖掘的新兴领域。Admantx公司将文本挖掘技术定为上下文重定向的核心引擎,并因此取得了巨大的成功。与传统的基于cookie的方法相比,上下文广告有更高的准确性,并完全保护了客户的隐私。

    l

    商业智能

    在公司的决策制定上,文本挖掘技术起了极大的作用。该技术使得数据分析师面对庞大的内部和开源数据时,能快速地得到答案。例如,诸如Cogito的智能平台能够监控成千个数据来源并分析大量数据,以便从中提取相关信息。

    l

    垃圾邮件过滤

    对于互联网提供商来说,垃圾邮件增加了服务管理和软件更新的成本;对于用户来说,垃圾邮件是病毒的入口,是浪费生产时间的元凶。文本挖掘技术可以提高基于统计的过滤方法的有效性,以达到过滤垃圾邮件的目的。

    l

    社交媒体数据分析

    如今,社交媒体是大多数非结构化数据的产源地。公司可以使用这些非结构化数据去分析和预测客户需求并了解客户对其品牌的看法。通过分析大量非结构化数据,文本分析能够提取意见,了解情感和品牌之间的关系,以帮助公司的发展。

    由灵玖软件研发的NLPIR大数据语义智能分析平台是满足大数据挖掘对语法、词法和语义的综合应用。NLPIR大数据语义智能分析平台是根据中文数据挖掘的综合需求,融合了网络精准采集、自然语言理解、文本挖掘和语义搜索的研究成果,并针对互联网内容处理的全技术链条的共享开发平台。

    NLPIR大数据语义智能分析平台主要有精准采集、文档转化、新词发现、批量分词、语言统计、文本聚类、文本分类、摘要实体、智能过滤、情感分析、文档去重、全文检索、编码转换等十余项功能模块,平台提供了客户端工具,云服务与二次开发接口等多种产品使用形式。各个中间件API可以无缝地融合到客户的各类复杂应用系统之中,可兼容Windows,Linux, Android,Maemo5, FreeBSD等不同操作系统平台,可以供Java,Python,C,C#等各类开发语言使用。

    展开全文
  • 2.1 实用意义上的文本挖掘文本挖掘是指采用大量非结构化语言并快速提取可能影响利益相关者决策的有用且新颖的见解的能力。(视文本挖掘为手段)❓文本挖掘适合使用传统的ML工作流程吗? 传统上,机器学习工作流程...
  • 文本挖掘浅述

    千次阅读 2018-06-29 08:05:18
    文本挖掘是一个融合了多学科的交叉领域,对文本挖掘的研究需要将自然语言处理、数据挖掘、信息检索、机器学习等众多领域的知识相结合。文本挖掘是对大量文本信息源的内容进行预处理、特征提取等,转化为数据集合后,...

空空如也

1 2 3 4 5 ... 20
收藏数 5,662
精华内容 2,264
关键字:

文本挖掘