Python情感分析实战演练——以特朗普的推文为例

姓名:闫伟  学号:15020150038

转载自:https://zhuanlan.zhihu.com/p/47341761

【嵌牛导读】:自然语言处理专家 Rodolfo Ferro 可以通过Python来对一个人的情感进行分析。

【嵌牛鼻子】:Python Pandas Numpy 

【嵌牛提问】:如何用 Python 完成情感分析

【嵌牛正文】:

在本文我们会学习以下三方面内容:

使用 Tweepy 提取 Twitter 数据,学习如何用 Pandas 处理数据

使用 Numpy,Matplotlib 和 Seaborn 完成一些简单的统计和可视化工作

使用 Textblob 对提取的特朗普的推文进行情感分析

项目代码地址见文末。

我们需要什么?

首先,我们需要安装 Python。

我(作者 Rodolfo Ferro——译者注)使用的是 Python 3.6,不过所有代码在 Python 2.7 上应该也都能运行。强烈推荐安装 Anaconda,这是个非常有用的 Python 包管理平台,内置了很多有用的工具,比如 Jupyter Notebooks。我会用 Jupyter Notebook 讲解本文的代码,如果你在用其它的文本编辑器,也能跑一些简单的脚本,只是需要做些适配(不难)。

我们需要安装的环境依赖包括:

Numpy:使用 Python 进行科学计算的基本工具包。此外,Numpy 也可以用作通用数据的高效多维容器。

Pandas:开源库,提供高性能、易于使用的数据结构和数据分析工具。

Tweepy:一个使用方便的 Python 库,用于获取 Twitter API。

Matplotlib:一个 Python 2D 绘图库,可生成多种格式的高质量图形。

Seaborn:基于 matplotlib 的 Python 可视化库,提供的 API 可绘制美观的统计图形。

Textblob:用于执行文本数据处理的 Python 库,提供的 API 可执行常见的自然语言处理任务。

以上环境依赖都通过 pip 安装。待安装完所有软件包后,开始写代码!

1 提取推特数据(Tweepy+Pandas)

导入库

这一步很简单,将如下代码复制到你的 Jupyter Notebook 上就行:

# General:import tweepy          # 用来使用推特APIimport pandas as pd    # 用来处理数据import numpy as np      # 用来计算数字# 用来绘图和可视化from IPython.display import displayimport matplotlib.pyplot as pltimport seaborn as sns%matplotlib inline

很好!下载运行该代码块,进入下个部分。

创建一个推特应用

要想提取推文用于分析,我们需要登录我们的推特开发者账号,并创建一个应用。进行这项操作的网站地址是:https://apps.twitter.com/。没有账号的话,就注册一个。

从这个我们在创建的应用中,会将如下信息保存为一个叫credentials.py的脚本中:

消费方密钥(Consumer Key)

消费方机密(Consumer Secret)

访问令牌密钥(Access Token Key)

访问令牌机密(Access Token Secret)

该操作的代码示例:

# Twitter App access keys for @user# Consume:CONSUMER_KEY    = ''CONSUMER_SECRET = ''# Access:ACCESS_TOKEN  = ''ACCESS_SECRET = ''

额外创建这个文件的原因是我们只想导出这些变量的值,但在我们的主代码(Notebook)中看不见。现在我们可以使用 Twitter API 了,为此需要创建一个允许我们进行密钥身份验证的函数。我们会在另一个代码单元中调用此函数,并运行它:

# 导入访问密钥:from credentials import *    

# This will allow us to use the keys as variables

# API设置:def twitter_setup():    """    Utility function to setup the Twitter's API    with our access keys provided.    """    # 用密钥授权和访问:    

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)    

auth.set_access_token(ACCESS_TOKEN, ACCESS_SECRET)    

# 用访问返回API:    api = tweepy.API(auth)return api

目前为止还是很简单的,对吧?在下个部分我们开始提取推文。

推文提取

