Matplotlib详解

Matplotlib 是一款用于数据可视化的 Python 软件包,支持跨平台运行,它能够根据 NumPy  ndarray 数组来绘制 2D 图像,它使用简单、代码清晰易懂,深受广大技术爱好者喜爱。

NumPy 是 Python 科学计算的软件包,ndarray 则是 NumPy 提供的一种数组结构。

Matplotlib 由 John D. Hunter 在 2002 年开始编写, 2003 年 Matplotlib 发布了第一个版本,并加入了 BSD 开源软件组织。Matplotlib 1.4 是最后一个支持 Python 2 的版本,它的最新版本 3.1.1 已于 2019 年 7 月 1 日发布。
Matplotlib 提供了一个套面向绘图对象编程的 API 接口,能够很轻松地实现各种图像的绘制,并且它可以配合 Python GUI 工具(如 PyQt、Tkinter 等)在应用程序中嵌入图形。同时 Matplotlib 也支持以脚本的形式嵌入到 IPython shell、Jupyter 笔记本、web 应用服务器中使用。

Matplotlib架构组成

Matplotlib 由三个不同的层次结构组成,分别是脚本层、美工层和后端层。
 

Matplotlib架构图


图2:Matplotlib架构图

1) 脚本层

脚本层是 Matplotlib 结构中的最顶层。我们编写的绘图代码大部分代码都在该层运行,它的主要工作是负责生成图形与坐标系。

2) 美工层

美工层是结构中的第二层,它提供了绘制图形的元素时的给各种功能,例如,绘制标题、轴标签、坐标刻度等。

3) 后端层

后端层是 Matplotlib 最底层,它定义了三个基本类,首先是 FigureCanvas(图层画布类),它提供了绘图所需的画布,其次是 Renderer(绘图操作类),它提供了在画布上进行绘图的各种方法,最后是 Event(事件处理类),它提供了用来处理鼠标和键盘事件的方法。

Matplotlib图形组成

Matplotlib 生成的图形主要由以下几个部分构成:
 

Matplotlib图像组成


图3:Matplotlib图像组成

  • Figure:指整个图形,您可以把它理解成一张画布,它包括了所有的元素,比如标题、轴线等;
  • Axes:绘制 2D 图像的实际区域,也称为轴域区,或者绘图区;
  • Axis:指坐标系中的垂直轴与水平轴,包含轴的长度大小(图中轴长为 7)、轴标签(指 x 轴,y轴)和刻度标签;
  • Artist:您在画布上看到的所有元素都属于 Artist 对象,比如文本对象(title、xlabel、ylabel)、Line2D 对象(用于绘制2D图像)等。

第一个绘图程序

首先导入 Matplotlib 包中的 Pyplot 模块,并以 as 别名的形式简化引入包的名称。

import matplotlib.pyplot as plt

接下来,使用 NumPy 提供的函数 arange() 创建一组数据来绘制图像。

  1. #引入numpy包
  2. import numpy as np
  3. #获得0到2π之间的ndarray对象
  4. x = np.arange(0, math.pi*2, 0.05)

上述所得 x 的值作用到 x 轴上,而该值对应的正弦值,也就是 y 值,使用以下方法获取:

y = np.sin(x)

使用 plot() 函数对 x、y 进行绘制。

plt.plot(x,y)

主要的绘图工作已经完成,不过还需要绘制一些细节,需要我们补充一下,比如图像的标题(title)、x 轴与 y 轴的标签(label)等。

plt.xlabel("angle")
plt.ylabel("sine")
plt.title('sine wave')

完整的程序代码如下:

from matplotlib import pyplot as plt
import numpy as np
import math
#调用math.pi方法弧度转为角度
x = np.arange(0, math.pi*2, 0.05)
y = np.sin(x)
plt.plot(x,y)
plt.xlabel("angle")
plt.ylabel("sine")
plt.title('sine wave')
#使用show展示图像
plt.show()

代码执行后,显示结果如下:
 

正弦函数图像


图1:sine正弦函数图像

Matplotlib figure图形对象

通过前面的学习,我们知道matplotlib.pyplot模块能够快速地生成图像,但如果使用面向对象的编程思想,我们就可以更好地控制和自定义图像。

在 Matplotlib 中,面向对象编程的核心思想是创建图形对象(figure object)。通过图形对象来调用其它的方法和属性,这样有助于我们更好地处理多个画布。在这个过程中,pyplot 负责生成图形对象,并通过该对象来添加一个或多个 axes 对象(即绘图区域)。

