使用 pandas 和 matplotlib 分析推特
Python有着各种各样的可视化库,其中包括了seaborn, networkx 和 vispy。大部分的可视化Python库都是基于或部分基于matplotlib, matplotlib往往是绘制一些简单图的首选,但是同时对于太过复杂的图往往无能为力而不得不借助于其他库。
在本篇的matplotlib教程中,我们将会涉及到该库的基础,并通过一些中等难度的可视化示例进行阐释。
我们所使用的数据集为大概240,000条推特,这些推特的内容都有关于目前的美国总统候选人,也即Hillary Clinton, Donald Trump 和 Bernie Sanders。
包含全部 240,000 条推特的csv数据文件可在这里下载,如果你想爬取更多数据,可以再这里查找爬虫代码.
使用pandas探索推特
在开始绘图之前,我们先载入数据并做一些简单的探索。可以使用一个数据分析的Python库 pandas来帮助我们做这些。
先看一下当前工作目录,把下载的数据集 ,把下载的数据集 tweets.csv 放到该目录下即可。比如我的当前目录为/Users/xuliucheng
, tweets.csv应该放到/Users/xuliucheng/tweets.csv
。
import os
print(os.getcwd())
准备好数据集后开始导入数据。在下面的代码,我们将会:
- 导入pandas库
- 将tweets.csv读取到pandas DataFrame
- 打印DataFrame的前5行
# 导入pandas库
import pandas as pd
# 将tweets.csv读取到pandas DataFrame
tweets = pd.read_csv("tweets.csv")
# 打印DataFrame的前5行
tweets.head()
id | id_str | user_location | user_bg_color | retweet_count | user_name | polarity | created | geo | user_description | user_created | user_followers | coordinates | subjectivity | text | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 729828033092149248 | Wheeling WV | 022330 | 0 | Jaybo26003 | 0.00 | 2016-05-10T00:18:57 | NaN | NaN | 2011-11-17T02:45:42 | 39 | NaN | 0.0 | Make a difference vote! WV Bernie Sanders Coul… |
1 | 2 | 729828033092161537 | NaN | C0DEED | 0 | brittttany_ns | 0.15 | 2016-05-10T00:18:57 | NaN | 18 // PSJAN | 2012-12-24T17:33:12 | 1175 | NaN | 0.1 | RT @HlPHOPNEWS: T.I. says if Donald Trump wins… |
2 | 3 | 729828033566224384 | NaN | C0DEED | 0 | JeffriesLori | 0.00 | 2016-05-10T00:18:57 | NaN | NaN | 2012-10-11T14:29:59 | 42 | NaN | 0.0 | You have no one to blame but yourselves if Tru… |
3 | 4 | 729828033893302272 | global | C0DEED | 0 | WhorunsGOVs | 0.00 | 2016-05-10T00:18:57 | NaN | Get Latest Global Political news as they unfold | 2014-02-16T07:34:24 | 290 | NaN | 0.0 | ‘Ruin the rest of their lives’: Donald Trump c… |
4 | 5 | 729828034178482177 | California, USA | 131516 | 0 | BJCG0830 | 0.00 | 2016-05-10T00:18:57 | NaN | Queer Latino invoking his 1st amendment privil… | 2009-03-21T01:43:26 | 354 | NaN | 0.0 | RT @elianayjohnson: Per source, GOP megadonor … |
这里是对数据集中一些重要列的简要解释:
id
: 数据库的行id(并不重要)id_str
: 当前tweet在Twitter网站的iduser_location
: 在Twitter bio中发推人指定的地点user_bg_color
: 发推人profile的背景色user_name
: 发推人的Twitter用户名polarity
: 推特的情感指向,从-1到1,1代表非常积极,-1代表非常消极created
: 何时推特被发送user_description
: 发推人在个人档案中的自我简介user_created
: 发推人创建账户的时间user_follower
: 发推人拥有的粉丝数text
: 推特文本内容subjectivity
: 推特的主观性,0代表objective,1代表subjective
生成candidate列
对于这个数据集我们所能做的一些有趣的事情大多会涉及到关于不同候选者之间tweet的相互比较。 比如, 我们可以比较关于Donald Trump的推特其客观性怎么样,关于Bernie Sanders的推特其客观性又如何。
为此,我们首先需要生成一列数据来告诉我们在每条推特中都提及了哪些候选者。在下面的代码,我们将会:
- 创建一个函数来找到在一段文本中出现了哪些候选者的名字
- 使用DateFrame的apply方法生成一个
candidate
的新列,其中包含了每条推特所提及的候选者的名字
# 创建一个函数来找到在一段文本中出现了哪些候选者的名字
def get_candidate(row):
candidates = []
text = row["text"].lower()
if "clinton" in text or "hillary" in text:
candidates.append("clinton")
if "trump" in text or "donald" in text:
candidates.append("trump")
if "sanders" in text or "bernie" in text:
candidates.append("sanders")
return ",".join(candidates)
# 使用DateFrame的apply方法生成一个candidate的新列,其中包含了每条推特所提及的候选者的名字
tweets["candidate"] = tweets.apply(get_candidate,axis=1)
绘制第一幅图
现在我们已经完成了使用matplotlib绘制第一幅图的一些准备工作。在matplotlib中,绘制一幅图像主要涉及到:
鉴于其灵活的机构, 你可以在matplotlib将多个plot绘制到一个单独的image中。每个Axes对象代表了一个单独的plot, 比如一个bar plot或histogram。
这听起来似乎有点复杂,但是matplotlib对于创建 Figure 与 Axes 提供了一些方便的途径。
导入matplotlib
为了能够使用matplotlib, 您首先需要使用 import matplotlib.pyplot as plt
导入该库。如果使用Jupyter notebook, 您可以使用 %matplotlib inline
使得matplotlib能够在notebook内运行。
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
之所以导入 matplotlib.pyplot
是因为它包含了matplotlib的绘制函数, 而为了使用时更方便将其重命名为 plt
.
绘制一个bar plot
一旦导入了matplotlib, 我们就能够对有多少tweet提及了每个候选者制作一个bar plot. 为此,我们将会:
- 使用pandas Series 的 value_counts来计算有多少tweet提及了每个候选者。
- 使用
plt.bar
来创建一个bar plot. - 显示计算结果
counts = tweets["candidate"].value_counts()
plt.bar(range(len(counts)), counts)
plt.show()
print(counts)
trump 119998
clinton,trump 30521
25429
sanders 25351
clinton 22746
clinton,sanders 6044
clinton,trump,sanders 4219
trump,sanders 3172
Name: candidate, dtype: int64
令人惊奇的是有关于Trump的tweet明显多于Sandars或Clinton!
您也许注意到我们没有显式创建一个Figure或任何的Axes对象,这是因为调用plt.bar
会自动生成一个Figure和一个单独的Axes对象来表示bar plot.调用plt.show
方法将会显式当前Figure中的一切内容。在当前情况下,它显示包含了bar plot的image。
matplotlib在pyplot模块有一些方法更快更方便的方式来创建一些常见类型的plot,它们将会自动创建一个Figure和一个Axes对象。如下是几种常见的方法:
- plt.bar : a bar chart
- plt.boxplot : a box and whisker plot
- plt.hist : a histogram
- plt.plot : a line plot
- plt.scatter : a scatter plot
调用这些方法将会自动创建Figure和Axes对象进行绘制。这些方法可以传入不同的参数来绘制不同的图像结果。
个性化绘制
现在我们已经制作了第一个基本的图像,接下来我们将继续绘制一个稍微个性化一点的图像。我们将制作一个直方图(histogram),然后为其添加标签以及一些其他信息。
在下面的代码,我们将会:
- 将
created
和user_created
列转化为pandas datetime 类型 - 创建一个
user_age
列来显示账户创建以来的时间 - 创建用户年龄的histogram
- 显示图像
from datetime import datetime
tweets["created"] = pd.to_datetime(tweets["created"])
tweets["user_created"] = pd.to_datetime(tweets["user_created"])
tweets["user_age"] = tweets["user_created"].apply(lambda x : (datetime.now() - x).total_seconds()/3600/24/365 )
plt.hist(tweets["user_age"])
plt.show()
添加label
我们可以在matplotlib plot中加入标题(title)或坐标轴标签(axis label)。
- plt.title : 添加标题
- plt.xlabel : 添加x轴标签
- plt.ylabel : 添加y轴标签
由于我们之前所讨论的所有方法,比如 bar
或 hist
, 会自动在figure内部创建一个Figure或Axes对象,当上面的方法被调用时这些标签便会被加到Axes对象上。
我们可以使用上述方法给之前的直方图加上一些标签。在下面的代码,我们将会:
- 生成一个与之前一样的直方图
- 为直方图绘制一个标题
- 为直方图绘制x轴标签
- 为直方图绘制y轴标签
- 显示plot
plt.hist(tweets["user_age"])
plt.title("Tweets mentioning candidates")
plt.xlabel("Twitter account age in years")
plt.ylabel("# of tweets")
plt.show()
制作一个堆叠的直方图
目前的直方图的确能够显示出所有发推者的年龄,但是它并没有根据候选者进行隔断,而这可能会更有趣。我们可以在hist
方法中利用额外选项来创建一个stacked histogram.
在下面的代码,我们将会:
- 生成3个 pandas series, 每个仅包含某一指定候选者的tweet
user_age
数据 - 通过调用带有额外选项的
hist
函数绘制一个stacked histogram- 指定一个列表作为输入
- 指定
stacked=True
将会堆叠3个条状 - 添加
label
选项将会为图例生成合适的标签
- 调用plt.legend 方法将会在右上角绘制一个图例
- 添加一个标题,x轴与y轴标签
- 显示plot
cl_tweets = tweets["user_age"][tweets["candidate"] == "clinton"]
sa_tweets = tweets["user_age"][tweets["candidate"] == "sanders"]
tr_tweets = tweets["user_age"][tweets["candidate"] == "trump"]
plt.hist([cl_tweets,sa_tweets,tr_tweets],
stacked=True,
label=["clinton","sanders","trump"]
)
plt.legend()
plt.title("Tweets mentioning each candidate")
plt.xlabel("Twitter account age in years")
plt.ylabel("# of tweets")
plt.show()
为直方图添加注释
我们可以利用matplotlib的长处在plot上绘制一些文本来添加注释。注释指向图表的一个指定部分,并且允许我们添加一个简短的描述片段。
在下面的代码,将会绘制一个与之前一样的直方图,不过我们将会调用plt.annnotate方法来添加一个注释到plot中。
plt.hist([cl_tweets,sa_tweets,tr_tweets],
stacked=True,
label=["clinton","sanders","trump"])
plt.legend()
plt.title("Tweets mentioning each candidate")
plt.xlabel("Twitter account age in years")
plt.ylabel("# of tweets")
plt.annotate(‘More Trump tweets‘,
xy=(1,35000),
xytext=(2,35000),
arrowprops=dict(facecolor=‘black‘))
plt.show()
这里是传入annotate
方法参数的简要介绍:
xy
: 决定箭头开始处的(x,y)坐标xytext
: 决定注释文本开始处的(x,y)坐标arroeprops
: 指定关于箭头的一些选项,比如颜色
如上图所示,关于Trump的tweet显然要比其他候选人多出许多,不过在不同账户年限间,这种现象似乎没什么不同。
多个子图
到目前为止,我们已经使用了像plt.bar
和plt.hist
这样的方法,它们自动创建了了一个Figure对象和一个Axes对象。不过,当我们想对plot有更多的控制能力,可以显式创建这些对象。我们想对plot有更多的控制能力的其中一种情况便是在同一个image中绘制多个plot。
我们可以通过调用plt.subplots
来生成一个Figure和多个Axes。我们传入两个参数nrows
和ncols
,这将定义在Figure中Axes对象的布局。比如,plt.subplots(nrows=2,ncols=2)
将会生成 2*2 的 网格 Axes objects. plt.subplots(nrows=2,ncols=1)
将会生成一个2*1的网格Axes objects, 然后垂直地将两个Axes堆叠在一起。
每个Axes对象都支持来自pyplot
的大部分方法。比如,Axes对象可以调用bar
方法来生成一个条形图。
提取颜色
首先,我们将会生成两列,red
和blue
,来显示在每个推特用户的profile background中每种颜色有多少,从0到1.
在下面的代码,我们将会:
- 使用
apply
方法遍历每一行的user_bg_color
列,并在其中提取有多少的red. - 使用
apply
方法遍历每一行的user_bg_color
列,并在其中提取有多少的blue.
import matplotlib.colors as colors
tweets["red"] = tweets["user_bg_color"].apply(lambda x: colors.hex2color(‘#{0}‘.format(x))[0])
tweets["blue"] = tweets["user_bg_color"].apply(lambda x: colors.hex2color(‘#{0}‘.format(x))[2])
创建plot
每个直方图将会显示有多少的推特用户的profile background包含了red或blue。
在下面的代码,我们将会:
- 使用
subplots
方法生成一个Figure和多个Axes, 这些Axes将会以数组的形式返回。 - axes以2*2的numpy数组的形式返回。我们使用数组的flat属性来提取每个axes个体。于是我们会有四个axes对象。
- 使用hist方法在第一个axes中绘制一个直方图
- 使用set_title方法设置第一个axes的title为
Red in all backgrounds
. 同样的功能也可用plt.title()
实现。 - 使用hist方法在第二个axes中绘制一个直方图
- 使用set_title方法设置第二个axes的title为
Blue in all backgrounds
. 同样的功能也可用plt.title()
实现。 - 调用plt.tight_layout方法来减少图表间的内边距以适应所以的元素。
- 显示图像。
fig, axes = plt.subplots(nrows=2, ncols=2)
ax0, ax1, ax2, ax3 = axes.flat
ax0.hist(tweets["red"])
ax0.set_title(‘Red in backgrounds‘)
ax1.hist(tweets["red"][tweets["candidate"] == "trump"].values)
ax1.set_title(‘Red in Trump tweeters‘)
ax2.hist(tweets["blue"])
ax2.set_title(‘Blue in backgrounds‘)
ax3.hist(tweets["blue"][tweets["candidate"] == "trump"].values)
ax3.set_title(‘Blue in Trump tweeters‘)
plt.tight_layout()
plt.show()
移除通用背景色
Twitter有默认的profile background color, 所以我们可能需要取出它们一遍能够减少噪声生成一个更准确的plot。以16进制形式,#000000
表示黑色,#ffffff
表示白色。
这里是如何找到背景色中的最常见颜色。
tweets["user_bg_color"].value_counts()
C0DEED 108977
000000 31119
F5F8FA 25597
131516 7731
1A1B1F 5059
022330 4300
0099B9 3958
642D8B 3767
FFFFFF 3101
9AE4E8 2651
ACDED6 2383
352726 2338
C6E2EE 1978
709397 1518
EBEBEB 1475
FF6699 1370
BADFCD 1336
FFF04D 1300
EDECE9 1225
B2DFDA 1218
DBE9ED 1113
ABB8C2 1101
8B542B 1073
3B94D9 623
89C9FA 414
DD2E44 351
94D487 318
4A913C 300
9266CC 287
F5ABB5 267
...
5470A8 1
00AEFF 1
C49C4B 1
778877 1
09380E 1
09536E 1
3D3C3D 1
48394D 1
3D3C3A 1
140C0E 1
AE1BCF 1
EBE39B 1
056785 1
FCF3EA 1
2E332F 1
FCF7F8 1
FCF7F7 1
0F6B2C 1
1D1F1B 1
180018 1
2686B3 1
8F0E8F 1
CCD4E8 1
FFEF42 1
08F5F5 1
4E5254 1
42373E 1
272D29 1
F00CC2 1
A3004D 1
Name: user_bg_color, dtype: int64
现在,我们可以移除三种最常见的颜色,并仅绘制有着唯一背景色的用户。下面的代码基本就是我们之前所做的事情,但是我们将会:
- 从
user_bg_color
中移除C0DEED
,000000
,F5F8FA
tc = tweets[~tweets["user_bg_color"].isin(["C0DEED","000000","F5F8FA"])]
def create_plot(data):
fig, axes = plt.subplots(nrows=2, ncols=2)
ax0, ax1, ax2, ax3 = axes.flat
ax0.hist(data["red"])
ax0.set_title(‘Red in backgrounds‘)
ax1.hist(data["red"][data["candidate"] == "trump"].values)
ax1.set_title(‘Red in Trump tweets‘)
ax2.hist(data["blue"])
ax2.set_title(‘Blue in backgrounds‘)
ax3.hist(data["blue"][data["candidate"] == "trump"].values)
ax3.set_title(‘Blue in Trump tweeters‘)
plt.tight_layout()
plt.show()
create_plot(tc)
如图所示,对于那些发推内容关于Trump的用户,其红蓝背景色分布与所有用户的红蓝背景色分布大致相同。
Plotting sentiment
我们可以使用TextBlob对每条tweet生成一个情感得分,并存储在polarity
列。 我们可以为每个候选人绘制平均值和标准差。标准差将会告诉我们在所有的tweet间其变化偏差有多大,而平均值将会告诉我们tweet的平均值是多少。
为此,我们可以将2个Axes添加到一个Figure,在其中一个绘制polarity
的平均值,另一个绘制标准差。因为在这些plot里有很多文本标记,所以我们需要增大生成的figure的size来进行匹配。可以通过plt.subplots
方法的figsize
选项来实现。
下面的代码将会:
- 根据candidate对tweet进行聚合,计算每个数值列的平均值与标准差(包括
polarity
) - 创建一个7*7 inches的Figure,带有2个Axes对象,垂直排列
- 第一个Axes对象绘制关于标准差的一个bar plot
- 使用
set_xticklabels
设置tick label, 并使用rotate
参数将标签旋转45
度 - 设置标题
- 使用
- 将上一步操作同样应用到第二个Axes对象
- 显示图像
gr = tweets.groupby("candidate").agg([np.mean, np.std])
fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(7,7))
ax0, ax1 = axes.flat
std = gr["polarity"]["std"].iloc[1:]
mean = gr["polarity"]["mean"].iloc[1:]
ax0.bar(range(len(std)), std)
ax0.set_xticklabels(std.index, rotation=45)
ax0.set_title(‘Standard deviation of tweet sentiment‘)
ax1.bar(range(len(mean)), mean)
ax1.set_xticklabels(mean.index, rotation=45)
ax1.set_title(‘Mean tweet sentiment‘)
plt.tight_layout()
plt.show()
Generating a side by side bar plot
我们可以根据candidate绘制关于tweet长度的bar plot. 首先,我们将tweet分割为short
,medium
和long
三类。然后,我们计算关于candidate的每类tweet的数量并为之生成一个bar plot。
Generating tweet lengths
为了绘制tweet的长度,我们首先必须对tweet进行分类,然后计算出每类tweet的数量。
在下面的代码,我们将会:
- 定义一个函数对tweet进行标记,tweet少于
100
个字符标记为short
,100
到135
个字符标记为medium
,超过135
个字符标记为long
- 使用
apply
生成新列tweet_length
- 根据每个candidate计算每类有多少tweet
def tweet_lengths(text):
if len(text) < 100:
return "short"
elif len(text) <= 135:
return "medium"
else:
return "long"
tweets["tweet_length"] = tweets["text"].apply(tweet_lengths)
tl = {}
for candidate in ["clinton", "sanders", "trump"]:
tl[candidate] = tweets["tweet_length"][tweets["candidate"] == candidate].value_counts()
Plotting
fig, ax = plt.subplots()
width = .5
x = np.array(range(0, 6, 2))
ax.bar(x, tl["clinton"], width, color=‘g‘)
ax.bar(x+width, tl["sanders"], width, color=‘b‘)
ax.bar(x+(width*2), tl["trump"], width, color=‘r‘)
ax.set_ylabel(‘## of tweets‘)
ax.set_title(‘Number of Tweets per candidate by length‘)
ax.set_xticks(x+(width*1.5))
ax.set_xticklabels((‘long‘, ‘medium‘, ‘short‘))
ax.set_xlabel(‘Tweet length‘)
plt.show()
Next steps
至此我们对于matplotlib如何绘制图像与数据集已经有了一定了解,如果您想了解更多的matplotlib内部如何运作,可以阅读这里.
接下来,您可以:
- 分析用户的描述信息,看一下关于候选者的描述信息的长度如何变化
- 探索时间信息 – 候选人的支持者是否偏向于某些特定的时间发推?
- 探索用户地点,看一下哪位candidate的支持者在哪些州最多
- 看一下是否用户名称的类型是否会偏向于发推于某些类型的candidate
- 是否用户名称的长度与对候选人的支持有些相关性?
- 哪位候选人拥有最多的caps supporter
- 抓取更多数据,看模式是否会有所变化
注:
- 原文地址:Matplotlib tutorial: Plotting tweets mentioning Trump, Clinton & Sanders
- ipython notebook生成的HTML,可能样式会csdn渲染的md样式更好一些Matplotlib_tutorial.html