现在,我们创建一个函数来设置推特 API,可以用该函数创建一个“extractor”对象。然后我们会使用 Tweepy 的函数 extractor.user_timeline(screen_name, count) 从 screen_name 的用户中提取 count 数量的推文。

在文章标题里也说过了,我们本文选择川普的推文作为素材,从中提取数据进行情感分析。没办法,谁叫他推特发的那么频繁呢。

提取推特数据的方法如下:

# 创建一个extractor对象extractor = twitter_setup()# 创建一个推文列表tweets = extractor.user_timeline(screen_name="realDonaldTrump", count=200)print("Number of tweets extracted: {}.\n".format(len(tweets)))# 打印最近的5条推文print("5 recent tweets:\n")for tweet in tweets[:5]:    print(tweet.text)print()

有了这个,我们会得到一个与之类似的输出,并且将该输出和推特账号进行比较(用以检查我们是否保持一致):

提取的推文数量:200

最近的 5 条推文:

On behalf of @FLOTUS Melania & myself, THANK YOU for today's update & GREAT WORK! #SouthernBaptist @SendRelief,…https://t.co/4yZCeXCt6n

I will be going to Texas and Louisiana tomorrow with First Lady. Great progress being made! Spending weekend working at White House.

Stock Market up 5 months in a row!

'President Donald J. Trump Proclaims September 3, 2017, as a National Day of Prayer' #HurricaneHarvey #PrayForTexas…https://t.co/tOMfFWwEsN

Texas is healing fast thanks to all of the great men & women who have been working so hard. But still so much to do. Will be back tomorrow!

现在我们有了提取程序以及提取后的数据,均位于 tweets 变量中。这里必须说一句,该列表中的每个元素都是一个来自 Tweepy 的 tweet 对象,我们会在下部分学习怎样处理该数据。

创建(Pandas)DataFrame

现在我们用初始信息构造一个 Pandas DataFrame,从而能以很容易的方式处理信息。

Ipython 的 display 函数会以很浅显易懂的方式绘制出输出结果,dataframe 的 head 方法则能让我们可视化该 dataframe 的前 5 个元素(或作为参数传递的第一个参数)。

所以,使用 Python 的列表表达式:

# 创建一个Pandas dataframedata = pd.DataFrame(data=[tweet.text for tweet in tweets], columns=['Tweets'])# 展示dataframe的前10个元素display(data.head(10))

这会创建一个与此类似的输出:

<img src="https://pic1.zhimg.com/v2-48c19a5087b82cbcfb899266c88437a4_b.jpg" data-caption="" data-size="normal" data-rawwidth="707" data-rawheight="625" class="origin_image zh-lightbox-thumb" width="707" data-original="https://pic1.zhimg.com/v2-48c19a5087b82cbcfb899266c88437a4_r.jpg">

现在我们获得了一个数据排列有序的干净图表。

一个有趣的地方就是 Tweepy 中 tweets 结构具有的内部方法数量:

# 单个tweet对象中的内部方法print(dir(tweets[0]))

这会输出如下元素列表:

['class', 'delattr', 'dict', 'dir', 'doc', 'eq', 'format', 'ge', 'getattribute', 'getstate', 'gt', 'hash', 'init', 'init_subclass', 'le', 'lt', 'module', 'ne', 'new', 'reduce', 'reduce_ex', 'repr', 'setattr', 'sizeof', 'str', 'subclasshook', 'weakref', '_api', '_json', 'author', 'contributors', 'coordinates', 'created_at', 'destroy', 'entities', 'favorite', 'favorite_count', 'favorited', 'geo', 'id', 'id_str', 'in_reply_to_screen_name', 'in_reply_to_status_id', 'in_reply_to_status_id_str', 'in_reply_to_user_id', 'in_reply_to_user_id_str', 'is_quote_status', 'lang', 'parse', 'parse_list', 'place', 'possibly_sensitive', 'retweet', 'retweet_count', 'retweeted', 'retweets', 'source', 'source_url', 'text', 'truncated', 'user']

这里比较有意思的部分就是每条推文中所包含的元数据数量,如果我们想获取像发推日期或发推源这样的数据,我们就可以用该属性获取信息。下面是一个例子:

