声明
本文涉及的代码基于python 3.6.5
numpy1.14.3
pandas 0.23.0
matplotlib 2.2.2
。
matplotlib是python中常用的图表绘制工具;pandas是常用的数据处理工具,关于pandas的使用可以参考我的这篇文章:Pandas基本操作与常用接口;numpy是常用的科学计算工具,关于numpy的使用可以参考我的这篇文章:NumPy基本操作与常用函数。
在使用numpy、pandas和matplotlib之前我们首先要导入相关模块:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
绘制简易折线图
现在我们有一个美国从1948年起每个年份每个月份的失业率的数据unrate.csv,数据只有两列,一列是DATE对应相应的月份,一列是VALUE就是相应的失业率。我们先用pandas把它导入进来
unrate = pd.read_csv('unrate.csv')
# 将DATE列数据由字符串转换为datetime类型
unrate['DATE'] = pd.to_datetime(unrate['DATE'])
print(unrate.head(12))
# DATE VALUE
# 0 1948-01-01 3.4
# 1 1948-02-01 3.8
# 2 1948-03-01 4.0
# 3 1948-04-01 3.9
# 4 1948-05-01 3.5
# 5 1948-06-01 3.6
# 6 1948-07-01 3.6
# 7 1948-08-01 3.9
# 8 1948-09-01 3.8
# 9 1948-10-01 3.7
# 10 1948-11-01 3.8
# 11 1948-12-01 4.0
然后我们利用这些数据先绘制一个简单的折线图:
first_twelve = unrate[0:12]
plt.plot(first_twelve['DATE'], first_twelve['VALUE'])
plt.show()
绘制出来的折线图如上所示,plot()方法的第一个参数为x坐标值,x参数是可选的,若没有设置,默认为[0, ..., N-1]
。第二个参数为y坐标值,该参数也可以是多个列数据,分别对应不同的数据集合。下面我们看一下当y为多个数据集合时的用法:
first_twelve = unrate[0:12]
second_twelve = unrate[12:24]
y = np.vstack((first_twelve['VALUE'], second_twelve['VALUE'])).T
plt.plot(first_twelve['DATE'].dt.month, y) # 横轴取值为每年的12个月份
plt.show()
对于y轴上的多个数据集,常用的写法其实是这样的:
first_twelve = unrate[0:12]
second_twelve = unrate[12:24]
plt.plot(first_twelve['DATE'].dt.month, first_twelve['VALUE'], color='red', marker='.', linestyle='dashed',
linewidth=2, markersize=12)
plt.plot(second_twelve['DATE'].dt.month, second_twelve['VALUE'], 'g-')
plt.show()
一般多个图线我们是多次调用plot()方法实现。而plot()方法还可以设置关键字参数来进行一些个性化定制,比如红色折线的color=‘red’设置了折线颜色,marker=’.'设置了折线上标记的样式,linestyle设置了折线的样式,linewidth设置了折线的宽度等等。还有一种方法是像绿线’g-'这样设置格式化字符串。当格式化字符串和关键字参数同时设置且有冲突时,关键字参数优先被使用,也就说:
plt.plot(first_twelve['DATE'].dt.month, first_twelve['VALUE'], 'g-', color='red', marker='.', linestyle='dashed',
linewidth=2, markersize=12)
和
plt.plot(first_twelve['DATE'].dt.month, first_twelve['VALUE'], color='red', marker='.', linestyle='dashed',
linewidth=2, markersize=12)
是等效的。
matplotlib为图形提供了很多自定义样式,具体可以查看文档,这里就不一一列举了。
上面的折线图还并不完整,下面我们进一步将其完善,使用下面这段代码,将得到一个较为完善的图:
first_twelve = unrate[0:12]
plt.plot(first_twelve['DATE'], first_twelve['VALUE'])
plt.xticks(rotation=45)
plt.xlabel('Month')
plt.ylabel('Unemployment Rate')
plt.title('Monthly Unemployment Trends, 1948')
plt.legend(loc='upper left')
plt.show()
我们使用xticks()方法设置rotation参数为45度使得x坐标轴上的坐标标记旋转45度(图中"1948-02"等),使用xlabel()方法设置x坐标轴的名称为"Month",使用ylabel()方法设置y坐标轴的名称为"Unemployment Rate",使用title()方法设置图像标题为"Monthly Unemployment Trends, 1948",使用legend()方法为图像设置图例(loc参数表示图例放置的位置,图中为左上角)。
至此,我们就完成了一个简单的折线图了。
子图操作
我们先通过下面这段代码了解子图的概念:
fig = plt.figure()
ax1 = fig.add_subplot(2, 2, 1)
ax2 = fig.add_subplot(2, 2, 2)
ax3 = fig.add_subplot(2, 2, 4)
plt.show()
在上述代码中我们首先使用figure()方法创建子图,然后使用add_subplot()方法添加子图。子图在整张图中的分布类似于矩阵,add_subplot()的前两个参数表示矩阵的形状,第一个参数表示表示矩阵的行数,第2个参数表示矩阵的列数,在这里就是2行2列的矩阵,第三个参数表示子图在矩阵中的位置序号,位置序号从1开始,从左至右从上到下依次递增。
下面我们在子图上绘制折线图:
fig = plt.figure(figsize=(6, 6))
ax1 = fig.add_subplot(2, 2, 1)
ax2 = fig.add_subplot(2, 2, 2)
ax3 = fig.add_subplot(2, 2, 4)
ax1.plot(np.arange(6), np.random.randint(1, 10, 6))
ax2.plot(np.arange(12), np.random.randint(1, 10, 12))
ax3.plot(np.arange(12), np.random.randint(5, 15, 12))
plt.show()
如上图所示,为了使显示效果看起来好一点,我们设置figure()方法的figsize为(6, 6),也就是宽、高为6英寸,figure()方法还提供了其他一些参数供设置图像的表现形式,详情请参考文档。设置好图像之后,我们添加了3个子图ax1、ax2、ax3,然后调用plot()方法在每个子图上绘制出折线图。注意,这里figure()是设置整个图像的一些性质的方法,而plot()方法才是控制折线图或柱状图等图像的方法。
条形图与散点图
条形图
现在我们手上有一个电影评分数据fandango_scores.csv,格式如下所示:
FILM,RottenTomatoes,RottenTomatoes_User,Metacritic,Metacritic_User,IMDB,Fandango_Stars,Fandango_Ratingvalue,RT_norm,RT_user_norm,Metacritic_norm,Metacritic_user_nom,IMDB_norm,RT_norm_round,RT_user_norm_round,Metacritic_norm_round,Metacritic_user_norm_round,IMDB_norm_round,Metacritic_user_vote_count,IMDB_user_vote_count,Fandango_votes,Fandango_Difference
Avengers: Age of Ultron (2015),74,86,66,7.1,7.8,5,4.5,3.7,4.3,3.3,3.55,3.9,3.5,4.5,3.5,3.5,4,1330,271107,14846,0.5
Cinderella (2015),85,80,67,7.5,7.1,5,4.5,4.25,4,3.35,3.75,3.55,4.5,4,3.5,4,3.5,249,65709,12640,0.5
我们使用pandas从该文件里提取电影名称列和其他5列评分数据:
reviews = pd.read_csv('fandango_scores.csv')
cols = ['FILM', 'RT_user_norm', 'Metacritic_user_nom', 'IMDB_norm', 'Fandango_Ratingvalue', 'Fandango_Stars']
norm_reviews = reviews[cols]
然后我们利用这些数据创建条形图:
num_cols = ['RT_user_norm', 'Metacritic_user_nom', 'IMDB_norm', 'Fandango_Ratingvalue', 'Fandango_Stars']
bar_heights = norm_reviews.ix[0, num_cols].values
bar_positions = np.arange(5) + 0.75
tick_positions = np.arange(5) + 0.5
fig, ax = plt.subplots()
ax.bar(bar_positions, bar_heights, 0.5)
ax.set_xticks(tick_positions)
ax.set_xticklabels(num_cols)
ax.set_xlabel('Rating Source')
ax.set_ylabel('Average Rating')
ax.set_title('Average User Rating For Avengers: Age of Ultron (2015)')
plt.show()
那么,上面的代码是什么意思呢?让我们一起来看一下。
首先num_cols定义了我们想要选取数据的列,我们选取这一列数据第一行的值作为bar_heights,也就是条形图里的各个条形的高度。
然后我们定义了bar_positions,也就是条形图中各个条形区域左下角的坐标值。而tick_positions是各个条形区域下方文字标记的坐标。
然后我们利用subplots()方法获取figure和ax。接下来我们的绘图操作都通过ax实现。
我们调用ax的bar()方法来创建条形图,传入bar_postions和bar_heights,而0.5表示的是每个条形区域的宽度。然后调用set_xticks()来设置各条形区域在横轴上的标记位置,set_xticklabels()来设置各条形区域在横轴上的名称。
最后,调用set_xlabel()方法来设置横轴的标签,set_ylabel()方法设置纵轴的标签,set_title()方法设置图表的标题。
散点图
下面我们看一下如何创建散点图:
fig, ax = plt.subplots()
ax.scatter(norm_reviews['Fandango_Ratingvalue'], norm_reviews['RT_user_norm'])
ax.set_xlabel('Fandango')
ax.set_ylabel('Rotten Tomatoes')
plt.show()
如上所示,我们只需要调用ax(Axes object or array of Axes objects)的scatter()方法,并传入x坐标值和y坐标值就可以绘制一幅散点图了,其他的一些设置和折线图、条形图类似,这里就不多说了。
直方图和箱线图
直方图
我们可以使用以下的代码绘制直方图:
fig, ax = plt.subplots()
ax.hist(norm_reviews['Fandango_Ratingvalue'], range=(3, 4), bins=10)
plt.show()
有的朋友可能会说了,上面的图和条形图不是没差别吗?为什么还要用ax的hist()方法去另外绘制呢?
这里就涉及到直方图和条形图的区别了,下面我简单说一下。
- 直方图一般用来描述数据的分布情况,而条形图用来描述数据的类别信息;
- 直方图中的条形面积表示频数、宽度表示组距、高度就是频数/组距,条形图中的条形高度表示频数,宽度是某个类别;
- 直方图横轴上的数据是一个连续的范围,条形图上横轴是各个孤立的类别;
- 直方图各个条形之间一般没有空隙,而条形图一般是有的。
接下来我们看一下直方图统计了哪些信息,要了解直方图的作用我们先看下面这幅图
假设有一组电影评分的原始数据,每个评分数据只保留到小数点后一位,分值在0至5之间。我们按照分数统计这些评分的频数,然后进行排序,再平均划分分组范围,统计得到每个分组的分数频数,也就是分数范围为Bins,频数为Count。
上面的ax.hist()方法做的也正是这个事。norm_reviews[‘Fandango_Ratingvalue’]是需要统计的数据,range=(3, 4)表示直方图上只展示分数3至4之间的数据分布情况,bins=10表示将区间[3, 4]划分为10组,也就是组距为0.1。这样就得到了一个直方图了。
箱线图
箱线图又称箱式图或盒图,一般用来反映数据的大小、占比、趋势等,主要可以得到均值、分位数、极值等,揭示数据离散程度、异常值、分布差异等。
下面我们直接看一个案例:
num_cols = ['RT_user_norm', 'Metacritic_user_nom', 'IMDB_norm', 'Fandango_Ratingvalue']
fig, ax = plt.subplots()
ax.boxplot(norm_reviews[num_cols].values)
ax.set_xticklabels(num_cols)
ax.set_ylim(0, 5)
plt.show()
上面的箱线图反映了四组电影评分的情况。
以第2个箱子为例,我们先介绍箱线图里的几个概念。
首先连接箱子的最下方一条横线称为下边缘,是这组数据的最小值;箱子的下边框表示下四分位数,又称“第一四分位数”,等于该组数据从小到大排列后第25%的数字;箱子中间的红线是中位数,又称“第二四分位数”,等于该组数据从小到大排列后第50%的数字;箱子的上边框表示上四分位数,又称“第三四分位数”,等于该组数据从小到大排列后第75%的数字;连接箱子上方的最上面一条横线称为上边缘,是这组数据的最大值。在上边缘和下边缘之外的值就是异常值,比如第2个箱子下方的圆圈。
从上面四个箱子我们可以看到IMDB的评分分布比较集中,但异常值也比较多,而Fandango的评分相对其他评分都比较高,RT_user的评分就比较分散(箱子较长)。当我们想了解一个电影大致怎么样时,也就可以根据箱线图的效果来选择一个评分作为参考标准。
生成箱线图只需要调用ax.boxplot()方法并传入数据即可,还有很多其他的参数一般并不常用,具体可以参考官方文档。
顺便提一下,set_ylim()方法设置了y轴上数据的上下限,那么同样的也有set_xlim()方法来对x轴进行设置。
其他
有一些图像设置的细节,前文都没有提到,这里再说一下。
我们仍然以前面绘制失业率折线图的代码为例,先看一下原图:
现在我们想把坐标轴上值的刻度线给去掉,在原代码的基础上添加一行代码:
unrate = pd.read_csv('unrate.csv')
# 将DATE列数据由字符串转换为datetime类型
unrate['DATE'] = pd.to_datetime(unrate['DATE'])
print(unrate.head(12))
first_twelve = unrate[0:12]
fig, ax = plt.subplots()
ax.plot(first_twelve['DATE'], first_twelve['VALUE'])
# 添加的代码,去除坐标轴上的刻度线
ax.tick_params(bottom='off', top='off', left='off', right='off')
plt.show()
得到下图:
很明显,x轴和y轴上的刻度线没了。如果我们连坐标轴和图像边框都不想显示了,可以在plt.show()之前加上如下代码:
for key, spine in ax.spines.items():
spine.set_visible(False)
得到下图:
好了,关于matplotlib的介绍就到这了,限于篇幅,有的东西可能讲的不够细致,但对于基本的操作,这些知识是够用了。最后,感谢各位读者花费时间来阅读这篇文章,如果文章有什么错误之处,欢迎在评论区批评指正,我会及时纠正。