Matplotlib 提供了matplotlib.figure图形类模块,它包含了创建图形对象的方法。通过调用 pyplot 模块中 figure() 函数来实例化 figure 对象。如下所示:

 
  1. from matplotlib import pyplot as plt
  2. #创建图形对象
  3. fig = plt.figure()

该函数的参数值,如下所示:
 

参数说明
figsize指定画布的大小,(宽度,高度),单位为英寸。
dpi指定绘图对象的分辨率,即每英寸多少个像素,默认值为80。
facecolor背景颜色。
dgecolor边框颜色。
frameon是否显示边框。


下面使用 figure() 创建一个空白画布:

fig = plt.figure()

我们使用 add_axes() 将 axes 轴域添加到画布中。如下所示:

ax=fig.add_axes([0,0,1,1])

add_axes() 的参数值是一个序列,序列中的 4 个数字分别对应图形的左侧,底部,宽度,和高度,且每个数字必须介于 0 到 1 之间。

设置 x 和 y 轴的标签以及标题,如下所示:

 
  1. ax.set_title("sine wave")
  2. ax.set_xlabel('angle')
  3. ax.set_ylabel('sine')

调用 axes 对象的 plot() 方法,对 x 、 y 数组进行绘图操作:

ax.plot(x,y)

完整的代码如下所示:

from matplotlib import pyplot as plt
import numpy as np
import math
x = np.arange(0, math.pi*2, 0.05)
y = np.sin(x)
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
ax.plot(x,y)
ax.set_title("sine wave")
ax.set_xlabel('angle')
ax.set_ylabel('sine')
plt.show()

输出结果如下:
 

面向对象接口matplotlib


图1:运行结果图

Matplotlib axes类使用详解

Matplotlib 定义了一个 axes 类(轴域类),该类的对象被称为 axes 对象(即轴域对象),它指定了一个有数值范围限制的绘图区域。在一个给定的画布(figure)中可以包含多个 axes 对象,但是同一个 axes 对象只能在一个画布中使用。

2D 绘图区域(axes)包含两个轴(axis)对象;如果是 3D 绘图区域,则包含三个。

通过调用 add_axes() 方法能够将 axes 对象添加到画布中,该方法用来生成一个 axes 轴域对象,对象的位置由参数rect决定。

rect 是位置参数,接受一个由 4 个元素组成的浮点数列表,形如 [left, bottom, width, height] ,它表示添加到画布中的矩形区域的左下角坐标(x, y),以及宽度和高度。如下所示:

ax=fig.add_axes([0.1,0.1,0.8,0.8])

注意:每个元素的值是画布宽度和高度的分数。即将画布的宽、高作为 1 个单位。比如,[ 0.1, 0.1, 0.8, 0.8],它代表着从画布 10% 的位置开始绘制, 宽高是画布的 80%。

下面介绍 axes 类的其他成员函数,这些函数在绘图过程中都承担着不同的作用。

legend()绘制图例

axes 类的 legend() 方法负责绘制画布中的图例,它需要三个参数,如下所示:

ax.legend(handles, labels, loc)

  • labels 是一个字符串序列,用来指定标签的名称;
  • loc 是指定图例位置的参数,其参数值可以用字符串或整数来表示;
  • handles 参数,它也是一个序列,它包含了所有线型的实例;


下面是 loc 参数的表示方法,分为字符串和整数两种,如下所示:
 

位置字符串表示整数数字表示
自适应Best0
右上方upper right1
左上方upper left2
左下lower left3
右下lower right4
右侧right5
居中靠左center left6
居中靠右center right7
底部居中lower center8
上部居中upper center9
中部center10

axes.plot()

这是 axes 类的基本方法,它将一个数组的值与另一个数组的值绘制成线或标记,plot() 方法具有可选格式的字符串参数,用来指定线型、标记颜色、样式以及大小。

颜色代码如下表:
 

'b'蓝色
'g'绿色
'r'红色
'c'青色
'm'品红色
'y'黄色
'k'黑色
'w'白色


标记符号如下表:
 

标记符号描述
'.'点标记
'o'圆圈标记
'x''X'标记
'D'钻石标记
'H'六角标记
's'正方形标记
'+'加号标记


线型表示字符,如下表:
 

字符描述
'-'实线
'--'虚线
'-.'点划线
':'虚线
'H'六角标记


下面的例子,以直线图的形式展示了电视、智能手机广告费与其所带来产品销量的关系图。其中描述电视的是带有黄色和方形标记的实线,而代表智能手机的则是绿色和圆形标记的虚线。

