Notes on Matplotlib
0. 摘要
本文重点不在于具体画图操作,更在意 matplotlib
的基本概念,或者说画图的组件结构。在系统地学习了【莫烦Python】Matplotlib Python 画图教程 后,进行一些思考和总结,整个 matplotlib 包就清楚了,希望以后再画图的时候不再挠头。
1. 基本概念
最基本的就是我们看到的,一张图中可以含有多个坐标系,可以想象为:在一张白纸上,用笔画坐标系。所以有两个最基本的概念:figure(白纸),axes(坐标系)。
1.1 figure
一个 figure
就是输出图的窗口(框),相当于一个容器,其中包含图象。如上图,一个框包含了 5 张图象。有点像 word 中的画布。如果不声明 plt.figure()
,系统会有一个默认的 figure,所以你直接 plt.plot(x, y)
时,也可以画出图象。
1.2 axes
直译的话,axes
是坐标轴的意思,但是目前来看,它是一个坐标系,更确切地说,它是一张图象。上图中的 5 个图象就是 5 个 axes 。ps:就当是在画布上画了 5 个坐标系,坐标系中可以画线。我们画出来的图象实例,如散点或者曲线(上图中的蓝色直线),其实是由 axes 对象操作的,画在当前的 axes 对象对应的坐标系中。
刚开始对程序中“时而 plt.plot(x, y)
时而 ax.plot(x, y)
”感到迷惑,图象到底是怎么画出来的?
由 figure 和 axes 的基本概念以及两者之间的关系来看,图象是由 axes 画出来的。那为什么 plt.plot(x, y)
也可以?
还是“默认”的问题,既然“不声明 plt.figure()
,系统会有一个默认的 figure”,那么 figure 中自然也可以有默认的 axes。调用函数 axes = figure.get_axes()
就可以拿到当前 figure 中所有 axes 对象的列表。
from matplotlib import pyplot as plt
import numpy as npx = np.linspace(-1, 1, 50)
y = 2 * x + 1figure = plt.figure()
plt.plot(x, y)
plt.show()axess = figure.get_axes()
print(len(axess)) # 1
print(axess[0]) # AxesSubplot(0.125,0.11;0.775x0.77)
可以看到,即使没有明确地声明 axes 坐标系,figure 中依然存在一个 AxesSubplot
。所以我猜测 plt.plot(x, y)
其实是 figure.get_axes()[0].plot(x, y)
,即:自动调用默认的 axes 进行画图的。
但进一步的试验打脸了,如果把 plt.plot(x, y)
一句注释掉,axess
就是空的,也就是说,不画图像,就没有 axes 哦!(也不会出现框框,啥反应没有)。这至少说明 plt.plot(x, y)
画图的时候发现没有 axes,暗地里执行了 plt.axes()
,创建了一个 axes。
更常看到的是
ax = plt.gca()
获取当前 axes,# gca = ‘get current axes’1,如果没有,也会自动创建一个。所以plt.plot(x, y)
也可能是plt.gca().plot(x, y)
。反正是偷偷建了一个 axes。
2. 组件结构
明白了基本的两个概念之后,我们看一看 matplotlib
包是如何组织这些组件的,画出来的形形色色的图象是怎样的结构。
2.1 plt.xxx() vs. ax.set_xxx() && 设置坐标
由以上对 plt.plot(x, y)
和 ax.plot(x, y)
的分析可知,两者联系紧密。再由下一节的坐标轴设置可知,plt.xxx()
对坐标图象的设置很可能也是由类似 plt.gca().plot(x, y)
的 plt.gca().set_xxx()
操作完成的。
具体看下面两段代码,结果是一样的:
from matplotlib import pyplot as plt
import numpy as npx = np.linspace(-3, 3, 50)
y1 = 2 * x + 1
y2 = x ** 2plt.figure()plt.plot(x, y2)
plt.plot(x, y1, color='red', linewidth=1.0, linestyle='--')
# 标题
plt.title('title')
# 坐标轴范围
plt.xlim([-1, 2])
plt.ylim([-2, 3])
# 坐标轴标签
plt.xlabel('I am X')
plt.ylabel('I am Y')
# ticks 是坐标轴的数字记号
new_ticks = np.linspace(-1, 2, 5)
print(new_ticks)
plt.xticks(new_ticks) # 设置 xticks,没有提供符号,就直接显示当前位置数值
plt.yticks( # set yticks[-1, 0, 1, 2, 3], # 位置,下面提供符号,显示符号[r'$v\ b$', r'$b\ \alpha$', r'$m$', r'$g$', r'$vg$'] # 显示的符号,tex 公式
)plt.show()
from matplotlib import pyplot as plt
import numpy as npx = np.linspace(-3, 3, 50)
y1 = 2 * x + 1
y2 = x ** 2plt.figure()
ax = plt.gca()ax.plot(x, y2)
ax.plot(x, y1, color='red', linewidth=1.0, linestyle='--')
# 标题
ax.set_title('title')
# 坐标轴范围
ax.set_xlim([-1, 2])
ax.set_ylim([-2, 3])
# 坐标轴标签
ax.set_xlabel('I am X')
ax.set_ylabel('I am Y')
# ticks 是坐标轴的数字记号
new_ticks = np.linspace(-1, 2, 5)
print(new_ticks)
ax.set_xticks(new_ticks) # 设置 xticks,没有提供符号,就直接显示当前位置数值
ax.set_yticks([-1, 0, 1, 2, 3])
ax.set_yticklabels([r'$v\ b$', r'$b\ \alpha$', r'$m$', r'$g$', r'$vg$'])
# 警告说新版本不支持放在一起了,就用set_yticklabels
plt.show()
2.2 继续设置坐标轴(spines)
ax.spines
就是坐标轴的线(ax 对象的属性),默认是 x
轴和 y
轴各两条,默认 left 和 bottom 带刻度(主轴),right 和 top 不带刻度,当然,这可以改变。可以设置这些线的位置、颜色和可见性等各种属性。
ax = plt.gca()
# spine: 脊梁,就是上下左右四周的四个线
ax.spines['right'].set_color('none') # 颜色 none,消失
ax.spines['top'].set_color('none') # 这个的作用主要还是设置颜色吧
ax.spines['right'].set_visible(False) # 这样意思更明确吧
ax.spines['top'].set_visible(False)
# 设置 x, y 轴是 spine 的哪根线
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
# 再设置 spines 的位置,就设置了原点位置
ax.spines['bottom'].set_position(['data', 0]) # data, 定位方式为:数据值
ax.spines['left'].set_position(['data', 0]) # 还有 axes, 定位方式:坐标轴比例
这样一来,xticklabels
的概念也就清晰了:
for label in ax.get_xticklabels() + ax.get_yticklabels(): # 再看 ax.set_yticklabels() 是多么自然label.set_fontsize(12) # 设置 ticks 的字体label.set_bbox(dict(facecolor='white', edgecolor='None', alpha=0.3)) # ticks 的显示块
2.3 annotate, text, legend,… 成了和 plot 和 scatter 一样的概念
我们常看到 plt.plot(x, y)
以及 plt.scatter(x, y)
,知道它们是画线和散点。经过上面对 axes 的理解,我们又知道了画线和散点其实就是在 axes 坐标系中根据坐标点 (x, y)
画图,由 axes 对象操作。但其他像 annotate, text, legend 的东西也是经常用到的,它们是什么?
# annotate ############# plt.annotate 也一样
ax.annotate( # 这么说,annotate 是和 scatter、plot 一样的概念text=r'$2x+1=%s$' % y0, # 注释字符串xy=(x0, y0), # 注释点(xycoords='data')xycoords='data', # 以数据点为标准找位置xytext=(+30, -30), # 这个就是下一行的 offset 值,横坐标+30, 纵坐标-30textcoords='offset points', # 注释的位置,相对点进行 offsetfontsize=16,arrowprops=dict( # 箭头属性arrowstyle='->', # 简头样式connectionstyle='arc3, rad=.2' # 弧度之类的)
)# text ############# plt.text 也一样
ax.text(x=-1.0, y=1.5,s=r'$This\ is\ some\ text.\ \mu\ \sigma_i\ \alpha_t$',fontdict=dict(size=12, color='red')
)# handle:就是线的句柄,plot 的返回值,不知道为啥要带','
line1, = plt.plot(x, y2, label='up') # 先设置 label
line2, = plt.plot(x, y1, color='red', linewidth=1.0, linestyle='--', label='down')
# plt.legend() # 简单的默认图例
plt.gca().legend( # 这样一看,legend 也和 plot 是一样的概念handles=[line1, ], # handles 为空,则自动默认 plt.legend()labels=['aaa'],loc='best'
)
结构一下清晰了不少,原来 annotate, text, legend
这些奇奇怪怪的东西,就跟 plot
一条线一样,都是往 figure 里的 axes 上敷东西,顺便带上一些属性呈现不同个性。就连直方图上标的数值都是用 ax.text()
敷上去的。
2.4 3D 图不过是换了 axes
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits import mplot3dfig = plt.figure()
ax = mplot3d.Axes3D(fig) # 创建 3d 的 axes,并放入 figurex = np.arange(-6, 6, 0.2)
y = np.arange(-6, 6, 0.2)
x, y = np.meshgrid(x, y)
r = np.sqrt(x ** 2 + y ** 2)
z = np.sin(r * np.pi / 0.8)ax.plot_surface( # 这和 2d 的 ax.xxx() 一样的概念,只不过换成了 3d 坐标x, y, z,rstride=1, cstride=1, # 颜色变化跨度cmap=plt.get_cmap('rainbow')
)
ax.contourf( # 这和 2d 的 ax.xxx() 一样的概念,只不过换成了 3d 坐标x, y, z,zdir='z', # 沿着 z 轴投影offset=-2, # 投影位置cmap='rainbow'
)ax.set_zlim3d(-5, 5) # 就连设置属性都是一样的概念plt.show()
看,还是 axes 调用 ax.xxx() 往坐标系敷东西。
2.5 多子图(多 axes)
开头就说过“在一张白纸上画多个坐标系”,那多子图就是一个 figure 里放多个 axes 对象咯!有多种方法画多子图,包括:plt.subplot2grid
, gridspec.GridSpec
, plt.subplot(s)
以及 figure.add_axes
等。这里重点不在于画多子图的方法,而在于理解多子图的结构,不同的方法仅仅是 axes 的排列方式不同,本质是一样的。
2.5.1 AxesSubplot
& Axes
老是说 axes 是一个坐标系,那么它在代码编程中到底是个啥?这里先给出答案:在 figure.add_axes
方法中,axes 是 Axes
,其他方法中是 AxesSubplot
,甚至是默认的 axes 也是 AxesSubplot
。AxesSubplot
& Axes
有什么区别吗?figure.add_axes
这么特别,有什么特殊用途吗?
2.5.1.1 AxesSubplot
对于 AxesSubplot
以及其属性,就不多说了,这里也不打算深入探究,钻得太深容易陷进去。反正就是一个坐标系,可以往里画线画点。print(ax)
可以得到类似 AxesSubplot(0.125,0.53;0.352273x0.35)
的输出,数字依次是[left, bottom; width*height],不同多子图方法可能就是通过调整子图的这些参数来排版子图的。
2.5.1.2 Axes
对于 Axes
,print(ax)
也可以得到类似 Axes(0.2,0.6;0.25x0.25)
的输出,参数的意义是一样的。
2.5.2 多子图示例
感觉 gridspec.GridSpec
最方便,可以通过切片索引的方式像表格一样方便地布局子图,下面是画出开头图片的代码:
plt.figure()
gs = gridspec.GridSpec(3, 3)ax1 = plt.subplot(gs[0, :]) # 切片索引方式确定行列
ax1.plot([1, 2],[1, 2]
)
ax1.set_title('ax1_title')
print(ax1) # AxesSubplot(0.125,0.653529;0.775x0.226471)plt.subplot(gs[1, :2])
plt.subplot(gs[1:, 2])
plt.subplot(gs[2, 0])
plt.subplot(gs[2, 1])plt.show()
可以看到子图1是 AxesSubplot(0.125,0.653529;0.775x0.226471).
2.5.3 figure.add_axes
多图实现图中图
2.5.1 中问道“figure.add_axes
这么特别,有什么特殊用途吗?”,接下来就展示一下图中图的用途,说一下它的特别之处。
from matplotlib import pyplot as pltfigure = plt.figure()
x = [1, 2, 3, 4, 5, 6, 7]
y = [1, 3, 4, 2, 5, 8, 6]left, bottom, width, height = 0.1, 0.1, 0.8, 0.8
ax1 = figure.add_axes([left, bottom, width, height])
ax1.plot(x, y, 'r')
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.set_title('title')
# 这不是平级的吗
left, bottom, width, height = 0.2, 0.6, 0.25, 0.25
ax2 = figure.add_axes([left, bottom, width, height])
ax2.plot(x, y, 'b')
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.set_title('title inside1')
print(ax2) # Axes(0.2,0.6;0.25x0.25)
一张图,上面又贴了一张小图。看看代码才知道,这并不是什么图中带图,而是两个平级的图,先画的图在下面,后画的小图在上面覆盖一下,就成了图中带图的样子。print(ax2)
会打印出 Axes(0.2,0.6;0.25x0.25)
。仔细想想,这种 Axes
应该是比 AxesSubplot
更低级的概念,后者带了 Subplot
,就是子图的意思,可见这种 AxesSubplot
是专门为子图设计的,它本身就是一张图,位置也由 gridspec.GridSpec
之类的布局工具方便地确定了。而 Axes
是坐标系的概念,声明的时候就要用参数说明具体位置和大小。
既然可以自由地明确地说明坐标系的位置和大小,自然也就可以手动布局多子图了:
# 虽然不太方便
figure = plt.figure()
x = [1, 2, 3, 4, 5, 6, 7]
y = [1, 3, 4, 2, 5, 8, 6]
# 1, 1
left, bottom, width, height = 0.075, 0.625, 0.4, 0.32
ax1 = figure.add_axes([left, bottom, width, height])
ax1.plot(x, y, 'r')
ax1.set_xlabel('x1')
ax1.set_ylabel('y1')
ax1.set_title('title 1')
# 1, 2
left, bottom, width, height = 0.575, 0.625, 0.4, 0.32
ax2 = figure.add_axes([left, bottom, width, height])
ax2.plot(x, y, 'g')
ax2.set_xlabel('x2')
ax2.set_ylabel('y2')
ax2.set_title('title 2')
# 2, 1
left, bottom, width, height = 0.075, 0.125, 0.4, 0.32
ax3 = figure.add_axes([left, bottom, width, height])
ax3.plot(x, y, 'b')
ax3.set_xlabel('x3')
ax3.set_ylabel('y3')
ax3.set_title('title 3')
# 2, 2
left, bottom, width, height = 0.575, 0.125, 0.4, 0.32
ax4 = figure.add_axes([left, bottom, width, height])
ax4.plot(x, y, 'k')
ax4.set_xlabel('x4')
ax4.set_ylabel('y4')
ax4.set_title('title 4')
plt.show()
Summary
关于课程中诸如主次坐标、动画等其他的概念就不多说了,了解画图基本概念的目的以达到,其他的花里胡哨的东西只是翻翻文档的事,画图再也不用一头雾水地瞎改参数、瞎查博客,搞得乱七八糟的了。
fig = plt.gcf()
可获取当前 figure. 还有一点就是,一旦调用plt.figure()
就会生成新的 figure,后续的默认操作也是在新 figure 上,除非用 figure 句柄指定 figure 对象。plt.show() 会打印出当前所有的 figure,并清空,后续 plt.xxx() 会创建新的 figure. ↩︎
Notes on Matplotlib相关推荐
- seaborn 教程_使用Seaborn进行数据可视化教程
seaborn 教程 "Seaborn makes the exploratory data analysis phase of your data science project beau ...
- python matplotlib绘制等高线,plt.contour(),ax3.contour()和plt.contourf(),ax3.contour(), 同名函数
引用文章 https://blog.csdn.net/lanchunhui/article/details/70495353 首先这是由不同对象调用的函数,ax3指3D Figure对象即<cl ...
- python matplotlib.figure.Figure.add_subplot()方法的使用
官方文档 https://matplotlib.org/api/_as_gen/matplotlib.figure.Figure.html?highlight=add_subplot#matplotl ...
- Matplotlib课程–学习Python数据可视化
Learn the basics of Matplotlib in this crash course tutorial. Matplotlib is an amazing data visualiz ...
- fastai 2019 lesson9 notes 笔记
lesson9 How to train your model 本文markdown源文件:lesson9.md 2019年视频地址:https://course19.fast.ai/videos/? ...
- 数据可视化第八章使用matplotlib绘制高级图表
8.1 绘制等高线图 import numpy as np import matplotlib.pyplot as plt def calcu_elevation(x1, y1): h = (1-x1 ...
- 第八章、使用matplotlib绘制高级图表
8.1.绘制等高线图 等高线图是地形上高程相等的相邻各点所连成的闭合曲线,它会将地面上海拔高度相同的点连成环线,之后将环线垂直投影到某一水平面上,并按照一定的比例缩绘到图纸上. 在matplotlib ...
- 使用matplotlib绘制高级图表8
1.绘制等高线图 使用contour()和contourf()函数绘制和填充等高线图 import numpy as np import matplotlib.pyplot as plt # 计算高度 ...
- python 堆叠柱状图 多列 复杂_[Python Study Notes]堆叠柱状图绘制
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ...
最新文章
- 【spring】编程式事务控制
- [PHP] Phalcon操作示范
- JAVA 读取图片储存至本地
- 字符常量在C和C++中的区别
- RMAN异机复制数据库(相同路径)
- Hadoop入门(二十二)Mapreduce的求平均值程序
- desktop docker 无法卸载_Docker容器无法停止或移除-权限被拒绝错误
- oracle监听管理工具,oracle监听器管理
- 华为交换机eth口作用_华为交换机口如何绑定端口号
- Python六大开源框架对比:Web2py略胜一筹(转)
- 暴走英雄坛怎么领服务器维护奖励,暴走英雄坛琅嬛福地奖励在哪里?福地奖励获取方法一览...
- 读书笔记:《重来REWORK》
- Java基础23 网络编程 socket套接字流 TCP传输总结
- 计算机运行游戏卡顿,Win7电脑玩游戏出现卡顿如何解决?
- mysql连接数尖刺激增_mysql最大连接数max_connections
- Android调试工具adb的高逼格使用方式
- 2021秋软工实践个人作业一
- Appium 自动化测试 手机操作
- vue之router莫名其妙的bug
- 三万字,七十图详解计算机网络六十二问(建议收藏)
热门文章
- 玩转树莓派(一):树莓派更换国内源、设置中文显示及输入法
- 牧场物语移植java_牧场物语整合包-新手入门
- 毕业生进入IT 业如何进行角色转换
- python 大智慧 dll_Python调用windows下DLL详解
- win7 android双系统,联想乐Pad平板电脑发布:Win7/Android双系统
- JavaSwing基础
- 活期储蓄账目管理系统(C++课程设计)
- SRPG游戏开发(四十二)第九章 战斗系统 - 四 计算战斗数据II(Calculate Combat Data II)
- 大物流行业未来发展格局研判
- WordXP巧画禁烟标志(转)