文章目录(Table of Contents)
Matploylib介绍
在数据分析的时候,我们经常需要将数据可视化,这是就需要使用matplotlib
模块了。
我们首先将matplotlib.pyplot
导入后命名为plt
, 在后面的内容中,出现的plt
都默认是matplotlib.pyplot
包
- import matplotlib.pyplot as plt
- %matplotlib inline
在使用 jupyter 的时候需要加上后面这句。
可视化的一些 Tips
- 当数据相差比较大的时候,绘制图像的时候有一部分数据挤在一起,这个时候可以使用 Log 对数据先进行对数处理,接着在进行绘图;
- 当坐标轴的数字比较大的时候,可以将其除一个数,使其变小,在 label 上进说明即可;
结合 pandas 进行绘图
这一部分内容可以参考自,Pandas 指南-Pandas 绘图说明
matplotlib基础
matplotlib
是面向对象的绘图工具包,绘制的图形中每一个元素都是一个对象,比如线条,文字,刻度等信息,可以通过修改这些对象的属性,从而改变绘图样式。
matplotlib
中主要的绘图对象列表如下:
Figure
对象,可以想象为一张画布;Axes
对象,字面理解为坐标轴(因为每一个 Axes 都有一套 X Y轴坐标系,绘制图形时基于此坐标系绘制。) 也可以认为是子图,在一个Figure
对象中可以包含多个Axes
对象,也就是说一张画布可以包含多个子图;Line2D
对象,代表线条;Text
对象,代表了文字,比如一张子图需要标题,就可以使用一个Text
对象;
下面我们先来看一个简单的例子:
- N = 50
- x = np.random.random(N) # 横坐标
- y = np.random.random(N) # 纵坐标
- colors = np.random.random(N) # 每个坐标的颜色
- area = np.pi*(10*np.random.random(N))**2 # 每个坐标的颜色
- plt.scatter(x,y,s=area,c=colors,alpha=0.5)
- plt.show()
我们可以看一下画出来的图像,是一个散点图
在上面这个例子中,我们没有创建任何Figure
对象,这是因为plt
会默认创建Figure
对象并保存在plt
内部。
下面我们通过plt.figure()
创建一个新的Figure
对象,然后在此Figure
对象上创建Axies
对象。
- fig = plt.figure()
- ax1 = fig.add_subplot(2,2,1) #添加一个Axes对象到布局为两行两列的第一个位置
- ax2 = fig.add_subplot(2,2,2)
- ax3 = fig.add_subplot(2,2,3)
- ax1.plot(np.random.randn(50).cumsum(), 'k--')
- ax2.hist(np.random.randn(100), bins=20, color='k')
- ax3.scatter(np.arange(30), np.arange(30) + 3 * np.random.randn(30))
- fig.show()
我们首先创建一个Figure对象,接着插入三个Axes对象,接着在Axes对象上绘制累积和线图,直方图以及散点图。
我们要注意的是上面的fig.add_subplot(2,2,1)
表示添加一个Axes对象到布局为两行两列的第一个位置。我们看一下上面代码绘制出图像的样子:
到这里我们就把 matplotlib 的基本功能看完了,下面我们看一下常用的属性设置。
子图的绘制
这里我们详细说明一下子图的绘制. matplotlib使用plt.subplots
来绘制子图, 我们可以使用下面的命令绘制一个2行, 5列的子图. 这里可以通过循环的方式进行子图的绘制,同时通过 subplots_adjust 来控制子图之间的间距。
- fig, axs = plt.subplots(2, 5, figsize=(15, 6), facecolor='w', edgecolor='k')
- fig.subplots_adjust(hspace = .5, wspace=.001)
- axs = axs.ravel()
- for i in range(10):
- axs[i].contourf(np.random.rand(10,10),5,cmap=plt.cm.Oranges)
- axs[i].set_title(str(250+i))
如果我们希望子图中的坐标轴关闭,可以在循环中加入下面的语句:
- axs[i].axis("off")
子图共享坐标轴
有下面两种方式可以共享子图的坐标轴, 我们可以设置为全局共享, 及所有的子图都能共享坐标轴:
- fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
我们也可以为某一个单独的子图设置共享:
- fig=plt.figure()
- ax1 = plt.subplot(211)
- ax2 = plt.subplot(212, sharex = ax1)
下面看一个简单的例子, 下面的setp
表示Set a property
, 是用来设置坐标轴的属性的:
- import matplotlib.pyplot as plt
- import numpy as np
- t = np.arange(0.01, 5.0, 0.01)
- s1 = np.sin(2 * np.pi * t)
- s2 = np.exp(-t)
- s3 = np.sin(4 * np.pi * t)
- ax1 = plt.subplot(311)
- plt.plot(t, s1)
- plt.setp(ax1.get_xticklabels(), fontsize=6)
- # share x only
- ax2 = plt.subplot(312, sharex=ax1)
- plt.plot(t, s2)
- # make these tick labels invisible
- plt.setp(ax2.get_xticklabels(), visible=False)
- # share x and y
- ax3 = plt.subplot(313, sharex=ax1, sharey=ax1)
- plt.plot(t, s3)
- plt.xlim(0.01, 5.0)
- plt.show()
科学绘图
如果想要使用 matplotlib 绘制一些论文需要的图像,可以使用 Science Plots 这个库。我们可以直接使用 pip 来进行安装。
- # for lastest release (from PyPI)
- pip install SciencePlots
使用的时候,只需要在最开始指定样式即可,详细的样式说明可以查看链接Science Plots:
- plt.style.use(['science','ieee','no-latex'])
当然也可以只针对某一个图像指定样式:
- with plt.style.context(['science', 'ieee']):
- plt.figure()
- plt.plot(x, y)
- plt.show()
SciencePlots
默认使用 serif
字体,如果想要更换字体,可以使用下面的方式:
- import matplotlib.pyplot as plt
- plt.style.use('science')
- plt.rcParams.update({
- "font.family": "serif", # specify font family here
- "font.serif": ["Times"], # specify font here
- "font.size":11}) # specify font size here
特别的,我们可以通过更换字体使得 SciencePlots
支持中文。具体来说,首先下载字体,收集的一些字体。接着将下载的字体文件复制到 site-packages/matplotlib/mpl-data/fonts/ttf
,最后修改 SciencePlots
的默认字体即可。
更多关于 SciencePlots
常见的问题,可以查看链接,SciencePlots FAQ
常用属性设置
matplotlib
绘制的图形可以设置各种属性,比如设置坐标系的刻度,标题,标签等属性。 matplotplib
绘制图形时,基于X, Y
轴坐标系绘图。我们可以设置X, Y
坐标的各种属性,比如刻度,范围,标签等属性。
matplotlib 保存图像
关于图像的保存, 可以直接使用下面的方式.
- plt.savefig("filename.png")
matplotlib 刷新图像
有的时候我们会使用 for 循环来保存多个图像,这个时候每次保存新的图像都要对之前的图像进行清空,否则所有的图像内容都会叠加在一起。这个使用 plt.cla() 即可进行清空画布。
- for camera_id, camera_flow in camera_interval_flow.items():
- plt.cla() # 清空图片
- ax.plot(camera_flow) # 绘制曲线
- plt.savefig("{}.png".format(camera_id)) # 图像保存
matplotlib显示中文
我们在开头加上下面的代码即可正常显示中文.
- from pylab import * # 设置显示中文
- mpl.rcParams['font.sans-serif'] = ['SimHei']
- plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
关于这里字体名称的设置,我们可以通过下面的方式来进行查看:
- import matplotlib
- a=sorted([f.name for f in matplotlib.font_manager.fontManager.ttflist])
- for i in a:
- print(i)
这里会打印出所有的注册的字体的名称,大致如下所示:
可以使用「FZShuTi」表示方正舒体。
可以显示 Latex 的公式
为了使得 matplotlib 可以显示 latex 中的公式,我们需要指定一些参数,如下所示:
- import matplotlib.pyplot as plt
- plt.rcParams.update({
- "text.usetex": True,
- "font.family": "serif",
- "font.serif": ["Palatino"],
- })
我们可以看一个下面的例子,绘制柱状图,此时 xticks 中含有数学公式:
- fig = plt.figure(figsize=(7, 5))
- ax = fig.add_subplot(1, 1, 1)
- menMeans = (1, 2, 3, 4, 5, 6, 7)
- menStd = [i/5 for i in [7, 6, 5, 4, 3, 2, 1]]
- ind = np.arange(N) # the x locations for the groups
- width = 0.35 # the width of the bars: can also be len(x) sequence
- ax.bar(ind, menMeans, width, yerr=menStd)
- plt.xticks(ind, ('$\\alpha=0.001$', '$\\alpha=0.01$', '$\\alpha=0.1$', '$\\alpha=1$', '$\\alpha=5$', '$\\alpha=10$', '$\\alpha=100$'))
- ax.xaxis.set_tick_params(labelsize=12)
- plt.ylim(0, 10) # 设置 y 轴的范围
- plt.yticks(np.arange(0, 10, 0.5)) # 设置 y 轴的显示
- ax.yaxis.set_tick_params(labelsize=12)
- plt.show()
最终的结果如下所示:
数字与颜色的转换-map number to color
- import matplotlib.cm as cm
- print(cm.hot(0.3))
也可以使用下面的样式进行绘制:
- cmap = plt.get_cmap('rainbow', 12)
参考资料: How to map number to color using matplotlib's colormap?
设置 x 和 y 轴的范围
我们可以使用 set_xlim
或是 set_ylim
来控制 x 和 y 轴的范围,如下所示:
- axes = plt.gca()
- axes.set_xlim([xmin,xmax])
- axes.set_ylim([ymin,ymax])
设置标题,坐标刻度
- fig = plt.figure()
- ax = fig.add_subplot(1,1,1)
- # 设置标题
- ax.set_title('Test Title')
- major_ticks = np.arange(0, 101, 20)
- minor_ticks = np.arange(0, 101, 5)
- # 设置刻度
- ax.set_xticks(major_ticks)
- ax.set_xticks(minor_ticks, minor=True) # 这个是短线
- ax.set_yticks(major_ticks)
- ax.set_yticks(minor_ticks, minor=True)
- # 设置 X, Y 轴 标签
- ax.set_xlabel("X axis")
- ax.set_ylabel("Y axis")
- # 设置网格
- ax.grid(which='minor', alpha=0.2)
- ax.grid(which='major', alpha=0.5)
- # 添加文字
- ax.text(42.5, 50, "Hello World")
- fig.show()
可以看到下面的效果
当我们不想要显示刻度的时候,我们也可以将刻度不显示,使用下面的方式可以关闭刻度的显示:
- ax.set_xticks([])
- ax.set_yticks([])
如果我们想要只显示 y 轴
的格子线,可以使用以下的方式:
- ax1.grid(axis='y', which='major', alpha=0.4, linestyle='--')
最终的效果会如下所示:
设置标题位置
直接使用 set_title
会默认将标题生成在图像的上部。但是有的时候我们需要自动调整 title 的位置。我们可以通过设置 set_title
中的参数 y 来改变 title 的位置。下面看一个简单的例子,显示默认生成的标题的位置:
- fig, ax = plt.subplots(figsize=(6, 3), subplot_kw=dict(aspect="equal"))
- ax.set_title("Title Position")
生成的结果如下所示:
上面是默认生成的标题的位置,我们可以通过控制 y 来修改标题的位置。如下所示:
- fig, ax = plt.subplots(figsize=(6, 3), subplot_kw=dict(aspect="equal"))
- ax.set_title("Title Position", y=1.5)
这个时候看生成的效果,可以看到他的标题的位置就在图像的上方一些:
参考资料,Python Matplotlib figure title overlaps axes label when using twiny
这里除了使用 y 来控制标题的位置之后,还可以使用 pad 来控制间距。如下所示:
- ax.set_title(u'这是标题', fontsize=24, pad=30)
设置 x-label 和 y-label 的间距
当我们设置了 x 和 y 轴的 label 之后,我们有的时候希望他距离我们的坐标距离远一些,这个时候可以使用 labelpad 来控制间距。
- ax.set_ylabel("y 的 Label", labelpad=30)
加入文字
对于文字, 我们可以再外面加上边框等. 上面是在文字周围加上边框, 底色是红色.
- text(x, y, s, bbox=dict(facecolor='red', alpha=0.5))
例如下面是对MNIST降维的结果
- ax.text(np.mean(v_x[ix][:,0]), np.mean(v_x[ix][:,1]), key, fontsize=18, bbox=dict(facecolor='white', alpha=0.5))
关于这张图片的详细生成过程, 可以参考链接, t-SNE与AE对MNIST可视化
设置坐标轴范围
有的时候需要设置坐标轴的范围,这个需要使用 xlim 和 ylim 来分别进行设置。一般设置完毕之后,还可以配合 xticks 和 yticks 来设置坐标轴的内容。如下面的例子:
- fig = plt.figure(figsize=(7, 5))
- ax = fig.add_subplot(1, 1, 1)
- plt.xlim(0, 10) # 设置 x 轴的范围
- plt.xticks(np.arange(0, 10, 2)) # 设置 x 轴的显示
- plt.ylim(0, 10) # 设置 y 轴的范围
- plt.yticks(np.arange(0, 10, 0.5)) # 设置 y 轴的显示
- ax.yaxis.set_tick_params(labelsize=12)
- plt.show()
最终的效果如下所示,可以看到 x 和 y 轴的显示间隔是不一样的,同时字体大小也是不一样的:
设置坐标轴刻度与坐标轴名称
注意, 在图中有两个部分,我把他叫做坐标轴刻度与刻度名称,坐标轴本身的名称。我们通过下面一个小例子来看一下是如何来进行设置他们的。
- from pylab import * # 设置显示中文
- mpl.rcParams['font.sans-serif'] = ['SimHei']
- plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
- x = np.linspace(0, 1, 100)
- fig = plt.figure(figsize=(12,8))
- ax = fig.add_subplot(1,1,1)
- ax.set_title(u'设置曲线颜色与图例', fontsize=17)
- # 绘制图形
- ax.plot(x, x ** 2, 'b.', label=r'$y = x^{2}$')
- # 设置坐标轴的标签
- ax.yaxis.set_tick_params(labelsize=15) # 设置y轴的字体的大小
- ax.set_xticks(np.linspace(0, 1, 11)) # 设置xticks出现的位置
- xticks = [i if i in [0, 0.5, 0.8, 1.0] else '' for i in np.linspace(0, 1, 11)] # 只显示特定数字
- ax.set_xticklabels(xticks, rotation=-45, fontsize=17) # 设置xticks的值
- # 设置坐标轴名称
- ax.set_ylabel("这是Y轴", fontsize='xx-large')
- ax.set_xlabel('这是X轴', fontsize='xx-large')
- # 自动生成图例
- ax.legend(fontsize=17)
- # 设置图像x,y轴的范围,都是从0-1
- ax.axis([0, 1, 0, 1])
- fig.show()
这上面的内容包括:
- 设置坐标轴刻度的间隔;
- 设置坐标轴刻度的名称,调整大小和倾斜角度;
- 设置 x 轴,y 轴的名称;
设置坐标轴为log scale
有的时候,我们需要设置坐标轴为 log scale,我们可以使用 plt.yscale
进行设置。可以参考下面的代码:
- dt = 0.01
- x = np.arange(-50.0, 50.0, dt)
- y = np.arange(0, 100.0, dt)
- fig = plt.figure()
- ax = fig.add_subplot(1,1,1)
- ax.plot(x, y)
- # 设置y scale
- plt.yscale('log')
- plt.ylabel('logy') # 设置y轴的label
最后绘制出来的结果如下图所示, 看y轴的刻度, 可以看到此时是log scale了:
设置曲线颜色,图例
- # 设置显示中文
- from pylab import *
- mpl.rcParams['font.sans-serif'] = ['SimHei']
- plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
- x = np.linspace(0, 1,100)
- fig = plt.figure()
- ax = fig.add_subplot(1,1,1)
- ax.set_title(u'设置曲线颜色与图例')
- # 四个参数分别表示x,y轴数据,'b--'表示颜色是蓝色样式是虚线,最后一个表示标签,支持LaTex
- ax.plot(x, x ** (1/8), 'b--', label=r'$y = x^{1/8}$')
- ax.plot(x, x ** 8, 'r--', label=r'$y = x^{8}$')
- ax.plot(x, x ** (1/2), 'r.', label=r'$y = x^{1/2}$')
- ax.plot(x, x ** 2, 'b.', label=r'$y = x^{2}$')
- ax.plot(x, x, 'g-', label=r'$y = x$')
- # 自动生成图例
- ax.legend()
- # 设置图像x,y轴的范围,都是从0-1
- ax.axis([0, 1, 0, 1])
- fig.show()
上面的代码运行完毕后我们可以看到如下的图像:
关于一些常用的样式,我们记录一下:
『线的样式』linestyle=
有
- '-' 默认实线
- '--' 虚线
- '-.' 间断线
- ':' 点状线
常见的『颜色』有:
- 'b' blue
- 'g' green
- 'r' red
- 'c' cyan
- 'm' magenta
- 'y' yellow
- 'k' black(黑色)
- 'w' white
图例与 pandas 结合使用
有的时候,我们的使用 pandas 的数据来进行绘图的时候。图例是 dataframe 中某一列的数据,这个时候我们就可以使用下面的方式插入图例:
- plt.legend(legend.values,loc=4)
参考资料:Pandas matplotlib.pyplot add legend by a column value
关于图例位置自定义
要是我们想要自定义图例的位置,比如说希望图例出现在曲线的上面,或者周围,我们可以使用 ax.text
来进行实现。我们看一下下面的这个例子:
- from pylab import * # 设置显示中文
- mpl.rcParams['font.sans-serif'] = ['SimHei']
- plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
- x = np.linspace(0, 1,100)
- fig = plt.figure(figsize=(12,8))
- ax = fig.add_subplot(1,1,1)
- ax.set_title(u'设置曲线颜色与图例')
- # 四个参数分别表示x,y轴数据,'b--'表示颜色是蓝色样式是虚线,最后一个表示标签,支持LaTex
- ax.plot(x, x ** 8, 'r--', label=r'$y = x^{8}$')
- ax.plot(x, x ** 2, 'b.', label=r'$y = x^{2}$')
- # 手动设置图例
- ax.text(x[-10], 0.9*(x ** 8) [-10], r'$y = x^{8}$', fontsize=17)
- ax.text(x[-26], 1.2*(x ** 2) [-26], r'$y = x^{2}$', fontsize=17)
- # 自动生成图例
- ax.legend()
- # 设置图像x,y轴的范围,都是从0-1
- ax.axis([0, 1, 0, 1])
- fig.show()
最终的实现的效果如下图所示,可以看到在两条曲线周围都有了图例:
绘制图像的样式
绘制xkcd样式图片
我们可以绘制漫画风格的图片,只需要在初始设定 plt.xkcd()
即可:
- plt.xkcd()
- fig = plt.figure()
- ax = fig.add_subplot(1, 1, 1)
- np.random.seed(7)
- x = np.linspace(1, 10, 50)
- y = 2*x + 0.5*x**2 + 0.3*x**4 - 0.025*x**5 + 5*np.random.rand(50)
- ax.scatter(x, y)
最终的绘制效果如下图所示:
我们需要注意的是,在设置 xkcd 样式的时候,可以写成下面这样(使用 with
包住),这样可以保证只有当前的图像应用这个风格:
- with plt.xkcd():
- # This figure will be in XKCD-style
- fig1 = plt.figure()
- # ...
- # This figure will be in regular style
- fig2 = plt.figure()
关于 xkcd 样式中文的设置,需要在定义风格之后进行,下面是一个例子:
- plt.xkcd()
- plt.rcParams['font.family'] = ['FZShuTi']
下面看一个简单的例子,包含中文的 xkcd 样式的图像:
- plt.xkcd()
- plt.rcParams['font.family'] = ['FZShuTi'] # 这个需要在设置样式之后再设置
- fig = plt.figure()
- ax = fig.add_subplot(1, 1, 1)
- ax.set_title(u'是否考虑自己未来的规划', fontsize=25)
- x = np.arange(3)*1.2
- y = [137, 140, 176] # 有
- z = [33, 45, 38] # 没有
- sum = [i+j for i,j in zip(y,z)] # 计算每个年纪所占的比例
- y = [i/j for i,j in zip(y,sum)]
- z = [i/j for i,j in zip(z,sum)]
- w=0.3
- # 绘制多个bar在同一个图中, 这里需要控制width
- plt.bar(x-w/2, y, width=w, align='center', label='有')
- plt.bar(x+w/2, z, width=w, align='center', label='没有')
- ax.set_xticks(x) # 设置xticks出现的位置
- ax.set_xticklabels(['高一', '高二', '高三'], rotation='30', fontsize='xx-large')
- ax.plot(x, y, c='green') # 画出曲线
- ax.legend(bbox_to_anchor=(1, 0.95)) # 绘制图例
- plt.show()
最终的输出格式如下所示:
关于具体的说明,可以参考链接,用matplotlib库绘制手涂风格桥表
图像的样式概览
除了上面的风格之外,我们还有不同类型的风格可以进行设置。例如下面是设置为默认的风格,完整的风格间下面的参考链接:
- # 设置画图的style
- matplotlib.style.use('default')
matplotlib风格类型: Style sheets reference
绘制其他样式
对于绘制其他样式的图片,只需要修改plt.style.use即可,可以看下面的简单的例子:
- # 生成随机数据
- np.random.seed(7)
- x = np.linspace(1, 12, 10)
- y = 2*x + 0.7*x**2 - 20*np.sin(x) - 20*np.cos(x) + 5*np.random.rand(10)
- plt.style.use("ggplot") # 使用美观的样式
- plt.scatter(x, y)
设置图像颜色风格
除了上面设置图像的风格之外,我们还可以设置图像的颜色的风格。我们可以使用下面的命令来设置颜色的样式。
- plt.set_cmap('RdBu')
我们来看一下最终的效果,使用柱状图来举一个例子。
- x = np.arange(3)*1.2
- y = [4, 9, 2]
- z = [1, 2, 3]
- k = [11, 12, 13]
- ax = plt.subplot(111)
- plt.set_cmap('RdBu')
- w=0.3
- # 绘制多个bar在同一个图中, 这里需要控制width
- plt.bar(x-w, y, width=w, align='center')
- plt.bar(x, z, width=w, align='center')
- plt.bar(x+w, k, width=w, align='center')
- ax.set_xticks(x) # 设置xticks出现的位置
- ax.set_xticklabels(['1','2','3'], rotation='vertical', fontsize = 'xx-large')
- ax.autoscale(tight=True)
- plt.show()
这是一个绘制多个柱状图的方式,最终的效果如下所示。
设置图像颜色
除了上面使用指定的颜色,我们还可以指定我们需要的颜色,还是使用上面的柱状图作为例子我们简单来看一下。
其实基本是一样的,只是在绘图的时候指定了颜色,color=.。这三种颜色搭配我感觉还是不错的,在这里放一下(最终的效果见下面)。
- #e0109c
- #1b71f1
- #9eed3b
- x = np.arange(3)*1.2
- y = [4, 9, 2]
- z = [1, 2, 3]
- k = [11, 12, 13]
- ax = plt.subplot(111)
- w=0.3
- # 绘制多个bar在同一个图中, 这里需要控制width
- plt.bar(x-w, y, width=w, color='#e0109c', align='center')
- plt.bar(x, z, width=w, color='#1b71f1', align='center')
- plt.bar(x+w, k, width=w, color='#9eed3b', align='center')
- ax.set_xticks(x) # 设置xticks出现的位置
- ax.set_xticklabels(['1','2','3'], rotation='vertical', fontsize = 'xx-large')
- ax.autoscale(tight=True)
- plt.show()
上面的颜色,绘制出来的效果如下所示。
常用图像
我们在数据分析的时候,经常会用到各种各样的图像,我们就在这里总结一下。
水平线
我们使用 axhline
可以直接绘制水平线。下面看一个例子:
- import seaborn as sns
- import matplotlib.pyplot as plt
- import numpy as np
- import pandas as pd
- x = np.array([2, 2, 4, 4])
- y = np.array([5, 10, 10, 15])
- isBool = np.array([True, False, True, False])
- data = pd.DataFrame(
- np.column_stack((x, y, isBool)),
- columns=["x", "y", "someBoolean"]
- )
- ax = sns.lineplot(x="x", y="y", hue="someBoolean", data=data)
- ax.axhline(y=7, c='red', linestyle='dashed', label="h7")
- ax.axhline(y=15, c='blue', linestyle='dashed', label="h15")
- plt.legend()
- plt.show()
绘制的结果如下图所示,可以看到包含两个水平线:
参考资料:matplotlib-axhline
线形图(折线图)
这个就有点类似把一些散点连起来,我们使用Axes.plot
进行绘制
- fig = plt.figure()
- ax = fig.add_subplot(1,1,1)
- x = np.arange(10)
- y = x**2
- ax.plot(x,y)
- fig.show()
- plt.style.use(['science','ieee','no-latex']) # 设置格式
- fig, ax = plt.subplots(figsize=(6, 4), nrows=1,ncols=1)
- # 定义数据
- x_indexs = [0, 1, 2, 3, 4, 5]
- heights = [0.98, 0.93, 0.83, 0.72, 0.68, 0.66]
- # 绘图
- ax.plot(x_indexs, heights, '--', linewidth=2, marker='o', markersize=4) # 绘制折线图
- # 绘制竖线
- for x_index, height in zip(x_indexs,heights):
- ax.plot([x_index, x_index], [0, height], 'k--', linewidth=0.5)
- # 设置图的样式
- ax.set(ylim=[0.5, 1]) # 设置 y 轴范围
- ax.set_xticks(x_indexs) # 设置xticks出现的位置
- ax.set_xticklabels(['$\gamma$=0.01','$\gamma$=0.1','$\gamma$=0.2', '$\gamma$=0.5', '$\gamma$=0.9', '$\gamma$=0.99'])
- ax.set_ylabel('F-measure')
绘制误差区间
例如现在有一个3×5×20
的数据,我们想要绘制三条折线,同时绘制出他的误差范围。于是数据生成与绘制的代码如下所示(我们可以理解为 5×20 是生成了 5 条实验数据,我们想要绘制 20 个点。但是因为有 5 条数据,因此每隔位置其实有 5 个数据,因此我们计算他的均值和方差,绘制出误差区间):
- import numpy as np
- import matplotlib.pyplot as plt
- import seaborn as sns
- if __name__ == '__main__':
- data = np.random.random((3, 5, 20))
- data[1] += 5
- data[2] += 10
- # Reward 与时间的关系
- clrs = sns.color_palette("husl", 5)
- fig = plt.figure(figsize=plt.figaspect(0.6))
- ax = fig.add_subplot(1,1,1)
- labels = ['A', 'B', 'C']
- for i, label in enumerate(labels):
- steps = list(range(20))
- mean = np.mean(data[i], 0) # 计算均值
- std = np.std(data[i], 0) # 计算方差
- ax.plot(steps, mean, label=label, c=clrs[i])
- ax.fill_between(steps, mean-std, mean+std ,alpha=0.3, facecolor=clrs[i])
- plt.legend()
- plt.show()
绘制箭头
我们可以使用 arrow 来在 Axes 上面绘制一个箭头。arrow(x, y, dx, dy)
会有四个参数,意思如下:This draws an arrow from (x,y)
to (x+dx,y+dy)
.
我们尝试以下的代码:
- fig = plt.figure(figsize=plt.figaspect(1))
- ax1 = fig.add_subplot(111)
- ax1.arrow(x=0, y=0, dx=0.5, dy=0.5, width=0.01)
- plt.show()
柱状图
纵向的柱状图
- fig = plt.figure(figsize=(7,5))
- ax = fig.add_subplot(1,1,1)
- bar_data = [5, 20, 15, 25, 10]
- bar_labels = ['Tom', 'Dick', 'Harry', 'Slim', 'Jim']
- plt.bar(range(len(bar_data)), bar_data, tick_label=bar_labels)
- plt.show()
堆叠柱状图
我们可以将两个柱状图以堆叠的形式画在一起。下面看一个简单的例子。- data = pd.DataFrame({'A': [1,3,2,4], 'B': [4,2,3,1], 'index': ['test1', 'test2', 'test3', 'test4']})
- plt.rcParams['figure.figsize'] = (10, 6)
- plt.bar(x='index', height='A', data=data, width=1.0, label='下')
- plt.bar(x='index', height='B', bottom='A', data=data, width=1, label='上')
- ax = plt.gca()
- ax.legend(prop=myfont, bbox_to_anchor=(1.0, 0.9))
横向柱状图
将上面的plt.bar替换为plt.barh可以画出横向的柱状图. 一般用在labels比较长,或是比较多的时候,比如说我们可以绘制出下面的图像. 参考链接 : matplotlib绘图——柱状图绘制多条柱状图
有的时候,我们需要绘制多条柱状图在一起,从而来进行比较,可以使用下面的方式进行绘制.(其实这一部分的内容是和上面介绍设置图像颜色, 图像风格是重复的, 在这里再简单说明一下)- x = np.arange(3)*1.2
- y = [4, 9, 2]
- z = [1, 2, 3]
- k = [11, 12, 13]
- ax = plt.subplot(111)
- w=0.3
- # 绘制多个bar在同一个图中, 这里需要控制width
- plt.bar(x-w, y, width=w, color='#e0109c', align='center')
- plt.bar(x, z, width=w, color='#1b71f1', align='center')
- plt.bar(x+w, k, width=w, color='#9eed3b', align='center')
- ax.set_xticks(x) # 设置xticks出现的位置
- ax.set_xticklabels(['1','2','3'], rotation='vertical', fontsize = 'xx-large')
- ax.autoscale(tight=True)
- plt.show()
TP, FP, FN
这些指标。如下图所示(下面是使用 NSL-KDD
做的实验的结果):
参考链接:Python matplotlib multiple bars
包含标准差的柱状图
有的时候我们柱状图上面需要包含标准差,可以使用参数yerr
来进行设置,如下所示:
- fig = plt.figure(figsize=(7, 5))
- ax = fig.add_subplot(1, 1, 1)
- menMeans = (1, 2, 3, 4, 5, 6, 7)
- menStd = [i/5 for i in [7, 6, 5, 4, 3, 2, 1]]
- ind = np.arange(N) # the x locations for the groups
- width = 0.35 # the width of the bars: can also be len(x) sequence
- ax.bar(ind, menMeans, width, yerr=menStd)
- plt.xticks(ind, ('$\\alpha=0.001$', '$\\alpha=0.01$', '$\\alpha=0.1$', '$\\alpha=1$', '$\\alpha=5$', '$\\alpha=10$', '$\\alpha=100$'))
- ax.xaxis.set_tick_params(labelsize=12)
- plt.ylim(0, 10) # 设置 y 轴的范围
- plt.yticks(np.arange(0, 10, 0.5)) # 设置 y 轴的显示
- ax.yaxis.set_tick_params(labelsize=12)
- plt.show()
yerr
是可以设置样式的。例如可以更换颜色,粗细等。我们通过下面的方式进行更换,error_kw 这个参数的设置:
- ax.bar(ind, menMeans, width, color='k', yerr=menStd, error_kw=dict(elinewidth=2, ecolor='#7f8c8d', capsize=5))
yerr
更换为 xerr
。下面来看一个简单的例子(这种情况更加适用于每个柱的标题比较长):
- ax.barh(x_ticks, data_mean, xerr=data_std, error_kw=dict(elinewidth=2, ecolor='#7f8c8d', capsize=3))
barh
的时候需要使用 xerr
而不是 yerr
,即误差画在「y-轴」方向。
参考资料,Matplotlib学习---用matplotlib画误差线(errorbar)
使用柱状图和折线图显示累计占比
对于某一类数据来说我们可以首先统计不同区间数据的数量。- binRange = [0, 784, 1024, 4096, 100000]
- sample_num, _ = np.histogram(pcaps_size_list, bins=binRange)
- print(sample_num)
- """
- [46054 1413 3437 3691]
- """
- sample_num_percentage = np.cumsum(sample_num, dtype=float)/sum(sample_num)
- print(sample_num_percentage)
- """
- [0.8435571 0.86943859 0.93239308 1. ]
- """
- # 绘制直方图和折线图
- fig = plt.figure()
- ax = fig.add_subplot(1,1,1)
- fig_index = ['0-784', '784-1024', '1024-4096', '>4096']
- # 绘制柱状图
- ax.bar(fig_index, sample_num)
- ax.set_ylim([0, 70000]) # 设置 y 轴范围
- ax.set_xlabel('Pcap Size (Byte)') # 设置 x 轴的 label
- ax.set_ylabel('Pcap Number') # 设置 y 轴的 label
- # 绘制折线图
- ax_twin = ax.twinx() # 次坐标
- ax_twin.plot(fig_index, sample_num_percentage, marker='o', markersize=3)
- ax_twin.set_ylim([0, 1.1])
- # 添加文字
- for tmp_index, tmp_percentage in zip(fig_index, sample_num_percentage):
- ax_twin.text(tmp_index, 1.03* tmp_percentage, "{:<6.2f}".format(tmp_percentage))
饼图的绘制
基础饼图的绘制
我们只需要给定饼图中每一块的大小,每一块的 label,每一块是否要突出,即可绘制出一个最基础的饼图。- # Pie chart, where the slices will be ordered and plotted counter-clockwise:
- labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
- sizes = [15, 30, 45, 10]
- explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs')
- fig1, ax1 = plt.subplots()
- ax1.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%',
- shadow=True, startangle=90)
- ax1.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle.
- plt.show()
对饼图进行 Label
下面代码只需要修改 data,title,和 recipe 即可,后面的都可以不进行修改。- fig, ax = plt.subplots(figsize=(7, 5), subplot_kw=dict(aspect="equal"))
- y1 = [135, 148, 166]
- y3 = [73, 95, 93]
- y4 = [85, 99, 82]
- y5 = [114, 121, 122]
- y6 = [105, 107, 100]
- data = [np.sum(i) for i in [y1, y2, y3, y4, y5, y6]]
- title = ["开设职业生涯规划课程",
- "举办职业规划讲座",
- "在学校开通职业规划评价系统",
- "在学校设立专门的职业指导中心",
- "创建丰富多彩的职业体验课程",
- "加强教师和家长的指导"]
- recipe = ['{} {:<3.2f} %'.format(i,j/np.sum(data)*100) for i,j in zip(title, data)] # 设置标题
- # 下面的不需要修改
- wedges, texts = ax.pie(data, wedgeprops=dict(width=0.4), startangle=-40)
- bbox_props = dict(boxstyle="square,pad=0.3", fc="w", ec="k", lw=0.72)
- kw = dict(arrowprops=dict(arrowstyle="-"),
- bbox=bbox_props, zorder=0, va="center")
- for i, p in enumerate(wedges):
- ang = (p.theta2 - p.theta1)/2. + p.theta1
- y = np.sin(np.deg2rad(ang))
- x = np.cos(np.deg2rad(ang))
- horizontalalignment = {-1: "right", 1: "left"}[int(np.sign(x))]
- connectionstyle = "angle,angleA=0,angleB={}".format(ang)
- kw["arrowprops"].update({"connectionstyle": connectionstyle})
- ax.annotate(recipe[i], xy=(x, y), xytext=(1.35*np.sign(x), 1.4*y),
- horizontalalignment=horizontalalignment, **kw)
- ax.set_title(u"您认为学校开展职业生涯规划教育的措施有哪些", fontsize=25, y=1.2)
- plt.show()
- 关于 label 的位置,我们可以通过 xytext 来控制,及上面的 1.35*np.sign(x), 和 1.4*y, 可以控制前面的系数, 1.35 和 1.4,来使我们的 label 的位置不一样;
- 控制 label 的字体大小,可以直接在 axannotate 部分增加参数 fontsize 即可。如下面的例子:
- for x, y, label, size in zip(X, Y, labels, sizes):
- ax.annotate(label, (x, y), fontsize=size)
直方图
直方图X轴一般是统计的样本,而Y轴一般是样本对应的统计度量。
为了构建直方图,第一步是将值的范围分段,即将整个值的范围分成一系列间隔,然后计算每个间隔中有多少值。 这些值通常被指定为连续的,不重叠的变量间隔。 间隔必须相邻,并且通常是(但不是必须的)相等的大小。在 matplotlib 中可以使用Axes.hist
方法绘制直方图。
- fig = plt.figure()
- ax = fig.add_subplot(1,1,1)
- data = np.random.normal(0,20,1000) # 在0-20之间产生1000个随机数
- bins = np.arange(-100,100,5) # 产生区间刻度
- ax.hist(data,bins=bins)
- fig.show()
- import random
- from scipy import stats
- # 产生测试数据
- x = [random.gauss(3,1) for _ in range(400)]
- y = [random.gauss(4,2) for _ in range(400)]
- # 拟合密度曲线
- kde1 = stats.gaussian_kde(x)
- kde2 = stats.gaussian_kde(y)
- bins = np.linspace(-10, 10, 100)
- # 绘制直方图
- plt.hist(x, bins, alpha=0.5, label='x', normed=True, color='#2980b9')
- plt.hist(y, bins, alpha=0.5, label='y', normed=True, color='#f39c12')
- # 绘制曲线
- plt.plot(bins, kde1(bins), lw=4, color='#2980b9')
- plt.plot(bins, kde2(bins), lw=4, color='#f39c12')
- plt.legend(loc='upper right')
- plt.show()
散点图
散点图,将所有的数据值在图形中绘制成点,这样有多少数据值在图形中就会有多少个点。通过这些数据点可以看出数据值的分布模式,比如是否有聚类模式,或者相关关系或者发现离群点。在 matplotlib 中可以通过Axes.scatter
绘制散点图。
- fig = plt.figure()
- ax = fig.add_subplot(1,1,1)
- x = np.arange(1,100,1)
- y = x**2+100*x*np.random.random(len(x))
- ax.scatter(x,y)
- fig.show()
散点图的美化
一些比较规整的,类似于表格的数据,也是可以使用散点图进行绘制。我们首先生成样例数据,这里是生成每一年,每一个年龄段的人口的数据。也就是1年,如果年龄段是0-80,也就是一年会有80组数据。- data = []
- years = [str(i) for i in list(range(1990, 2020, 1))] # 所有的年份
- ages = [str(i) for i in list(range(0, 81, 5))] # 所有的年龄段
- populations = [10000*i for i in list(range(0, 81, 5))] # 每个年龄段的人口
- for year_index, year in enumerate(years):
- for age_index, age in enumerate(ages):
- data.append([year, age, (1+0.1*year_index)*populations[age_index]])
- plot_data = pd.DataFrame(data, columns=['year', 'age', 'population'])
- plot_data.head()
- plt.rcParams['figure.figsize'] = (18, 8)
- plt.scatter(
- x=plot_data['year'],
- y=plot_data['age'],
- c=plot_data['population'],
- s=plot_data['population']*0.0005,
- ec='tab:gray',
- cmap=plt.cm.RdBu_r,
- alpha=0.8)
- ax = plt.gca()
- # 坐标轴控制
- ax.set_xticks([years[i] for i in range(0, len(years), int(len(years)/5))])
- ax.set_yticks([ages[i] for i in range(0, len(ages), int(len(ages)/5))])
- # 不显示左侧和底部纵坐标轴
- ax.spines['left'].set_visible(False)
- ax.spines['bottom'].set_visible(False)
- # 显示 y 轴网格线
- ax.grid(b=False, axis='y', lw=1)
- plt.ylabel('Mean Age')
高阶散点图-指定不同类别使用不同颜色
如果散点图中有不同的类别, 同时希望可以对不同类别使用不同的颜色, 并使用图例标注出来, 我们需要依序进行绘制. 以下是一个完整的例子, 详细说明可以查看链接: matplotlib绘图优化-散点图绘制图例- cmap = plt.get_cmap('rainbow', 12) # 数字与颜色的转换
- scatter_x = np.array([1,2,3,4,5])
- scatter_y = np.array([5,4,3,2,1])
- group = np.array([1,3,2,1,3])
- cdict = {1: '111', 2: '222', 3: '333'}
- fig, ax = plt.subplots()
- for key in cdict.keys():
- ix = np.where(group == key)
- ax.scatter(scatter_x[ix], scatter_y[ix], color = cmap(key), label = key, s = 100) # 注意这里color的指定
- ax.legend()
- plt.show()
- markers = ["8","s","p","P","*","h","H","+","x","X","D","d"]
绘制多边形(区间的绘制)
我们可以使用plt.fill进行多边形的绘制,我们先看一个简单的例子:- # 绘制一个多边形的区域
- test_x = np.array([1,2,3])
- test_y = np.array([4,1,2])
- fig, axes = plt.subplots(nrows=1, ncols=1,figsize=(13,7))
- axes.fill(test_x,test_y)
- # 绘制一个多边形的区域
- test_x = np.linspace(0.1, 9.9, 200)
- test_y = np.sin(test_x)
- fig, axes = plt.subplots(nrows=1, ncols=1,figsize=(13,7))
- axes.plot(test_x,test_y,c='r')
- axes.fill(np.concatenate([test_x,test_x[::-1]]),np.concatenate([test_y + 1, test_y - 1]))
箱线图
箱线图可以看出数据的分散程度,异常值等信息,箱线图根据一组数据的以下 5 个统计值进行绘制:
- 最小值;
- 第1四分位数;
- 中位数;
- 第3四分位数;
- 最大值;
在 matplotlib 中可以使用Axes.boxplot
方法绘制箱线图
- fig = plt.figure()
- ax = fig.add_subplot(1,1,1)
- # 产生 50 个小于 100 的随机数
- spread = np.random.rand(50) * 100
- # 产生 25 个值为 50 的数据
- center = np.ones(25) * 50
- # 异常值
- outlier_high = np.random.rand(10) * 100 + 100
- outlier_low = np.random.rand(10) * -100
- data = np.concatenate((spread, center, outlier_high, outlier_low), 0)
- ax.boxplot(data)
- fig.show()
上面的代码中,我们特意创建了 data 数据,可以推断出该数据的中位数是 50,还有一些其他异常值,绘制的图形如下:
同样的,我们可以绘制多组数据的箱线图,可以看一下下面的例子:
- fig1, ax1 = plt.subplots(figsize=(16,8))
- # 产生数据
- test1 = np.random.randint(100, 200, 1000)
- test2 = np.random.randint(300, 400, 1000)
- ax1.boxplot([test1, test2], labels=['test1', 'tets2'])
- ax1.set_xticklabels(['test1', 'tets2'], rotation=-45, fontsize=18)
- ax1.yaxis.set_tick_params(labelsize=18)
我们只需要把数据使用 List 组合起来即可,上面的代码最终效果如下所示:
如果想要是的箱线图可以中间有折进去(带有缺口),可以设置 notch=True
,最终的结果会如下图所示:
关于更多箱线图的例子,可以查看链接,Matplotlib Boxplot Demo
绘制动态图
除了能绘制上面的静态图像,我们还可以绘制动态图像,下面我们来举一个小例子来说明matplotlib如何绘制动态图像。
简单的动画制作
动态图主要是通过animation
模块实现。具体就是matplotlib.animation.FuncAnimation(fig, func)
。其中fig
代表所绘制的图像。而func
可以看作是更新函数,它刷新每一帧的值。
- import numpy as np
- import matplotlib.pyplot as plt
- import matplotlib.animation as animation
- fig = plt.figure()
- ax = fig.add_subplot(1,1,1)
- x = np.arange(0,2*np.pi,0.01)
- line, = plt.plot(x,np.sin(x))
- # 更新函数
- def update(i):
- line.set_ydata(np.sin(x+i/10))
- return line
- animation = animation.FuncAnimation(fig,update,interval=20)
- plt.show()
上面的interval表示更新频率,以 ms 计。我们在上面定义了一个 update 函数,该函数可以通过重新设置 y 的值使其动起来。看一下上面的绘制出的效果
通过更新列表更新图像
现在假设我们已经将数据保存在 list 中,我们希望有逐渐向里面添加的动画效果。所以我们在 animate
函数中,每次都比前一步多一个数据。具体代码如下所示:
- import matplotlib.pyplot as plt
- import matplotlib.animation as animation
- fig = plt.figure()
- #creating a subplot
- ax1 = fig.add_subplot(1,1,1)
- Date = [1,2,3,4,5,6,7,8,9]
- Price = [100, 120, 320, 370, 420, 600, 550, 720, 900]
- def animate(i):
- xs = Date[:i]
- ys = Price[:i]
- print(xs, ys)
- ax1.clear() # 清除画布
- ax1.plot(xs, ys)
- plt.xlabel('Date')
- plt.ylabel('Price')
- plt.title('Live graph with matplotlib')
- ani = animation.FuncAnimation(fig, animate, interval=500)
- plt.show()
最终的可视化的效果如下所示:
参考资料,Animations with Matplotlib
绘制三维图像
最后,我们来说一下三维图像的绘制。
绘制三维散点图
- import numpy as np
- # 导入2d,3d的绘图模块
- from mpl_toolkits.mplot3d import Axes3D
- import matplotlib.pyplot as plt
- # x, y, z 均为 0 到 1 之间的 100 个随机数
- x = np.random.normal(0, 1, 100)
- y = np.random.normal(0, 1, 100)
- z = np.random.normal(0, 1, 100)
- fig = plt.figure()
- ax = Axes3D(fig)
- ax.scatter(x,y,z)
- plt.show()
最终的效果如下图所示:
绘制三维线形图
- # 载入模块
- from mpl_toolkits.mplot3d import Axes3D
- import matplotlib.pyplot as plt
- import numpy as np
- # 生成数据
- x = np.linspace(-6 * np.pi, 6 * np.pi, 1000)
- y = np.sin(x)
- z = np.cos(x)
- # 创建 3D 图形对象
- fig = plt.figure()
- ax = Axes3D(fig)
- # 绘制线型图
- ax.plot(x, y, z)
- # 显示图
- plt.show()
绘制三维曲面图
- # 载入模块
- import numpy as np
- import matplotlib.pyplot as plt
- from mpl_toolkits.mplot3d import Axes3D
- # 创建 3D 图形对象
- fig = plt.figure()
- ax = Axes3D(fig)
- # 生成数据
- X = np.arange(-2, 2, 0.1)
- Y = np.arange(-2, 2, 0.1)
- X, Y = np.meshgrid(X, Y) #注意这一步
- Z = np.sqrt(X ** 2 + Y ** 2)
- # 绘制曲面图,并使用 cmap 着色
- ax.plot_surface(X, Y, Z, cmap=plt.cm.winter)
- plt.show()
这个上面的meshgrid是用来生成底部的网格的,我们可以看一下x,y是什么
运行上面的代码,于是我们可以得到下面的图像
子图的绘制
之前绘制二维图像的时候我们是通过.add_subplot()
来绘制子图的,其实在三维的时候也是一样的,只是注意 3D 绘图时要添加 projection='3d' 参数,下面我们看一个具体的例子。
- # -*- coding: utf-8 -*
- # 载入模块
- from mpl_toolkits.mplot3d import Axes3D
- import matplotlib.pyplot as plt
- import numpy as np
- # 创建 1 张画布
- fig = plt.figure()
- #===============
- # 向画布添加子图 1
- ax1 = fig.add_subplot(1, 2, 1, projection='3d')
- # 生成子图 1 数据
- x = np.linspace(-6 * np.pi, 6 * np.pi, 1000)
- y = np.sin(x)
- z = np.cos(x)
- # 绘制第 1 张图
- ax1.plot(x, y, z)
- #===============
- # 向画布添加子图 2
- ax2 = fig.add_subplot(1, 2, 2, projection='3d')
- # 生成子图 2 数据
- X = np.arange(-2, 2, 0.1)
- Y = np.arange(-2, 2, 0.1)
- X, Y = np.meshgrid(X, Y)
- Z = np.sqrt(X ** 2 + Y ** 2)
- # 绘制第 2 张图
- ax2.plot_surface(X, Y, Z, cmap=plt.cm.winter)
- # 显示图
- plt.show()
上面就是matplotlib的一些常用的用法了,记录在此也方便自己的查阅。
- 微信公众号
- 关注微信公众号
- QQ群
- 我们的QQ群号
评论