import matplotlib.pyplot as plt
y = [1, 4, 9, 16, 25,36,49, 64]
x1 = [1, 16, 30, 42,55, 68, 77,88]
x2 = [1,6,12,18,28, 40, 52, 65]
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
#使用简写的形式color/标记符/线型
l1 = ax.plot(x1,y,'ys-') 
l2 = ax.plot(x2,y,'go--') 
ax.legend(labels = ('tv', 'Smartphone'), loc = 'lower right') # legend placed at lower right
ax.set_title("Advertisement effect on sales")
ax.set_xlabel('medium')
ax.set_ylabel('sales')
plt.show()

输出结果如下:

Matplotlib绘图


图1:输出结果

Matplotlib subplot()函数用法详解

在使用 Matplotlib 绘图时,我们大多数情况下,需要将一张画布划分为若干个子区域,之后,我们就可以在这些区域上绘制不用的图形。在本节,我们将学习如何在同一画布上绘制多个子图。

matplotlib.pyplot模块提供了一个 subplot() 函数,它可以均等地划分画布,该函数的参数格式如下:

plt.subplot(nrows, ncols, index)

nrows 与 ncols 表示要划分几行几列的子区域(nrows*nclos表示子图数量),index 的初始值为1,用来选定具体的某个子区域。

例如: subplot(233)表示在当前画布的右上角创建一个两行三列的绘图区域(如下图所示),同时,选择在第 3 个位置绘制子图。
 

subplot()函数示意图


图1:示意图


如果新建的子图与现有的子图重叠,那么重叠部分的子图将会被自动删除,因为它们不可以共享绘图区域。

import matplotlib.pyplot as plt
plt.plot([1,2,3])
#现在创建一个子图,它表示一个有2行1列的网格的顶部图。
#因为这个子图将与第一个重叠,所以之前创建的图将被删除
plt.subplot(211)
plt.plot(range(12))
#创建带有黄色背景的第二个子图
plt.subplot(212, facecolor='y')
plt.plot(range(12))
上述代码运行结果,如下图所示:

subplot函数绘制子图


图2:subplot绘制结果


如果不想覆盖之前的图,需要使用 add_subplot() 函数,代码如下:

import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.plot([1,2,3])
ax2 = fig.add_subplot(221, facecolor='y')
ax2.plot([1,2,3])

执行上述代码,输出结果如下:
 

subplot绘图函数


图3:add_subplot()绘图结果
 

通过给画布添加 axes 对象可以实现在同一画布中插入另外的图像。

import matplotlib.pyplot as plt
import numpy as np
import math
x = np.arange(0, math.pi*2, 0.05)
fig=plt.figure()
axes1 = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # main axes
axes2 = fig.add_axes([0.55, 0.55, 0.3, 0.3]) # inset axes
y = np.sin(x)
axes1.plot(x, y, 'b')
axes2.plot(x,np.cos(x),'r')
axes1.set_title('sine')
axes2.set_title("cosine")
plt.show()

输出结果如下:

matplotlib绘图


图4:输出结果图

Matplotlib subplots()函数详解

matplotlib.pyplot模块提供了一个 subplots() 函数,它的使用方法和 subplot() 函数类似。其不同之处在于,subplots() 既创建了一个包含子图区域的画布,又创建了一个 figure 图形对象,而 subplot() 只是创建一个包含子图区域的画布。

subplots 的函数格式如下:

fig , ax = plt.subplots(nrows, ncols)

nrows 与 ncols 表示两个整数参数,它们指定子图所占的行数、列数。

函数的返回值是一个元组,包括一个图形对象和所有的 axes 对象。其中 axes 对象的数量等于 nrows * ncols,且每个 axes 对象均可通过索引值访问(从1开始)。

下面我们创建了一个 2 行 2 列的子图,并在每个子图中显示 4 个不同的图像。

import matplotlib.pyplot as plt
fig,a =  plt.subplots(2,2)
import numpy as np
x = np.arange(1,5)
#绘制平方函数
a[0][0].plot(x,x*x)
a[0][0].set_title('square')
#绘制平方根图像
a[0][1].plot(x,np.sqrt(x))
a[0][1].set_title('square root')
#绘制指数函数
a[1][0].plot(x,np.exp(x))
a[1][0].set_title('exp')
#绘制对数函数
a[1][1].plot(x,np.log10(x))
a[1][1].set_title('log')
plt.show()

上述代码的输出结果如下:
 

subplotl函数


图1:输出结果

Matplotlib中文乱码解决方案(两种方式)

Matplotlib 默认不支持中文字体,这因为 Matplotlib 只支持 ASCII 字符,但中文标注更加符合中国人的阅读习惯。因此,本节重点讲解如何在 Windows 环境下让 Matplotlib 显示中文。

Matplotlib中文乱码

当不对 Matplotlib 进行设置,而直接使用中文时,绘制的图像会出现中文乱码。下面是一个含有中文乱码的折线图:
 

