25. matplotlib

Hi, 大家好。我是茶桁。
在上一节课中,我们结束了 Python 正式的所有内容,但是咱们的 Python 课程还未结束。从这节课开始,我们要来学习一下 Python 的第三方库。
Python 的生态非常完善也非常活跃,我们不太可能讲目前所有的第三方库全部都介绍一遍,只介绍几个有影响力并且和处理数据相关的。那今天第一 Part,我们就先来学习matplotlib
。
在之后的课程中,Python 的基础理论我就不会细讲了,咱们重点是要快速的认识和学会第三方库。有什么语法上的问题,可以翻看前面几节的教程。
matplotlib 是什么?

Matplotlib 是一个 Python 2D 绘图库,它可以在各种平台上以各种硬拷贝格式和交互式环境生成出具有 出版品质的图形。 Matplotlib 可用于 Python 脚本,Python 和 IPython shell,Jupyter 笔记本,Web 应用 程序服务器和四个图形用户界面工具包。
Matplotlib 试图让简单的事情变得更简单,让无法实现的事情变得可能实现。 只需几行代码即可生成绘图,直方图,功率谱,条形图,错误图,散点图等。
为了简单绘图,pyplot 模块提供了类似于 MATLAB 的界面,特别是与 IPython 结合使用时。 对于高级用户,您可以通过面向对象的界面或 MATLAB 用户熟悉的一组函数完全控制线条样式,字体属性,轴属性等。
那么,我们为什么要学习 matplotlib 呢?
可视化是在整个数据挖掘的关键辅助工具,可以清晰的理解数据,从而调整我们的分析方法。
能将数据进行可视化,更直观的呈现 使数据更加客观、更具说服力
例如下面两个图为数字展示和图形展示:

以上两个图形中,第一组是完全的数据。我们基本很难看出这组数据到底谁大谁小,当然,从位数上我们还是可以比较容易辨认,但是比起第二张图呢?是不是第二张图就非常清晰的展示了数据的大小,一目了然?
那既然我们要学习的是数据可视化,我们首先要做的,必定是要先了解一下常见数据图表,知道其种类和意义。
常见图形种类及意义
我们首先要先了解具体的图形,才能知道我们在什么情况下使用什么图形来表示。
折线图:以折线的上升或下降来表示统计数量的增减变化的统计图。特点是:能够显示数据的变化趋势,反映事物的变化情况。(变化)

散点图:用两组数据构成多个坐标点,考察坐标点的分布,判断两变量之间是否存在某种关联或总结坐标点的分布模式。特点是:判断变量之间是否存在数量关联趋势,展示离群点(分布规律)

柱状图:排列在工作表的列或行中的数据可以绘制到柱状图中。特点是:绘制连离散的数据,能够一眼看出各个数据的大小,比较数据之间的差别。(统计/对比)

直方图:由一系列高度不等的纵向条纹或线段表示数据分布的情况。 一般用横轴表示数据范围, 纵轴表示分布情况。特点是:绘制连续性的数据展示一组或者多组数据的分布状况(统计)

饼图:用于表示不同分类的占比情况,通过弧度大小来对比各种分类。特点是:分类数据的占比情况(占比)。

matplotlib 画图实现
首先我们要知道,Python 的第三方库几乎全部都需要额外安装才行。我们之前在讲 Python 环境的时候有提到如何创建虚拟环境以及如何安装第三方库。那我们现在,就来直接安装一下,还记得么?我用的环境是conda
, 所以我的安装命令都是使用conda
的,不过你将其咱们课程中还是要使用最普遍的方式,所以以下我都会替换成pip
。
执行完毕后,motplotlib
就安装到您的 Python 环境内了。
建议在 Python 环境内安装好 Jupyter 来进行学习,你会发现简直太方便了。如果是在 Jupyter 内,那么会在代码执行之后的下方直接显示出结果,而如果我们是使用python xx.py
来执行,那 Python 会调用内部的绘图器来进行显示。
Jupyter for vscode

Python 绘图器

当然,你也可以自己在命令行内起一个Jupyter notebook
服务,那就可以直接在浏览器上进行操作了。
Jupyter Notebook

当然几种方法中,我还是最推崇在 VSCode 中进行。毕竟我们还是需要代码提示的。
在正式开始之前,让我们对 matplotlib 的图像结构建立一个认识:

现在让我们来具体的实现一下,做一个简单的图形:
在这段代码中,我们使用plot
来进行了绘制,x
和y
分别是plot
的两个参数,代表了x
轴和y
轴。那么这两个轴分别接受了一个列表,那就是有三个点,第一个点是(1,4)
, 第二个点是(0,5)
, 第三个点是(9,6)
。最后,我们使用show()
函数来进行最终呈现。

绘图折线图
首先,我们来绘制一个折线图,这次我们用变量存储数据的方式:

然后,我们对这个折线图进行一下设置,修改颜色和形状:

我们对plot
函数传递了几个参数修改了折线的样式,其中color
是折线的颜色,alpha
是折线的透明度(0-1), linestyle
是折线的样式, linewidth
是折线的宽度。
linestyle
的几个值分别是:-
实线(solid),这个也是默认值;--
短线(dashed); -.
短点相间线(dashdot); :
虚线点(dotted)。
关键点样式
让我们接着进行修改这段折线样式:

我们这次只增加了一个参数,很明显,marker
就是关键点的样式。
折线和关键点到底有哪些值呢?我们看一下下面这个表:

如果看不懂描述的小伙伴,最直接的办法就是放到代码里直接运行一下看看。
当然,我们还可以改变关键点的大小等参数:
那这里面,markersize
是表示关键点的大小,markeredgecolor
是关键点边框的颜色,markeredgewidth
就是关键点边框的宽度。

既然图片渲染出来了,那我们总是需要进行保存的。那么下面,我们就看看如何将图片保存下来,在保存之前,我们还会根据需要设置一下图片的大小。
设置图片大小和保存
我们依次来看这段代码,里面有我们认识的也有我们不认识的。其中的random
是为了生成随机数,这个我们就先不管了。直接看设置部分。
figsize
这参数是为了指定figure
的宽和高,单位为英寸。
dpi
参数指定绘图对象的分辨率,即每英寸多少个像素,缺省值为 80,1 英寸等于 2.5cm, A4 纸为 21*30cm 的纸张。
然后我们继续往下看,savefig
就是指定目录进行保存。这里我们需要注意两点:
如果保存的目录有路径不存在,则会报错无法保存。
我们需要
savefig
的时候尽量不要使用show
方法,因为savefig
也具备了展示图片的功能,并且,如果show
存在的话,在展示完图片之后,会释放figuer
资源,那么savefig
保存下来的图片将会是空白的。就好比我们open('file', 'a+') as fp
一个文件,在使用fp.write
的时候没有往里面写入内容。因为这些内容被上面一个方法清空了,但是文件我还是会保存的,只是文件内没有任何内容。
然后我们回过头来继续看我们写的这段代码,其实savefig
能存储的文件格式很多,包括能够存储svg
格式的矢量图。只需要plt.savefig('./t1.svg')
,保存的时候换一下后缀名就可以了。
现在让我们看下保存好的图片:

绘制轴上的刻度
有没有发现,虽然我们折线图是正常的,但是似乎 x 轴上的刻度区间太大了,并不是所有关键点都明显展示出来了。那现在我们就来设置一下 x 轴和 y 轴的刻度。
这段代码中我们将保存文件的代码去掉了。因为主要是进行设置,所以我们展示一下看看正确与否就行了。

我们看到现在的图片,刻度上x
轴遵循了我们之前对x
的设定,(2, 26, 2), 从 2 开始,到 25, 并且步进值为 2.y
轴呢?因为是随机数,所以关键点的分布并不均匀。
这个时候,我就又需要进行修改了,一个是y
轴的刻度要分配的更均匀,再有就是x
轴上,我希望区间为 1,而不是 2。让我们来对其进行下修改:

似乎图不太一样,原因是因为我在代码中使用的是随机数函数random.randint
来差生y
轴的数据,所以每次生成的图片都会有些不同。
我们来好好看看轴线上的刻度。确实和关键点都对应上了。并且比起关键点来说更密集一点。原因就在于,我们将x
的刻度点范围改为(1, 25)
,无步进值。y
轴在绘制的时候也做了定义,范围设置为(最小的y值, 最大的y值+1)
,同样,也是没有步进值。这样,两个轴上的刻度分布就非常均匀了。
这里的关键知识点就是:我们可以使用xticks
和yticks
来生成x
轴刻度或者y
轴刻度,并且,在其中可以传递参数来对x
轴上的刻度和y
轴上的刻度进行定义。
不过我们现在这个还是无法满足需求,原因就在于我们这个折线图是为了显示不同时间点上的温度变化。那么我们就必须要让 x 轴显示时间,而 y 轴显示温度。
让我们继续修改一下,这里我们就只展示轴线代码的修改:
最后生成的图片:

