d、Matplotlib

Pandas

参考文档:

刘江博客教程_Matplotlib

菜鸟教程_Matplotlib教程

概述

img

官网: https://matplotlib.org/

本教程使用版本: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))

img

这两种使用方式你必须都会用,因为它们随机出现在代码、文献、测试场景中,你没得选。

使用中文

在默认情况下,如果你使用中文,会显示为方框乱码,解决这个问题只要在代码的起始处进行如下设置:

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颜色

img

下面是常用的颜色:

  • 蓝色: '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=':'); 

img

可以通过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='+')

img

更多标记类型:

  • '.' 实点标记
  • ',' 像素标记
  • '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');  # 红色实点线

img

对于plot()方法,大部分可配置的参数如下:

参数 取值范围 说明
alpha 0-1 透明度
color或c 颜色格式 设置线条颜色
label 字符串 为图形设置标签
linestyle或ls 可用线型 设置线条风格
linewidth或lw 数值 线宽
marker 可用标记 标记
markeredgecolor或mec 颜色 标记的边缘颜色
markeredgewidth或mew 数值 标记的边缘宽度
markerfacecolor或mfc 颜色 标记的颜色
markersize或ms 数值 标记的大小
solid_capstyle buttroundprojecting 实线的线端风格
solid_joinstyle miterroundbevel 实线的连接风格
drawstyle defaultstepssteps-presteps-midsteps-post 连线的规则
visible TrueFalse 显示或隐藏
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)

img

也可以让坐标轴逆序显示,只需要逆序提供坐标轴的限值:

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())

img

我们可以手动提供刻度值,并调整刻度的角度和大小:

#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')

img

刻度文本的特殊语法 -- 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()

img

注意:大多数的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)

img

颜色条

在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()

img

我们得到了一个默认的蓝绿配色的图形。

实际上在plt.cm中有大量可选的颜色配置方案。具体使用哪种方案,要根据你的需求和你对美术的修养。一般情况下,你只需要关注三种类型的配色方案:

  • 顺序配色:由一组连续的颜色构成,比如binary和viridis
  • 互逆色:通常由互补的颜色构成,比如RdBu或者PuOr
  • 定性配色:随机顺序的一组颜色,比如rainbow或jet

这些配色方案的名字很多都是缩写组合,一定要注意字母大小写。

jet是Matplotlib2.0之前的默认配色方案,已经比较老旧了。viridis是目前的默认配色方案。下面我们看看'RdBu'的配色:

plt.imshow(I, cmap='RdBu')
plt.colorbar()

img

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)  # 限制颜色显示范围 

img

颜色条默认都是连续的,但有时候你可能也需要使用离散的颜色数据,最简单的方法就是使用plt.cm.get_cmap()方法,将适当的颜色方案和需要的区间数量作为参数传递进去即可:

plt.imshow(I, cmap=plt.cm.get_cmap('Blues',6))
plt.colorbar()
plt.clim(-1,1)

img

文本、箭头和注释

很多时候,光是图像不足以表达所有的内容,需要一些说明性的文字来辅助。

在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')

img

一定要将文字和图例、标签、标注等组件区别开。

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),))

img

很多时候,文本是以数学公式出现的:

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)

img

除了文本,简单的箭头也是一种很有用的说明性注释。虽然有一个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"));

img

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

关于箭头的绘制方式,内容实在太多太细,根本无法详解,最好的办法是参考下面两个官方连接:

这其中的例子,喜欢哪个,复制对应的参数配置方法即可-

下面是一些具体例子的展示,大家可以对号挑选:

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"))

img

绘图种类

线型图

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)

img

可以看到这种方式下,两个线条共用一个坐标轴,并且自动区分颜色。

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()

img

pandas和matplotlib的plot方法你愿意用哪个都行,但要注意参数格式和使用场景。

散点图

与线型图类似的是,散点图也是一个个点集构成的。但不同之处在于,散点图的各点之间不会按照前后关系以线条连接起来。

  • 用plt.plot画散点图
x = np.linspace(0,10,30)
y = np.sin(x)
plt.plot(x,y,'bo', ms=5)

img

奇怪,代码和前面的例子差不多,为什么这里显示的却是散点图而不是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() # 绘制颜色对照条

img

上面的例子可以拓展到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]);

img

这个散点图让我们看到了不同维度的数据:每个点的坐标值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$')

img

绘制直方图,最主要的是一个数据集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)

img

除了一维的直方图,还可以使用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')  # 设置颜色条的标签

img

hist2d是使用坐标轴正交的方块分割区域,还有一种常用的方式是正六边形也就是蜂窝形状的分割。Matplotlib提供的plt.hexbin就是满足这个需求的:

plt.hexbin(x,y,gridsize=30, cmap='Blues')
plt.colorbar(label='count in bin')

img

更多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)

img

将上面的代码稍微修改一下,就可以得到下面的图形:

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)

img

更多内容参考: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%%':标签所占比例格式

img

更多内容参考: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')

img

做一些格式上的调整:

plt.errorbar(x, y, yerr=dy, fmt='ok',ecolor='lightgray',elinewidth=3, capsize=0)

img

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')

img

contour函数需要三个基本参数:x、y、z轴三个坐标轴的网格数据。x与y轴表示图形中的位置,而z轴通过等高线的等级来表示。

当图形中只使用一种颜色的时候,会使用虚线来表示负数,实线表示正数。

我们可以将数据范围等分,比如10份,然后设置camp参数定义一个线条颜色的配色方案,用不同的颜色表示等高线。

plt.contour(X, Y, Z, cmap='RdGy')
plt.colorbar()

imgRdGy红-灰配色方案(Red-Gray)。Matplotlib有很多配色方案可选,都在plt.cm模块里,使用plt.cm.可以查看有哪些方案。

上面的图形里,线条之间的间隙比较大,可以通过contourf方法进行填充(注意方法名多了个f):

plt.contourf(X, Y, Z, cmap='RdGy')
plt.colorbar()

img

我们还可以通过imshow方法,将二维数组渲染成渐变图:

plt.imshow(Z, extent=[0,5,0,5], origin='lower',cmap='RdGy')
plt.colorbar()
plt.axis(aspect='image')

img

  • 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()

img

现在你或许会有感悟,绘图画到最后,缺少的不是工具,而是创意灵感。

热成像图

# 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]) # 在右上角指定位置

img

上面是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))

img

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')

img

子图间距好像不太恰当,可以使用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')

img

通过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])

img

下面是一个使用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() # 可以尝试不要这行,看看结果

img

自定义坐标轴刻度

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')

我们发现每个主要刻度都显示未一个较大的刻度线和标签,而次要刻度都显示为一个较小的刻度线,并且不现实标签。

img

每种刻度线都包含一个坐标轴定位器(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())

img

可以看出,没有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))

img

常用刻度器如下:

# 空定位器:不绘制刻度
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()

image-20221106210254246

半对数坐标

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)

img

风格样式展示

到现在,我们可以追求一下整体的风格样式了:

>>> 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

img

真的比较丑,和matlab一样。

  • fivethirtyeight

img

  • ggplot

img

这个风格是模仿R语言的ggplot工具风格。

  • bmh

img

  • 灰度grayscale

比较适合黑白打印的需求

img

  • seaborn

img

这个样式来自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()
posted @   昵称已经被使用  阅读(77)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示