matplotlib折线图

从上图可以看出,本应该显示在红框内的中文字体没有显示出来(红框是自己标注出来的),下面给出了两种解决方案:第一种是临时解决方案,第二种是一劳永逸的解决方案。

重写配置文件

通过临时重写配置文件的方法,可以解决 Matplotlib 显示中文乱码的问题,代码如下所示:

import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"]=["SimHei"] #设置字体
plt.rcParams["axes.unicode_minus"]=False #该语句解决图像中的“-”负号的乱码问题

将上述代码添加到您的绘图程序中,即可解决中文乱码的问题。这是一种非常灵活、便捷的解决方法。完整的程序代码如下:

#绘制折线图
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"]=["SimHei"] #设置字体
plt.rcParams["axes.unicode_minus"]=False #正常显示负号
year = [2017, 2018, 2019, 2020]
people = [20, 40, 60, 70]
#生成图表
plt.plot(year, people)
plt.xlabel('年份')
plt.ylabel('人口')
plt.title('人口增长')
#设置纵坐标刻度
plt.yticks([0, 20, 40, 60, 80])
#设置填充选项:参数分别对应横坐标,纵坐标,纵坐标填充起始值,填充颜色
plt.fill_between(year, people, 20, color = 'green')
#显示图表
plt.show()


输出结果如下:

matplotlib解决中文乱码问题


不过上述解决方案适用于所有操作系统,其唯一弊端是每编写一个绘图程序就要添加一次相同的代码。

修改配置文件

下面介绍第二种方式:通过直接修改配置文件的方法,可以一劳永逸的解决 Matplotlib 的中文乱码问题。注意此过程在 Windows 环境下进行。

Matplotlib 从配置文件 matplotlibrc 中读取相关配置信息,比如字体、样式等,因此我们需要对该配置文件进行更改。首先查看 matplotlibrc 所在的目录,使用如下代码确定目录位置:

import matplotlib

matplotlib.matplotlib_fname()

输出结果:

D:\python\python37\lib\site-packages\matplotlib\mpl-data\matplotlibrc

然后修改配置文件 matplotlibrc。打开配置文件后,找到以下信息:

#font.family: sans-serif

#font.serif: DejaVu Serif, Bitstream Vera Serif, Computer Modern Roman, New Century Schoolbook, Century Schoolbook L, Utopia, ITC Bookman, Bookman, Nimbus Roman No9 L, Times New Roman, Times, Palatino, Charter, serif

将上述配置项前面的#去掉,并修改的配置项,如下所示:

font.family   :  Microsoft YaHei, sans-serif
font.serif: Microsoft YaHei, DejaVu Serif, Bitstream Vera Serif, Computer Modern Roman, New Century Schoolbook, Century Schoolbook L, Utopia, ITC Bookman, Bookman, Nimbus Roman No9 L, Times New Roman, Times, Palatino, Charter, serif

注意,由于版本问题,上述内容配置信息可能存在一些差异,请自动忽略。

最后,在以下目录中复制中文字体微软雅黑:

C:\Windows\Fonts\Microsoft YaHei UI

复制完成后,将字体粘贴至以下路径文件中:

D:\python\python37\lib\site-packages\matplotlib\mpl-data\fonts\ttf

字体粘贴后会出现一个 MSYH.ttc 的字体文件,如下所示:
 

字体路径


编写如下代码进行测试:

import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-8, 8, 1024)
y1 = 0.618 * np.abs(x) - 0.8 * np.sqrt(64 - x ** 2)
y2 = 0.618 * np.abs(x) + 0.8 * np.sqrt(64 - x ** 2)
plt.plot(x, y1, color='r')
plt.plot(x, y2, color='r')
plt.title("我爱C语言中文网",fontsize=20,color="b")
plt.show()

输出结果如下:
 

matplotlib中文乱码解决


如果你对自己编写的程序没有强烈的“洁癖”,可以接受重复的代码,那么建议您选择第一种解决方法,因为这种方法灵活、轻便。当然您也可以选择第二种方式,一劳永逸的解决中文乱码问题。

多个子图(axes)

subplot 方法可以用来创建多个子图(axes)。

前面的示例中,我们并没有创建子图,其实, matplotlib缺省会帮我们调用 plt.subplot(1,1,1) 指定 1行,1列,共1个子图,当前子图为第1个.

如果你想指定更多的子图,可以这样,

import matplotlib.pyplot as plt
import numpy as np

# arange 就像 Python中的range
# 从 0 到 5 步长为 0.2
t = np.arange(0, 5, 0.2)