这里,我们使用了x_ticks_label
来设置了x
轴的刻度上显示的信息。当然,y
轴也是相同的方式。然后将label
传入轴刻度生成方法xticks
中进行刻度生成,在生成的时候,我们还使用了xticks
的参数rotation
设置为45
来完成了label
的旋转。
设置显示中文
不过这并未结束,matplotlib
默认是只显示英文的,无法显示中文。但是我们无论是刻度,图标题,很多时候都必须显示中文。该怎么办呢?
接下来就让我们来看看如何修改 matplotlib 的默认字体。这一段,我们重新写一个需求,来看看 2 个小时内每分钟跳动变化。

我们引入了font_manager
,然后利用它设置了我们需要用到的字体(必须是中文字体)给到一个变量my_font
内。最后在设置 label 的时候,将字体设置为这个变量。
这样,我们就完成了中文字体的显示。
作为对比,我们来看看这样设置的是什么样:

一图多线
大多数时候,我们的一张图表上可能不仅需要一条线。而是两条线相互交错。这就形成了两组数据的对比,我们打个比方来说:我们正在和一位同事竞争销售额,需要查看去年(2022 年)全年的数据对比:
我们在代码中设置两两组数据,分别为y1, y2
。这两组数据一共 12 个,对应了 12 个月份。然后我们在plot
方法中设置了线条的颜色,设置了这条线条对应的数据,并且添加了label
。意在对这个线条写个说明。
在之后,我们添加了grid
网格,意图让线条上的关键点更明显。
最后使用legend
来完成plot
中设置的label
的显示,并在其中设置了显示所用字体。这里需要注意的是,在legend
方法中设置字体所用的形参是prop
而非fontproperties
。在最后,我们使用loc
设置了这两个label
显示的位置,其中的关键字upper right
代表的是两个方位「上,右」,来确定显示位置为右上角。
最后,我们绘制的图片显示如图:

从图上能明显看出来,我的销售数据是在稳步上升的,而同事起伏比较大。大部分时候我占优势,可是旺季时顶峰数据同事比我高很多,所以说基本上是各有千秋。
基于折线图,我们再来看几个拓展的部分。
首先,我们在绘图的时候,实际上是支持多个坐标系绘制在一张图上的。这也经常是数据图对比经常用到的方式:
我们利用add_subplot
方法,在一个figure
上添加了三个子图。
其次,有一些时候,我们需要对坐标轴范围进行设定。
在这段代码中,我展示了三个调整范围的方式。
第一个是使用数组划定范围来进行调整。
第二个方式是分别设定一边的值(最小值或者最大值)。
最后一个方式是只设定 x 轴和 y 轴的最小值。下图展示的是第三个方式绘制的图:

当然,坐标轴并不会是一成不变的。有的时候我们可能需要 y 轴在 x 轴的正中间。所以我们需要改变坐标轴的默认显示方式
我们先来看看原本的图默认样式是什么样:

然后我们对这张图进行修改,获取图像之后设置四周边线,并且移动底边,移动到 y 轴的 0 位置
代码中,我们使用gca()
获取了图像赋值给变量ax
,然后对其进行修改,spines
可以修改四周边线,包括颜色和位置等。分别设置完颜色之后,我们分别对底边和左边使用了set_position
进行了位移。移动到0
点位置。那其实,底边对应的就是 x 轴,左边对应的就是 y 轴。

绘制散点图
我们拿到了一组数据,就是今年 3 月份每天的最高气温,现在我想在图表上进行展示。为了展示气温的分布,我们准备使用散点图进行展示。

本来这样就已经完成绘制了。不过我们看一下图表,虽然散点是绘制完成了,但是整张图上我们看不出太多信息,包括刻度值,月份等等,另外,我记得咱们之前加过图例,现在我们都加上:
以上代码中的内容,基本都是咱们之前学过的内容,我就不多做解释了。其中,我们可以看到,plt.
之后的方法就是绘制不同的图形,之前我们看到的plot
是折线图,这次我们学到scatter
是散点图。在图形绘制之后,剩下的就是对其进行修饰和设置。

