d、Matplotlib
Pandas
参考文档:
概述
本教程使用版本:2.2.2
对于进行数据处理、分析、科学计算、机器学习等相关工作而言,将枯燥乏味、难以理解的数据、过程、结果等以图形的方式展现出来,既有助于推动任务发展,又能给与客户直观形象可理解的交付成果,是必不可少的一个工作环节。
在Python的生态环境中,Matplotlib是最著名的绘图库,它支持几乎所有的2D绘图和部分3D绘图,被广泛地应用在科学计算和数据可视化领域,是每个数据处理领域人员几乎必学的工具。通过Matplotlib,开发者仅需要几行代码,便可以生成线图、直方图、功率谱、条形图、错误图、散点图等各类型图谱。
从Matplotlib的名字上就可以看到,它对标的是大名鼎鼎的MATLAB,无论是从提供的功能、函数名、参数使用方法,两者都非常相似。所以,有MATLAB基础的人员,学Matplotlib会相对比较轻松,反之亦然。
Matplotlib在使用上往往和Numpy、Pandas、Ipython、Jupyter notebook等配合进行,跨平台,可无缝集成,使用方便,效率高。
尽管现在出现了一些更加酷炫的绘图工具,但Matplotlib仍然是我们必学的绘图工具。
安装
截至2019年1月,Matplotlib目前最新稳定版本为3.0.2和2.2.3。从3.0开始,Matplotlib将不再支持Python2.7,请大家注意。
对于Matplotlib的安装,我只推荐Anaconda发行版。这是最好的选择!
如果您真的要自己安装,可以使用下面的命令:
python -m pip install -U pip
python -m pip install -U matplotlib
Matplotlib依赖Numpy、lbpng和freetype等第三方库,在这个过程中,可能需要你自行解决依赖相关的问题。
更多的安装详细说明,参考官网:https://matplotlib.org/users/installing.html
安装完之后,可以通过下面的方法查看当前版本信息:
import matplotlib as mpl
mpl.__version__
'2.2.2'
配置环境
通用的导入方式:
import matplotlib as mpl
import matplotlib.pyplot as plt
在开始使用Matplolib之前,我们可以对它进行一些基本的配置,带有一点个性化特点。当然,你也可以保持原样,直接使用。
matplotlib的配置文件中包含大部分属性的默认值,使用下面的方法查看当前配置:
import matplotlib as mpl
mpl.rcParams
mpl.get_configdir() # 查看配置文件所在目录
或者:
import matplotlib.pyplot as plt
plt.rcParams
有两种方式可以动态配置参数:
- 使用参数字典rcParams
rcParams是我们修改Matplolib参数的接口,对于matplotlib而言,它是全局性的,任何的修改动作都会影响接下来的所有图表。其基本修改方式如下:
import matplotlib as mpl
mpl.rcParams['lines.linewidth'] = 2
可以看出这是一种典型的字典配置模式。
- 使用rc()配置函数
除了字典模式,还有一种调用函数的配置方式:
import matplotlib as mpl
mpl.rc('lines', linewidth=2)
如果需要重置动态修改后的配置参数,可以调用matplotlib.rcdefaults()
方法,将配置恢复成默认值。
我们来看一个简单的例子:
%matplotlib notebook
import matplotlib.pyplot as plt
import numpy as np
t = np.arange(0.0, 1.0, 0.01)
s = np.sin(2*np.pi*t)
plt.plot(t,s)
c = np.cos(2*np.pi*t)
plt.rcParams['lines.linewidth'] = '4'
plt.plot(t,c)
如果需要更深入的定制和查看全局选项,可以打开位于matplotlib/mpl-data下面的matplotlibrc文件。如果你修改了这个文件,并将它放置在home路径下以.matplotlibrc
命名,那么每次你使用matplotlib时都会读取该文件的配置。
更多相关内容,参考官网https://matplotlib.org/tutorials/introductory/customizing.html
使用常识
1. 魔法命令%matplotlib
魔法命令%matplotlib有三种用法
- 不带参数: 弹出额外的图形显示窗口,适用于shell环境
- inline:行内显示模式,不可交互
- notebook:jupyter notebook专用模式,可交互,图像清晰锐利。但可能需要经常手动停止交互,或者使用plt.draw()重新绘制。
另外,使用jupyter notebook时有个细节需要注意,在每个单元运行后,图表会被重置。因此,对于复杂的图表,你必须将所有的绘图命令放在一个单元cell中。
2. 风格样式
从Matplotlib 1.5版本之后,开始支持不同的风格样式,默认是classic经典样式。
- 通过plt.style.available查看可用的样式
- 设置样式方法:plt.style.use('classic')
- 常用样式:seaborn
3. show()的使用
matplotlib的使用场景通常有3种:
- 执行脚本
- shell
- notebook
从脚本执行绘图操作的时候,最后一行代码往往是plt.show(),这样才能让图形显示出来。
在shell中使用matplotlib一般不需要执行show方法,但是有可能图形不会自动刷新,可以使用plt.draw()强制更新。
而notebook则是web形式,可以将绘图内嵌到web里,可以交互,也可以单独的窗口。
保存图形
可以通过savefig()方法,将绘制的图形保存为文件:
x = np.linspace(0,10,100)
fig = plt.figure()
plt.plot(x,np.sin(x),'-')
plt.plot(x,np.cos(x),'--')
fig.savefig('d:/my_fig.png')
然后通过IPython的Image来显示文件内的图像:
from IPython.display import Image
Image('d:/my_fig.png')
可以通过下面的方法查看系统支持的图片格式:
>>> fig.canvas.get_supported_filetypes()
{'ps': 'Postscript',
'eps': 'Encapsulated Postscript',
'pdf': 'Portable Document Format',
'pgf': 'PGF code for LaTeX',
'png': 'Portable Network Graphics',
'raw': 'Raw RGBA bitmap',
'rgba': 'Raw RGBA bitmap',
'svg': 'Scalable Vector Graphics',
'svgz': 'Scalable Vector Graphics',
'jpg': 'Joint Photographic Experts Group',
'jpeg': 'Joint Photographic Experts Group',
'tif': 'Tagged Image File Format',
'tiff': 'Tagged Image File Format'}
savefig方法有一些可定制的参数,比如你想得到一个600dpi的图片,并且尽量少的空白:
plt.savefig('image_name.png', dpi=600,bbox_inches='tight')
savefig也可以写入到文件对象中,比如BytesIO:
from io import BytesIO
buffer = BytesIO()
plt.savefig(buffer)
plot_data = buffer.getvalue()
下面是savefig方法的参数说明:
- fname:文件路径或文件对象,根据扩展名推断文件格式
- dpi:分辨率,默认100
- format: 指定文件格式
- bbox_inches: 要保存的图片范围。‘tight’表示去掉周边空白。
- facecolor:子图之外的背景颜色,默认白色
- edgecolor:边界颜色
两种图画接口
Matplotlib有个容易让人迷惑和混淆的情况,就是它的两种画图接口:一是便捷的MATLAB风格接口,一个是功能更强大的面向对象接口。
- MATLAB接口:
MATLAB的历史渊源,很多人都曾经学过用过。Matplotlib最初作为MATLAB的Python替代品,许多语法都类似,所以上手快,用得熟,自然而然就成习惯了。MATLAB风格的工具位于pyplot接口中,比如:
x = np.linspace(0,10,100) # 生成点列表
plt.figure() # 创建图形
plt.subplot(2,1,1) # 行、列、子图编号
plt.plot(x,np.sin(x))
plt.subplot(2,1,2)
plt.plot(x,np.cos(x)) # 第二个子图
这种接口最重要的特性是有状态的,他会持续跟踪当前的图形和坐标轴,所有plt命令都可以使用。你可以使用plt.gcf()方法获取当前图形和plt.gca()获取当前坐标轴的具体信息。
但是这种接口也有问题。比如,当创建第二个子图的时候,怎么才能回到第一个子图,并增加新内容呢?虽然也能实现,但方法比较复杂。而下面的方式则不存在这个问题。
- 面向对象的接口
这种方式可以适应更加复杂的场景,更好地控制你的图形。画图函数不再受到当前‘活动’图形或者坐标轴的限制,而变成了显式的Figure和Axes的方法。下面是个例子:
fig, ax = plt.subplots(2) # ax是包含两个Axes对象的数组
ax[0].plot(x,np.sin(x)) # 在每个对象上调用plot()方法
ax[1].plot(x,np.cos(x))
这两种使用方式你必须都会用,因为它们随机出现在代码、文献、测试场景中,你没得选。
使用中文
在默认情况下,如果你使用中文,会显示为方框乱码,解决这个问题只要在代码的起始处进行如下设置:
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
以上方法是只能临时使用,每次都要执行。如果想一劳永逸,那么必须修改配置文件。
>>> import matplotlib as mpl
>>> mpl.matplotlib_fname()
'C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\mpl-data\\matplotlibrc'
用文本编辑器打开这个matplotlibrc文件,找到下面这行:
#font.serif : DejaVu Serif, Bitstream Vera Serif,...
将前面的注释符号去掉。
然后去C:\Windows\Fonts\Microsoft
中复制一个你想要的字体ttf文件,或者别处的字体文件,将它粘贴到..\mpl-data\fonts\ttf
文件夹中,并给它重命名为Vera.ttf
。
还有一个坐标轴的负号正常显示的配置修改:
#去掉注释符号,同时,改为False
axes.unicode_minus : False
基本绘图
颜色、线型和标记
使用color参数可以指定线条的颜色,有多种提供方式:
plt.plot(x, np.sin(x - 0), color='blue') # 英文字符串
plt.plot(x, np.sin(x - 1), color='g') # 颜色代码(rgbcmyk)
plt.plot(x, np.sin(x - 2), color='0.75') # 0~1之间的灰度
plt.plot(x, np.sin(x - 3), color='#FFDD44') # 十六进制形式
plt.plot(x, np.sin(x - 4), color=(1.0,0.2,0.3)) # RGB元组
plt.plot(x, np.sin(x - 5), color='chartreuse'); # HTML颜色
下面是常用的颜色:
- 蓝色: 'b' (blue)
- 绿色: 'g' (green)
- 红色: 'r' (red)
- 蓝绿色(墨绿色): 'c' (cyan)
- 红紫色(洋红): 'm' (magenta)
- 黄色: 'y' (yellow)
- 黑色: 'k' (black)
- 白色: 'w' (white)
可以使用linestyle参数指定线型。线型有两种表示方式:一是英文单词,二是形象符号。
常用的线型和符号对应:
- 实线:
solid
(-
) - 虚线:
dashed
(--
) - 点划线:
dashdot
(-.
) - 实点线:
dotted
(:
)
plt.plot(x, x + 0, linestyle='solid')
plt.plot(x, x + 1, linestyle='dashed')
plt.plot(x, x + 2, linestyle='dashdot')
plt.plot(x, x + 3, linestyle='dotted');
plt.plot(x, x + 4, linestyle='-')
plt.plot(x, x + 5, linestyle='--')
plt.plot(x, x + 6, linestyle='-.')
plt.plot(x, x + 7, linestyle=':');
可以通过marker参数来设置标记的类型:
x = np.linspace(0,10,10)
plt.plot(x, x + 0, marker='.')
plt.plot(x, x + 1, marker=',')
plt.plot(x, x + 2, marker='o')
plt.plot(x, x + 3, marker='+')
更多标记类型:
'.'
实点标记','
像素标记'o'
圆形标记'v'
向下三角符号'^'
向上三角符号'<'
向左三角符号'>'
向右三角符号'1'
三叉星符号'2'
三叉星符号'3'
三叉星符号'4'
三叉星符号's'
方形'p'
五边形'*'
星型'h'
六边形1'H'
六边形2'+'
加号'x'
叉叉'D'
钻石形状'd'
菱形'|'
竖条'_'
横条
此外,还有一种更便捷的做法,那就是组合颜色、线型和标记的设置。三者顺序有时可以随意,但最好使用‘颜色+标记+线型’的顺序。:
plt.plot(x, x + 0, 'go-') # 绿色实线圆点标记
plt.plot(x, x + 1, 'c--') # 青色虚线
plt.plot(x, x + 2, '-.k*') # 黑色点划线星型标记
plt.plot(x, x + 3, ':r'); # 红色实点线
对于plot()方法,大部分可配置的参数如下:
参数 | 取值范围 | 说明 |
---|---|---|
alpha | 0-1 | 透明度 |
color或c | 颜色格式 | 设置线条颜色 |
label | 字符串 | 为图形设置标签 |
linestyle或ls | 可用线型 | 设置线条风格 |
linewidth或lw | 数值 | 线宽 |
marker | 可用标记 | 标记 |
markeredgecolor或mec | 颜色 | 标记的边缘颜色 |
markeredgewidth或mew | 数值 | 标记的边缘宽度 |
markerfacecolor或mfc | 颜色 | 标记的颜色 |
markersize或ms | 数值 | 标记的大小 |
solid_capstyle |
butt 、round 、projecting |
实线的线端风格 |
solid_joinstyle |
miter 、round 、bevel |
实线的连接风格 |
drawstyle | default 、steps 、steps-pre 、steps-mid 、steps-post |
连线的规则 |
visible | True 、False |
显示或隐藏 |
xdata | np.array | 主参数x的输入 |
ydata | np.array | 主参数y的输入 |
实际上,上面大多数的参数都可以用在matplotlib中的大部分图形绘制中!
坐标轴上下限
使用plt.xlim()
和plt.ylim()
来调整上下限的值:
#x_limt_min: <float> x轴范围最小值
#x_limit_max: <float> x轴范围最大值
mp.xlim(x_limt_min, x_limit_max)
#y_limt_min: <float> y轴范围最小值
#y_limit_max: <float> y轴范围最大值
mp.ylim(y_limt_min, y_limit_max)
示例:
x = np.linspace(0,10,100)
plt.plot(x,np.sin(x))
plt.xlim(-1,11)
plt.ylim(-1.5,1.5)
也可以让坐标轴逆序显示,只需要逆序提供坐标轴的限值:
plt.plot(x,np.sin(x))
plt.xlim(11,-1)
plt.ylim(1.5,-1.5)
或者使用plt.axis()方法设置坐标轴的上下限(注意区别axes和axis),参数方式是[xmin, xmax, ymin, ymax]
:
plt.plot(x,np.sin(x))
plt.axis([-1,11,-1.5,1.5])
axis的作用不仅于此,还可以按照图形的内容自动收缩坐标轴,不留空白。此种情况下,x和y轴的限值会自动计算,不用提供:
plt.plot(x,np.sin(x))
plt.axis('tight')
# -0.5, 10.5, -1.0993384025373631, 1.0996461858110391)
更多类似的常用设置值有:
- off:隐藏轴线和标签
- tight:紧缩模式
- equal:以1:1的格式显示,x轴和y轴的单位长度相等
- scaled: 通过更改绘图框的尺寸来获得相同的结果
- square: x轴和y轴的限制值一样
坐标轴刻度
通常情况下,系统会自动根据提供的原始数据,生成x和y轴的刻度标签。但是很多时候,我们往往需要自定义刻度,让它符合我们的需要,比如下面的例子:
plt.plot(np.random.randn(1000).cumsum())
我们可以手动提供刻度值,并调整刻度的角度和大小:
#x_val_list: x轴刻度值序列
#x_text_list: x轴刻度标签文本序列 [可选]
mp.xticks(x_val_list , x_text_list )
#y_val_list: y轴刻度值序列
#y_text_list: y轴刻度标签文本序列 [可选]
mp.yticks(y_val_list , y_text_list )
示例:
plt.plot(np.random.randn(1000).cumsum())
plt.xticks([0,250,500,750,1000],rotation=30, fontsize='large')
plt.yticks([-45,-35,-25,-15,0],rotation=30, fontsize='small')
刻度文本的特殊语法 -- LaTex排版语法字符串
r'$x^n+y^n=z^n$', r'$\int\frac{1}{x} dx = \ln |x| + C$', r'$-\frac{\pi}{2}$'
$$
x2+y2=z^2, \int\frac{1}{x} dx = \ln |x| + C, -\frac{\pi}{2}
$$
设置坐标轴
坐标轴名:left / right / bottom / top
# 获取当前坐标轴字典,{'left':左轴,'right':右轴,'bottom':下轴,'top':上轴 }
ax = mp.gca() # getCurrentaxis
# 获取其中某个坐标轴
axis = ax.spines['坐标轴名']
# 设置坐标轴的位置。 该方法需要传入2个元素的元组作为参数
# type: <str> 移动坐标轴的参照类型 一般为'data' (以数据的值作为移动参照值)
# val: 参照值
axis.set_position(('data', val))
# 设置坐标轴的颜色
# color: <str> 颜色值字符串
axis.set_color(color)
案例:设置坐标轴至中心。
#设置坐标轴
ax = mp.gca()
axis_b = ax.spines['bottom']
axis_b.set_position(('data', 0))
axis_l = ax.spines['left']
axis_l.set_position(('data', 0))
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
图题、轴标签和图例
分别是下面三种方法:
- 图题: plt.title()
- 轴标签:plt.xlabel()、plt.ylabel()
- 图例:plt.legend()
使用label参数,为绘制的每条线添加一个标签,然后使用legend方法展示出来。
plt.plot(x, np.sin(x),'-g',label='sin(x)')
plt.plot(x, np.cos(x),':b',label='cos(x)')
plt.title('a sin curve')
plt.xlabel("X")
plt.ylabel("sin(X)")
plt.legend()
注意:大多数的plt方法都可以直接转换成ax方法,比如plt.plot()->ax.plot()
,plt.legend()->ax.legend()
。但并不是所有的都可以,比如下面的需要这么转换:
- plt.xlabel() -> ax.set_xlabel()
- plt.ylabel() -> ax.set_ylabel()
- plt.xlim() -> ax.set_xlim()
- plt.ylim() -> ax.set_ylim()
- plt.title() -> ax.set_title()
在面向对象接口画图的时候,不需要单独调用这些函数,使用ax.set()方法一次性设置即可:
x = np.linspace(0,10,100)
ax = plt.axes()
ax.plot(x,np.sin(x))
ax.set(xlim=(0,10),ylim=(-2,2),xlabel='x',ylabel='sin(x)',title='a sin plot')
配置图题
title标题方法,也有许多可以配置的参数:
- fontsize:字体大小,默认12,也可以使用
xx-small....
字符串系列 - fontweight:字体粗细,或者'light'、'normal'、'medium'、'semibold'、'bold'、 'heavy'、'black'。
- fontstyle: 字体类型,或者'normal'、'italic'、'oblique'。
- verticalalignment:垂直对齐方式 ,或者'center'、'top'、'bottom'、'baseline'
- horizontalalignment:水平对齐方式,可选参数:‘left’、‘right’、‘center’
- rotation:旋转角度
- alpha: 透明度,参数值0至1之间
- backgroundcolor: 背景颜色
- bbox:给标题增加外框 ,常用参数如下:
- boxstyle:方框外形
- facecolor:(简写fc)背景颜色
- edgecolor:(简写ec)边框线条颜色
- edgewidth:边框线条大小
下面是一些使用的例子:
plt.title('A Title',fontsize='large',fontweight='bold') #设置字体大小与尺寸
plt.title('A Title',color='blue') #设置字体颜色
plt.title('A Title',loc ='left') #设置字体位置
plt.title('A Title',verticalalignment='bottom') #设置垂直对齐方式
plt.title('A Title',rotation=45) #设置字体旋转角度
plt.title('A Title',bbox=dict(facecolor='g', edgecolor='blue', alpha=0.65 )) #设置标题边框
实际上,title标题方法的大部分参数也适用于xlabel和ylabel坐标轴标记方法,大家可以自行尝试。
配置图例
legend图例方法有很多可以配置的参数和选项:
参数 | 说明 |
---|---|
loc | 图例的位置 |
prop | 字体参数 |
fontsize | 字体大小 |
markerscale | 图例标记与原始标记的相对大小 |
markerfirst | 如果为True,则图例标记位于图例标签的左侧 |
numpoints | 为线条图图例条目创建的标记点数 |
scatterpoints | 为散点图图例条目创建的标记点数 |
scatteryoffsets | 为散点图图例条目创建的标记的垂直偏移量 |
frameon | 是否显示图例边框 |
fancybox | 边框四个角是否有弧度 |
shadow | 控制是否在图例后面画一个阴影 |
framealpha | 图例边框的透明度 |
edgecolor | 边框颜色 |
facecolor | 背景色 |
ncol | 设置图例分为n列展示 |
borderpad | 图例边框的内边距 |
labelspacing | 图例条目之间的垂直间距 |
handlelength | 图例句柄的长度 |
handleheight | 图例句柄的高度 |
handletextpad | 图例句柄和文本之间的间距 |
borderaxespad | 轴与图例边框之间的距离 |
columnspacing | 列间距 |
title | 图例的标题 |
对于loc这个图例在坐标轴中的放置位置,有两种表示方法:数字或者字符串,其对应关系如下:
- 0: ‘best' : 自动选择最适合的位置
- 1: ‘upper right': 右上
- 2: ‘upper left': 左上
- 3: ‘lower left': 左下
- 4: ‘lower right':右下
- 5: ‘right':右
- 6: ‘center left':左中
- 7: ‘center right':右中
- 8: ‘lower center':下中
- 9: ‘upper center': 上中
- 10: ‘center':中间
设置字体大小的参数fontsize可以使用整数或者浮点数,以及字符串‘xx-small’、 ‘x-small’、 ‘small’、‘medium’、 ‘large’、 ‘x-large’和‘xx-large’。
下面是一些使用的例子
plt.legend(loc='best',frameon=False) #去掉图例边框
plt.legend(loc='best',edgecolor='blue') #设置图例边框颜色
plt.legend(loc='best',facecolor='blue') #设置图例背景颜色,若无边框,参数无效
plt.legend(loc='best',title='figure') #去掉图例边框
plt.legend(loc='upper left', ncol=2, frameon=False) # 分两列显示,在左上角
plt.legend(fancybox=True,framealpha=1, shadow=True, borderpad=1)
默认情况下,图例会忽略那些不带标签的绘图,只为设置了label参数的绘图添加图例。
有时候我们可能需要在同一张图上显示多个图例。不过,用普通方法解决不了这个问题,标准的legend接口只能为一张图创建一个图例。但是我们可以通过plt.gca().add_artist()
方法(或者ax.add_artist()
),将我们先前创建过的图例用底层的接口重新添加上去。
lines = []
styles= ['-', '--','-.',':']
x = np.linspace(0,10,1000)
for i in range(4): # 制造四条sin曲线
lines += plt.plot(x, np.sin(x-i*np.pi/2), styles[i])
plt.axis('equal')
# 生成第一个图例,并保存引用
leg = plt.legend(lines[:2], ['line A', 'line B'], loc=1,frameon=False)
# 生成第二个图例,这会让第一个图例被抹去
plt.legend(lines[2:], ['line C', 'line D'], loc=4,frameon=False)
# gca方法获取当前坐标轴,再使用它的`add_artist`方法将第一个图例重新画上去
plt.gca().add_artist(leg)
颜色条
在Matplotlib中,颜色条是一个独立的坐标轴,可以指明图形中的颜色的含义。
我们在前面已经使用过颜色条,这里再专门介绍一下:
%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0,10,1000)
I = np.sin(x)* np.cos(x[:,np.newaxis])
I.shape
plt.imshow(I)
plt.colorbar()
我们得到了一个默认的蓝绿配色的图形。
实际上在plt.cm中有大量可选的颜色配置方案。具体使用哪种方案,要根据你的需求和你对美术的修养。一般情况下,你只需要关注三种类型的配色方案:
- 顺序配色:由一组连续的颜色构成,比如binary和viridis
- 互逆色:通常由互补的颜色构成,比如RdBu或者PuOr
- 定性配色:随机顺序的一组颜色,比如rainbow或jet
这些配色方案的名字很多都是缩写组合,一定要注意字母大小写。
jet是Matplotlib2.0之前的默认配色方案,已经比较老旧了。viridis是目前的默认配色方案。下面我们看看'RdBu'的配色:
plt.imshow(I, cmap='RdBu')
plt.colorbar()
colorbar可以看作是plt.Axes的一个实例,因此前面关于坐标轴的格式配置技巧都可以使用,下面我们看一个噪点图像,注意其中的clim方法:
speckles = np.random.random(I.shape) < 0.01 # 制造bool噪点
I[speckles] = np.random.normal(0,3,np.count_nonzero(speckles)) # 生成噪点
plt.figure(figsize=(10,3.5))
plt.subplot(1,2,1) # 多子图模式
plt.imshow(I, cmap='RdBu')
plt.colorbar()
plt.subplot(1,2,2)
plt.imshow(I, cmap='RdBu')
plt.colorbar(extend='both') # 颜色条变成两端尖
plt.clim(-1,1) # 限制颜色显示范围
颜色条默认都是连续的,但有时候你可能也需要使用离散的颜色数据,最简单的方法就是使用plt.cm.get_cmap()
方法,将适当的颜色方案和需要的区间数量作为参数传递进去即可:
plt.imshow(I, cmap=plt.cm.get_cmap('Blues',6))
plt.colorbar()
plt.clim(-1,1)
文本、箭头和注释
很多时候,光是图像不足以表达所有的内容,需要一些说明性的文字来辅助。
在Matplotlib中,使用plt.text方法为图例添加文字。
x = np.linspace(0,10,100)
plt.plot(x, np.sin(x))
plt.text(5,0.5,'this is a sin(x) curve',ha='center',va='center')
一定要将文字和图例、标签、标注等组件区别开。
plt.text方法的签名如下:
plt.text(x, y, s, fontdict=None, withdash=False, **kwargs)
下面是常用的参数说明:
- x,y:坐标值,文字放置的位置
- string:文字内容字符串
- size:字体大小
- alpha:设置字体的透明度
- family: 设置字体
- style:设置字体的风格
- wight:字体的粗细
- verticalalignment:垂直对齐方式,缩写为va。可用值
‘center’ | ‘top’ | ‘bottom’ | ‘baseline’
。 - horizontalalignment:水平对齐方式 ,缩写为ha。可用值
‘center’ | ‘right’ | ‘left’
- xycoords:选择指定的坐标轴系统
- bbox给标题增加外框 ,常用参数如下:
- boxstyle:方框外形
- facecolor:(简写fc)背景颜色
- edgecolor:(简写ec)边框线条颜色
- edgewidth:边框线条大小
- 其它未列出
下面是个彩色的例子:
plt.text(0.3, 0.3, "hello", size=50, rotation=30.,ha="center", va="center",bbox=dict(boxstyle="round",ec=(1., 0.5, 0.5),fc=(1., 0.8, 0.8),))
plt.text(0.8, 0.8, "world", size=50, rotation=-40.,ha="right", va="top",bbox=dict(boxstyle="square",ec=(1., 0.2, 0.5),fc=(1., 0.3, 0.8),))
很多时候,文本是以数学公式出现的:
x = np.linspace(0,10,100)
plt.plot(x, np.sin(x))
plt.title(r'$\alpha_i > \beta_i$', fontsize=20)
plt.text(1, -0.6, r'$\sum_{i=0}^\infty x_i$', fontsize=20)
plt.text(3, 0.6, r'$\mathcal{A}\mathrm{sin}(2 \omega t)$',fontsize=20)
除了文本,简单的箭头也是一种很有用的说明性注释。虽然有一个plt.arrow()方法可以实现箭头的功能,但是由于它生成的是SVG向量对象,会随着图形分辨率的变化而变化,这有可能给我们带来困扰。所以,我们一般使用plt.annotate()方法来实现箭头和注释的功能。下面是其用法演示:
fig, ax = plt.subplots()
x = np.linspace(0, 20, 1000)
ax.plot(x, np.cos(x))
ax.axis('equal')
ax.annotate('local maximum', xy=(6.28, 1), xytext=(10, 4),
arrowprops=dict(facecolor='black', shrink=0.05))
ax.annotate('local minimum', xy=(5 * np.pi, -1), xytext=(2, -6),
arrowprops=dict(arrowstyle="->",
connectionstyle="angle3,angleA=0,angleB=-90"));
annotate方法的签名如下:
annotate(s='str' ,xy=(x,y) ,xytext=(l1,l2) ,arrowprops=dict(...)..)
主要参数说明:
- s:注释文本内容
- xy:被注释对象的坐标位置,实际上就是图中箭头的箭锋位置
- xytext: 具体注释文字的坐标位置
- xycoords:被注释对象使用的参考坐标系
- extcoords:注释文字的偏移量
- arrowprops:可选,增加注释箭头
下面是一些箭头arrowprops参数的基本配置项:
- width:箭头宽度,以点为单位
- frac:箭头头部所占据的比例
- headwidth:箭头底部的宽度,以点为单位
- shrink:移动提示,并使其离注释点和文本一些距离
**kwargs
:matplotlib.patches.Polygon的任何键,例如facecolor
关于箭头的绘制方式,内容实在太多太细,根本无法详解,最好的办法是参考下面两个官方连接:
- https://matplotlib.org/users/annotations.html#plotting-guide-annotation
- https://matplotlib.org/examples/pylab_examples/annotation_demo2.html
这其中的例子,喜欢哪个,复制对应的参数配置方法即可-。
下面是一些具体例子的展示,大家可以对号挑选:
fig = plt.figure(1, figsize=(8, 5))
ax = fig.add_subplot(111, autoscale_on=False, xlim=(-1, 5), ylim=(-4, 3))
t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2*np.pi*t)
line, = ax.plot(t, s, lw=3)
ax.annotate('straight',
xy=(0, 1), xycoords='data',
xytext=(-50, 30), textcoords='offset points',
arrowprops=dict(arrowstyle="->"))
ax.annotate('arc3,\nrad 0.2',
xy=(0.5, -1), xycoords='data',
xytext=(-80, -60), textcoords='offset points',
arrowprops=dict(arrowstyle="->",
connectionstyle="arc3,rad=.2"))
ax.annotate('arc,\nangle 50',
xy=(1., 1), xycoords='data',
xytext=(-90, 50), textcoords='offset points',
arrowprops=dict(arrowstyle="->",
connectionstyle="arc,angleA=0,armA=50,rad=10"))
ax.annotate('arc,\narms',
xy=(1.5, -1), xycoords='data',
xytext=(-80, -60), textcoords='offset points',
arrowprops=dict(arrowstyle="->",
connectionstyle="arc,angleA=0,armA=40,angleB=-90,armB=30,rad=7"))
ax.annotate('angle,\nangle 90',
xy=(2., 1), xycoords='data',
xytext=(-70, 30), textcoords='offset points',
arrowprops=dict(arrowstyle="->",
connectionstyle="angle,angleA=0,angleB=90,rad=10"))
ax.annotate('angle3,\nangle -90',
xy=(2.5, -1), xycoords='data',
xytext=(-80, -60), textcoords='offset points',
arrowprops=dict(arrowstyle="->",
connectionstyle="angle3,angleA=0,angleB=-90"))
ax.annotate('angle,\nround',
xy=(3., 1), xycoords='data',
xytext=(-60, 30), textcoords='offset points',
bbox=dict(boxstyle="round", fc="0.8"),
arrowprops=dict(arrowstyle="->",
connectionstyle="angle,angleA=0,angleB=90,rad=10"))
ax.annotate('angle,\nround4',
xy=(3.5, -1), xycoords='data',
xytext=(-70, -80), textcoords='offset points',
size=20,
bbox=dict(boxstyle="round4,pad=.5", fc="0.8"),
arrowprops=dict(arrowstyle="->",
connectionstyle="angle,angleA=0,angleB=-90,rad=10"))
ax.annotate('angle,\nshrink',
xy=(4., 1), xycoords='data',
xytext=(-60, 30), textcoords='offset points',
bbox=dict(boxstyle="round", fc="0.8"),
arrowprops=dict(arrowstyle="->",
shrinkA=0, shrinkB=10,
connectionstyle="angle,angleA=0,angleB=90,rad=10"))
绘图种类
线型图
Matplotlib为我们提供了大量的绘图种类,我们不可能一一介绍,学习量太大,也没必要。正确的方式是以典型绘图为例,掌握基本绘制技巧,然后在需要的时候,查阅官方文档,模仿操作。
官方展示链接:https://matplotlib.org/gallery/index.html。有一句话可以形容它的丰富程度:没有做不到的,只有你想不到的。
线型图是学习matplotlib绘图的最基础案例。我们来看看具体过程:
%matplotlib notebook
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
下面我们将两条曲线绘制到一个图形里:
x = np.linspace(-10,10,200)
plt.plot(x, x**2)
plt.plot(x, x**3)
可以看到这种方式下,两个线条共用一个坐标轴,并且自动区分颜色。
plot方法的核心是plot(x,y),x表示横坐标值的序列,y表示x某个坐标对应的y值,实际上就是y=f(x)函数。当只提供y的时候,x默认使用0-n的整数序列。这里的序列必然是个有限的点集,而不是我们想象中的无穷个点组成一条线。如果你的点很稀疏,那么图形看起来就像折线,如果点很多,看起来就比较圆滑,形似曲线。
题外话:matplotlib其实是一个相当底层的工具,你可以从其基本组件中组装一个图标、显示格式、图例、标题、注释等等。Pandas在此基础上对绘图功能进行了一定的封装,每个Series和DataFrame都有一个plot方法,一定要区分pandas的plot和matplotlib的plot方法。比如:
import pandas as pd
df = pd.DataFrame(np.random.randn(10, 4).cumsum(0),
columns=['A', 'B', 'C', 'D'],
index=np.arange(0, 100, 10))
df.plot()
pandas和matplotlib的plot方法你愿意用哪个都行,但要注意参数格式和使用场景。
散点图
与线型图类似的是,散点图也是一个个点集构成的。但不同之处在于,散点图的各点之间不会按照前后关系以线条连接起来。
- 用plt.plot画散点图
x = np.linspace(0,10,30)
y = np.sin(x)
plt.plot(x,y,'bo', ms=5)
奇怪,代码和前面的例子差不多,为什么这里显示的却是散点图而不是sin曲线呢?原因有二:一是点集比较少,稀疏,才30个;二是没有指定线型。
- 用plt.scatter画散点图
mp.scatter(x, y, marker='', s=10, color='', edgecolor='', facecolor='', zorder='')
scatter专门用于绘制散点图,使用方式和plot方法类似,区别在于前者具有更高的灵活性,可以单独控制每个散点与数据匹配,并让每个散点具有不同的属性。
主要参数说明:
- x,y:x/y轴坐标数组
- s:标记大小,以像素为单位
- color:颜色
- marker:标记
- alpha:透明度
- edgecolors :边界颜色
- facecolor:填充色
- zorder:图层序号
一般使用scatter方法,如下例子就可以了:
plt.scatter(x, y, marker='o')
下面看一个随机不同透明度、颜色和大小的散点例子:
rng = np.random.RandomState(10)
x = rng.randn(100)
y = rng.randn(100)
colors = rng.rand(100)
sizes = 1000* rng.rand(100)
plt.scatter(x, y, c=colors, s=sizes, alpha=0.3)
plt.colorbar() # 绘制颜色对照条
上面的例子可以拓展到Scikit-learn中经典的鸢尾花iris数据来演示。
Iris数据集是常用的分类实验数据集,由Fisher在1936收集整理,是一类多重变量分析的数据集。数据集包含150个数据,分为3类,每类50个数据,每个数据包含4个属性。通过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性预测鸢尾花卉属于(Setosa,Versicolour,Virginica)三个种类中的哪一类。
>>> from sklearn.datasets import load_iris
>>> iris = load_iris() # 载入iris数据集
>>> iris # 查看一下
>>> iris.data # 查看一下
>>> iris.target # 查看一下
>>> iris.feature_names # 查看一下
>>> features = iris.data.T # 转置
>>> plt.scatter(features[0],features[1],alpha=0.2, s=100*features[3],c=iris.target)
plt.xlabel(iris.feature_names[0])
plt.ylabel(iris.feature_names[1]);
这个散点图让我们看到了不同维度的数据:每个点的坐标值x和y分别表示花萼的长度和宽度,点的大小表示花瓣的宽度,三种颜色对应三种不同类型的鸢尾花。这类多颜色多特征的散点图在探索和演示数据时非常有用。
在处理较少点集的时候scatter方法灵活度更高,可单独配置并渲染,但所需消耗的计算和内存资源也更多。当数据成千上万个之后,plot方法的效率更高,因为它对所有点使用一样的颜色、大小、类型等配置,自然更快。
scatter的更多内容请参考官网:https://matplotlib.org/api/_as_gen/matplotlib.pyplot.scatter.html
直方图
使用hist方法来绘制直方图:
np.random.seed(2019)
# 创建数据
mu = 100 # 分布的均值
sigma = 15 # 分布标准差
x = mu + sigma * np.random.randn(400) # 生成400个数据
num_bins = 50 # 分50组
plt.hist(x, num_bins, density=1, )
plt.xlabel('Smarts')
plt.ylabel('Probability density')
plt.title(r'Histogram of IQ: $\mu=100$, $\sigma=15$')
绘制直方图,最主要的是一个数据集data和需要划分的区间数量bins,另外你也可以设置一些颜色、类型参数:
plt.hist(np.random.randn(1000), bins=30,normed=True, alpha=0.5, histtype='stepfilled', color='steelblue', edgecolor='none')
histtype直方图的类型,可以是'bar'、 'barstacked'、'step'和'stepfilled'。
有时候我们也会做一些直方对比图:
x1 = np.random.normal(0,0.8,1000)
x2 = np.random.normal(-2,1,1000)
x3 = np.random.normal(3,2,1000)
params = dict(histtype='stepfilled', alpha=0.3, normed=True,bins=40)
plt.hist(x1, **params) # 以字典的形式提供参数
plt.hist(x2, **params) # 在同一个子图中绘制,颜色会自动变化
plt.hist(x3, **params)
除了一维的直方图,还可以使用hist2d方法绘制二维的直方图:
mean = [0,0] # 忽略数据的创建过程
cov = [[1,1],[1,2]]
x,y = np.random.multivariate_normal(mean, cov,10000).T
plt.hist2d(x,y,bins=30,cmap='Blues') #以蓝色为基调
cb = plt.colorbar() # 插入颜色条
cb.set_label('counts in bin') # 设置颜色条的标签
hist2d是使用坐标轴正交的方块分割区域,还有一种常用的方式是正六边形也就是蜂窝形状的分割。Matplotlib提供的plt.hexbin就是满足这个需求的:
plt.hexbin(x,y,gridsize=30, cmap='Blues')
plt.colorbar(label='count in bin')
更多hist相关内容参考:https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.hist.html
条形图
条形图,也称柱状图,看起来像直方图,但完是两码事。条形图根据不同的x值,为每个x指定一个高度y,画一个一定宽度的条形;而直方图是对数据集进行区间划分,为每个区间画条形。
mp.figure('Bar', facecolor='lightgray')
mp.bar(x, y, width, color='', label='', alpha=0.2)
主要参数说明:
- x:水平坐标数组
- y:柱状图高度数组
- width:柱子的宽度
- color:填充颜色
- label:标签
- alpha:透明度
示例:
n = 12 # 12组数据
X = np.arange(n)
Y1 = (1 - X / n) * np.random.uniform(0.5, 1.0, n) # 生成对应的y轴数据
Y2 = (1 - X / n) * np.random.uniform(0.5, 1.0, n)
plt.bar(X, +Y1, facecolor='#9999ff', edgecolor='white') # +号让所有y值变成正数
plt.bar(X, -Y2, facecolor='#ff9999', edgecolor='white') # 负号让所有y值变成复数
# 加上数值
for x, y in zip(X, Y1): # 显示文本
plt.text(x, y + 0.05, '%.2f' % y, ha='center', va='bottom')
for x, y in zip(X, Y2):
plt.text(x, -y - 0.05, '-%.2f' % y, ha='center', va='top')
plt.xlim(-0.5, n)
plt.ylim(-1.25, 1.25)
将上面的代码稍微修改一下,就可以得到下面的图形:
plt.bar(X, Y1, width=0.4, facecolor='lightskyblue', edgecolor='white')
plt.bar(X+0.4, Y2, width=0.4, facecolor='yellowgreen', edgecolor='white')
for x,y in zip(X,Y1):
plt.text(x, y+0.05, '%.2f' % y, ha='center', va= 'bottom')
for x,y in zip(X,Y2):
plt.text(x+0.4, y+0.05, '%.2f' % y, ha='center', va= 'bottom')
plt.xlim(-0.5,6)
plt.ylim(0,1.25)
更多内容参考:https://matplotlib.org/api/_as_gen/matplotlib.pyplot.bar.html
饼图
通过pie方法,可以绘制饼图:
labels = '狗', '猫', '青蛙', '乌龟'
sizes = [15, 30, 45, 10]
explode = (0, 0.1, 0, 0)
plt.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%',
shadow=True, startangle=90)
plt.axis('equal') # 设置x轴和y轴均等
主要参数说明:
- x:输入的数据数组
- explode:数组,可选参数,默认为None。 用来指定每部分从圆中外移的偏移量。 例如:explode=[0,0.2,0,0],第二个饼块被拖出。
- labels:每个饼块的标记
- colors:每个饼块的颜色
- autopct:自动标注百分比,并设置字符串格式
- shadow:是否添加阴影
- labeldistance:被画饼标记的直径
- startangle:从x轴逆时针旋转饼图的开始角度
- radius:饼图的半径
- counterclock:指定指针方向,顺时针或者逆时针
- center:图表中心位置
- spaces:形之间的间距列表
- '%d%%':标签所占比例格式
更多内容参考:https://matplotlib.org/api/_as_gen/matplotlib.pyplot.pie.html
误差线
使用errorbar方法可以绘制误差线。
x = np.linspace(0,10,50)
dy=0.8
y = np.sin(x) + dy*np.random.randn(50)
plt.errorbar(x, y, yerr=dy, fmt='.k')
做一些格式上的调整:
plt.errorbar(x, y, yerr=dy, fmt='ok',ecolor='lightgray',elinewidth=3, capsize=0)
errorbar方法的一些参数说明:
- yerr: 绘制垂直方向的误差线
- xerr:绘制水平方向的误差线
- fmt: 线条格式
- ecolor: 误差线的颜色
- elinewidth:误差线的宽度
- capsize: 误差线的长度
更多内容参考:https://matplotlib.org/api/_as_gen/matplotlib.pyplot.errorbar.html
等高线
我们经常在二维图上用等高线来表示三维数据,Matplotlib提供了三个函数来实现这一功能:
- plt.contour: 绘制等高线
- plt.contourf: 绘制带有填充色的等高线
- plt.imshow: 显示图形
mp.contour(
x, # 网格坐标矩阵的x坐标 (2维数组)
y, # 网格坐标矩阵的y坐标 (2维数组)
z, # 网格坐标矩阵的z坐标 (2维数组)
8, # 把等高线绘制成8部分
colors='black', # 等高线的颜色
linewidths=0.5 # 线宽
)
主要参数说明:
- x:网格坐标矩阵的x坐标 (2维数组)
- y:网格坐标矩阵的y坐标 (2维数组)
- z:网格坐标矩阵的z坐标 (2维数组)
- 8:把等高线绘制成8部分
- colors:等高线的颜色
- linewidths:线宽
示例:
def f(x, y):
return np.sin(x)**10 + np.cos(10 +y*x)*np.cos(x)
x = np.linspace(0,5,50)
y = np.linspace(0,5,40)
X, Y = np.meshgrid(x, y) # 使用两个一维数组构建一个二维数组
Z = f(X, Y)
plt.contour(X, Y, Z, colors='blue')
contour函数需要三个基本参数:x、y、z轴三个坐标轴的网格数据。x与y轴表示图形中的位置,而z轴通过等高线的等级来表示。
当图形中只使用一种颜色的时候,会使用虚线来表示负数,实线表示正数。
我们可以将数据范围等分,比如10份,然后设置camp参数定义一个线条颜色的配色方案,用不同的颜色表示等高线。
plt.contour(X, Y, Z, cmap='RdGy')
plt.colorbar()
RdGy
是红-灰
配色方案(Red-Gray)。Matplotlib有很多配色方案可选,都在plt.cm模块里,使用plt.cm.
可以查看有哪些方案。
上面的图形里,线条之间的间隙比较大,可以通过contourf方法进行填充(注意方法名多了个f):
plt.contourf(X, Y, Z, cmap='RdGy')
plt.colorbar()
我们还可以通过imshow方法,将二维数组渲染成渐变图:
plt.imshow(Z, extent=[0,5,0,5], origin='lower',cmap='RdGy')
plt.colorbar()
plt.axis(aspect='image')
- imshow不支持使用x和y轴的数据设置网格,必须通过extend参数设置图形的坐标范围
xmin、xmax,ymin、ymax
- imshow默认使用标准的图形数组定义,原点位于左上角,类似浏览器。而不是绝大多数等高线的左下角
- imshow会自动调整坐标轴的精度以适应数据显示。可以通过
plt.axis(aspect='image')
来设置x与y轴的单位。
最后,我们还可以使用clabel方法,将一幅背景半透明的彩色图与另一幅坐标相同、带数据标签的等高线叠放在一起,画出相当高端的图形:
contours = plt.contour(X,Y,Z,3,colors='black') # 分3级
plt.clabel(contours,inline=True, fontsize=8) # 内部带数值文字
plt.imshow(Z, extent=[0,5,0,5], origin='lower',cmap='RdGy', alpha=0.5)
plt.colorbar()
现在你或许会有感悟,绘图画到最后,缺少的不是工具,而是创意灵感。
热成像图
# origin: 坐标轴方向
# upper: 缺省值,原点在左上角
# lower: 原点在左下角
mp.imshow(z, cmap='jet', origin='low')
矩阵z图形化,使用cmap表示矩阵中每个元素值的大小
主要参数说明:
- z:矩阵数据
- cmap:颜色映射方案
- origin:坐标轴方向
- upper:缺省值,原点在左上角
- lower:原点在左下角
使用颜色条显示热度值:
mp.colorbar()
3D图像绘制
matplotlib支持绘制三维曲面。若希望绘制三维曲面,需要使用axes3d提供的3d坐标系。
from mpl_toolkits.mplot3d import axes3d
ax3d = mp.gca(projection='3d') # class axes3d
matplotlib支持绘制三维点阵、三维曲面、三维线框图:
ax3d.scatter(..) # 绘制三维点阵
ax3d.plot_surface(..) # 绘制三维曲面
ax3d.plot_wireframe(..) # 绘制三维线框图
1、绘制三维点阵
ax3d.scatter(x, y, z, marker='', s=10, zorder='', color='', edgecolor='', facecolor='', c=v, cmap='')
主要参数说明:
- x:x轴坐标数组
- y:y轴坐标数组
- z:z轴坐标数组
- marker:点型
- s:大小
- zorder:图层序号
- color:颜色
- edgecolor:边缘颜色
- facecolor:填充色
- c:颜色值 根据cmap映射应用相应颜色
- cmap:颜色映射方式
示例:
n = 1000
x = np.random.normal(0, 1, n)
y = np.random.normal(0, 1, n)
z = np.random.normal(0, 1, n)
d = np.sqrt(x ** 2 + y ** 2 + z ** 2)
mp.figure('3D Scatter')
ax = mp.gca(projection='3d') # 创建三维坐标系
mp.title('3D Scatter', fontsize=20)
ax.set_xlabel('x', fontsize=14)
ax.set_ylabel('y', fontsize=14)
ax.set_zlabel('z', fontsize=14)
mp.tick_params(labelsize=10)
ax.scatter(x, y, z, s=60, c=d, cmap='jet_r', alpha=0.5)
mp.show()
2、绘制三维曲面
ax3d.plot_surface(x, y, z, rstride=30, cstride=30, cmap='jet')
主要参数说明:
- x:网格坐标矩阵的x坐标 (2维数组)
- y:网格坐标矩阵的y坐标 (2维数组)
- z:网格坐标矩阵的z坐标 (2维数组)
- rstride:行跨距
- cstride:列跨距
- cmap:颜色映射
示例:
n = 1000
# 生成网格化坐标矩阵
x, y = np.meshgrid(np.linspace(-3, 3, n),
np.linspace(-3, 3, n))
# 根据每个网格点坐标,通过某个公式计算z高度坐标
z = (1 - x/2 + x**5 + y**3) * np.exp(-x**2 - y**2)
mp.figure('3D', facecolor='lightgray')
ax3d = mp.gca(projection='3d')
mp.title('3D', fontsize=20)
ax3d.set_xlabel('x', fontsize=14)
ax3d.set_ylabel('y', fontsize=14)
ax3d.set_zlabel('z', fontsize=14)
mp.tick_params(labelsize=10)
# 绘制3D平面图
# rstride: 行跨距
# cstride: 列跨距
ax3d.plot_surface(x,y,z,rstride=30,cstride=30, cmap='jet')
3、绘制三维线框图
ax3d.plot_wireframe(x, y, z, rstride=30, cstride=30, linewidth=1, color='dodgerblue')
主要参数说明:
- x:网格坐标矩阵的x坐标 (2维数组)
- y:网格坐标矩阵的y坐标 (2维数组)
- z:网格坐标矩阵的z坐标 (2维数组)
- rstride:行跨距
- cstride:列跨距
- 图形对象(图形窗口)
简单动画
动画即是在一段时间内快速连续的重新绘制图像的过程
matplotlib提供了方法用于处理简单动画的绘制。定义update函数用于即时更新图像。
import matplotlib.animation as ma
#定义更新函数行为
def update(number):
pass
# 每隔10毫秒执行一次update更新函数,作用于mp.gcf()当前窗口对象
# mp.gcf(): 获取当前窗口
# update: 更新函数
# interval: 间隔时间(单位:毫秒)
anim = ma.FuncAnimation(mp.gcf(), update, interval=10)
mp.show()
示例:随机生成各种颜色的100个气泡。让他们不断的增大。
#自定义一种可以存放在ndarray里的类型,用于保存一个球
ball_type = np.dtype([
('position', float, 2), # 位置(水平和垂直坐标)
('size', float, 1), # 大小
('growth', float, 1), # 生长速度
('color', float, 4)]) # 颜色(红、绿、蓝和透明度)
#随机生成100个点对象
n = 100
balls = np.zeros(100, dtype=ball_type)
balls['position']=np.random.uniform(0, 1, (n, 2))
balls['size']=np.random.uniform(40, 70, n)
balls['growth']=np.random.uniform(10, 20, n)
balls['color']=np.random.uniform(0, 1, (n, 4))
mp.figure("Animation", facecolor='lightgray')
mp.title("Animation", fontsize=14)
mp.xticks
mp.yticks(())
sc = mp.scatter(
balls['position'][:, 0],
balls['position'][:, 1],
balls['size'],
color=balls['color'], alpha=0.5)
#定义更新函数行为
def update(number):
balls['size'] += balls['growth']
#每次让一个气泡破裂,随机生成一个新的
boom_ind = number % n
balls[boom_ind]['size']=np.random.uniform(40, 70, 1)
balls[boom_ind]['position']=np.random.uniform(0, 1, (1, 2))
# 重新设置属性
sc.set_sizes(balls['size'])
sc.set_offsets(balls['position'])
# 每隔30毫秒执行一次update更新函数,作用于mp.gcf()当前窗口对象
# mp.gcf(): 获取当前窗口
# update: 更新函数
# interval: 间隔时间(单位:毫秒)
anim = ma.FuncAnimation(mp.gcf(), update, interval=30)
mp.show()
使用生成器函数提供数据,实现动画绘制
在很多情况下,绘制动画的参数是动态获取的,matplotlib支持定义generator生成器函数,用于生成数据,把生成的数据交给update函数更新图像:
import matplotlib.animation as ma
#定义更新函数行为
def update(data):
t, v = data
...
pass
def generator():
yield t, v
# 每隔10毫秒将会先调用生成器,获取生成器返回的数据,
# 把生成器返回的数据交给并且调用update函数,执行更新图像函数
anim = ma.FuncAnimation(mp.gcf(), update, generator,interval=10)
示例:绘制信号曲线:y=sin(2 * π * t) * exp(sin(0.2 * π * t)),数据通过生成器函数生成,在update函数中绘制曲线。
mp.figure("Signal", facecolor='lightgray')
mp.title("Signal", fontsize=14)
mp.xlim(0, 10)
mp.ylim(-3, 3)
mp.grid(linestyle='--', color='lightgray', alpha=0.5)
pl = mp.plot([], [], color='dodgerblue', label='Signal')[0]
pl.set_data([],[])
x = 0
def update(data):
t, v = data
x, y = pl.get_data()
x.append(t)
y.append(v)
#重新设置数据源
pl.set_data(x, y)
#移动坐标轴
if(x[-1]>10):
mp.xlim(x[-1]-10, x[-1])
def y_generator():
global x
y = np.sin(2 * np.pi * x) * np.exp(np.sin(0.2 * np.pi * x))
yield (x, y)
x += 0.05
anim = ma.FuncAnimation(mp.gcf(), update, y_generator, interval=20)
mp.tight_layout()
mp.show()
图形对象(图形窗口)
绘制图形窗口
mp.figure('', figsize=(4, 3), dpi=120, facecolor='')
主要参数说明:
- '':窗口标题栏文本
- figsize:窗口大小 <元组>
- dpi:像素密度
- facecolor:图表背景色
注:mp.figure方法不仅可以构建一个新窗口,如果已经构建过title='AAA'的窗口,又使用figure方法构建了title='AAA' 的窗口的话,mp将不会创建新的窗口,而是把title='AAA'的窗口置为当前操作窗口。
设置当前窗口的参数
# 设置图表标题 显示在图表上方
mp.title(title, fontsize=12)
# 设置水平轴的文本
mp.xlabel(x_label_str, fontsize=12)
# 设置垂直轴的文本
mp.ylabel(y_label_str, fontsize=12)
# 设置刻度参数 labelsize设置刻度字体大小
mp.tick_params(..., labelsize=8, ...)
# 设置图表网格线 linestyle设置网格线的样式
# - or solid 粗线
# -- or dashed 虚线
# -. or dashdot 点虚线
# : or dotted 点线
mp.grid(linestyle='')
# 设置紧凑布局,把图表相关参数都显示在窗口中
mp.tight_layout()
多子图
很多时候,我们需要从多个角度对数据进行比较,在可视化上也是一样的。Matplotlib通过子图subplot的概念来实线这一功能。
1、自由布局(axes)
mp.axes([left_bottom_x, left_bottom_y, width, height])
设置图标的位置,给出左下角点坐标与宽高即可
主要参数说明:
- left_bottom_x:坐下角点x坐标
- left_bottom_x:坐下角点y坐标
- width:宽度
- height:高度
通过plt.axes函数可以创建基本子图,默认情况下它会创建一个标准的坐标轴,并填满整张图。但是我们可以通过参数配置,实现想要的子图效果。
这个参数是个列表形式,有四个值,从前往后,分别是子图左下角基点的x和y坐标以及子图的宽度和高度,数值的取值范围是0-1之间,画布左下角是(0,0),画布右上角是(1,1)。
下面我们看个例子:
ax1 = plt.axes() # 使用默认配置,也就是布满整个画布
ax2 = plt.axes([0.65,0.65,0.2,0.2]) # 在右上角指定位置
上面是Matlab接口的风格,面向对象画图接口中有类似的fig.add_axes()
方法:
fig = plt.figure()
ax1 = fig.add_axes([0.1,0.5,0.8,0.4],xticklabels=[],ylim=(-1.2,1.2))
ax2 = fig.add_axes([0.1,0.1,0.8,0.4],ylim=(-1.2,1.2))
x = np.linspace(0,10)
ax1.plot(np.sin(x))
ax2.plot(np.cos(x))
2、 矩阵式布局(subplot)
mp.subplot(rows, cols, num)
拆分矩阵
主要参数说明:
- rows:行数
- cols:列数
- num:编号
subplot的方法接收三个整数参数,分别表示几行、几列、子图索引值。索引值从1开始,从左上角到右下角依次自增。
for i in range(1, 7): # 想想为什么是1-7
plt.subplot(2,3,i)
plt.text(0.5,0.5,str((2,3,i)), fontsize=16, ha='center')
子图间距好像不太恰当,可以使用plt.subplots_adjust方法进行调整,它接受水平间距hspace和垂直间距wspace两个参数。
同样的,面向对象接口也有fig.add_subplot()方法可以使用:
fig = plt.figure()
fig.subplots_adjust(hspace=0.4, wspace=0.4)
for i in range(1,7):
ax = fig.add_subplot(2,3,i)
ax.text(0.5,0.5,str((2,3,i)), fontsize=16, ha='center')
3、 快速创建多子图
可以使用subplots()方法快速的创建多子图环境,并返回一个包含子图的Numpy数组。
fig, ax = plt.subplots(2,3,sharex='col', sharey='row')
通过sharex和sharey参数,自动地去掉了网格内部子图的坐标刻度等内容,实现共享,让图形看起来更整齐整洁。
通过对返回的ax数组进行调用,可以操作每个子图,绘制图形:
for i in range(2):
for j in range(3):
ax[i,j].text(0.5,0.5,str((2,3,i)), fontsize=16, ha='center')
结果不再展示。但是需要注意的是,subplot()和subplots()两个方法在方法名上差个字母s外,subplots的索引是从0开始的。
4、复杂网格(GridSpec)
mg.GridSpec(rows, cols)
调用GridSpec方法拆分网格式布局
主要参数说明:
- rows:行数
- cols:列数
前面的子图其实都比较规整,如果想实现不规则的多行多列子图,可以使用plt.GridSpec方法。
grid = plt.GridSpec(2,3,wspace=0.4,hspace=0.4) # 生成两行三列的网格
plt.subplot(grid[0,0]) # 将0,0的位置使用
plt.subplot(grid[0,1:]) # 同时占用第一行的第2列以后的位置
plt.subplot(grid[1,:2])
plt.subplot(grid[1,2])
下面是一个使用plt.GridSpec方法创建多轴频次直方图的例子:
# 创建一些正态分布数据,这不是我们关心的内容
mean = [0, 0]
cov = [[1, 1], [1, 2]]
x, y = np.random.multivariate_normal(mean, cov, 3000).T
# 建立网格和子图
fig = plt.figure(figsize=(6, 6))
grid = plt.GridSpec(4, 4, hspace=0.2, wspace=0.2)
main_ax = fig.add_subplot(grid[:-1, 1:]) # 注意切片的方式
y_hist = fig.add_subplot(grid[:-1, 0], xticklabels=[], sharey=main_ax)
x_hist = fig.add_subplot(grid[-1, 1:], yticklabels=[], sharex=main_ax)
# 在主子图上绘制散点图
main_ax.plot(x, y, 'ok', markersize=3, alpha=0.2)
# 在附属子图上绘制直方图
x_hist.hist(x, 40, histtype='stepfilled',
orientation='vertical', color='gray')
x_hist.invert_yaxis() # 让y坐标轴的值由大到小,逆序
y_hist.hist(y, 40, histtype='stepfilled',
orientation='horizontal', color='gray')
y_hist.invert_xaxis() # 可以尝试不要这行,看看结果
自定义坐标轴刻度
Matplotlib图形对象具有层级关系。Figure对象其实就是一个盛放图形元素的盒子box,每个figure都会包含一个或多个axes对象,而每个axes对象又会包含其它表示图形内容的对象,比如xais和yaxis,也就是x轴和y轴。
刻度相关API:
# 获取当前坐标轴
ax = mp.gca()
# 设置水平坐标轴的主刻度定位器
ax.xaxis.set_major_locator(mp.NullLocator())
# 设置水平坐标轴的次刻度定位器为多点定位器,间隔0.1
ax.xaxis.set_minor_locator(mp.MultipleLocator(0.1))
- 主要刻度和次要刻度
每个坐标轴都有主要刻度和次要刻度,主要刻度往往更大或者突出显示,而次要刻度往往更小,一般不直接显示。
下面是一个对数坐标轴,可以看到次要刻度:
ax = plt.axes(xscale='log', yscale='log')
我们发现每个主要刻度都显示未一个较大的刻度线和标签,而次要刻度都显示为一个较小的刻度线,并且不现实标签。
每种刻度线都包含一个坐标轴定位器(locator)和格式生成器(formatter),可以通过下面的方法查看:
>>> ax.xaxis.get_major_locator()
<matplotlib.ticker.LogLocator at 0x276ff49db38>
>>> ax.xaxis.get_major_formatter()
<matplotlib.ticker.LogFormatterSciNotation at 0x276ff49d978>
>>> ax.xaxis.get_minor_locator()
<matplotlib.ticker.LogLocator at 0x276ff49d940>
>>> ax.xaxis.get_minor_formatter()
<matplotlib.ticker.LogFormatterSciNotation at 0x276ff493320>
我们可以发现,针对x或y轴,针对主要和次要,针对定位器和格式器,分别有不同的对象来处理。
- 隐藏刻度与标签
如果我们想隐藏刻度或标签,就要着落在locator和formatter这两大属性上了:
ax = plt.axes()
x = np.linspace(0,10,100)
ax.plot(np.sin(x))
ax.yaxis.set_major_locator(plt.NullLocator())
ax.xaxis.set_major_formatter(plt.NullFormatter())
可以看出,没有locator,刻度和标签都会被隐藏起来;没有formatter,隐藏标签,但刻度还存在。
- 设置刻度数量
默认情况下,matplotlib会自动帮我们调节刻度的数量,但有时候也需要我们自定义刻度数量:
fig, ax = plt.subplots(4, 4, sharex=True, sharey=True)
for axi in ax.flat:
axi.xaxis.set_major_locator(plt.MaxNLocator(4))
axi.yaxis.set_major_locator(plt.MaxNLocator(4))
常用刻度器如下:
# 空定位器:不绘制刻度
mp.NullLocator()
# 最大值定位器:
# 最多绘制nbins+1个刻度
mp.MaxNLocator(nbins=3)
# 定点定位器:根据locs参数中的位置绘制刻度
mp.FixedLocator(locs=[0, 2.5, 5, 7.5, 10])
# 自动定位器:由系统自动选择刻度的绘制位置
mp.AutoLocator()
# 索引定位器:由offset确定起始刻度,由base确定相邻刻度的间隔
mp.IndexLocator(offset=0.5, base=1.5)
# 多点定位器:从0开始,按照参数指定的间隔(缺省1)绘制刻度
mp.MultipleLocator()
# 线性定位器:等分numticks-1份,绘制numticks个刻度
mp.LinearLocator(numticks=21)
# 对数定位器:以base为底,绘制刻度
mp.LogLocator(base=2)
刻度网格线
mp.gca().grid(which='', axis='', linewidth=1, linestyle='', color='', alpha=0.5)
绘制刻度网格线
主要参数说明:
- which:'major'/'minor' <-> '主刻度'/'次刻度'
- axis:'x'/'y'/'both' <-> 绘制x或y轴
- linewidth:线宽
- linestyle:线型
- color:颜色
- alpha:透明度
案例:绘制曲线 [1, 10, 100, 1000, 100, 10, 1],然后设置刻度网格线,测试刻度网格线的参数。
y = np.array([1, 10, 100, 1000, 100, 10, 1])
mp.figure('Normal & Log', facecolor='lightgray')
mp.subplot(211)
mp.title('Normal', fontsize=20)
mp.ylabel('y', fontsize=14)
ax = mp.gca()
ax.xaxis.set_major_locator(mp.MultipleLocator(1.0))
ax.xaxis.set_minor_locator(mp.MultipleLocator(0.1))
ax.yaxis.set_major_locator(mp.MultipleLocator(250))
ax.yaxis.set_minor_locator(mp.MultipleLocator(50))
mp.tick_params(labelsize=10)
ax.grid(which='major', axis='both', linewidth=0.75,
linestyle='-', color='orange')
ax.grid(which='minor', axis='both', linewidth=0.25,
linestyle='-', color='orange')
mp.plot(y, 'o-', c='dodgerblue', label='plot')
mp.legend()
其他
绘制线条
利用hlines和vlines绘制线条
# vertical 绘制垂直线
mp.vlines(vval, ymin, ymax, ...)
# horizotal 绘制水平线
mp.hlines(xval, xmin, xmax, ...)
示例:
import numpy as np
import matplotlib.pyplot as mp
x = np.array([0, 1, 2, 3, 4, 5, 6, 7])
y = np.array([45,81,36,51,19,92,23,51])
mp.plot(x, y)
# 绘制水平线
mp.hlines(60, 1, 6.5)
# 绘制垂直线
mp.vlines([1,2,3,4,5], [10,20,30,40,50], [25,35,45,55,65])
mp.show()
半对数坐标
y轴将以指数方式递增。 基于半对数坐标绘制第二个子图,表示曲线:[1, 10, 100, 1000, 100, 10, 1]。
mp.figure('Grid', facecolor='lightgray')
y = [1, 10, 100, 1000, 100, 10, 1]
mp.semilogy(y)
mp.show()
patch
前面都是根据数据集来绘图,那如果我想绘制一些常见的图形对象呢?比如圆形、矩形、三角等等。可以在matplotlib.patches中找到它们。使用mpl.patches.
可以查看有哪些可用的patch。下面简要介绍如何使用ax的add_patch
方法绘制patch:
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
# 注意下面三种形状的参数提供方式
rect = plt.Rectangle((0.2,0.75),0.4,0.15,alpha=0.3,color='b')
circ = plt.Circle((0.7,0.2), 0.15,)
pgon = plt.Polygon([[0.15,0.15],[0.35,0.4],[0.2,0.6]])
ax.add_patch(rect)
ax.add_patch(circ)
ax.add_patch(pgon)
风格样式展示
到现在,我们可以追求一下整体的风格样式了:
>>> plt.style.available
['bmh',
'classic',
'dark_background',
'fast',
'fivethirtyeight',
'ggplot',
'grayscale',
'seaborn-bright',
'seaborn-colorblind',
'seaborn-dark-palette',
'seaborn-dark',
'seaborn-darkgrid',
'seaborn-deep',
'seaborn-muted',
'seaborn-notebook',
'seaborn-paper',
'seaborn-pastel',
'seaborn-poster',
'seaborn-talk',
'seaborn-ticks',
'seaborn-white',
'seaborn-whitegrid',
'seaborn',
'Solarize_Light2',
'tableau-colorblind10',
'_classic_test']
下面展示一下一些经典的风格,为此我们设计了一个可以绘制直方图和正弦余弦曲线的函数,然后使用with的上下文管理器语法,测试不同的风格:
def hist_and_lines():
np.random.seed(10)
fig, ax = plt.subplots(1, 2, figsize=(11, 4))
ax[0].hist(np.random.randn(1000))
x = np.linspace(0,10,100)
ax[1].plot(np.sin(x))
ax[1].plot(np.cos(x))
ax[1].legend(['a', 'b', 'c'], loc='lower left')
with语句:
with plt.style.context('classic'):
hist_and_lines()
将其中的‘classic’字符串替换成你想要的风格名称,就能在with管理区内使用风格,而不影响后面的绘图。
- 默认风格classic
真的比较丑,和matlab一样。
- fivethirtyeight
- ggplot
这个风格是模仿R语言的ggplot工具风格。
- bmh
- 灰度grayscale
比较适合黑白打印的需求
- seaborn
这个样式来自Seaborn库。在Jupyter notebook中import seaborn后,会自动加载这个样式。
填充
mp.fill_between(x, sin_x, cos_x, sin_x<cos_x, color='', alpha=0.2)
以某种颜色自动填充两条曲线的闭合区域
主要参数说明:
- x:x轴的水平坐标
- sin_x:下边界曲线上点的垂直坐标
- cos_x:上边界曲线上点的垂直坐标
- sin_x<cos_x:填充条件,为True时填充
- color:填充颜色
- alpha:透明度
案例:绘制两条曲线: sin_x = sin(x) cos_x = cos(x / 2) / 2 [0-8π]
n = 1000
x = np.linspace(0, 8 * np.pi, n)
sin_y = np.sin(x)
cos_y = np.cos(x / 2) / 2
mp.figure('Fill', facecolor='lightgray')
mp.title('Fill', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(x, sin_y, c='dodgerblue',
label=r'$y=sin(x)$')
mp.plot(x, cos_y, c='orangered',
label=r'$y=\frac{1}{2}cos(\frac{x}{2})$')
mp.fill_between(x, cos_y, sin_y, cos_y < sin_y,
color='dodgerblue', alpha=0.5)
mp.fill_between(x, cos_y, sin_y, cos_y > sin_y,
color='orangered', alpha=0.5)
mp.legend()
mp.show()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)