Python—Matplotlib基础学习
简介
使用指南
导入
import matplotlib.pyplot as plt
import numpy as np #matplotlib常与numpy库搭配使用
在这里举个简单的例子:
用坐标轴创建图形最简单的方法是使用pyplot.subplots
。我们可以用 Axes.plot
绘制坐标轴上的一些数据:
fig,ax = plt.subplots() #创建一个包含单个坐标轴的图形。
ax.plot([1,2,3,4],[1,4,2,3]) #在坐标轴上绘制一些数据
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/15/7384d398ec67aafa2de878a4c7ff9413.png)
Figure的组成
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/15/7297f12faa0667f3706318353082da47.png)
Pyplot教程
pyplot简介
matplotlib.pyplot
是使matplotlib像MATLAB一样工作的函数的集合。每个pyplot
功能都会对图形进行一些更改:例如,创建图形,在图形中创建绘图区域,在绘图区域中绘制一些线条,用标签装饰绘图等。
使用pyplot生成可视化的效果非常快:
plt.plot([1,2,3,4])
plt.ylabel('num')
plt.show()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/15/7de3260fe8f8ea7e78c0e32906e6d4d5.png)
关于上面这个示例中,x轴范围0-3,y轴范围1-4,原因是如果你向Plot传递单个列表或数组,那么matplotlib假定它是y轴的序列,并自动为你生成从0开始的x值
plot的样式
对于每对x,y参数,都有一个可选的第三个参数,它是表示图的颜色和线型的格式字符串。格式字符串的字母和符号来自MATLAB,您将颜色字符串与线型字符串连接在一起。默认格式字符串是“ b-”,这是一条蓝色实线。
例如,要用红色圆圈绘制以上内容:
plt.plot([1,2,3,4],[1,4,9,16],'ro') #'r':red,'o':点
plt.axis([0,6,0,20]) #确定坐标轴的范围,x轴0-6,y轴0-20
plt.show()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/15/db2a3f7ecb19275e460762ead8cffa7d.png)
格式字符串由颜色,标记物和线条组成
fmt = '[marker][line][color]' #其他组合也是允许的,例如[color][marker][line]
每一个参数都是可选的。
Markers(标记物)
字符 | 描述 |
---|---|
'.' |
点标记 |
',' |
像素标记 |
'o' |
圆圈标记 |
'v' |
triangle_down标记 |
'^' |
三角形标记 |
'<' |
triangle_left标记 |
'>' |
triangle_right标记 |
'1' |
tri_down标记 |
'2' |
tri_up标记 |
'3' |
tri_left标记 |
'4' |
tri_right标记 |
's' |
方形标记 |
'p' |
五边形标记 |
'*' |
星标 |
'h' |
六角形标记 |
'H' |
六角标记 |
'+' |
加号 |
'x' |
X标记 |
'D' |
钻石笔 |
'd' |
thin_diamond标记 |
`' | '` |
'_' |
标记线 |
line(线形)
字符 | 描述 |
---|---|
'-' |
实线样式 |
'--' |
虚线样式 |
'-.' |
点划线样式 |
':' |
虚线样式 |
color(颜色)
字符 | 颜色 |
---|---|
'b' |
蓝色 |
'g' |
绿色 |
'r' |
红 |
'c' |
青色 |
'm' |
品红 |
'y' |
黄色 |
'k' |
黑色 |
'w' |
白色 |
如何参数中只有颜色这一选择项,则可以使用任何matplotlib.colors
规格,例如全名(‘green')或十六进制表示('#008000')
处理多个图形和轴
在下面这个例子中一个figure下创建了两个子图:
import matplotlib.pyplot as plt
import numpy as np
def f(t):
return np.exp(-t) * np.cos(2*np.pi*t)
t1 = np.arange(0.0, 5.0, 0.1)
t2 = np.arange(0.0, 5.0, 0.02)
plt.figure()
plt.subplot(211) #211: 2行1列的第一个子图
plt.plot(t1, f(t1), 'bo', t2, f(t2), 'k') #在其一子图上绘制了两个图形
plt.subplot(212) #212: 2行1列的第二个子图
plt.plot(t2, np.cos(2*np.pi*t2), 'r--')
plt.show()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/17/32e0260e251fb0ddf0e72d5ba651fd2b.png)
同样:可以创建多个figure
plt.figure(1) # 第一个图形
plt.subplot(211) # 第一个图形中的第一个子图
plt.plot([1, 2, 3])
plt.subplot(212) # 第一个图形中的第二个子图
plt.plot([4, 5, 6])
plt.figure(2) # 第二个图形
plt.plot([4, 5, 6]) # 默认创建一个 subplot(111)
plt.figure(1) # 此时figure(1)仍然存在,并且此时subplot(212)是figure(1)当前使用
plt.subplot(211) # 使得subplot(211)是figure(1)当前使用
plt.title('Easy as 1, 2, 3') # 声明subplot(211)的标题
plt.show()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/17/74788e34b8c8f82e96ad774108a9e433.png)
为图形添加文字说明
annotate
方法提供了注释方式
参数:xy表示注释的位置,xytext是文本的位置。这两个参数都是(x,y)元组
ax = plt.subplot(111)
t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2*np.pi*t)
line, = plt.plot(t, s, lw=2)
plt.annotate('local max', xy=(2, 1), xytext=(3, 1.5),
arrowprops=dict(facecolor='black', shrink=0.05),
)
plt.ylim(-2, 2) #规定y值范围(-2,2)
plt.show()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/20/19ab7b6707f8b7acd6dce6401fc2750c.png)
图片教程
利用matplotlib处理图片
导入
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
将图像数据导入到NumPy数组
这是我们要处理的图片:
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/20/eca78bc2a4cd3044e704e56767078adf.png)
让我们打开并输出它:
img = mpimg.imread('C:\\Users\\wangz\\Desktop\\Pic.png') #图片路径
print(img)
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/20/b92eba8f96442abb65015ae5862c0264.png)
将NumPy数组绘制成图像
imshow()
函数将numpy数组中的数据渲染成图像。
imgplot = plt.imshow(img)
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/20/d876abea0674df9600ae45f3e725bf76.png)
伪彩色应用于图像
伪彩色是增强对比度和更轻松地可视化数据的有用工具。
伪彩色仅与单通道,灰度,发光度图像有关。我们目前有一个RGB图像。由于R,G和B都是相似的(请参见上方或数据中的内容),我们可以选择一个数据通道:
lum_img = img[:,:,0] #数组切片,更多相关切片请看个人博客关于NumPy学习
plt.imshow(lum_img)
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/20/f81bafc9654111311ebc0f39d0108936.png)
同样,对于亮度图像,将应用默认的颜色图(又名查找表:LUT)。默认名称为viridis,也有其他可供选择:
plt.imshow(lum_img,cmap="hot") #暖色调显示
您还可以使用
set_cmap()
方法更改现有打印对象上的颜色图
imgplot = plt.imshow(lum_img)
imgplot.set_cmap('nipy_spectral')
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/20/d14d93979e5c002dede7d045f745d30b.png)
可以通过添加颜色条来了解颜色代表什么价值,可通过matplotlib.pyplot.colorbar()函数实现
imgplot = plt.imshow(lum_img)
plt.colorbar()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/20/dfdb2bb41d08ec8de061672881d7630f.png)
如果你想增强图像的对比度,或扩大特定区域的对比度,同时牺牲颜色变化不大或无关紧要的细节。直方图是个好工具。要创建图像数据的直方图,我们使用
hist()
函数。
plt.hist(lum_img.ravel(),bins=256,range=(0.0,1.0),fc='k',ec='k')
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/20/add5f57e923c19a1ee1d71fafbfa2daa.png)
在获取图像的主要的区域[0.0,0.7]后 (如直方图所示),
我们可以通过传递clim参数或者调用set_clim()方法修改图像对比度
imgplot = plt.imshow(lum_img,clim=(0.0,0.7))
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/20/d3cf83c9688e336ab13dd9ce35dd1d44.png)
Artist教程
使用Artist对象在画布上渲染
matplotlib API有三层。
-
matplotlib.backend_bases.FigureCanvas是图形被绘制到的区域
-
matplotlib.backend_bases.Renderer是知道如何在FigureCanvas上绘制的对象
-
matplotlib.artist.Artist是知道如何使用渲染器在画布上作画的对象。
FigureCanvas和Renderer处理与用户界面工具包(如wxPython)或绘图语言(如PostScript®)对话的所有细节,而Artist处理所有高层构造,如表示和布局图形、文本和线条。
引言
有两种类型的Artist: primitives和containers。
primitives代表了我们想要在画布上绘制的标准图形对象:Line2D
, Rectangle
, Text
, AxesImage
等;
containers是放置它们的地方 (Axis
, Axes
and Figure
)。
标准用法是创建一个Figure实例,使用Figure创建一个或多个轴或子图实例,并使用Axes实例方法创建primitives。
在下面的示例中,我们使用
matplotlib.pyplot.figure()
创建一个Figure实例.
fig = plt.figure()
ax = fig.add_subplot(2,1,1)
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/21/1c272f0ea6f2440708d470c7c08984bf.png)
如果要在任意位置创建Axes,只需使用add_axes()方法
参数:[left,bottom,width,height]
fig2 = plt.figure()
ax2 = fig2.add_axes([0,0,0.7,0.3])
ax3 = fig2.add_axes([1,1,0.7,0.3])
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/21/d0ac5fad59709d82828ccc99530f100c.png)
自定义对象
每个matplotlib的Artist对象具有以下属性
属性 | 描述 |
---|---|
alpha | 透明度 - 范围0-1 |
animated | 用于促进动画绘制的布尔值 |
axes | Artist对象存在的轴,可能没有 |
clip_box | 剪辑Artist的边界框 |
clip_on | 是否开启剪辑 |
clip_path | Artist被剪辑的路径 |
contains | 一个选择函数,用于测试Artist是否包含选择的点 |
figure | Artist存在的figure示例,可能没有 |
label | 文本标签(例如:用于自动标签) |
picker | 控制对象选择的python对象 |
transform | 转换 |
visible | 是否应该绘制Artist的布尔值 |
zorder | 决定绘图顺序的数字 |
rasterized | 布尔值;将向量转换为光栅图形 (用于压缩和eps透明度) |
每个属性都可以通过老式的setter或getter访问。
例如,将当前的alpha乘以一半:
a = o.get_alpha()
o.set_alpha(0.5*a)
如果要一次设置多个属性,则还可以将set方法与关键字参数一起使用
例如:
o.set(alpha=0.5,zorder=2)
如果你是在python shell上进行交互工作,检查Artist属性的便捷方法是使用matplotlib.artist.getp()
函数
该函数列出了属性及其值。
这也适用于从Artist派生的类,例如Figure和Rectangle。
对于创建的figure对象,使用上述命令:
#可以自己创建一个figure实例查看
matplotlib.artist.getp(fig.patch)
#结果
agg_filter = None
alpha = None
animated = False
antialiased or aa = False
bbox = Bbox(x0=0.0, y0=0.0, x1=1.0, y1=1.0)
capstyle = butt
children = []
clip_box = None
clip_on = True
clip_path = None
contains = None
data_transform = BboxTransformTo( TransformedBbox( Bbox...
edgecolor or ec = (1.0, 1.0, 1.0, 0.0)
extents = Bbox(x0=0.0, y0=0.0, x1=432.0, y1=288.0)
facecolor or fc = (1.0, 1.0, 1.0, 0.0)
figure = Figure(432x288)
fill = True
gid = None
hatch = None
height = 1
in_layout = False
joinstyle = miter
label =
linestyle or ls = solid
linewidth or lw = 0.0
patch_transform = CompositeGenericTransform( BboxTransformTo( ...
path = Path(array([[0., 0.], [1., 0.], [1.,...
path_effects = []
picker = None
rasterized = None
sketch_params = None
snap = None
transform = CompositeGenericTransform( CompositeGenericTra...
transformed_clip_path_and_affine = (None, None)
url = None
verts = [[ 0. 0.] [432. 0.] [432. 288.] [ 0. 288....
visible = True
width = 1
window_extent = Bbox(x0=0.0, y0=0.0, x1=432.0, y1=288.0)
x = 0
xy = (0, 0)
y = 0
zorder = 1
更多关于给定对象的属性列表可查看 matplotlib.artist
对象容器
既然我们知道了如何检查和设置要配置的给定对象的属性,那么我们需要知道如何获取该对象。
在本节中,我们将回顾各种容器对象存储您想要获取的Artist的位置。
Figure容器
顶层容器Artist是matplotlib.figure.Figure
,它包含图中的所有内容。图形的背景是一个矩形,存储在figure .patch中。
当你在途中添加子图(add_subplot()
)和轴(add_axes()
)时,它们将被附加到Figure.axes
,这些也有创建它们的方法返回
举个栗子
fig = plt.figure()
ax1 = fig.add_subplot(211) #添加一个子图
ax2 = fig.add_axes([0.1,0.1,0.7,0.3])#添加一个新轴,参数分别是[left,bottom,width,height]
ax1
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/22/6301a5c1d03690a06069cab845adcebe.png)
print(fig.axes)
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/22/845809658b4ccf59f69abc810e486494.png)
由于该图保留了“当前轴”(请参见Figure.gca
和 Figure.sca
)的概念 以支持pylab / pyplot状态机,66因此您不应直接从轴列表中插入或删除轴,而应使用 add_subplot()
和 add_axes()
方法插入,以及 delaxes()
方法删除。但是,您可以自由地遍历轴列表或对其进行索引以访问Axes
要自定义的实例。
这是一个打开所有轴网格的示例:
for ax in fig.axes:
ax.grid(True)
图形还有自己的文本、线条、形状和图像,您可以使用它们直接添加primitives。图形的默认坐标系统将简单地以像素为单位(这通常不是你想要的),但是你可以通过设置你添加到图形中的Artist的transform属性来控制它。
更有用的是“图形坐标”,其中(0,0)是图形的左下角,而(1,1)是图形的右上角,可以通过将Artist
变换设置为来获得fig.transFigure
:
import matplotlib.lines as lines
fig = plt.figure()
l1 = lines.Line2D([0, 1], [0, 1], transform=fig.transFigure, figure=fig)
l2 = lines.Line2D([0, 1], [1, 0], transform=fig.transFigure, figure=fig)
fig.lines.extend([l1, l2])
plt.show()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/22/6c744e039bd0c8a201ff28664b0440dc.png)
下面是该图包含的Artist的摘要
图形属性 | 描述 |
---|---|
axes | 轴的实例 (包括子图) |
patch | 矩形背景 |
images | 图形图像形状列表-有用的原始像素显示 |
legends | 图像图例(和Axes.legends不同) |
lines | 图像Line2D实例(很少使用,看Axes.lines) |
patches | 图像的形状(很少使用,看 Axes.patches) |
texts | 图像文本 |
Axes容器
matplotlib.axes.Axes是matplotlib的中心——它包含图形中使用的绝大多数美工,以及用于创建和添加这些美工到自身的许多辅助方法,以及用于访问和定制其中包含的美工的辅助方法。
和图一样,它包含了一个 Patch
,在笛卡尔坐标系中是一个矩形,在极坐标中是一个圆;这个patch决定了作图区域的形状、背景和边界:
ax = fig.add_subplot(111)
rect = ax.patch # 一个矩形实例
rect.set_facecolor('green') #设置背景颜色为绿色
当你调用绘图方法,例如规范化 plot()
并且传入数组或列表的值为参数时,该方法将创建一个matplotlib.lines.Line2D()
实例,使用作为关键字参数传递的所有Line2D属性更新该line,并将该line添加到Axes.lines容器,并返回给您:
x,y = np.random.rand(2,100)
line,=ax.plot(x,y,'-',color='blue',linewidth=2)
plot返回的是一个lines列表,因为你可以传入多个x,y来绘制。
例如:
print(ax.lines)
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/23/0fbc0abe28431d8b944b25ee0dd0492e.png)
同样,创建patches的方法,像bar()
创建了矩阵列表,会添加patches到Axes.patches列表中
fig = plt.figure()
ax = fig.add_subplot(111)
n,bins,rectangles = ax.hist(np.random.randn(1000),50)
print(rectangles)
print(len(ax.patches))
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/23/7906d217c1f3663dee47e110d9727534.png)
有很多axis辅助方法用于创建primitive Artists并将它们添加到各自的容器中。
下面的表格总结了他们的一个小样例,他们创造的Artist类型,以及把他们储存在哪里
Helper method | Artist | Container |
---|---|---|
ax.annotate - text annotations | Annotate(注释) | ax.texts |
ax.bar - bar charts | Rectangle | ax.patches |
ax.errorbar - error bar plots | Line2D and Rectangle | ax.lines and ax.patches |
ax.fill - shared area | Polygon(多边形) | ax.patches |
ax.hist - histograms | Rectangle | ax.patches |
ax.imshow - image data | AxesImage | ax.images |
ax.legend - axes legends | Legend(图例) | ax.legends |
ax.plot - xy plots | Line2D | ax.lines |
ax.scatter - scatter charts | PolygonCollection (多边形集合) | ax.collections |
ax.text - text | Text | ax.texts |
下面这个是Axes容器的Artist对象的概括:
Axes attribute | Description |
---|---|
artists | Artist 实例的列表 |
patch | Axes的背景的矩阵实例的列表 |
collections | 集合实例的列表 |
images | 轴图像的列表 |
legends | 图例实例的列表 |
lines | Line2D实例的列表 |
patches | Patch实例列表 |
texts | 文本实例列表 |
xaxis | matplotlib.axis.XAxis实例 |
yaxis | matplotlib.axis.YAxis实例 |
Axis容器
matplotlib.axis.Axis
实例处理刻度线、网格线、刻度标签和axis标签的绘制。
每个Axis对象包含一个label属性(这是pyplot在调用xlabel和ylabel时修改的内容)以及一个主刻度和次要刻度列表。这些刻度是axis.XTick
和axis.YTick
的实例,其中包含呈现刻度和刻度标签的actual line和文本primitives。
因为刻度是根据需要动态创建的(例如,在移动和缩放时),所以应该通过它们的accessor方法axis.Axis.get_major_ticks
and axis.Axis.get_minor_ticks
访问主要刻度和次要刻度的列表。尽管刻度包含了所有的primitives,并且将在下面覆盖,Axis实例有accessor方法返回刻度行,刻度标签,刻度位置等:
fig,ax = plt.subplots()
axis = ax.xaxis
axis.get_ticklocs()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/23/7803d9942ea0d9e48e135acbf07bf329.png)
axis.get_ticklabels()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/23/6ac56f788e584febaf1cf95e323a9df8.png)
请注意,刻度线是标签的两倍,因为默认情况下,顶部和底部有刻度线,但x轴下方仅是刻度线;但是,可以自定义。
axis.get_ticklines()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/23/f6a8d7ad3ab96cd87247561467f3ac1e.png)
以下是的一些有用的Axis
的accessor方法的摘要 (这些访问器有相应的setters,例如 set_major_formatter()
)
Accessor 方法 | 描述 |
---|---|
get_scale | axis的比例:如“对数”或“线性” |
get_view_interval | The interval instance of the axis view limits |
get_data_interval | The interval instance of the axis data limits |
get_gridlines | 用于轴的网格线列表 |
get_label | 轴的标签 -文本实例 |
get_ticklabels | 一个文本实例列表-keyword minor=True|False |
get_ticklines | Line2D实例列表- keyword minor=True|False |
get_ticklocs | 刻度位置- keyword minor=True|False |
get_major_locator | 主要刻度的 ticker.Locator 实例 |
get_major_formatter | 主要刻度的 ticker.Formatter 实例 |
get_minor_locator | 次要刻度的 ticker.Locator 实例 |
get_minor_formatter | 次要刻度的ticker.Formatter 实例 |
get_major_ticks | 主要刻度的实例列表 |
get_minor_ticks | 次要刻度的实例列表 |
grid | 为主要刻度或次要刻度打开或关闭网格 |
举个栗子
可以自定义轴和刻度属性
fig = plt.figure()
rect = fig.patch #矩形实例
rect.set_facecolor('lightgoldenrodyellow')
ax1 = fig.add_axes([0.1,0.3,0.4,0.4])
rect = ax1.patch
rect.set_facecolor('lightslategray')
for label in ax1.xaxis.get_ticklabels():
#label是一个文本实例
label.set_color('red')
label.set_rotation(45)
label.set_fontsize(16)
for line in ax1.yaxis.get_ticklines():
#line是一个Line2D实例
line.set_color('green')
line.set_markersize(25)
line.set_markeredgewidth(3)
plt.show()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/23/1ab9b44582b36347143958a8f37dd3e4.png)
Tick容器
matplotlib.axis.Tick
是从Figure
到 Axes
再到 Axis
最后到 Tick
的容器对象。
Tick包含了刻度和网格线,以及上下刻度的标签实例。
这些中的每一个都可以作为Tick的属性直接访问
Tick属性 | 描述 |
---|---|
tick1line | Line2D 实例 |
tick2line | Line2D 实例 |
gridline | Line2D 实例 |
label1 | 文本实例 |
label2 | 文本实例 |
举个栗子
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
np.random.seed(19680801)
fig, ax = plt.subplots()
ax.plot(100*np.random.rand(20))
ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.2f'))
ax.yaxis.set_tick_params(which='major', labelcolor='green',
labelleft=False, labelright=True)
plt.show()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/23/89cdbacf05816030f8066139b9f4b274.png)
图例指南
在Matplotlib中灵活地生成图例
在阅读这一章节前请熟悉关于 legend()
的讲解
一些常用用语:
- 图例条目
图例由一个或多个图例条目组成。一项仅由一个键和一个标签组成。
- 图例键
每个图例标签左侧的彩色/图案标记。
- 图例标签
描述键所代表的文本。
- 图例句柄
用于在图例中生成适当条目的原始对象。
(也不知道这样子翻译的对不对,先放一下英文版,等明白了再回来改)
-
legend entry
A legend is made up of one or more legend entries. An entry is made up of exactly one key and one label.
-
legend key
The colored/patterned marker to the left of each legend label.
-
legend label
The text which describes the handle represented by the key.
-
legend handle
The original object which is used to generate an appropriate entry in the legend.
控制图例条目
调用无参的legend()
会自动获取图例handles和它们的关联标签
这个功能等效于:
#等效于ax.legend()
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels)
该get_legend_handles_labels()
函数返回存在于轴上的handles或artists,可用于生成结果图例的条目——(要注意的是,并非所有的Artist都可以添加到图例中,这种情况下“proxy(代理)”就需要创建)
带有空字符串作为标签或标签以“ _”开头的artists将被忽略。
要完全控制添加到图例中的内容,通常可以将适当的handles直接传递给legend()
:
line_up, = plt.plot([1,2,3],label='Line 2')
line_down, = plt.plot([3,2,1],label='Line 1')
plt.legend(handles=[line_up,line_down])
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/24/e39feee687989169c388c49c688e344b.png)
在某些情况下,无法设置handles的标签,因此可以将标签列表传递给legend()
:
line_up, = plt.plot([1,2,3],label='Line 2')
line_down, = plt.plot([3,2,1],label='Line 1')
plt.legend([line_up,line_down],['Line Up','Line Down'])
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/24/103fee29e96cd5aa9e3627150d5503c0.png)
创建专门用于添加图例的Artist(又名代理艺术家)
并不是所有的handles都能自动转换为图例条目,所以总是有必要创建一个artist。图例handles不必存在于图形或轴上才能使用。
import matplotlib.patches as mpatches
red_patch = mpatches.Patch(color='red',label='The red data')
plt.legend(handles=[red_patch])
plt.show()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/24/c5ac844fe89a9cd085597448e109685e.png)
有许多受支持的图例handles。除了创建颜色patch之外,我们还可以创建带有标记的线:
import matplotlib.lines as mlines
blue_line = mlines.Line2D([],[],color='blue',marker='*',markersize=15,label='Blue Star')
plt.legend(handles=[blue_line])
plt.show()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/24/9ac2fbb7519ce88a7bfcd4ead0e9e02a.png)
图例位置
图例的位置可以通过关键字loc来指定。请在官方文档legend()
查看更多细节
bbox_to_anchor关键字为手动图例的位置提供了很大程度的控制。
例如,如果你想让坐标轴图例位于图形的右上角而不是坐标轴的角落,只需指定角落的位置和该位置的坐标系统:
plt.plot([1,2,3],[3,2,1],label='Line')
#plt.gcf().transFigure获取当前figure
plt.legend(bbox_to_anchor=(1, 1),
bbox_transform=plt.gcf().transFigure)
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/24/35c5d4cbbbc48b25091c67bf521e5f47.png)
更多自定义图例放置的例子:
plt.subplot(211)
plt.plot([1,2,3],label="Line 1")
plt.plot([3,2,1],label="Line 2")
plt.legend(bbox_to_anchor=(0.,1.02,1.,.102),loc='lower left',ncol=2,mode="expand",borderaxespad=0.)
plt.subplot(223)
plt.plot([1,2,3],label="Line 1")
plt.plot([3,2,1],label="Line 2")
plt.legend(bbox_to_anchor=(1.05,1),loc='upper left',borderaxespad=0.)
plt.show()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/24/078bbd773ea2b5b201452c9aecb9cc7b.png)
同一个Axes下多个图例
手动将图例添加到当前Axes下:
line1, = plt.plot([1,2,3],label="Line 1",linestyle='--')
line2, = plt.plot([3,2,1],label="Line 2",linewidth=5)
#为第一条线创建图例
first_legend = plt.legend(handles=[line1],loc='upper right')
#将first_legend手动地添加到当前Axes
ax = plt.gca().add_artist(first_legend)
#为第二条线创建图例
plt.legend(handles=[line2],loc='lower right')
plt.show()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/24/cd0973359c7db5967e599c14527ace09.png)
图例Handlers
为了创建图例条目,handles被作为参数提供给适当的HandlerBase子类。
使用自定义Handlers的最简单示例是实例化一个现有的legend_handler.HandlerBase
的子类。
为了简单起见,我们选择legend_handler.HandlerLine2D
,它接受numpoints参数(numpoints也是legend()函数上的一个关键字)。
然后,我们可以将实例到Handler的映射作为关键字传递给legend。
from matplotlib.legend_handler import HandlerLine2D
line1, = plt.plot([3,2,1],marker='o',label='Line 1')
line2, = plt.plot([1,2,3],marker='o',label='Line 2')
plt.legend(handler_map={line1: HandlerLine2D(numpoints=3)})
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/24/90539974421a6fca5a2dc65692585ef8.png)
除了用于errorbar、stem plot和histograms等复杂图形类型的Handlers外,默认的handler_map还有一个特殊的元组handler(legend_handler.HandlerTuple
),它简单地为给定元组中的每个项绘制一个又一个的handles。
下面的例子演示了将两个图例键组合在一起:
from numpy.random import randn
z = randn(10)
red_dot, = plt.plot(z, "ro", markersize=15)
# 白色十字图案放在前五个数据上显示
white_cross, = plt.plot(z[:5], "w+", markeredgewidth=3, markersize=15)
plt.legend([red_dot, (red_dot, white_cross)], ["Attr A", "Attr A+B"])
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/24/2be1df404bf519ee9d238e605d88a29e.png)
legend_handler.HandlerTuple
类也可以用来为同一个条目分配几个图例键:
from matplotlib.legend_handler import HandlerLine2D, HandlerTuple
p1, = plt.plot([1, 2.5, 3], 'r-d')
p2, = plt.plot([3, 2, 1], 'k-o')
l = plt.legend([(p1, p2)], ['Two keys'], numpoints=1,
handler_map={tuple: HandlerTuple(ndivide=None)})
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/24/b040655da0667b35b05ba9e485cd997a.png)
实现自定义的图例handler
可以实现自定义handler来将任何handle转换为图例键(handles不一定需要是matplotlib artists对象)。
handler必须实现一个legend_artist方法,该方法为图例返回一个artist对象来使用。
legend_artist所需的签名,记录在legend_artist
中。
import matplotlib.patches as mpatches
class AnyObject:
pass
class AnyObjectHandler:
def legend_artist(self, legend, orig_handle, fontsize, handlebox):
x0, y0 = handlebox.xdescent, handlebox.ydescent
width, height = handlebox.width, handlebox.height
patch = mpatches.Rectangle([x0, y0], width, height, facecolor='red',
edgecolor='black', hatch='xx', lw=3,
transform=handlebox.get_transform())
handlebox.add_artist(patch)
return patch
plt.legend([AnyObject()], ['My first handler'],
handler_map={AnyObject: AnyObjectHandler()})
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/24/603d9465d641ab9fbc054b2be2210135.png)
或者,如果我们想要全局地接受AnyObject实例,而不需要一直手动设置handler_map关键字,我们可以用:
from matplotlib.legend import Legend
Legend.update_default_handler_map({AnyObject: AnyObjectHandler()})
虽然这里的功能很明显,但请记住,已经实现了许多handlers,并且您想要实现的可能已经很容易地通过现有的类实现了。
例如,产生椭圆的图例键,而不是矩形的:
from matplotlib.legend_handler import HandlerPatch
class HandlerEllipse(HandlerPatch):
def create_artists(self, legend, orig_handle,
xdescent, ydescent, width, height, fontsize, trans):
center = 0.5 * width - 0.5 * xdescent, 0.5 * height - 0.5 * ydescent
p = mpatches.Ellipse(xy=center, width=width + xdescent,
height=height + ydescent)
self.update_prop(p, orig_handle, legend)
p.set_transform(trans)
return [p]
c = mpatches.Circle((0.5, 0.5), 0.25, facecolor="green",
edgecolor="red", linewidth=3)
plt.gca().add_patch(c)
plt.legend([c], ["An ellipse, not a rectangle"],
handler_map={mpatches.Circle: HandlerEllipse()})
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/24/0068644f6e1423bf18d9aa56a5a08ae5.png)
使用GridSpec和其他函数自定义图形布局
以下函数或方法实现创建axes上的网格的组合
或许是创建figures和axes最主要的方式,与 matplotlib.pyplot.subplot()
类似,
但是同时创建并放置图形上的所有轴。更多详情请看matplotlib.figure.Figure.subplots
.
指定将放置子图的网格的几何形状。
需要设置网格的行数和列数。以及子图的布局参数可以调整
在给定的GridSpec上指定子图的位置
一个类似于subplot()
的辅助函数,但是使用基于0的索引并让subplot占用多个单元格。本文章没有介绍该函数。
导入
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
快速入门指南
使用
subplots()
非常简单。它返回一个Figure
实例和一个Axes
对象数组 。
fig1,f1_axes = plt.subplots(ncols=2,nrows=2,constrained_layout=True)
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/25/a84efe42f06c675b465982ed43f3c331.png)
对于像上面这样的简单用例,
gridspec
可能过于冗长。你必须分别创建图形和
GridSpec
实例,然后将gridspec实例的元素传递给add_subplot()
函数来创建axes对象。gridspec元素的访问方式通常与numpy数组相同。
fig2 = plt.figure(constrained_layout=True)
spec2 = gridspec.GridSpec(ncols=2,nrows=2,figure=fig2)
f2_ax1 = fig2.add_subplot(spec2[0,0])
f2_ax2 = fig2.add_subplot(spec2[0,1])
f2_ax3 = fig2.add_subplot(spec2[1,0])
f2_ax4 = fig2.add_subplot(spec2[1,1])
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/25/c8cfe4f0d45137f5ed014a638a7a928f.png)
可以看出和subplots()方式结果相同
gridspec的强大之处在于能够创建跨越行和列的子图。注意,NumPy slice Syntax 用于选择每个子图将占据的gridspec部分。
请注意,我们还使用了便捷方法Figure.add_gridspec
代替gridspec.GridSpec
,有可能为用户节省了导入时间,并使名称空间更整洁。
fig3 = plt.figure(constrained_layout=True)
gs = fig3.add_gridspec(3,3)
#关于切片可参考另一篇文章Numpy学习中的切片学习
f3_ax1 = fig3.add_subplot(gs[0,:])
f3_ax1.set_title('gs[0,:]')
f3_ax1 = fig3.add_subplot(gs[1,:-1])
f3_ax1.set_title('gs[1,:-1]')
f3_ax1 = fig3.add_subplot(gs[1:,-1])
f3_ax1.set_title('gs[1:,-1]')
f3_ax1 = fig3.add_subplot(gs[-1,0])
f3_ax1.set_title('gs[-1,0]')
f3_ax1 = fig3.add_subplot(gs[-1,-2])
f3_ax1.set_title('gs[-1,-2]')
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/25/1771e7f2c924222450b23f0d99d3d6de.png)
接下来展示的方法与上面的方法类似,
初始化统一的网格规范,然后使用numpy索引和切片为给定的子图分配多个“单元格”。
fig4 = plt.figure(constrained_layout=True)
spec4 = fig4.add_gridspec(ncols=2,nrows=2)
anno_opts = dict(xy=(0.5,0.5),xycoords='axes fraction',va='center',ha='center')
f4_ax1 = fig4.add_subplot(spec4[0,0])
f4_ax1.annotate('GridSpec[0,0]',**anno_opts)
fig4.add_subplot(spec4[0, 1]).annotate('GridSpec[0, 1:]', **anno_opts)
fig4.add_subplot(spec4[1, 0]).annotate('GridSpec[1:, 0]', **anno_opts)
fig4.add_subplot(spec4[1, 1]).annotate('GridSpec[1:, 1:]', **anno_opts)
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/25/03e448d6ea58673cb436c7f295203f59.png)
另一个方式是使用width_ratios和height_ratios参数。这些关键字参数是数字列表。
请注意,绝对值是没有意义的,只有它们的相对比率重要。这意味着width_ratio =[2, 4, 8]与width_ratio =[1, 2, 4]在同样宽的数字内相等。
fig5 = plt.figure(constrained_layout=True)
widths = [2,3,1.5]
heights = [1,3,2]
spec5 = fig5.add_gridspec(ncols=3,nrows=3,width_ratios=widths,height_ratios=heights)
for row in range(3):
for col in range(3):
ax = fig5.add_subplot(spec5[row,col])
label = 'Width: {}\nHeight: {}'.format(widths[col],heights[row])
ax.annotate(label,(0.1,0.5),xycoords='axes fraction',va='center')
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/25/41445b35cc1d0c8b71c98c482ba277cb.png)
学习使用width_ratio和height_ratio特别有用,因为函数
subplots()
在gridspec_kw参数中接受它们。为此,
GridSpec
接受的任何参数都可以通过gridspec_kw参数传递给subplots()
。此示例在不直接使用gridspec实例的情况下重新创建前面的图。
gs_kw = dict(width_ratios=widths,height_ratios=heights)
fig6,f6_axes = plt.subplots(ncols=3,nrows=3,constrained_layout=True,gridspec_kw=gs_kw)
for r,row in enumerate(f6_axes):
for c,ax in enumerate(row):
label = 'Width:{}\nHeight:{}'.format(widths[c],heights[r])
ax.annotate(label,(0.1,0.5),xycoords='axes fraction',va='center')
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/25/b918ee705c15a77a63e27fdc6b63fc80.png)
subplots和get_gridspec方法可以组合使用,因为有时使用subplots创建大多数子图,然后删除其中一些并组合它们更方便。
在这里,我们将最后一列中底部的两个轴组合起来创建一个布局。
fig7,f7_axs = plt.subplots(ncols=3,nrows=3)
gs = f7_axs[1,2].get_gridspec()
#删除[1,-1],[2,-1]的axes
for ax in f7_axs[1:,-1]:
ax.remove()
axbig = fig7.add_subplot(gs[1:,-1])
axbig.annotate('Big Axes \nGridSpec[1:, -1]', (0.1, 0.5),
xycoords='axes fraction', va='center')
#调整子图之间及其周围的填充。
fig7.tight_layout()
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/25/fd722ff151b1ec23501358966706ccfa.png)
精细调整Gridspec布局
当显示地使用GridSpec时,可以通过改变gridspec的参数来调整子图的布局
请注意,此选项与constrained_layout
或者 Figure.tight_layout
都不兼容,两者都通过调整子图大小以填充figure。
参数请看:matplotlib.gridspec.GridSpec
fig8 = plt.figure(constrained_layout=False)
gs1 = fig8.add_gridspec(nrows=3,ncols=3,left=0.05,right=0.48,wspace=0.05)
f8_ax1 = fig8.add_subplot(gs1[:-1,:])
f8_ax2 = fig8.add_subplot(gs1[-1,:-1])
f8_ax3 = fig8.add_subplot(gs1[-1,-1])
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/26/9af56c0dbbb846fa118121498dc6b478.png)
GridSpec之SubplotSpec
你可以通过SubplotSpec
创建GridSpec,在这种情况下,它的布局参数被设置为给定SubplotSpec的位置。
fig10 = plt.figure(constrained_layout=False)
gs0 = fig10.add_gridspec(1,2)
gs00 = gs0[0].subgridspec(2,3)
gs01 = gs0[1].subgridspec(3,2)
for i in range(2):
for j in range(3):
fig10.add_subplot(gs00[i,j])
fig10.add_subplot(gs01[j,i])
![](https://cdn.jsdelivr.net/gh/zangwhe/Image@main/2020/11/26/d9c94e19a272e34c530f3ae846026b68.png)
文章翻译自官方文档
如果你发现文章中出现的错误或者任何疑问欢迎在下方评论区评论
利益相关
更多资源关注公众号“北方向北”(扫描右侧二维码也可关注)
点个大拇指支持支持,感谢