# 指定2行,1列,共两个axe,当前使用第1个绘图块
plt.subplot(2,1,1)   
plt.plot(t, t**2, 'b.')


# 当前使用第2个绘图块
plt.subplot(2,1,2)   
plt.plot(t, t**2, 'r-')
plt.show()

结果如下:

image

多个绘图(Figure)

matplotlib 每个绘图区都对应一个 Figure 对象。

一个绘图 Figure 对象 里面可以包含多个 subplot对象。

而我们的程序可以同时打开多个绘图 Figure 对象。

比如下图,你可以发现有两个绘图窗口,对应两个 Figure 对象

image

前面的示例中,我们并没有声明创建Figure对象,其实是默认使用了 matplotlib 缺省Figure 对象。

默认Figure ,也就是相当于调用 plt.figure(1) 指定第一个绘图。

我们可以像下面这样创建多个Figure

import matplotlib.pyplot as plt
plt.figure(1)                # the first figure
plt.subplot(211)             # the first subplot in the first figure
plt.plot([1, 2, 3])
plt.subplot(212)             # the second subplot in the first figure
plt.plot([4, 5, 6])


plt.figure(2)                # a second figure
plt.plot([4, 5, 6])          # creates a subplot(111) by default

plt.figure(1)                # figure 1 current; subplot(212) still current
plt.subplot(211)             # make subplot(211) in figure1 current
plt.title('Easy as 1, 2, 3') # subplot 211 title

plt.show()

运行代码,就可以产生上面的图形。

Matplotlib使用例程

使用默认配置

Matplotlib 的默认配置都允许用户自定义。你可以调整大多数的默认配置:图片大小和分辨率(dpi)、线宽、颜色、风格、坐标轴、坐标轴以及网格的属性、文字与字体属性等。不过,matplotlib 的默认配置在大多数情况下已经做得足够好,你可能只在很少的情况下才会想更改这些默认配置。


import numpy as np
import matplotlib.pyplot as plt

X = np.linspace(-np.pi, np.pi, 256, endpoint=True)
C,S = np.cos(X), np.sin(X)

plt.plot(X,C)
plt.plot(X,S)

plt.show()

默认配置的具体内容

下面的代码中,我们展现了 matplotlib 的默认配置并辅以注释说明,这部分配置包含了有关绘图样式的所有配置。代码中的配置与默认配置完全相同,你可以在交互模式中修改其中的值来观察效果。

# 导入 matplotlib 的所有内容(nympy 可以用 np 这个名字来使用)
from pylab import *

# 创建一个 8 * 6 点(point)的图,并设置分辨率为 80
figure(figsize=(8,6), dpi=80)

# 创建一个新的 1 * 1 的子图,接下来的图样绘制在其中的第 1 块(也是唯一的一块)
subplot(1,1,1)

X = np.linspace(-np.pi, np.pi, 256,endpoint=True)
C,S = np.cos(X), np.sin(X)

# 绘制余弦曲线,使用蓝色的、连续的、宽度为 1 (像素)的线条
plot(X, C, color="blue", linewidth=1.0, linestyle="-")

# 绘制正弦曲线,使用绿色的、连续的、宽度为 1 (像素)的线条
plot(X, S, color="green", linewidth=1.0, linestyle="-")

# 设置横轴的上下限
xlim(-4.0,4.0)

# 设置横轴记号
xticks(np.linspace(-4,4,9,endpoint=True))

# 设置纵轴的上下限
ylim(-1.0,1.0)

# 设置纵轴记号
yticks(np.linspace(-1,1,5,endpoint=True))

# 以分辨率 72 来保存图片
# savefig("exercice_2.png",dpi=72)

# 在屏幕上显示
show()

改变线条的颜色和粗细

首先,我们以蓝色和红色分别表示余弦和正弦函数,而后将线条变粗一点。接下来,我们在水平方向拉伸一下整个图。

...
figure(figsize=(10,6), dpi=80)
plot(X, C, color="blue", linewidth=2.5, linestyle="-")
plot(X, S, color="red",  linewidth=2.5, linestyle="-")
...

设置图片边界

当前的图片边界设置得不好,所以有些地方看得不是很清楚。

... xlim(X.min()*1.1, X.max()*1.1) ylim(C.min()*1.1, C.max()*1.1) ...

更好的方式是这样:

xmin ,xmax = X.min(), X.max()
ymin, ymax = Y.min(), Y.max()

dx = (xmax - xmin) * 0.2
dy = (ymax - ymin) * 0.2

xlim(xmin - dx, xmax + dx)
ylim(ymin - dy, ymax + dy)

设置记号

我们讨论正弦和余弦函数的时候,通常希望知道函数在 ±π 和 ±π2 的值。这样看来,当前的设置就不那么理想了。