print(tweets[0].id)print(tweets[0].created_at)print(tweets[0].source)print(tweets[0].favorite_count)print(tweets[0].retweet_count)print(tweets[0].geo)print(tweets[0].coordinates)print(tweets[0].entities)

获取一个这样的输出:

9037781308501319702017-09-02 00:34:32Twitter for iPhone245725585NoneNone{'hashtags': [{'text': 'SouthernBaptist', 'indices': [90, 106]}], 'symbols': [], 'user_mentions': [{'screen_name': 'FLOTUS', 'name': 'Melania Trump', 'id': 818876014390603776, 'id_str': '818876014390603776', 'indices': [13, 20]}, {'screen_name': 'sendrelief', 'name': 'Send Relief', 'id': 3228928584, 'id_str': '3228928584', 'indices': [107, 118]}], 'urls': [{'url': 'https://t.co/4yZCeXCt6n', 'expanded_url': 'https://twitter.com/i/web/status/903778130850131970', 'display_url': 'twitter.com/i/web/status/9…', 'indices': [121, 144]}]}

现在我们整理相关数据,并将其添加到我们的dataframe中。

将相关信息添加至Dataframe中

可以看到,我们能从单条推特中获取很多数据,但并非所有数据都对我们有用。我们这里只需要向 dataframe 中添加部分数据。我们会使用 Python 列表表达式,并为 dataframe 添加一个新列,只需在方括号之间添加内容名称并分配内容即可。代码如下:

# 添加相关数据data['len']  = np.array([len(tweet.text) for tweet in tweets])data['ID']  = np.array([tweet.id for tweet in tweets])data['Date'] = np.array([tweet.created_at for tweet in tweets])data['Source'] = np.array([tweet.source for tweet in tweets])data['Likes']  = np.array([tweet.favorite_count for tweet in tweets])data['RTs']    = np.array([tweet.retweet_count for tweet in tweets])

要想再次展示 dataframe 查看变化,只需:

# 展示dataframe中的前10个元素display(data.head(10))

<img src="https://pic1.zhimg.com/v2-29b2bcaec6fe11c18d5509983be50d54_b.jpg" data-caption="" data-size="normal" data-rawwidth="800" data-rawheight="1301" class="origin_image zh-lightbox-thumb" width="800" data-original="https://pic1.zhimg.com/v2-29b2bcaec6fe11c18d5509983be50d54_r.jpg">

现在我们提取出了数据,也将数据进行了整理,方便处理。下面我们进一步处理数据,可视化一些图表,并收集一些统计数据。本文的第一部分就算完成了。

2 可视化和基本统计

平均值和受欢迎度

我们首先计算一些基本的统计数据,比如所有推文字符长度的平均值,被点赞和转发次数最多的推文,等等。

现在我们只需在代码下面添加一些输入代码和输出。

使用 Numpy 获得平均值:

# 提取长度平均值mean = np.mean(data['len'])print("The lenght's average in tweets: {}".format(mean))

推文的平均长度为:125.925

为了能提取更多数据,我们使用 Pandas 的一些功能:

# 提取点赞数和转发数最多的推文fav_max = np.max(data['Likes'])rt_max  = np.max(data['RTs'])fav = data[data.Likes == fav_max].index[0]rt  = data[data.RTs == rt_max].index[0]# 点赞数最多:print("The tweet with more likes is: \n{}".format(data['Tweets'][fav]))print("Number of likes: {}".format(fav_max))print("{} characters.\n".format(data['len'][fav]))# 转发数最多:print("The tweet with more retweets is: \n{}".format(data['Tweets'][rt]))print("Number of retweets: {}".format(rt_max))print("{} characters.\n".format(data['len'][rt]))

点赞更多的推文是:

The United States condemns the terror attack in Barcelona, Spain, and will do whatever is necessary to help. Be tough & strong, we love you!

点赞数:222205

字数:144

转发数更多的推文是:

The United States condemns the terror attack in Barcelona, Spain, and will do whatever is necessary to help. Be tough & strong, we love you!

转发数:66099

字数:144

一个很常见的事就是“点赞数更多的推文,转发数同样更多”,不过也不是每回都这样。我们使用 Numpy 的 max 函数找出‘likes’列里面的最大点赞数和‘RTs’中的最大转发数,只需寻找两列中每一列里满足最大值条件的索引。由于转发数与点赞数(最大值)相同的推文可能不止一条,我们只需取用找到的第一个结果,所以我们使用 .index[0] 将索引分配给变量 fa 和 rt。要想输出满足条件的推文,我们访问数据的方式和访问矩阵或任何索引对象的方式是一样的。

下面准备画点图。

时序

Pandas 内置了用于时序的对象。因为我们有带有发推日期的整个向量,所以分别按照推文长度、点赞数和转发数来构造数据的时序。

操作方式为:

# 创建数据的时序tlen = pd.Series(data=data['len'].values, index=data['Date'])tfav = pd.Series(data=data['Likes'].values, index=data['Date'])tret = pd.Series(data=data['RTs'].values, index=data['Date'])

如果我们想绘制出时序,可以使用 Pandas 在对象中的方法。绘制方法如下:

# 随着时间推移的长度tlen.plot(figsize=(16,4), color='r');

这会得到如下输出:

<img src="https://pic1.zhimg.com/v2-e48b414acba2937e14f2b6247d4eba4c_b.jpg" data-caption="" data-size="normal" data-rawwidth="880" data-rawheight="254" class="origin_image zh-lightbox-thumb" width="880" data-original="https://pic1.zhimg.com/v2-e48b414acba2937e14f2b6247d4eba4c_r.jpg">

在同一图表中绘出点赞数和转发数对比的方式为:

tfav.plot(figsize=(16,4), label="Likes", legend=True)tret.plot(figsize=(16,4), label="Retweets", legend=True);

得到如下输出:

<img src="https://pic2.zhimg.com/v2-08589dc64053f8b6b785f07f72330281_b.jpg" data-caption="" data-size="normal" data-rawwidth="880" data-rawheight="245" class="origin_image zh-lightbox-thumb" width="880" data-original="https://pic2.zhimg.com/v2-08589dc64053f8b6b785f07f72330281_r.jpg">

以饼状图表示发推源

本文的第二部分我们就快完成了,现在我们用饼状图绘制出发推设备来源,因为我们发现并非每条推文都是相同的来源。首先清洗所有来源:

# 获得所有可能来源sources = []for source in data['Source']:    if source not in sources:        sources.append(source)# 打印来源列表print("Creation of content sources:")for source in sources:print("* {}".format(source))

获得了如下输出后,我们意识到特朗普的推文基本来自以下两个来源:

iPhone

Media Studio

现在我们计算每个来源的数量,并为之创建一个饼状图。有些朋友可能注意到这段代码不是很好,但是先凑合看吧,我是凌晨 4 点多熬夜写的···

# 创建映射到标签的Numpy向量percent = np.zeros(len(sources))for source in data['Source']:    for index in range(len(sources)):        if source == sources[index]:            percent[index] += 1            passpercent /= 100# 饼状图pie_chart = pd.Series(percent, index=sources, name='Sources')pie_chart.plot.pie(fontsize=11, autopct='%.2f', figsize=(6, 6));

这里会得到如下输出:

<img src="https://pic3.zhimg.com/v2-025363815167c86b86b1e884a5ef1f46_b.jpg" data-caption="" data-size="normal" data-rawwidth="880" data-rawheight="334" class="origin_image zh-lightbox-thumb" width="880" data-original="https://pic3.zhimg.com/v2-025363815167c86b86b1e884a5ef1f46_r.jpg">

然后就可以看到发推源的占比。

接下来完成本文的最后一部分——情感分析。

3 情感分析

导入 Textblob