绘制条形图
今天刷到一条新闻,说目前 2023 年暑期档电影的票房基本已经定型,目前全部累计票房已经超过 2019 年称为历史最高暑期档,而最引人瞩目的是,国产电影全线压制好莱坞大片。这真是一个值的骄傲的事情。那现在,我们就来展现一下暑期档电影票房的对比吧,我们一步一步来,最开始当然是拿到数据。
本数据来源于猫眼,2023 年 8 月 20 日 14:00 的实时数据,后期未下线的电影数据可能会有变化。
然后我们就可以开始绘制柱状图了:

现在图形是绘制出来了,但是完全无法让人满意。我们并不知道哪个柱子是哪个电影的,并且对比之下,我们只能看出大概高低,并不知道具体的票房。那我们现在来进行修改, 先加上 x 轴和 y 轴上的刻度标识,并且将 y 轴刻度范围放大。

目前的图我们是能看出谁是谁了,而且比起刚才看起来 y 轴上也舒服了很多,没有顶天立地。
接下来,我们继续修改。给柱子加上颜色,好做区分。并且将绘制的图形赋值给到变量rects
,在下面我们好对每一根柱子上写一个数字,将票房数值写上去。这样,具体的票房我们就能一目了然。
这段代码中其他的都好理解,就是加标注这一段。我们使用for
从rects
中分别获取到每一根柱子,然后设定了一个高,这个高度就是柱子的实际高度。再设定一个text
给到这根柱子,text
的高度为柱子的定位为每根柱子的x
轴位置+其宽度的二分之一,高度为柱子高度+1, 字符串为柱子高度本身,设置水平居中。
来,让我们看看效果:

现在,我们的显示虽然还是不完美,但是已经非常清晰了。
当然,柱状图除了竖向的,还有横向,但是横向就不能称之为柱状图,而是条形图:

其方法和原理都和柱状图是一样的,不同的点就在于柱状图的方法是bar
,横向条形图的方法为barh
。另外,别忘了切换x,y
轴。
在这里,恭喜《封神》破 22 亿,加油...
我们平时看到的柱状图或者条形图,一定不只是这样一根一根的,还有那种两根或两根以上并列的对吧?其形式也很简单,就是在当前的柱子旁边多加一根而已:

直方图
这个图形的学习,我们还是拿电影数据来做,但是这次我们不拿刚才用过的数据了,我们拿到一个 250 部电影的时长数据,现在我们要统计处这些电影时长的分布状态。(比如,市场为 100 分钟到 120 分钟的电影数量,出现频率等),我们该如何去做呢?
来,让咱们尝试一下,还是老样子,先落位数据:
没问题,250 个数据齐了。现在让我们来开始绘图:
从代码中看,直方图的方法是hist
, 其中参数为需要展示的数据和组距。
x 轴刻度上,我们将最小的电影时长和最大的电影时长顺序分布,步进值为 2。

现在我们从图中能看到,110 ~ 112
这个时间段的电影数量是最多的,其次就是116 ~ 118
分钟的。
现在,我们更清晰的感受了 i 库 55 起 1422
直方图的作用。
饼状图
最后我们来看看饼状图,饼状图相信大家平时看的也很多。基本上,我们遇到比例,份额对比的时候会使用这个图形。
来,让我们先绘制一个基本图形:

现在让我们在这个基础的饼状图上进行设置和修改,首先,我们需要这个图形各个区域都有个名称,然后是我们自己设定下他们的颜色,而不是用默认的,接着,我们需要将其中一部分突出显示。

现在是按照我刚才希望的去变化了,但是并不让人满意。我们需要将文字设置成中文显示,然后整个图形旋转一下,让突出的部分部要向下,接着我们希望显示出百分比,并且有一个图例,分别标识出颜色和区域的文字。
我们在这段代码中,将之前在pie
方法中的三组列表数据拿出来赋值给了三个变量,然后增加了一些其他参数用于标签距离、设置旋转、百分比距离等。
然后将生成的图赋值给到三个变量,patches, l_text, p_text
,这三个变量分别接受的参数为「饼的区域」、「饼的外部说明文字」、「饼的内部标识文字」。
然后,对这三个变量分别进行设置。
最后,将图例的字体设置一下,然后显示出来。

总结
今天的课程到这里也就结束了。最后让我们来总结一下

最后,我们留个作业吧,好久没留作业了。将咱们之前做的这个饼图加上阴影,扩大分离的那块区域的分离距离。如图:

小伙伴们,记得认真学习并且完成作业。
好,下课。
版权声明: 本文为 InfoQ 作者【茶桁】的原创文章。
原文链接:【http://xie.infoq.cn/article/9937a820d3b8b123fea6c62b6】。文章转载请联系作者。
评论