...

xticks( [-np.pi, -np.pi/2, 0, np.pi/2, np.pi])
yticks([-1, 0, +1])
...

设置记号的标签

记号现在没问题了,不过标签却不大符合期望。我们可以把 3.142 当做是 π,但毕竟不够精确。当我们设置记号的时候,我们可以同时设置记号的标签。注意这里使用了 LaTeX。

...
xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi],
       [r'$-\pi$', r'$-\pi/2$', r'$0$', r'$+\pi/2$', r'$+\pi$'])

yticks([-1, 0, +1],
       [r'$-1$', r'$0$', r'$+1$'])
...

移动脊柱

坐标轴线和上面的记号连在一起就形成了脊柱(Spines,一条线段上有一系列的凸起,是不是很像脊柱骨啊~),它记录了数据区域的范围。它们可以放在任意位置,不过至今为止,我们都把它放在图的四边。

实际上每幅图有四条脊柱(上下左右),为了将脊柱放在图的中间,我们必须将其中的两条(上和右)设置为无色,然后调整剩下的两条到合适的位置——数据空间的 0 点。

...
ax = gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0))
...

添加图例

我们在图的左上角添加一个图例。为此,我们只需要在 plot 函数里以「键 - 值」的形式增加一个参数。

...
plot(X, C, color="blue", linewidth=2.5, linestyle="-", label="cosine")
plot(X, S, color="red",  linewidth=2.5, linestyle="-", label="sine")

legend(loc='upper left')
...

给一些特殊点做注释

我们希望在 2π/3 的位置给两条函数曲线加上一个注释。首先,我们在对应的函数图像位置上画一个点;然后,向横轴引一条垂线,以虚线标记;最后,写上标签。

...

t = 2*np.pi/3
plot([t,t],[0,np.cos(t)], color ='blue', linewidth=2.5, linestyle="--")
scatter([t,],[np.cos(t),], 50, color ='blue')