前面在开头说过,Textblob 能让我们以很简单的方式执行情感分析。我们也会使用 Python 中的 re 库,可用于处理正则表达式。这里我得分享两个实用函数:a) 清洗文本(意思是任何与字母数字值不同的符号将重新映射到满足此条件的新符号)b) 创建一个分类器,在清洗文本后分析每条推文的归一性。这里就不再深入解释每种函数的具体工作原理了,因为那就扯远了,看官方文档应该就明白了:

https://docs.python.org/3/library/re.html

代码如下:

from textblob import TextBlobimport redef clean_tweet(tweet):    '''    Utility function to clean the text in a tweet by removing    links and special characters using regex.    '''    return ' '.join(re.sub("(@[A-Za-z0-9]+)|([^0-9A-Za-z \t])|(\w+:\/\/\S+)", " ", tweet).split())def analize_sentiment(tweet):    '''    Utility function to classify the polarity of a tweet    using textblob.    '''    analysis = TextBlob(clean_tweet(tweet))    if analysis.sentiment.polarity > 0:        return 1    elif analysis.sentiment.polarity == 0:        return 0    else:        return -1

因为 Textblob 提供训练好的分析器,所以事情就好办多了。Textblob 能使用多种不同的自然语言处理模型。如果你想训练你自己的分类器(或者想知道它的工作方式),点击这个链接:

https://textblob.readthedocs.io/en/dev/classifiers.html

效果应该差不多,因为我们用的是预训练模型。

总之我们回到代码部分,向数据中再添加一列。这个列会包含语义分析,我们可以绘制出 dataframe 查看更新结果:

# 创建包含分析结果的列:data['SA'] = np.array([ analize_sentiment(tweet) for tweet in data['Tweets'] ])# 展示添加新列后的dataframedisplay(data.head(10))

获取新的输出:

<img src="https://pic3.zhimg.com/v2-a91f8cf0de2b6de75d1b2b6fd6572f7a_b.jpg" data-caption="" data-size="normal" data-rawwidth="1027" data-rawheight="1695" class="origin_image zh-lightbox-thumb" width="1027" data-original="https://pic3.zhimg.com/v2-a91f8cf0de2b6de75d1b2b6fd6572f7a_r.jpg">

可以看到,最后一列包含了语义分析结果(SA)。现在只需检查结果。

分析结果

我们以简单的方式检查语义分析结果,分别计算出包含正面情绪、负面情绪和中性情绪的推文,及其比例。

pos_tweets = [ tweet for index, tweet in enumerate(data['Tweets']) if data['SA'][index] > 0]neu_tweets = [ tweet for index, tweet in enumerate(data['Tweets']) if data['SA'][index] == 0]neg_tweets = [ tweet for index, tweet in enumerate(data['Tweets']) if data['SA'][index] < 0]

现在我们获得了列表,只需打印出比例:

print("Percentage of positive tweets: {}%".format(len(pos_tweets)*100/len(data['Tweets'])))print("Percentage of neutral tweets: {}%".format(len(neu_tweets)*100/len(data['Tweets'])))print("Percentage de negative tweets: {}%".format(len(neg_tweets)*100/len(data['Tweets'])))

获得如下结果:

具有正面情绪的推文:51.0%

具有中性情绪的推文:27.0%

具有负面情绪的推文:22.0%

&amp;lt;img src="https://pic2.zhimg.com/v2-e87e903018d10bac0c11703f0e268fd5_b.jpg" data-caption="" data-size="small" data-rawwidth="630" data-rawheight="420" class="origin_image zh-lightbox-thumb" width="630" data-original="https://pic2.zhimg.com/v2-e87e903018d10bac0c11703f0e268fd5_r.jpg"&amp;gt;

不过考虑到我们只收集了特朗普推特账户的 200 条推文,如果想获得更高的准确度,可以收集更多推文。

从本文我们可以看到,使用 Python 能够完成提取数据、处理数据、可视化数据和分析数据这一套流程。希望本文能对大家使用 Python 进行文本处理有所帮助。

本项目代码地址:

https://github.com/RodolfoFerro/pandas_twitter

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335