annotate(r'$\sin(\frac{2\pi}{3})=\frac{\sqrt{3}}{2}$',
         xy=(t, np.sin(t)), xycoords='data',
         xytext=(+10, +30), textcoords='offset points', fontsize=16,
         arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2"))

plot([t,t],[0,np.sin(t)], color ='red', linewidth=2.5, linestyle="--")
scatter([t,],[np.sin(t),], 50, color ='red')

annotate(r'$\cos(\frac{2\pi}{3})=-\frac{1}{2}$',
         xy=(t, np.cos(t)), xycoords='data',
         xytext=(-90, -50), textcoords='offset points', fontsize=16,
         arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2"))
...

精益求精

坐标轴上的记号标签被曲线挡住了,作为强迫症患者(雾)这是不能忍的。我们可以把它们放大,然后添加一个白色的半透明底色。这样可以保证标签和曲线同时可见。

...
for label in ax.get_xticklabels() + ax.get_yticklabels():
    label.set_fontsize(16)
    label.set_bbox(dict(facecolor='white', edgecolor='None', alpha=0.65 ))
...

图像、子图、坐标轴和记号

到目前为止,我们都用隐式的方法来绘制图像和坐标轴。快速绘图中,这是很方便的。我们也可以显式地控制图像、子图、坐标轴。Matplotlib 中的「图像」指的是用户界面看到的整个窗口内容。在图像里面有所谓「子图」。子图的位置是由坐标网格确定的,而「坐标轴」却不受此限制,可以放在图像的任意位置。我们已经隐式地使用过图像和子图:当我们调用 plot 函数的时候,matplotlib 调用 gca() 函数以及 gcf() 函数来获取当前的坐标轴和图像;如果无法获取图像,则会调用 figure() 函数来创建一个——严格地说,是用 subplot(1,1,1) 创建一个只有一个子图的图像。

图像

所谓「图像」就是 GUI 里以「Figure #」为标题的那些窗口。图像编号从 1 开始,与 MATLAB 的风格一致,而于 Python 从 0 开始编号的风格不同。以下参数是图像的属性:

参数默认值描述
num1图像的数量
figsizefigure.figsize图像的长和宽(英寸)
dpifigure.dpi分辨率(点/英寸)
facecolorfigure.facecolor绘图区域的背景颜色
edgecolorfigure.edgecolor绘图区域边缘的颜色
frameonTrue是否绘制图像边缘

这些默认值可以在源文件中指明。不过除了图像数量这个参数,其余的参数都很少修改。

你在图形界面中可以按下右上角的 X 来关闭窗口(OS X 系统是左上角)。Matplotlib 也提供了名为 close 的函数来关闭这个窗口。close 函数的具体行为取决于你提供的参数:

  1. 不传递参数:关闭当前窗口;
  2. 传递窗口编号或窗口实例(instance)作为参数:关闭指定的窗口;
  3. all:关闭所有窗口。

和其他对象一样,你可以使用 setp 或者是 set_something 这样的方法来设置图像的属性。

子图

你可以用子图来将图样(plot)放在均匀的坐标网格中。用 subplot 函数的时候,你需要指明网格的行列数量,以及你希望将图样放在哪一个网格区域中。此外,gridspec 的功能更强大,你也可以选择它来实现这个功能。

from pylab import *
import matplotlib.gridspec as gridspec

G = gridspec.GridSpec(3, 3)

axes_1 = subplot(G[0, :])
xticks([]), yticks([])
text(0.5,0.5, 'Axes 1',ha='center',va='center',size=24,alpha=.5)

axes_2 = subplot(G[1,:-1])
xticks([]), yticks([])
text(0.5,0.5, 'Axes 2',ha='center',va='center',size=24,alpha=.5)

axes_3 = subplot(G[1:, -1])
xticks([]), yticks([])
text(0.5,0.5, 'Axes 3',ha='center',va='center',size=24,alpha=.5)

axes_4 = subplot(G[-1,0])
xticks([]), yticks([])
text(0.5,0.5, 'Axes 4',ha='center',va='center',size=24,alpha=.5)

axes_5 = subplot(G[-1,-2])
xticks([]), yticks([])
text(0.5,0.5, 'Axes 5',ha='center',va='center',size=24,alpha=.5)

#plt.savefig('../figures/gridspec.png', dpi=64)
show()

效果如图: 

坐标轴

坐标轴和子图功能类似,不过它可以放在图像的任意位置。因此,如果你希望在一副图中绘制一个小图,就可以用这个功能。

from pylab import *

axes([0.1,0.1,.8,.8])
xticks([]), yticks([])
text(0.6,0.6, 'axes([0.1,0.1,.8,.8])',ha='center',va='center',size=20,alpha=.5)

axes([0.2,0.2,.3,.3])
xticks([]), yticks([])
text(0.5,0.5, 'axes([0.2,0.2,.3,.3])',ha='center',va='center',size=16,alpha=.5)

plt.savefig("../figures/axes.png",dpi=64)
show()

效果如图:

axes([0.1,0.1,.5,.5])
xticks([]), yticks([])
text(0.1,0.1, 'axes([0.1,0.1,.5,.5])',ha='left',va='center',size=16,alpha=.5)

axes([0.2,0.2,.5,.5])
xticks([]), yticks([])
text(0.1,0.1, 'axes([0.2,0.2,.5,.5])',ha='left',va='center',size=16,alpha=.5)

axes([0.3,0.3,.5,.5])
xticks([]), yticks([])
text(0.1,0.1, 'axes([0.3,0.3,.5,.5])',ha='left',va='center',size=16,alpha=.5)

axes([0.4,0.4,.5,.5])
xticks([]), yticks([])
text(0.1,0.1, 'axes([0.4,0.4,.5,.5])',ha='left',va='center',size=16,alpha=.5)

# plt.savefig("../figures/axes-2.png",dpi=64)
show()

效果如图:

记号

良好的记号是图像的重要组成部分。Matplotlib 里的记号系统里的各个细节都是可以由用户个性化配置的。你可以用 Tick Locators 来指定在那些位置放置记号,用 Tick Formatters 来调整记号的样式。主要和次要的记号可以以不同的方式呈现。默认情况下,每一个次要的记号都是隐藏的,也就是说,默认情况下的次要记号列表是空的——NullLocator。

Tick Locators

下面有为不同需求设计的一些 Locators。

类型说明
NullLocatorNo ticks.

IndexLocatorPlace a tick on every multiple of some base number of points plotted.

FixedLocatorTick locations are fixed.

LinearLocatorDetermine the tick locations.

MultipleLocatorSet a tick on every integer that is multiple of some base.

AutoLocatorSelect no more than n intervals at nice locations.

LogLocatorDetermine the tick locations for log axes.

这些 Locators 都是 matplotlib.ticker.Locator 的子类,你可以据此定义自己的 Locator。以日期为 ticks 特别复杂,因此 Matplotlib 提供了 matplotlib.dates 来实现这一功能。

其他类型的图

接下来的内容是练习。请运用你学到的知识,从提供的代码开始,实现配图所示的效果。具体的答案可以点击配图下载。

普通图


from pylab import *

n = 256
X = np.linspace(-np.pi,np.pi,n,endpoint=True)
Y = np.sin(2*X)

plot (X, Y+1, color='blue', alpha=1.00)
plot (X, Y-1, color='blue', alpha=1.00)
show()

散点图


from pylab import *

n = 1024
X = np.random.normal(0,1,n)
Y = np.random.normal(0,1,n)

scatter(X,Y)
show()

 效果如图:

条形图


from pylab import *

n = 12
X = np.arange(n)
Y1 = (1-X/float(n)) * np.random.uniform(0.5,1.0,n)
Y2 = (1-X/float(n)) * np.random.uniform(0.5,1.0,n)

bar(X, +Y1, facecolor='#9999ff', edgecolor='white')
bar(X, -Y2, facecolor='#ff9999', edgecolor='white')

for x,y in zip(X,Y1):
    text(x+0.4, y+0.05, '%.2f' % y, ha='center', va= 'bottom')

ylim(-1.25,+1.25)
show()

等高线图


from pylab import *

def f(x,y): return (1-x/2+x**5+y**3)*np.exp(-x**2-y**2)

n = 256
x = np.linspace(-3,3,n)
y = np.linspace(-3,3,n)
X,Y = np.meshgrid(x,y)

contourf(X, Y, f(X,Y), 8, alpha=.75, cmap='jet')
C = contour(X, Y, f(X,Y), 8, colors='black', linewidth=.5)
show()

灰度图(Imshow)

from pylab import *

def f(x,y): return (1-x/2+x**5+y**3)*np.exp(-x**2-y**2)

n = 10
x = np.linspace(-3,3,4*n)
y = np.linspace(-3,3,3*n)
X,Y = np.meshgrid(x,y)
imshow(f(X,Y)), show()

饼状图

from pylab import *

n = 20
Z = np.random.uniform(0,1,n)
pie(Z), show()

量场图(Quiver Plots)

from pylab import *

n = 8
X,Y = np.mgrid[0:n,0:n]
quiver(X,Y), show()

网格

from pylab import *

axes = gca()
axes.set_xlim(0,4)
axes.set_ylim(0,3)
axes.set_xticklabels([])
axes.set_yticklabels([])

show()

多重网格

from pylab import *

subplot(2,2,1)
subplot(2,2,3)
subplot(2,2,4)

show()

极轴图

from pylab import *

axes([0,0,1,1])

N = 20
theta = np.arange(0.0, 2*np.pi, 2*np.pi/N)
radii = 10*np.random.rand(N)
width = np.pi/4*np.random.rand(N)
bars = bar(theta, radii, width=width, bottom=0.0)

for r,bar in zip(radii, bars):
    bar.set_facecolor( cm.jet(r/10.))
    bar.set_alpha(0.5)

show()

3D 图

from pylab import *
from mpl_toolkits.mplot3d import Axes3D

fig = figure()
ax = Axes3D(fig)
X = np.arange(-4, 4, 0.25)
Y = np.arange(-4, 4, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)

ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='hot')

show()

手稿图

import numpy as np
import matplotlib.pyplot as plt

eqs = []
eqs.append((r"$W^{3\beta}_{\delta_1 \rho_1 \sigma_2} = U^{3\beta}_{\delta_1 \rho_1} + \frac{1}{8 \pi 2} \int^{\alpha_2}_{\alpha_2} d \alpha^\prime_2 \left[\frac{ U^{2\beta}_{\delta_1 \rho_1} - \alpha^\prime_2U^{1\beta}_{\rho_1 \sigma_2} }{U^{0\beta}_{\rho_1 \sigma_2}}\right]$"))
eqs.append((r"$\frac{d\rho}{d t} + \rho \vec{v}\cdot\nabla\vec{v} = -\nabla p + \mu\nabla^2 \vec{v} + \rho \vec{g}$"))
eqs.append((r"$\int_{-\infty}^\infty e^{-x^2}dx=\sqrt{\pi}$"))
eqs.append((r"$E = mc^2 = \sqrt{{m_0}^2c^4 + p^2c^2}$"))
eqs.append((r"$F_G = G\frac{m_1m_2}{r^2}$"))


plt.axes([0.025,0.025,0.95,0.95])

for i in range(24):
    index = np.random.randint(0,len(eqs))
    eq = eqs[index]
    size = np.random.uniform(12,32)
    x,y = np.random.uniform(0,1,2)
    alpha = np.random.uniform(0.25,.75)
    plt.text(x, y, eq, ha='center', va='center', color="#11557c", alpha=alpha,
             transform=plt.gca().transAxes, fontsize=size, clip_on=True)

plt.xticks([]), plt.yticks([])
# savefig('../figures/text_ex.png',dpi=48)
plt.show()

  • 12
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高亚奇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值