首页 > 编程语言 >Python Matplotlib绘制柏拉图以及在ax.table上绘制矩形、直线、椭圆

Python Matplotlib绘制柏拉图以及在ax.table上绘制矩形、直线、椭圆

时间:2024-09-08 21:17:52浏览次数:8  
标签:plot plt Python Axes Matplotlib set fig ax 绘制

快速入门指南

官网

官方网址:Matplotlib — Visualization with Python

官方教程:Tutorials — Matplotlib 3.9.2 documentation

官方指南:Using Matplotlib — Matplotlib 3.9.2 documentation

官方示例:Examples — Matplotlib 3.9.2 documentation

官方API说明:API Reference — Matplotlib 3.9.2 documentation

plot type

Matplotlib支持很多种绘图类型:绘图类型 — Matplotlib 3.9.2 文档 --- Plot types — Matplotlib 3.9.2 documentation

基本概念

figure:Matplotlib在figure上绘制您的数据,可以理解为画板,每个图都可以包含一个或者多个axes。

axes:轴,理解为画布更合理,每个图都可以包含一个或多个轴,可以在轴上根据x-y坐标指定点。

创建带有figure和axes最简单的方式:

fig, ax = plt.subplots()             # Create a figure containing a single Axes.
ax.plot([1, 2, 3, 4], [1, 4, 2, 3])  # Plot some data on the Axes.
plt.show()                           # Show the figure.

Figure的组成部分

../../_images/anatomy.png
  • Title:Axes的标题
  • Minor tick:次要刻度
  • Major tick label:主要刻度标签
  • ylabel:Y轴标签
  • Major tick:主刻度
  • y Axis:Y轴
  • x Axis:X轴
  • xlabel:X轴标签
  • Minor tick label:次要刻度标签
  • Line:绘制线型图
  • Markers:散点图的点的形状和颜色
  • Grid:网格
  • Legend:图例
  • Axes:画板

Figure

Figure对所有axes有效,一组Artists(比如titlesfigure legendscolorbars等),以及嵌套subfigures。

通常,使用下面的方式创建一个figure:

fig = plt.figure()             # 一个空的figure, 没有axes
fig, ax = plt.subplots()       # 一个figure上有一个axes
fig, axs = plt.subplots(2, 2)  # 一个figure上有2x2个axes, 网格方式存放
# 一个figure上, 一个axes在左边, 两个axes在右边
fig, axs = plt.subplot_mosaic([['left', 'right_top'], ['left', 'right_bottom']])

Axes

语义上,Axes是axis的复数(Plural of axis)。

Matplotlib 使用 Axes 来指代包含数据、x 轴x-axis和 y 轴y-axis、刻度ticks、标签labels、标题titile等的绘图区域。另一个经常使用的术语是“subplot”,它指的是与其他 Axes 对象位于网格中的 Axes。

Axes是一个Artist粘贴在Figure上,该Figure包含绘制数据的区域,通常包括两个Axis 对象。

Axis提供刻度(tick)和刻度标签(tick label),以便为Axes中的数据提供刻度比例scales。

每一个Axes也有一个title(set_title())、x-label(set_xlabel)、y-label(set_ylabel())。

Axes 方法是绘图大部分内容(添加数据、控制轴比例axis scales和限制limits、添加标签labels等)的主要接口。

Axis

Axis用来设置scale、limits、ticks (the marks on the Axis) 、tick-labels (strings labeling the ticks)。

ticks的位置由Locator决定。

tick-labels由Formatter决定。

正确的LocatorFormatter的组合可以对刻度位置和标签进行非常精细的控制。

Figure、Axes、Axis、x-label、y-label的区别:

img

Artist

基本上,Figure上所有可见的东西(比如Figure、Axes、Axis)都是Artist。

包括Text对象、Line2D对象、Collection对象、Patch对象等。

当Figure渲染时,所有的Artist都作用到Canvas上。

大部分Artist绑定在Axes上,这些Artist不能被多个Axis共享,也不能从一个轴移动到另一个轴。

绘图函数输入数据类型

绘图函数需要 numpy.array 作为输入,或者可以传递给 numpy.asarray 的对象

大多数方法还将解析字符串可索引对象,如dictstructured numpy arraypandas.DataFrame

Matplotlib 允许您提供 datakey作为参数并生成图,传递对应于 xy 变量的字符串。

编码风格

隐式和显示创建

有三种方法可以使用Matplotlib:

  • 显示创建Figure和Axes,并在此基础上调用方法,面向对象方式(the "object-oriented (OO) style")。本教程中示例代码主要使用这种方式
  • 依赖pyplot隐式创建和管理Figures和Axes,并使用pyplot函数进行绘图,类似MatLib。
  • 此外,还可以在GUI程序中嵌套Matplotlib,比如Tkinter。

OO-Style方式举例:

x = np.linspace(0, 2, 100)  # Sample data.

# Note that even in the OO-style, we use `.pyplot.figure` to create the Figure.
fig, ax = plt.subplots(figsize=(5, 2.7), layout='constrained')
ax.plot(x, x, label='linear')  		# Plot some data on the Axes.
ax.plot(x, x**2, label='quadratic') # Plot more data on the Axes...
ax.plot(x, x**3, label='cubic')  	# ... and some more.
ax.set_xlabel('x label')  			# Add an x-label to the Axes.
ax.set_ylabel('y label')  			# Add a y-label to the Axes.
ax.set_title("Simple Plot")  		# Add a title to the Axes.
ax.legend()  						# Add a legend.

pyplot-style方式举例:

x = np.linspace(0, 2, 100)  # Sample data.

plt.figure(figsize=(5, 2.7), layout='constrained')
plt.plot(x, x, label='linear')  # Plot some data on the (implicit) Axes.
plt.plot(x, x**2, label='quadratic')  # etc.
plt.plot(x, x**3, label='cubic')
plt.xlabel('x label')
plt.ylabel('y label')
plt.title("Simple Plot")
plt.legend()

Styling Artists

大多数绘图方法都有Artists的样式选项,也可以从Artists的setter访问。

下面的示例,手动设置plot的Artists的color、linewidth、linestyle:

fig, ax = plt.subplots(figsize=(5, 2.7))
x = np.arange(len(data1))
ax.plot(x, np.cumsum(data1), color='blue', linewidth=3, linestyle='--')
l, = ax.plot(x, np.cumsum(data2), color='orange', linewidth=2)
l.set_linestyle(':')

颜色

大部分Artist都可以有灵活的color,一些Artist会采用多种颜色,比如散点图Scatter,标记的边沿和内部可以是不同的颜色。

线宽、线的风格、标记大小

Labelling plots

Axes labels and text

set_xlabelset_ylabelset_title用于在指定位置添加文本。也可以直接使用text将文本添加到图中。

在文本中使用数学表达式

Matplotlib 接受任何文本表达式中的 TeX 方程表达式。 例如,要在标题中编写表达式 σi=15 , 你可以编写一个由美元符号包围的 TeX 表达式:

ax.set_title(r'$\sigma_i=15$')

Annotations注释

我们还可以通过将指向 xy 的箭头连接到 xytext 处的一段文本来注释图上的点。

Legends图例

我们通常希望使用 Axes.legend 识别线条或标记:

fig, ax = plt.subplots(figsize=(5, 2.7))
ax.plot(np.arange(len(data1)), data1, label='data1')
ax.plot(np.arange(len(data2)), data2, label='data2')
ax.plot(np.arange(len(data3)), data3, 'd', label='data3')
ax.legend()
quick start

Axis scales and ticks

每个 Axes 都有两个(或三个)Axis 对象,分别表示 x 轴和 y 轴。它们控制轴的比例scales、tick locators和tick formatters。

scales

scales设置从数据值到沿轴的间距的映射。这在两个方向上发生,并被组合成一个变换,这就是 Matplotlib 从数据坐标映射到轴、图形或屏幕坐标的方式。

除了线性刻度之外,Matplotlib 还提供非线性刻度,例如对数刻度。

import matplotlib.pyplot as plt
import numpy as np

from matplotlib.ticker import FixedLocator, NullFormatter

# Fixing random state for reproducibility
np.random.seed(19680801)

# make up some data in the interval ]0, 1[
y = np.random.normal(loc=0.5, scale=0.4, size=1000)
y = y[(y > 0) & (y < 1)]
y.sort()
x = np.arange(len(y))

# plot with various axes scales
fig, axs = plt.subplots(nrows=1, ncols=2, figsize=(6, 4))
print (axs)
# linear
ax = axs[0]
ax.plot(x, y)
ax.set_yscale('linear')
ax.set_title('linear')
ax.grid(True)

# log
ax = axs[1]
ax.plot(x, y)
ax.set_yscale('log')
ax.set_title('log')
ax.grid(True)

plt.show()
image-20240823000021325

Tick locators and formatters

每个 Axis 都有一个tick locator和formatter,用于选择沿 Axis 对象放置刻度线的位置。一个简单的接口是 set_xticks

axs[1].set_xticks(np.arange(0, 100, 30), ['zero', '30', 'sixty', '90'])
axs[1].set_yticks([-1.5, 0, 1.5])  # note that we don't need to specify labels

不同的scale可以有不同的 locator和formatter,例如,上面的 log-scale 使用 LogLocatorLogFormatter

Plotting dates and strings

Matplotlib 可以处理绘制日期数组和字符串数组,以及浮点数。这些会根据需要获得特殊的 locators 和formatters。

对于日期:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.dates import ConciseDateFormatter

fig, ax = plt.subplots(figsize=(5, 2.7), layout='constrained')
dates = np.arange(np.datetime64('2021-11-15'), np.datetime64('2021-12-25'), np.timedelta64(1, 'h'))
data = np.cumsum(np.random.randn(len(dates)))
ax.plot(dates, data)
ax.xaxis.set_major_formatter(ConciseDateFormatter(ax.xaxis.get_major_locator()))
plt.show()
image-20240824113650703

对于字符串:

fig, ax = plt.subplots(figsize=(5, 2.7), layout='constrained')
categories = ['turnips', 'rutabaga', 'cucumber', 'pumpkins']
ax.bar(categories, np.random.rand(len(categories)))
quick start

关于分类绘图的一个警告是,某些解析文本文件的方法会返回字符串列表,即使字符串都表示数字或日期。如果您传递 1000 个字符串,Matplotlib 会认为您指的是 1000 个类别,并将在您的绘图中添加 1000 个刻度!这种情况还是比较常见。

Additional Axis objects

在一个图表中绘制不同量级的数据可能需要额外的 y 轴。可以通过使用 twinx 添加一个不可见的 x 轴和一个位于右侧的 y 轴的新轴来创建这样的轴(类似于 twiny)。

同样,也可以添加与主轴具有不同比例的secondary_xaxissecondary_yaxis,以不同的比例或单位表示数据。有关更多示例,请参阅 Secondary Axis

import matplotlib.pyplot as plt
import numpy as np

# Create some mock data
t = np.arange(0.01, 10.0, 0.01)
data1 = np.exp(t)
data2 = np.sin(2 * np.pi * t)

fig, ax1 = plt.subplots()

color = 'tab:red'
ax1.set_xlabel('time (s)')
ax1.set_ylabel('exp', color=color)
ax1.plot(t, data1, color=color)
ax1.tick_params(axis='y', labelcolor=color)

ax2 = ax1.twinx()  # instantiate a second Axes that shares the same x-axis

color = 'tab:blue'
ax2.set_ylabel('sin', color=color)  # we already handled the x-label with ax1
ax2.plot(t, data2, color=color)
ax2.tick_params(axis='y', labelcolor=color)

fig.tight_layout()  # otherwise the right y-label is slightly clipped
plt.show()
image-20240824114359887

Color mapped data

通常,我们希望在绘图中拥有由颜色图中的颜色表示的第三个维度。Matplotlib 有许多绘图类型可以做到这一点。

Colormaps

这些都是从 ScalarMappable 对象派生的 Artist 示例。它们都可以将 vminvmax 之间的线性映射设置为 cmap 指定的颜色图。

Matplotlib 有许多颜色图可供选择(在 Matplotlib 中选择颜色图),您可以制作自己的颜色图(在 Matplotlib 中创建颜色图)或作为第三方包下载。

Matplotlib提供的Colormap:在 Matplotlib 中选择颜色图 — Matplotlib 3.9.2 文档 --- Choosing Colormaps in Matplotlib — Matplotlib 3.9.2 documentation

Normalizations

有时我们希望数据到颜色图的非线性映射。为此,我们向 ScalarMappable 提供 norm 参数,而不是 vminvmax

颜色图归一化中显示了更多归一化。

Colorbars

NA

使用多个 Figures 和 Axes

可以通过多次调用 fig = plt.figure()fig2, ax = plt.subplots() 来打开多个 Figure。

通过保留对象引用,您可以将 Artists 添加到任一个 Figure。

可以通过多种方式添加多个Axes,但最基本的是上面使用的 plt.subplots()

也可以使用 subplot_mosaic 实现更复杂的布局,其中 Axes 对象跨列或跨行。

fig, axd = plt.subplot_mosaic([['upleft', 'right'], ['lowleft', 'right']], layout='constrained')
axd['upleft'].set_title('upleft')
axd['lowleft'].set_title('lowleft')
axd['right'].set_title('right')
upleft, right, lowleft

Matplotlib 具有相当复杂的轴排列工具:See Arranging multiple Axes in a Figure and Complex and semantic figure composition (subplot_mosaic).

常见问题

Why do I have so many ticks, and/or why are they out of order?

意外ticks行为的一个常见原因是传递字符串列表,而不是数字或日期时间对象。在读取CSV的文本文件时,这很容易发生,而不会引起通知。

Matplotlib 将字符串列表视为strings as categorical variables,默认情况下为每个类别放置一个勾号,并按照提供它们的顺序绘制它们。

解决方案是将字符串列表转换为数字或日期时间对象(通常为 np.asarray(numeric_strings, dtype='float')np.asarray(datetime_strings, dtype='datetime64[s]'))。

Save multiple plots to one pdf file

许多图像文件格式每个文件只能有一个图像,但某些格式支持多页文件。目前,Matplotlib 仅通过 backend_pdf 使用 pdf 或 pgf backends为 pdf 文件提供多页输出。

PdfPagesbackend_pgf。PdfPages 类。

Make room for tick labels

默认情况下,Matplotlib 在subplots周围使用固定的百分比边距。这可能会导致Label重叠或在figure边界处被截断。

有多种方法可以解决此问题:

Draw multiple y-axis scales

一个常见的需求是为左轴和右 y 轴设置两个刻度,这可以使用 twinx() 实现(目前不支持两个以上的刻度,尽管它在愿望列表中)。

Generate images without having a window appear

只需不调用 show,直接将图形保存为所需的格式即可:

import matplotlib.pyplot as plt
plt.plot([1, 2, 3])
plt.savefig('myfig.png')

Figures介绍

在查看 Matplotlib 可视化时,您几乎总是在查看放置在 Figure 上的 Artists

在下面的示例中,Figure是蓝色区域,并且通过add_subplotFigure添加了Axes的Aritist。

更复杂的可视化可以在Figure中添加多个Axes。Colorbars、Legends、Annotations以及Axes都可以添加多个Artists(例如 ax.plotax.imshow)。

fig = plt.figure(figsize=(4, 2), facecolor='lightskyblue', layout='constrained')
fig.suptitle('A nice Matplotlib Figure')
ax = fig.add_subplot()
ax.set_title('Axes', loc='left', fontstyle='oblique', fontsize='medium')
index-1

Viewing Figures

Notebooks and IDEs

如果使用的是Notebooks and IDEs,例如 Jupyter,那么它们有一个后端,可以在执行代码单元时渲染 Matplotlib Figure。

Standalone scripts and interactive use

如果用户使用的是带有窗口系统的客户端,则可以使用许多后端将图形呈现到屏幕上,通常使用 Python Qt、Tk 或 Wx 工具包。

这些通常是在用户的 matplotlibrc 中选择的,或者通过在会话或脚本开始时调用 matplotlib.use('QtAgg') 来选择。

当从脚本或交互方式(例如从 IPython shell)运行时,在我们调用 plt.show() 之前,不会显示 Figure。Figure将出现在一个新的 GUI 窗口中,并且通常有一个工具栏,其中包含 Zoom、Pan 和其他用于与Figure交互的工具。

默认情况下,plt.show() 会阻止脚本或 shell 的进一步交互,直到 Figure 窗口关闭,但出于某些目的可以将其关闭。

Creating Figures

创建Figure的最常见方法是使用 pyplot 接口。pyplot 接口有两个用途:

  • 一种是启动 Backend 并跟踪 GUI 窗口。另一个是 Axes 和 Artists 的全局状态,它允许使用简短的 API 来绘制图形。
fig = plt.figure(figsize=(2, 2), facecolor='lightskyblue', layout='constrained')

在上面的示例中,我们首先使用 pyplot,并创建 Figure 对象 fig.作为返回值, fig 也被添加到 pyplot 的全局状态中,可以通过 gcf 访问。

用户在创建 Figure 时通常需要 Axes 或 Axes 网格,因此除了 Figure 之外,还有一些方便的API可以同时返回 Figure 和一些 Axe:

fig, axs = plt.subplots(2, 2, figsize=(4, 3), layout='constrained')

也可以使用 pyplot.subplot_mosaic实现更复杂的网格:

fig, axs = plt.subplot_mosaic([['A', 'right'], ['B', 'right']], figsize=(4, 3), layout='constrained')
for ax_name, ax in axs.items():
    ax.text(0.5, 0.5, ax_name, ha='center', va='center')
image-20240824143951063

有时我们希望在Figure中有一个嵌套布局,其中包含两组或多组Axes,这些Axes不共享相同的子绘图网格。我们可以使用add_subfigure()subfigures在父Figure中创建:

fig = plt.figure(layout='constrained', facecolor='lightskyblue')
fig.suptitle('Figure')
figL, figR = fig.subfigures(1, 2)
figL.set_facecolor('thistle')
axL = figL.subplots(2, 1, sharex=True)
axL[1].set_xlabel('x [m]')
figL.suptitle('Left subfigure')
figR.set_facecolor('paleturquoise')
axR = figR.subplots(1, 2, sharey=True)
axR[0].set_title('Axes 1')
figR.suptitle('Right subfigure')
img

Figure options

matplotlib.pyplot.figure(num=None, figsize=None, dpi=None, *, facecolor=None, edgecolor=None, frameon=True, FigureClass=<class 'matplotlib.figure.Figure'>, clear=False, **kwargs)

创建Figure时,有一些选项可以配置:

  • figsize:屏幕上的图形大小由 figsize 和 dpi 设置。figsize 是图形的(width, height),以英寸inches为单位。
  • dpi:是图形每英寸将呈现多少像素。要使您的图形以您请求的物理尺寸显示在屏幕上,应将 dpi 设置为与您的图形系统相同的 dpi。
  • facecolor、edgecolor、linewidth、frameon:以预期的方式更改图形的外观,如果设置为 False则 frameon 会使图形透明。
  • 用户可以使用 layout 参数为Figure指定布局引擎。目前 Matplotlib 提供 “constrained”“compressed”“tight” 布局引擎。这些功能会重新缩放 Figure 内的轴,以防止刻度标签重叠,并尝试对齐轴,并且可以在许多常见情况下节省对 Figure 上Artists的大量手动调整。

Adding Artists

Figure有很多将Artists添加到Figure或SubFigure的方法。最常见的有添加各种配置: (add_axes, add_subplot, subplots, subplot_mosaic)和 (subfigures)。

Colorbars被添加到Axes或Axes组的级别,也可能有Figure-Level的Legend。

其它Artist包括figure-wide标签,比如 (suptitle, supxlabel, supylabel) and text (text)。

最后,可以使用 add_artist 直接添加低级 Artist,但通常需要注意使用适当的转换。通常,这些包括 Figure.transFigure,它在每个方向上的范围从 0 到 1,表示当前 Figure 大小的分数,或者 Figure.dpi_scale_trans 以距离 Figure 左下角的英寸物理单位(有关详细信息,请参阅转换教程)。

Saving Figures

可以使用 savefig 方法将 Figures 保存到磁盘。比如fig.savefig('MyFigure.png', dpi=200) 会将 PNG 格式的Figure以每英寸 200 点的分辨率保存到磁盘上当前目录下的 MyFigure.png 文件中。

文件名可以包含文件系统上任何位置的相对或绝对路径。

支持多种类型的输出,包括 PNG、GIF、JPEG、TIFF 等光栅格式以及 PDF、EPS 和 SVG 等矢量格式。

默认情况下,保存的Figure的大小由Figure size(以英寸inches为单位)设置,对于栅格格式,则由 dpi 设置。如果未设置 dpi,则使用 Figure 的 dpi。请注意,如果Figure包含已栅格化的Artists,则 dpi*对于矢量格式(如 PDF)仍然有意义,指定的 DPI 将是栅格化对象的分辨率。

可以使用 savefigbbox_inches 参数更改 Figure 的大小。这可以手动指定,同样以英寸为单位。最常见的用法是 bbox_inches='tight'。此选项“收缩包装”,根据需要修剪或扩展图形的大小,使其紧密围绕Figure中的所有Artist,并使用一个可由 pad_inches 参数指定pad,默认为 0.1 英寸。

下图中的虚线框显示了如果在 savefig 中使用 bbox_inches='tight' 将保存的图形部分。

img

后端Backends

What is a backend?

Backends用于在屏幕上显示 Matplotlib 图形或写入文件。

Matplotlib 可能有许多不同的用例和输出格式:

  • 从 Python shell 以交互方式使用 Matplotlib,并在键入命令时弹出绘图窗口。
  • 运行 Jupyter 笔记本并绘制内联图以进行快速数据分析。
  • 其他应用程序将 Matplotlib 嵌入到 PyQt 或 PyGObject 等图形用户界面中,以构建丰富的应用程序。

为了支持所有这些用例,Matplotlib 可以针对不同的输出,这些功能中的每一种都称为后端;

“前端”是面向用户的代码,即绘图代码,而“后端”在幕后完成所有艰苦的工作来制作图形。

有两种类型的后端:

  • user interface backends用户界面后端:用于 PyQt/PySide、PyGObject、Tkinter、wxPython 或 macOS/Cocoa,也称为“交互式后端”
  • hardcopy backends硬拷贝后端:用于制作图像文件PNG、SVG、PDF、PS,也称为“非交互式后端”。

Selecting a backend

有三种方法可以配置您的后端:

  • matplotlibrc 文件中的 rcParams["backend"] 参数
  • MPLBACKEND 环境变量
  • matplotlib.use()函数

The builtin backends

默认情况下,Matplotlib 应该自动选择一个默认的后端,该后端允许交互式工作和脚本绘图,输出到屏幕和/或文件,因此至少在最初,您无需担心后端。

Matplotlib Application Interfaces (APIs)

Matplotlib 有两个主要的应用程序界面,或使用库的样式:

  • 一个显式的“Axes”接口,它使用 Figure 或 Axes 对象上的方法来创建其他 Artists,并逐步构建可视化效果。这也称为 “面向对象” 接口。
  • 一个隐式的 “pyplot” 接口,用于跟踪最后创建的 Figure 和 Axes,并将 Artists 添加到它认为用户想要的对象中。

Native Matplotlib interfaces

The explicit "Axes" interface

Axes接口是 Matplotlib 的实现方式,许多自定义和微调最终都在这个级别完成。

此接口的工作原理是实例化 Figure 类的实例,在该对象上使用subplots方法创建一个或多个Axes对象,然后在Axes上调用绘图方法。

比如:

import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.subplots()
ax.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2])

我们称其为 “显式” 接口,因为每个对象都被显式引用,并用于生成下一个对象。保留对象的引用非常灵活,并允许我们在对象创建之后但在显示对象之前对其进行自定义。

The implicit "pyplot" interface

pyplot 模块隐藏了大多数 Axes 绘图方法,以提供与上述等效内容,其中 Figure 和 Axes 的创建是为用户完成的:

import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2])

本教程基本不使用这种方式。

Appendix: "Axes" interface with data structures#

大多数 Axes 方法允许另一个 API 寻址,方法是将数据对象传递给绘图方法并将参数指定为字符串

import matplotlib.pyplot as plt
data = {'xdat': [0, 1, 2, 3], 'ydat': [0, 0.2, 0.4, 0.1]}
fig, ax = plt.subplots(figsize=(3, 2))
ax.plot('xdat', 'ydat', data=data)
plt.show()
image-20240824153740264

交互式Figures

在处理数据时,交互性可能非常宝贵。Matplotlib GUI 窗口中内置的平移/缩放和鼠标定位工具通常就足够了,但您也可以使用事件系统来构建自定义的数据探索工具。

Matplotlib 附带了绑定到多个 GUI 工具包(Qt、Tk、Wx、GTK、macOS、JavaScript)的后端

为了使图形能够响应鼠标、键盘和绘制事件,需要将 GUI 事件循环与交互式提示符集成。

pyplot 模块提供了用于显式创建图形的功能,其中包括交互式工具、工具栏、工具提示和键绑定

  • pyplot.figure:创建新的空Figure或选择现有的Figure
  • pyplot.subplots:创建一个新的Figure,并使用Axes
  • pyplot.gcf:获取当前Figure,如果当前没有Figure,会创建一个新的Figure
  • pyplot.gca:获取当前 Axes,如果图窗上当前没有Axes,则会创建一个新Axes

pyplot 中的几乎所有函数都根据需要通过当前的 Figure / Axes (或创建一个)。

交互模式

pyplot.ion:启用交互模式

pyplot.ioff:禁用交互模式

pyplot.isinteractive:返回是否在每次绘图命令后更新绘图

pyplot.show:显示所有打开的figures

pyplot.pause:运行GUI事件循环

如果您处于非交互模式(或在非交互模式下创建图形),则需要显式调用 pyplot.show 来在屏幕上显示窗口。当前教程就是非交互模式。

默认UI

pyplot 创建的窗口有一个交互式工具栏,带有导航按钮和光标指向的数据值的读数。

交互式导航

所有Figure窗口都带有一个导航工具栏,可用于在数据集中使用。

../../../_images/toolbar.png
  • ForwardBack 用于在先前定义的视图之间来回导航。除非您已经使用平移和缩放按钮导航到其他位置,否则它们没有任何意义。
  • Home 会将您带到数据的第一个默认视图。
  • Pan/Zoom 平移和缩放:单击按钮激活平移和缩放,然后将鼠标放在轴上的某个位置。按住鼠标左键可平移图窗,将其拖动到新位置。当释放鼠标时,按下的点下的数据将移动到您释放的点。
  • Zoom-to-Rectangle缩放到矩形:当鼠标按照坐标轴上,然后按住鼠标左键拖动会定义一个矩形区域并放大,相反,按住鼠标右键拖动会缩小。
  • Subplot-configuration:使用此按钮可配置子图的外观。可以拉伸或压缩子图的左侧、右侧、顶部或底部,或者行之间的间距或列之间的间距。
  • Save:单击此按钮可启动文件保存对话框。可以使用以下扩展名保存文件:pngpsepssvgpdf

Axes简介

Axes是创建数据可视化的关键,将Axes放在Figure上后,可以使用很多方法将数据添加到Axes。Axis通常是一对Artists,用于定义数据坐标系,并且包含用于添加注释(比如x-label、y-label、legends)的方法。

anatomy

上图中,Axes使用ax = fig.subplots()创建,Figure上的其它内容都是通过ax对象的方法创建,或者可以从该对象访问。

如果想更改x轴上的标签,调用ax.set_xlabel('new label')方法。如果想绘制一些数据,调用ax.plot(x, y)

上图中,唯一不属于Axes的Artist是Figure本身,也就是axes.Axes

创建Axes

Axes使用Figure对象上的方法添加,或者可以使用pyplot接口添加。

fig, axs = plt.subplots(ncols=2, nrows=2, figsize=(3.5, 2.5), layout="constrained")

还有其他方法可以将 Axes 添加到 Figure:

  • Figure.add_axes:比如fig.add_axes([0, 0, 1, 1])会创建一个填充整个图形的Axes。
  • pyplot.subplotsFigure.subplots:添加网格状的Axes,注意:fig, ax = plt.subplots添加的单个Axes。
  • pyplot.subplot_mosaicFigure.subplot_mosaic:添加一个名为Axes的网格并返回一个字典。比如fig, axs = plt.subplot_mosaic([['left', 'right'], ['bottom', 'bottom']]), 其中axs['left'])是左侧顶行的Axes,axs['botton']是跨越底部两列的轴。

Axes绘制方法

大多数高级绘图方法都基于axes.Axes类。请参阅 Plot types 以获取示例。

一个基本示例是 axes.Axes.plot

fig, ax = plt.subplots(figsize=(4, 3))
np.random.seed(19680801)
t = np.arange(100)
x = np.cumsum(np.random.randn(100))
lines = ax.plot(t, x)

plot返回的是lines Artists,这些lines可以人为操作。

下面是一个非常不完整的绘图方法列表,可以参考 Plot types 获取更完整的示例。

类别 支持的绘图类型
Pairwise data成对数据 plot、scatter、bar、step
Array objects数组对象 pcolormesh、contour、quiver、steamplot
Statistical distribution统计分布 hist、errorbar、hist2d、pie、boxplot、violinplot
Irregularly gridded data不规则网格化数据 tricontour、tripcolor

Axes标签和注释

通常我们希望用 xlabel、ylabel 和 title 来标记 Axes,并且通常我们希望有一个图例来区分绘图元素。Axes 类具有许多用于创建这些注释的方法。

ax.set_xlabel('Time [s]')
ax.set_ylabel('Distance [km]')
ax.set_title('Random walk example')
ax.legend()

这些方法比较简单,也可以使用Text对象将文本添加到Axes,并进行注释Annotate,但这比较复杂。

Axes limits, scales, and ticking

每个 Axes 都有两个(或多个)Axis 对象,可以通过 xaxisyaxis 属性访问这些对象。这些 Axis 上有大量的方法。

其他重要方法,比如设置Axes的范围 (set_xlimset_ylim),或者更基本地设置Axes的比例。

例如:可以使Axis具有对数刻度:

fig, ax = plt.subplots(figsize=(4, 2.5), layout='constrained')
np.random.seed(19680801)
t = np.arange(200)
x = 2**np.cumsum(np.random.randn(200))
linesx = ax.plot(t, x)
ax.set_yscale('log')
ax.set_xlim([20, 180])
img

Axes 类还具有用于处理 Axis 刻度ticks及其标签Label的帮助程序。最直接的是 set_xticksset_yticks,它们手动设置ticks位置及其Lable(可选)。

Axes 刻度ticks和刻度标签tick labeling的许多方面都可以使用 tick_params 进行调整。例如,要标记axes的顶部而不是底部,请将刻度线设置为红色,并将刻度标签设置为绿色:

fig, ax = plt.subplots(figsize=(4, 2.5))
ax.plot(np.arange(10))
ax.tick_params(top=True, labeltop=True, color='red', axis='x', labelcolor='green')
img

Axes布局

有时在数据空间中设置绘图的纵横比很重要,我们可以用set_aspect来做到这一点:

fig, axs = plt.subplots(ncols=2, figsize=(7, 2.5), layout='constrained')
np.random.seed(19680801)
t = np.arange(200)
x = np.cumsum(np.random.randn(200))
axs[0].plot(t, x)
axs[0].set_title('aspect="auto"')

axs[1].plot(t, x)
axs[1].set_aspect(3)
axs[1].set_title('aspect=3')
img

在一个 Figure 中排列多个 Axes

通常需要在一个Figure上有多个 Axes,组织成一个规则的网格。Matplotlib 有多种工具可用于处理Axes网格。

Matplotlib 使用 Axes 来指代包含数据、x 轴x-axis和 y 轴y-axis、刻度ticks、标签labels、标题titile等的绘图区域。另一个经常使用的术语是“subplot”,它指的是与其他 Axes 对象位于网格中的 Axes。

创建 Axes 的网格形状组合

  • subplots:用于创建Figures和 Axes 网格的主要功能。它一次创建所有Axes并放置在Figure上,并返回一个对象数组,其中包含网格中Axes的句柄。
  • subplot_mosaic:一种创建Figures和Axes网格的简单方法,并且Axes可以跨行或列,增加了灵活性。Axes以带标签的字典而不是数组的形式返回。
  • SubFigure:有时,拥有不止一组不同的 Axes 网格是很自然的,在这种情况下,Matplotlib 有 SubFigure 可以实现。

一次添加单个轴

Matplotlib可以一次添加一个 Axes。

  • add_axes:在 [left, bottom, width, height] 指定的位置以Figure宽度或高度的比例添加单个 Axes。
  • subplotFigure.add_subplot:在Figure上添加单个子图,具有从 1 开始的索引。可以通过指定网格单元格范围来跨越列和行。
  • subplot2grid:类似于 pyplot.subplot ,但使用从 0 开始的索引和二维 python 切片来选择单元格。

示例:将3x2英寸的Axes添加到4x3英寸的Figure中,子图的位置由[left, bottom, width, height] (以数字归一化单位表示):

import matplotlib.pyplot as plt
import numpy as np
w, h = 4, 3
margin = 0.5
fig = plt.figure(figsize=(w, h), facecolor='lightblue')		# facecolor: Figure的颜色
# width = 0.75, 4 * 0.75 = 3;  heigh = (2/3) * 3 = 2
ax = fig.add_axes([margin / w, margin / h, (w - 2 * margin) / w, (h - 2 * margin) / h])

创建网格的高级方法

基本 2x2 网格

可以使用subplots创建一个基本的 2×2 轴网格。它返回一个 Figure 实例和一个 Axes 对象数组。Axes 对象可用于访问将Artist放置在 Axes 上的方法。这里我们使用 Annotate,但其他示例可以是 PlotpColorMesh 等。

import matplotlib.pyplot as plt
import numpy as np
# Grid为2x2
fig, axs = plt.subplots(ncols=2, nrows=2, figsize=(5.5, 3.5), layout="constrained")
# add an artist, in this case a nice label in the middle...
for row in range(2):
    for col in range(2):
        axs[row, col].annotate(f'axs[{row}, {col}]', (0.5, 0.5),
                               transform=axs[row, col].transAxes,
                               ha='center', va='center', fontsize=18,
                               color='darkgrey')
fig.suptitle('plt.subplots()')
plt.subplots()

也可以使用 subplot_mosaic 也可以实现相同的效果,但返回类型是字典而不是数组,用户可以在其中为键提供有用的含义。在这里,我们提供了两个列表,每个列表代表一行,列表中的每个元素都有一个代表列的键。

import matplotlib.pyplot as plt
import numpy as np

def annotate_axes(ax, text, fontsize=18):
    ax.text(0.5, 0.5, text, transform=ax.transAxes,
            ha="center", va="center", fontsize=fontsize, color="darkgrey")

fig, axd = plt.subplot_mosaic([['upper left', 'upper right'],
                               ['lower left', 'lower right']],
                              figsize=(5.5, 3.5), layout="constrained")
for k, ax in axd.items():
    annotate_axes(ax, f'axd[{k!r}]', fontsize=14)
fig.suptitle('plt.subplot_mosaic()')
plt.subplot_mosaic()

固定纵横比轴的网格

固定纵横比轴通常用于images或maps。但是,它们对布局提出了挑战,因为对 Axes 的大小施加了两组约束 - 它们适合图中,并且它们具有固定的纵横比。默认情况下,这会导致 Axes 之间出现较大的间隙。

解决此问题的一种方法是将figure的纵横比更改为接近 Axes 的纵横比,但这需要反复试验。Matplotlib 还提供 layout='compressed',它将与简单的网格一起使用以减少 Axes 之间的间隙。

fig, axs = plt.subplots(2, 2, layout="compressed", figsize=(5.5, 3.5),
                        facecolor='lightblue')
for ax in axs.flat:
    ax.set_aspect(1)
fig.suptitle('Fixed aspect Axes: compressed')

跨网格中的行或列

有时我们希望 Axes 跨越网格的行或列。实际上有多种方法可以完成此操作,但最方便的可能是通过重复其中一个键来使用 subplot_mosaic

fig, axd = plt.subplot_mosaic([['upper left', 'right'],
                               ['lower left', 'right']],
                              figsize=(5.5, 3.5), layout="constrained")
for k, ax in axd.items():
    annotate_axes(ax, f'axd[{k!r}]', fontsize=14)
fig.suptitle('plt.subplot_mosaic()')
plt.subplot_mosaic()
fig, axd = plt.subplot_mosaic([['upper left', 'upper right'],
                               ['lower', 'lower']],
                              figsize=(5.5, 3.5), layout="constrained")
for k, ax in axd.items():
    annotate_axes(ax, f'axd[{k!r}]', fontsize=14)
fig.suptitle('plt.subplot_mosaic()')
image-20240825140054471

网格中的可变宽度或高度

subplots和和subplot_mosaic都使用 gridspec_kw 关键字参数允许网格中的行具有不同的高度,并且允许列具有不同宽度。

GridSpec 接受的间距参数可以传递给subplotssubplot_mosaic

gs_kw = dict(width_ratios=[1.4, 1], height_ratios=[1, 2])
fig, axd = plt.subplot_mosaic([['upper left', 'right'],
                               ['lower left', 'right']],
                              gridspec_kw=gs_kw, figsize=(5.5, 3.5),
                              layout="constrained")
for k, ax in axd.items():
    annotate_axes(ax, f'axd[{k!r}]', fontsize=14)
fig.suptitle('plt.subplot_mosaic()')
plt.subplot_mosaic()

嵌套轴布局

有时,可能需要两个或多个可能不需要彼此关联的 Axes 网格。实现此目的的最简单方法是使用 Figure.subfigures

请注意,SubFigures布局是独立的,因此每个subFigure中的 Axes 不一定对齐。

fig = plt.figure(layout="constrained")
subfigs = fig.subfigures(1, 2, wspace=0.07, width_ratios=[1.5, 1.])
axs0 = subfigs[0].subplots(2, 2)
subfigs[0].set_facecolor('lightblue')
subfigs[0].suptitle('subfigs[0]\nLeft side')
subfigs[0].supxlabel('xlabel for subfigs[0]')

axs1 = subfigs[1].subplots(3, 1)
subfigs[1].suptitle('subfigs[1]')
subfigs[1].supylabel('ylabel for subfigs[1]')
arranging axes

也可以使用 subplot_mosaic 嵌套 Axes 实现,此方法不使用SubFigures。

inner = [['innerA'],
         ['innerB']]
outer = [['upper left',  inner],
          ['lower left', 'lower right']]

fig, axd = plt.subplot_mosaic(outer, layout="constrained")
for k, ax in axd.items():
    annotate_axes(ax, f'axd[{k!r}]')
arranging axes

低级和高级网格方法

在内部,通过创建 GridSpecSubplotSpec 的实例来控制 Axes 网格的排列。GridSpec 定义一个(可能不均匀的)单元格网格。对 GridSpec 进行索引将返回一个 SubplotSpec,该 SubplotSpec 涵盖一个或多个网格单元,可用于指定 Axes 的位置。

基本 2x2 网格

我们可以用与 add_gridspec 方式完成 2x2 网格:

fig = plt.figure(figsize=(5.5, 3.5), layout="constrained")
spec = fig.add_gridspec(ncols=2, nrows=2)
ax0 = fig.add_subplot(spec[0, 0])
annotate_axes(ax0, 'ax0')
ax1 = fig.add_subplot(spec[0, 1])
annotate_axes(ax1, 'ax1')
ax2 = fig.add_subplot(spec[1, 0])
annotate_axes(ax2, 'ax2')
ax3 = fig.add_subplot(spec[1, 1])
annotate_axes(ax3, 'ax3')
fig.suptitle('Manually added subplots using add_gridspec')
Manually added subplots using add_gridspec

Axes跨网格中的行或网格

我们可以使用 NumPy slice 语法GridSpec 数组进行切片索引,新的 Axes 将跨越该 slice。这与fig, axd = plt.subplot_mosaic([['ax0', 'ax0'], ['ax1', 'ax2']], ...)相同:

fig = plt.figure(figsize=(5.5, 3.5), layout="constrained")
spec = fig.add_gridspec(2, 2)
ax0 = fig.add_subplot(spec[0, :])
annotate_axes(ax0, 'ax0')
ax10 = fig.add_subplot(spec[1, 0])
annotate_axes(ax10, 'ax10')
ax11 = fig.add_subplot(spec[1, 1])
annotate_axes(ax11, 'ax11')
fig.suptitle('Manually added subplots, spanning a column')
Manually added subplots, spanning a column

手动调整 GridSpec 布局

当显式使用 GridSpec 时,您可以调整从 GridSpec 创建的subplots的布局参数。请注意,此选项与约束布局Figure.tight_layout不兼容。

fig = plt.figure(layout=None, facecolor='lightblue')
gs = fig.add_gridspec(nrows=3, ncols=3, left=0.05, right=0.75,
                      hspace=0.1, wspace=0.05)
ax0 = fig.add_subplot(gs[:-1, :])
annotate_axes(ax0, 'ax0')
ax1 = fig.add_subplot(gs[-1, :-1])
annotate_axes(ax1, 'ax1')
ax2 = fig.add_subplot(gs[-1, -1])
annotate_axes(ax2, 'ax2')
fig.suptitle('Manual gridspec with right=0.75')
image-20240825142001914

使用 SubplotSpec 的嵌套布局

您可以使用 subgridspec 创建类似于subfigures的嵌套布局。

fig = plt.figure(layout="constrained")
gs0 = fig.add_gridspec(1, 2)

gs00 = gs0[0].subgridspec(2, 2)
gs01 = gs0[1].subgridspec(3, 1)

for a in range(2):
    for b in range(2):
        ax = fig.add_subplot(gs00[a, b])
        annotate_axes(ax, f'axLeft[{a}, {b}]', fontsize=10)
        if a == 1 and b == 1:
            ax.set_xlabel('xlabel')
for a in range(3):
    ax = fig.add_subplot(gs01[a])
    annotate_axes(ax, f'axRight[{a}, {b}]')
    if a == 2:
        ax.set_ylabel('ylabel')
fig.suptitle('nested gridspecs')
nested gridspecs

放置colorbars

Colorbars表示图像数据的定量范围。放置一个数字并非易事,因为需要为他们腾出空间。

自动放置colorbars

最简单的情况是为每个 Axes 附加一个颜色条。请注意,在此示例中,colorbars会从父 Axes 中窃取一些空间。

import matplotlib.pyplot as plt
import numpy as np
# Fixing random state for reproducibility
np.random.seed(19680801)
fig, axs = plt.subplots(1, 2, figsize = (7, 4))
cmaps = ['RdBu_r', 'viridis']
for col in range(2):
    ax = axs[col]
    pcm = ax.pcolormesh(np.random.random((20, 20)) * (col + 1), cmap=cmaps[col])
    fig.colorbar(pcm, ax=ax)
image-20240825144103660

被盗的空间可能导致同一subplots布局中的Axes大小不同,如果每个图上的 x 轴具有可比性,这通常是不希望的,如下所示:

fig, axs = plt.subplots(2, 1, figsize=(4, 5), sharex=True)
X = np.random.randn(20, 20)
axs[0].plot(np.sum(X, axis=0))
pcm = axs[1].pcolormesh(X)
fig.colorbar(pcm, ax=axs[1], shrink=0.6)
colorbar placement

可以通过多种方式解决,例如,向其他 Axes 添加颜色条,然后将其删除。但是,最直接的方法是使用 constrained layout

fig, axs = plt.subplots(2, 1, figsize=(4, 5), sharex=True, layout='constrained')
axs[0].plot(np.sum(X, axis=0))
pcm = axs[1].pcolormesh(X)
fig.colorbar(pcm, ax=axs[1], shrink=0.6)
colorbar placement

使用此范例可以实现相对复杂的彩条布局。请注意,此示例在layout='constrained'

fig, axs = plt.subplots(3, 3, layout='constrained')
for ax in axs.flat:
    pcm = ax.pcolormesh(np.random.random((20, 20)))
fig.colorbar(pcm, ax=axs[0, :2], shrink=0.6, location='bottom')
fig.colorbar(pcm, ax=[axs[0, 2]], location='bottom')
fig.colorbar(pcm, ax=axs[1:, :], location='right', shrink=0.6)
fig.colorbar(pcm, ax=[axs[2, 1]], location='left')
colorbar placement

自动缩放Axis

Axis上的限制可以手动设置(例如 ax.set_xlim(xmin, xmax)),或者 Matplotlib 可以根据Axis上已有的数据自动设置它们。这种自动缩放行为有许多选项。

我们将从一个简单的线图开始,显示自动缩放将axis限制扩展到数据限制 (-2π, 2π) 之外 5%。

import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl

x = np.linspace(-2 * np.pi, 2 * np.pi, 100)
y = np.sinc(x)
fig, ax = plt.subplots()
ax.plot(x, y)
autoscale

Margins 边距

数据限制周围的默认边距为 5%,这是基于下面三个值默认配置的:

  • rcParams["axes.xmargin"] (default: 0.05)
  • rcParams["axes.ymargin"] (default: 0.05)
  • rcParams["axes.zmargin"] (default: 0.05)

可以使用 margins 覆盖 margin 大小以使其更小或更大:

fig, ax = plt.subplots()
ax.plot(x, y)
ax.margins(0.2, 0.2)
autoscale

通常,边距可以在(-0.5, ∞)范围内,其中负边距将Axis限制设置为数据范围的子范围。使用单个数字作为外边距会影响两个轴,可以使用关键字参数 xy 自定义单个外边距,但不能组合位置和关键字接口。

fig, ax = plt.subplots()
ax.plot(x, y)
ax.margins(y=-0.2)
autoscale

粘性边缘

有一些元素 (Artists) 通常没有边距,例如使用 Axes.imshow 创建的图像。

xx, yy = np.meshgrid(x, x)
zz = np.sinc(np.sqrt((xx - 1)**2 + (yy - 1)**2))

fig, ax = plt.subplots(ncols=2, figsize=(12, 8))
ax[0].imshow(zz)
ax[0].set_title("default margins")
ax[1].imshow(zz)
ax[1].margins(0.2)
ax[1].set_title("margins(0.2)")
image-20240825145848604

这种外边距的覆盖由 “sticky edges” 决定,这是 Artist 类的一个属性,可以禁止向轴限制添加外边距。可以通过更改 Axes 来禁用粘滞边缘的效果use_sticky_edges

fig, ax = plt.subplots(ncols=3, figsize=(16, 10))
ax[0].imshow(zz)
ax[0].margins(0.2)
ax[0].set_title("default use_sticky_edges\nmargins(0.2)")
ax[1].imshow(zz)
ax[1].margins(0.2)
ax[1].use_sticky_edges = False
ax[1].set_title("use_sticky_edges=False\nmargins(0.2)")
ax[2].imshow(zz)
ax[2].margins(-0.2)
ax[2].set_title("default use_sticky_edges\nmargins(-0.2)")
default use_sticky_edges margins(0.2), use_sticky_edges=False margins(0.2), default use_sticky_edges margins(-0.2)

我们可以看到,将 use_sticky_edges 设置为 False 会渲染具有边距的图像。

控制自动缩放

默认情况下,每次向绘图添加新曲线时,都会重新计算限制:

fig, ax = plt.subplots(ncols=2, figsize=(12, 8))
ax[0].plot(x, y)
ax[0].set_title("Single curve")
ax[1].plot(x, y)
ax[1].plot(x * 2.0, y)
ax[1].set_title("Two curves")
Single curve, Two curves

但是,在某些情况下,您不希望自动将viewpoint调整为新数据。

禁用自动缩放的一种方法是手动设置轴限制。假设我们只想更详细地查看数据的一部分。即使我们向数据添加更多曲线,设置 xlim 也会保持不变。要重新计算新的limit,需要调用 Axes.autoscale 将手动切换该功能。

fig, ax = plt.subplots(ncols=2, figsize=(12, 8))
ax[0].plot(x, y)
ax[0].set_xlim(left=-1, right=1)
ax[0].plot(x + np.pi * 0.5, y)
ax[0].set_title("set_xlim(left=-1, right=1)\n")
ax[1].plot(x, y)
ax[1].set_xlim(left=-1, right=1)
ax[1].plot(x + np.pi * 0.5, y)
ax[1].autoscale()
ax[1].set_title("set_xlim(left=-1, right=1)\nautoscale()")
set_xlim(left=-1, right=1) , set_xlim(left=-1, right=1) autoscale()

autoscale 函数的参数使我们能够精确控制自动缩放的过程。

  • 参数enable:是否使能
  • 参数 axis: 为所选轴(或两者)设置自动缩放功能。
  • 参数 tight :将所选轴的边距设置为零。
  • 要保留 enabletight 的设置,您可以将相反的设置为 None,这样就不应该修改它。但是,将 enable 设置为 None 并将 tight 设置为 True 会影响两个轴,而不管 axis 参数如何。
fig, ax = plt.subplots()
ax.plot(x, y)
ax.margins(0.2, 0.2)
ax.autoscale(enable=None, axis="x", tight=True)

print(ax.margins())
autoscale

Axis ticks

可以使用 set_xticks 等高级方法自定义tick和tick label,也可以直接在轴上设置locators和formatters 。

手动Location 和 格式Formats

自定义tick location和formats的最简单方法是使用 set_xticksset_yticks。这些可以用于 major ticks 或 minor ticks。

fig, axs = plt.subplots(2, 1, figsize=(5.4, 5.4), layout='constrained')
x = np.arange(100)
for nn, ax in enumerate(axs):
    ax.plot(x, x)
    if nn == 1:
        ax.set_title('Manual ticks')
        ax.set_yticks(np.arange(0, 100.1, 100/3)) 	# 设置Y轴ticks
        xticks = np.arange(0.50, 101, 20)
        xlabels = [f'\\${x:1.2f}' for x in xticks]
        ax.set_xticks(xticks, labels=xlabels)		# 设置X轴ticks
    else:
        ax.set_title('Automatic ticks')
Automatic ticks, Manual ticks

请注意,labels 参数的个数必须与用于指定ticks的数组的成员个数相同。

默认情况下,set_xticksset_yticks 作用于 Axis 的major ticks,但是可以添加minor ticks:

fig, axs = plt.subplots(2, 1, figsize=(5.4, 5.4), layout='constrained')
x = np.arange(100)
for nn, ax in enumerate(axs):
    ax.plot(x, x)
    if nn == 1:
        ax.set_title('Manual ticks')
        ax.set_yticks(np.arange(0, 100.1, 100/3))
        ax.set_yticks(np.arange(0, 100.1, 100/30), minor=True)
    else:
        ax.set_title('Automatic ticks')
Automatic ticks, Manual ticks

Locators 和 Formatters

手动设置Location和Formatter随着用户与 Axes 的交互而无法适应。在较低级别,Matplotlib 具有 Locators,用于根据Axis的当前视图限制自动选择ticks,以及用于自动格式化tick label的 Formatters

Matplotlib 提供的定位器的完整列表列在 Tick locating中,格式化程序列在 Tick formatting 中。

def setup(ax, title):
    """Set up common parameters for the Axes in the example."""
    # only show the bottom spine
    ax.yaxis.set_major_locator(ticker.NullLocator())
    ax.spines[['left', 'right', 'top']].set_visible(False)

    ax.xaxis.set_ticks_position('bottom')
    ax.tick_params(which='major', width=1.00, length=5)
    ax.tick_params(which='minor', width=0.75, length=2.5)
    ax.set_xlim(0, 5)
    ax.set_ylim(0, 1)
    ax.text(0.0, 0.2, title, transform=ax.transAxes,
            fontsize=14, fontname='Monospace', color='tab:blue')

fig, axs = plt.subplots(8, 1, layout='constrained')

# Null Locator
setup(axs[0], title="NullLocator()")
axs[0].xaxis.set_major_locator(ticker.NullLocator())
axs[0].xaxis.set_minor_locator(ticker.NullLocator())

# Multiple Locator
setup(axs[1], title="MultipleLocator(0.5)")
axs[1].xaxis.set_major_locator(ticker.MultipleLocator(0.5))
axs[1].xaxis.set_minor_locator(ticker.MultipleLocator(0.1))

# Fixed Locator
setup(axs[2], title="FixedLocator([0, 1, 5])")
axs[2].xaxis.set_major_locator(ticker.FixedLocator([0, 1, 5]))
axs[2].xaxis.set_minor_locator(ticker.FixedLocator(np.linspace(0.2, 0.8, 4)))

# Linear Locator
setup(axs[3], title="LinearLocator(numticks=3)")
axs[3].xaxis.set_major_locator(ticker.LinearLocator(3))
axs[3].xaxis.set_minor_locator(ticker.LinearLocator(31))

# Index Locator
setup(axs[4], title="IndexLocator(base=0.5, offset=0.25)")
axs[4].plot(range(0, 5), [0]*5, color='white')
axs[4].xaxis.set_major_locator(ticker.IndexLocator(base=0.5, offset=0.25))

# Auto Locator
setup(axs[5], title="AutoLocator()")
axs[5].xaxis.set_major_locator(ticker.AutoLocator())
axs[5].xaxis.set_minor_locator(ticker.AutoMinorLocator())

# MaxN Locator
setup(axs[6], title="MaxNLocator(n=4)")
axs[6].xaxis.set_major_locator(ticker.MaxNLocator(4))
axs[6].xaxis.set_minor_locator(ticker.MaxNLocator(40))

# Log Locator
setup(axs[7], title="LogLocator(base=10, numticks=15)")
axs[7].set_xlim(10**3, 10**10)
axs[7].set_xscale('log')
axs[7].xaxis.set_major_locator(ticker.LogLocator(base=10, numticks=15))
plt.show()
axes ticks

同样,我们可以为每个Axis上的主要和Minor ticks指定 “Formatters”。

ticks format通过函数 set_major_formatterset_minor_formatter 进行配置。它接受:

有关完整列表,请参阅 Tick formatting (刻度格式)。

def setup(ax, title):
    """Set up common parameters for the Axes in the example."""
    # only show the bottom spine
    ax.yaxis.set_major_locator(ticker.NullLocator())
    ax.spines[['left', 'right', 'top']].set_visible(False)

    # define tick positions
    ax.xaxis.set_major_locator(ticker.MultipleLocator(1.00))
    ax.xaxis.set_minor_locator(ticker.MultipleLocator(0.25))

    ax.xaxis.set_ticks_position('bottom')
    ax.tick_params(which='major', width=1.00, length=5)
    ax.tick_params(which='minor', width=0.75, length=2.5, labelsize=10)
    ax.set_xlim(0, 5)
    ax.set_ylim(0, 1)
    ax.text(0.0, 0.2, title, transform=ax.transAxes,
            fontsize=14, fontname='Monospace', color='tab:blue')


fig = plt.figure(figsize=(8, 8), layout='constrained')
fig0, fig1, fig2 = fig.subfigures(3, height_ratios=[1.5, 1.5, 7.5])

fig0.suptitle('String Formatting', fontsize=16, x=0, ha='left')
ax0 = fig0.subplots()

setup(ax0, title="'{x} km'")
ax0.xaxis.set_major_formatter('{x} km')

fig1.suptitle('Function Formatting', fontsize=16, x=0, ha='left')
ax1 = fig1.subplots()

setup(ax1, title="def(x, pos): return str(x-5)")
ax1.xaxis.set_major_formatter(lambda x, pos: str(x-5))

fig2.suptitle('Formatter Object Formatting', fontsize=16, x=0, ha='left')
axs2 = fig2.subplots(7, 1)

setup(axs2[0], title="NullFormatter()")
axs2[0].xaxis.set_major_formatter(ticker.NullFormatter())

setup(axs2[1], title="StrMethodFormatter('{x:.3f}')")
axs2[1].xaxis.set_major_formatter(ticker.StrMethodFormatter("{x:.3f}"))

setup(axs2[2], title="FormatStrFormatter('#%d')")
axs2[2].xaxis.set_major_formatter(ticker.FormatStrFormatter("#%d"))


def fmt_two_digits(x, pos):
    return f'[{x:.2f}]'


setup(axs2[3], title='FuncFormatter("[{:.2f}]".format)')
axs2[3].xaxis.set_major_formatter(ticker.FuncFormatter(fmt_two_digits))

setup(axs2[4], title="FixedFormatter(['A', 'B', 'C', 'D', 'E', 'F'])")
# FixedFormatter should only be used together with FixedLocator.
# Otherwise, one cannot be sure where the labels will end up.
positions = [0, 1, 2, 3, 4, 5]
labels = ['A', 'B', 'C', 'D', 'E', 'F']
axs2[4].xaxis.set_major_locator(ticker.FixedLocator(positions))
axs2[4].xaxis.set_major_formatter(ticker.FixedFormatter(labels))

setup(axs2[5], title="ScalarFormatter()")
axs2[5].xaxis.set_major_formatter(ticker.ScalarFormatter(useMathText=True))

setup(axs2[6], title="PercentFormatter(xmax=5)")
axs2[6].xaxis.set_major_formatter(ticker.PercentFormatter(xmax=5))
axes ticks

设置ticks styling(tick parameters)

通过在Axis上找到单个 Tick ,可以在较低级别控制 Tick 的外观。但是,通常最简单的方法是使用 tick_params 一次更改所有对象。

tick_params 方法可以更ticks的属性:

  • length 长度
  • direction 方向 (in or out of the frame)
  • colors 颜色
  • width and length 宽度和长度
  • 以及刻度是在 Axes 的底部、顶部、左侧还是右侧绘制。

它还可以控制tick labels:

  • labelsize (fontsize) 字体大小
  • labelcolor (标签的颜色)
  • labelrotation 标签旋转
  • labelbottom, labeltop, labelleft, labelright

还有一个 pad 关键字参数,用于指定tick label与tick的距离。

最后,可以设置网格线型:

  • grid_color
  • grid_alpha
  • grid_linewidth
  • grid_linestyle

所有这些属性都可以限制为一个Axis,并且可以仅应用于 major tick 或 minor tick。

fig, axs = plt.subplots(1, 2, figsize=(6.4, 3.2), layout='constrained')

for nn, ax in enumerate(axs):
    ax.plot(np.arange(100))
    if nn == 1:
        ax.grid('on')
        ax.tick_params(right=True, left=False, axis='y', color='r', length=16,
                       grid_color='none')
        ax.tick_params(axis='x', color='m', length=4, direction='in', width=4,
                       labelcolor='g', grid_color='b')
axes ticks

绘制日期dates和字符串strings

最基本的X轴为数字,但是Matplotlib也支持其它类型,可以是python列表。

如果数据类型存在“单位转换器unit converter”,Matplotlib 还具有转换其他数据类型的能力。Matplotlib 有两个内置转换器,一个用于日期,另一个用于字符串列表。

将转换器添加到 Matplotlib 的方法在 matplotlib.units 中描述。在这里,我们简要概述了内置的日期和字符串转换器。

日期转换

如果 x 和/或 ydatetime列表或 numpy.datetime64 数组,则 Matplotlib 有一个内置转换器,可以将日期时间转换为浮点数,并将tick locators 和 formatters 添加到适合日期的Axis上。

在以下示例中,x 轴获得了一个从 numpy.datetime64 转换为 float 的转换器converter,一个将ticks放在月初的定位符locator ,以及一个适当标记ticks的formatter:

import numpy as np
import matplotlib.dates as mdates
import matplotlib.units as munits
import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(5.4, 2), layout='constrained')
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
x = np.arange(len(time))
ax.plot(time, x)
axes units

请注意,如果我们尝试在 x 轴上绘制浮点数,它将以converter的 “纪元” 以来的天数为单位绘制,在本例中为 1970-01-01 (参见 Matplotlib 日期格式)。因此,当我们绘制值 0 时,ticks 从 1970-01-01 开始。

fig, ax = plt.subplots(figsize=(5.4, 2), layout='constrained')
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
x = np.arange(len(time))
ax.plot(time, x)
# 0 gets labeled as 1970-01-01
ax.plot(0, 0, 'd')
ax.text(0, 0, ' Float x=0', rotation=45)
axes units

我们可以自定义 locator 和 formatter,在这里我们每隔一个月进行一次定位,并使用 "%b" 仅使用月份的 3 个字母名称进行格式设置格式:

fig, ax = plt.subplots(figsize=(5.4, 2), layout='constrained')
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
x = np.arange(len(time))
ax.plot(time, x)
ax.xaxis.set_major_locator(mdates.MonthLocator(bymonth=np.arange(1, 13, 2)))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b'))
ax.set_xlabel('1980')
axes units

默认定位器是 AutoDateLocator,默认格式化程序是 AutoDateFormatter。还有一些 “简洁” Formatter和Locators,它们提供更紧凑的标签,并且可以通过 rcParams 进行设置。请注意,如何改用 “1980” 而不是年初多余的 “Jan” 标签。

plt.rcParams['date.converter'] = 'concise'
fig, ax = plt.subplots(figsize=(5.4, 2), layout='constrained')
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
x = np.arange(len(time))
ax.plot(time, x)
axes units

字符串转换:categorical plots

有时我们想在Axis上标记类别,而不是数字。Matplotlib 允许使用 “categorical” converter。(见category

data = {'apple': 10, 'orange': 15, 'lemon': 5, 'lime': 20}
names = list(data.keys())
values = list(data.values())

fig, axs = plt.subplots(1, 3, figsize=(7, 3), sharey=True, layout='constrained')
axs[0].bar(names, values)
axs[1].scatter(names, values)
axs[2].plot(names, values)
fig.suptitle('Categorical Plotting')
Categorical Plotting

请注意,categories 是按照它们第一次指定的顺序绘制的,并且随后以不同的顺序绘制不会影响原始顺序。

data = {'apple': 10, 'orange': 15, 'lemon': 5, 'lime': 20}
names = list(data.keys())
values = list(data.values())

fig, ax = plt.subplots(figsize=(5, 3), layout='constrained')
ax.bar(names, values)

# plot in a different order:
ax.scatter(['lemon', 'apple'], [7, 12])

# add a new category, "pear", and put the other categories in a different order:
ax.plot(['pear', 'orange', 'apple', 'lemon'], [13, 10, 7, 12], color='C1')
axes units

category converter从类别映射到整数,从 0 开始。因此,也可以使用浮点数手动将数据添加到轴中。请注意,如果传入的浮点数没有与之关联的 “category”,则仍可以绘制数据点,但不会创建刻度。

在下文中,我们在 4.0 和 2.5 处绘制数据,但未在此处添加刻度,因为它们不是category。

fig, ax = plt.subplots(figsize=(5, 3), layout='constrained')
ax.bar(names, values)
# arguments for styling the labels below:
args = {'rotation': 70, 'color': 'C1',
        'bbox': {'color': 'white', 'alpha': .7, 'boxstyle': 'round'}}

# 0 gets labeled as "apple"
ax.plot(0, 2, 'd', color='C1')
ax.text(0, 3, 'Float x=0', **args)

# 2 gets labeled as "lemon"
ax.plot(2, 2, 'd', color='C1')
ax.text(2, 3, 'Float x=2', **args)

# 4 doesn't get a label
ax.plot(4, 2, 'd', color='C1')
ax.text(4, 3, 'Float x=4', **args)

# 2.5 doesn't get a label
ax.plot(2.5, 2, 'd', color='C1')
ax.text(2.5, 3, 'Float x=2.5', **args)
axes units

category axes对于某些绘图类型很有用,但如果将数据作为字符串列表读入,则可能会导致混淆,即使它是浮点数或日期列表。读取逗号分隔值CSV文件时,有时会发生这种情况,categorical locator 和 formatter将在每个字符串值放置一个tick标记每个字符串值:

axes units

如果不需要,则只需在绘图之前将数据转换为浮点数:

fig, ax = plt.subplots(figsize=(5.4, 2.5), layout='constrained')
x = np.asarray(x, dtype='float')  # array of float.
ax.plot(x, np.arange(100))
ax.set_xlabel('x is array of floats')

确定Axis上的Converter, Formattter, Locator

有时,能够调试 Matplotlib 用于转换传入数据的内容会很有帮助。我们可以通过查询轴上的 converter 属性来实现这一点。我们还可以使用 get_major_locatorget_major_formatter 查询Formattter和Locator。

请注意,默认情况下,Converter为 None

fig, axs = plt.subplots(3, 1, figsize=(6.4, 7), layout='constrained')
x = np.arange(100)
ax = axs[0]
ax.plot(x, x)
label = f'Converter: {ax.xaxis.converter}\n '
label += f'Locator: {ax.xaxis.get_major_locator()}\n'
label += f'Formatter: {ax.xaxis.get_major_formatter()}\n'
ax.set_xlabel(label)

ax = axs[1]
time = np.arange('1980-01-01', '1980-06-25', dtype='datetime64[D]')
x = np.arange(len(time))
ax.plot(time, x)
label = f'Converter: {ax.xaxis.converter}\n '
label += f'Locator: {ax.xaxis.get_major_locator()}\n'
label += f'Formatter: {ax.xaxis.get_major_formatter()}\n'
ax.set_xlabel(label)

ax = axs[2]
data = {'apple': 10, 'orange': 15, 'lemon': 5, 'lime': 20}
names = list(data.keys())
values = list(data.values())
ax.plot(names, values)
label = f'Converter: {ax.xaxis.converter}\n '
label += f'Locator: {ax.xaxis.get_major_locator()}\n'
label += f'Formatter: {ax.xaxis.get_major_formatter()}\n'
ax.set_xlabel(label)
axes units

Legend指南

本指南使用了一些常用术语,为清楚起见,此处记录了这些术语:

  • legend entry 图例条目:图例由一个或多个图例条目组成。一个条目由一个 key 和一个 label 组成。
  • legend key 图例键:每个图例标签左侧的彩色/图案标记。
  • legend label 图例标签:描述 key 表示的句柄的文本。
  • legend handle 图例句柄:用于在图例中生成相应条目的原始对象。

控制图例条目

不带参数调用 legend() 会自动获取图例句柄及其关联的标签。此功能等效于:

handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels)

get_legend_handles_labels() 函数返回 Axes 上存在的 handles/artists 列表,这些 handles/artists 可用于为生成的图例生成条目。

为了完全控制添加到图例中的内容,通常将适当的句柄直接传递给 legend()

fig, ax = plt.subplots()
line_up, = ax.plot([1, 2, 3], label='Line 2')
line_down, = ax.plot([3, 2, 1], label='Line 1')
ax.legend(handles=[line_up, line_down])

重命名图例条目

当标签不能直接在手柄上设置时,可以直接传递给 Axes.legend

fig, ax = plt.subplots()
line_up, = ax.plot([1, 2, 3], label='Line 2')
line_down, = ax.plot([3, 2, 1], label='Line 1')
ax.legend([line_up, line_down], ['Line Up', 'Line Down'])

创建专门用于添加到图例的artists(又名Proxy artists)

并非所有手柄都可以自动转换为图例条目,因此通常需要创建一个的艺术家。图例手柄不必存在于 Figure 或 Axes 上即可使用。

假设我们想创建一个图例,其中有一个数据条目,这些数据由红色表示:

import matplotlib.pyplot as plt

import matplotlib.patches as mpatches

fig, ax = plt.subplots()
red_patch = mpatches.Patch(color='red', label='The red data')
ax.legend(handles=[red_patch])

plt.show()
legend guide

有许多受支持的图例句柄。我们可以创建一条带有标记的线条,而不是创建颜色块:

import matplotlib.lines as mlines

fig, ax = plt.subplots()
blue_line = mlines.Line2D([], [], color='blue', marker='*',
                          markersize=15, label='Blue stars')
ax.legend(handles=[blue_line])

plt.show()
legend guide

图例位置

图例的位置可以通过关键字参数 loc 指定。

bbox_to_anchor 关键字为手动图例放置提供了很大程度的控制。例如,如果您希望 Axes 图例位于figure的右上角而不是 Axes 的角,只需指定角的位置和该位置的坐标系:

ax.legend(bbox_to_anchor=(1, 1), bbox_transform=fig.transFigure)

自定义图例放置的更多示例:

fig, ax_dict = plt.subplot_mosaic([['top', 'top'], ['bottom', 'BLANK']], empty_sentinel="BLANK")
ax_dict['top'].plot([1, 2, 3], label="test1")
ax_dict['top'].plot([3, 2, 1], label="test2")
# Place a legend above this subplot, expanding itself to
# fully use the given bounding box.
ax_dict['top'].legend(bbox_to_anchor=(0., 1.02, 1., .102), loc='lower left',
                      ncols=2, mode="expand", borderaxespad=0.)

ax_dict['bottom'].plot([1, 2, 3], label="test1")
ax_dict['bottom'].plot([3, 2, 1], label="test2")
# Place a legend to the right of this smaller subplot.
ax_dict['bottom'].legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0.)
legend guide

Figure图例

有时,相对于(sub)figure放置图例比放置单个 Axes 更有意义。通过使用constrained layout 并在 loc 关键字参数的开头指定 “outside”,图例绘制在(sub)figure的 Axes 之外。

fig, axs = plt.subplot_mosaic([['left', 'right']], layout='constrained')

axs['left'].plot([1, 2, 3], label="test1")
axs['left'].plot([3, 2, 1], label="test2")

axs['right'].plot([1, 2, 3], 'C2', label="test3")
axs['right'].plot([3, 2, 1], 'C3', label="test4")
# Place a legend to the right of this smaller subplot.
fig.legend(loc='outside upper right')
legend guide
ucl = ['upper', 'center', 'lower']
lcr = ['left', 'center', 'right']
fig, ax = plt.subplots(figsize=(6, 4), layout='constrained', facecolor='0.7')

ax.plot([1, 2], [1, 2], label='TEST')
# Place a legend to the right of this smaller subplot.
for loc in [
        'outside upper left',
        'outside upper center',
        'outside upper right',
        'outside lower left',
        'outside lower center',
        'outside lower right']:
    fig.legend(loc=loc, title=loc)

fig, ax = plt.subplots(figsize=(6, 4), layout='constrained', facecolor='0.7')
ax.plot([1, 2], [1, 2], label='test')

for loc in [
        'outside left upper',
        'outside right upper',
        'outside left lower',
        'outside right lower']:
    fig.legend(loc=loc, title=loc)
legend guide legend guide

同一Axes上的多个图例

有时,将图例条目拆分为多个图例会更清楚。

fig, ax = plt.subplots()
line1, = ax.plot([1, 2, 3], label="Line 1", linestyle='--')
line2, = ax.plot([3, 2, 1], label="Line 2", linewidth=4)

# Create a legend for the first line.
first_legend = ax.legend(handles=[line1], loc='upper right')

# Add the legend manually to the Axes.
ax.add_artist(first_legend)

# Create another legend for the second line.
ax.legend(handles=[line2], loc='lower right')

plt.show()
legend guide

subplot_mosaic

对于更复杂的布局,例如跨越布局的多列/行或将 Figure 的某些区域留空的 Axes,可以使用gridspec.GridSpec完成。

Figure.subplot_mosaic 旨在提供一个界面来直观地布置你的 Axes以简化这个过程。

这个接口支持命名你的 Axes。Figure.subplot_mosaic 返回一个字典,该字典以用于布局 Figure 的labels为键。通过返回带有名称的数据结构,可以更轻松地编写独立于 Figure 布局的绘图代码。

如果我们想要一个 2x2 的网格,我们可以使用 Figure.subplots,它返回一个二维的axes.Axes ,我们可以索引以进行绘图的Axes。

import matplotlib.pyplot as plt
import numpy as np

# Helper function used for visualization in the following examples
def identify_axes(ax_dict, fontsize=48):
    """
    Helper to identify the Axes in the examples below.

    Draws the label in a large font in the center of the Axes.

    Parameters
    ----------
    ax_dict : dict[str, Axes]
        Mapping between the title / label and the Axes.
    fontsize : int, optional
        How big the label should be.
    """
    kw = dict(ha="center", va="center", fontsize=fontsize, color="darkgrey")
    for k, ax in ax_dict.items():
        ax.text(0.5, 0.5, k, transform=ax.transAxes, **kw)
np.random.seed(19680801)
hist_data = np.random.randn(1_500)

fig = plt.figure(layout="constrained")
ax_array = fig.subplots(2, 2, squeeze=False)

ax_array[0, 0].bar(["a", "b", "c"], [5, 7, 9])
ax_array[0, 1].plot([1, 2, 3])
ax_array[1, 0].hist(hist_data, bins="auto")
ax_array[1, 1].imshow([[1, 2], [2, 1]])

identify_axes(
    {(j, k): a for j, r in enumerate(ax_array) for k, a in enumerate(r)},
)
mosaic

使用 Figure.subplot_mosaic我们可以生成相同的Axes,但为 Axes 提供语义名称。

fig = plt.figure(layout="constrained")
ax_dict = fig.subplot_mosaic(
    [
        ["bar", "plot"],
        ["hist", "image"],
    ],
)
ax_dict["bar"].bar(["a", "b", "c"], [5, 7, 9])
ax_dict["plot"].plot([1, 2, 3])
ax_dict["hist"].hist(hist_data)
ax_dict["image"].imshow([[1, 2], [2, 1]])
identify_axes(ax_dict)

Figure.subplotsFigure.subplot_mosaic 之间的主要区别在于返回值。前者返回用于索引访问的数组,而后者返回将标签映射到axes.Axes的实例。

字符串简写

通过将 Axes 标签限制为单个字符,我们可以将我们想要的 Axes “绘制”为 “ASCII art”。以下内容:

mosaic = """
    AB
    CD
    """
fig = plt.figure(layout="constrained")
ax_dict = fig.subplot_mosaic(mosaic)
identify_axes(ax_dict)

将给我们 4 个轴,布置在 2x2 网格中,并生成与上述相同的布局(但现在标签为 {“A”, “B”, “C”, “D”} 而不是 {“bar”, “plot”, “hist”, “image”})。

mosaic

或者,可以使用更紧凑的字符串表示法:

mosaic = "AB;CD"

其中 “;” 用作行分隔符而不是换行符。

跨多行/多列的Axes

指定一个 Axes 应该跨越几行或几列, Figure.subplot_mosaic 可以实现,而 Figure.subplots 不能实现。

如果我们想重新排列这四个Axes,让 "C" 在底部是一个水平跨度,而 "D" 是一个在右边的垂直跨度,我们可以这样做:

axd = plt.figure(layout="constrained").subplot_mosaic(
    """
    ABD
    CCD
    """
)
identify_axes(axd)
mosaic

如果我们不想用Axes填充 Figure 中的所有空格,我们可以将网格中的一些空格指定为空白:

axd = plt.figure(layout="constrained").subplot_mosaic(
    """
    A.C
    BBB
    .D.
    """
)
identify_axes(axd)
mosaic

如果更喜欢使用另一个字符(而不是句点 “.”)来标记空格,我们可以使用 empty_sentinel 来指定要使用的字符:

axd = plt.figure(layout="constrained").subplot_mosaic(
    """
    aX
    Xb
    """,
    empty_sentinel="X",
)
identify_axes(axd)
mosaic

在内部,我们使用的字母没有任何意义,任何 Unicode 代码点都是有效的:

axd = plt.figure(layout="constrained").subplot_mosaic(
    """αб
       ℝ☢"""
)
identify_axes(axd)
mosaic

控制mosaic创建

此功能构建在 gridspec 之上,您可以将关键字参数传递给底层gridspec.GridSpec

在这种情况下,如果想使用subplot_mosaic来指定排列,并设置行/列的相对宽度。可以使用gridspec.GridSpecheight_ratioswidth_ratiosFigure.subplot_mosaic 调用时配置。

axd = plt.figure(layout="constrained").subplot_mosaic(
    """
    .a.
    bAc
    .d.
    """,
    # set the height ratios between the rows
    height_ratios=[1, 3.5, 1],
    # set the width ratios between the columns
    width_ratios=[1, 3.5, 1],
)
identify_axes(axd)
mosaic

Constrained布局

使用Constrained layout使plots干净地适应Figure。

Constrained layout会自动调整subplots,以便tick labels、Legends和colorbars等修饰不会重叠,同时仍保留用户请求的逻辑布局。

Constrained layout (约束布局) 类似于 Tight layout (紧密布局),但要灵活得多。

通常需要在将任何 Axes 添加到Figure之前激活 Constrained layout。有两种方法:

  • 使用 subplotsfiguresubplot_mosaic 的相应参数,例如:

    plt.subplots(layout="constrained")
    
  • 通过 rcParams 激活,例如:

    plt.rcParams['figure.constrained_layout.use'] = True
    

Warning:调用 tight_layout 将关闭 Constrained Layout

简单示例

使用默认的 Axes 布局时,axes title、axis label或tick labels有时会超出图窗区域,从而被剪切。

import matplotlib.pyplot as plt
import numpy as np

import matplotlib.colors as mcolors
import matplotlib.gridspec as gridspec

plt.rcParams['savefig.facecolor'] = "0.8"
plt.rcParams['figure.figsize'] = 4.5, 4.
plt.rcParams['figure.max_open_warning'] = 50

def example_plot(ax, fontsize=12, hide_labels=False):
    ax.plot([1, 2])

    ax.locator_params(nbins=3)
    if hide_labels:
        ax.set_xticklabels([])
        ax.set_yticklabels([])
    else:
        ax.set_xlabel('x-label', fontsize=fontsize)
        ax.set_ylabel('y-label', fontsize=fontsize)
        ax.set_title('Title', fontsize=fontsize)

fig, ax = plt.subplots(layout=None)
example_plot(ax, fontsize=24)
Title

为防止这种情况,需要调整 Axes 的位置。对于subplots,可以通过使用 Figure.subplots_adjust 调整subplots参数来手动完成。但是,使用 layout=“constrained” 关键字参数指定您的图形将自动进行调整。

fig, ax = plt.subplots(layout="constrained")
example_plot(ax, fontsize=24)
Title

当有多个subplots时,经常会看到不同 Axes 的标签相互重叠。

Title, Title, Title, Title

在对 plt.subplots 的调用中指定 layout=“constrained” 可以将布局被正确约束。

fig, axs = plt.subplots(2, 2, layout="constrained")
for ax in axs.flat:
    example_plot(ax)
Title, Title, Title, Title

Colorbars

如果使用 Figure.colorbar 创建Colorbars,则需要为其腾出空间。Constrained layout 会自动执行此操作。请注意,如果指定 use_gridspec=True,它将被忽略,因为此选项通过 tight_layout 改进布局。

Suptitle

Constrained layout (约束布局)也可以为 suptitle 腾出空间。

fig, axs = plt.subplots(2, 2, figsize=(4, 4), layout="constrained")
for ax in axs.flat:
    im = ax.pcolormesh(arr, **pc_kwargs)
fig.colorbar(im, ax=axs, shrink=0.6)
fig.suptitle('Big Suptitle')
Big Suptitle

Legends

图例可以放置在其父轴之外。Constrained layout 旨在为 Axes.legend() 处理此问题。但是,Constrained layout 还不处理通过 Figure.legend() 创建的图例。

fig, ax = plt.subplots(layout="constrained")
ax.plot(np.arange(10), label='This is a plot')
ax.legend(loc='center left', bbox_to_anchor=(0.8, 0.5))
constrainedlayout guide

但是,这将从subplot布局中窃取空间:

fig, axs = plt.subplots(1, 2, figsize=(4, 2), layout="constrained")
axs[0].plot(np.arange(10))
axs[1].plot(np.arange(10), label='This is a plot')
axs[1].legend(loc='center left', bbox_to_anchor=(0.8, 0.5))
constrainedlayout guide

解决这种问题的更好方法是简单地使用 Figure.legend 提供的 legend 方法。

Padding and spacing内边距

Axes 之间的内边距在水平方向上由 w_padwspace 控制,垂直方向由 h_padhspace 控制。这些可以通过 set 进行编辑。w/h_pad 是Axes周围的最小间距,以英寸为单位:

fig, axs = plt.subplots(2, 2, layout="constrained")
for ax in axs.flat:
    example_plot(ax, hide_labels=True)
fig.get_layout_engine().set(w_pad=4 / 72, h_pad=4 / 72, hspace=0, wspace=0)
constrainedlayout guide

subplots之间的间距由 wspacehspace 进一步设置。这些被指定为整个subplot组大小的分数。如果这些值小于 w_padh_pad,则改用固定pading。请注意,在下面,边缘的间距与上面没有变化,但subplots之间的间距发生了变化。

fig, axs = plt.subplots(2, 2, layout="constrained")
for ax in axs.flat:
    example_plot(ax, hide_labels=True)
fig.get_layout_engine().set(w_pad=4 / 72, h_pad=4 / 72, hspace=0.2, wspace=0.2)
constrainedlayout guide

如果有两个以上的列,则 wspace 在它们之间共享,因此此处 wspace 一分为二,每列之间的 wspace 为 0.1:

fig, axs = plt.subplots(2, 3, layout="constrained")
for ax in axs.flat:
    example_plot(ax, hide_labels=True)
fig.get_layout_engine().set(w_pad=4 / 72, h_pad=4 / 72, hspace=0.2, wspace=0.2)
constrainedlayout guide

rcParams

可以在脚本或 matplotlibrc 文件中设置五个 rcParams参数。它们都有前缀 figure.constrained_layout

  • use:是否使用约束布局。默认值为 False
  • w_padh_pad:在 Axes 对象周围填充。表示英寸的浮点数。默认值为 3./72英寸(3 pts)
  • wspacehspace:subplots组之间的空间。Float 表示要分隔的子图宽度的一小部分。默认值为 0.02。

手动设置 Axes 位置

手动调用 set_position 将设置 Axes, constrained layout不再对其产生影响。

fig, axs = plt.subplots(1, 2, layout="constrained")
example_plot(axs[0], fontsize=12)
axs[1].set_position([0.2, 0.2, 0.4, 0.4])
Title

固定纵横比Axes的网格:compresse布局

Constrained layout在 Axes 的 “原始” 位置网格上运行。但是,当轴具有固定的纵横比时,一侧通常会变短,并在缩短的方向上留下较大的间隙。在下图中,Axes是方形的,但Fgiure相当宽,因此存在水平间隙:

fig, axs = plt.subplots(2, 2, figsize=(5, 3),
                        sharex=True, sharey=True, layout="constrained")
for ax in axs.flat:
    ax.imshow(arr)
fig.suptitle("fixed-aspect plots, layout='constrained'")
fixed-aspect plots, layout='constrained'

解决此问题的一种明显方法是使Figure大小更方正,但是,缩小间隙需要反复试验。对于简单的 Axes 网格,我们可以使用 layout="compressed" 来为我们完成这项工作:

fig, axs = plt.subplots(2, 2, figsize=(5, 3),
                        sharex=True, sharey=True, layout='compressed')
for ax in axs.flat:
    ax.imshow(arr)
fig.suptitle("fixed-aspect plots, layout='compressed'")
fixed-aspect plots, layout='compressed'

手动关闭 Constrained 布局

Constrained layout通常会调整Figure每次绘制上的 Axes 位置。如果你想获取 constrained layout 提供的间距但不更新它,然后进行初始绘制,然后调用 fig.set_layout_engine('none')。这对于tick labels可能更改长度的动画可能很有用。

限制

不兼容的功能

Constrained layout将与 pyplot.subplot 一起使用,但前提是每次调用的行数和列数相同。

所以下面的工作正常:

fig = plt.figure(layout="constrained")

ax1 = plt.subplot(2, 2, 1)	# 2行2列
ax2 = plt.subplot(2, 2, 3)	# 2行2列
# third Axes that spans both rows in second column:
ax3 = plt.subplot(2, 2, (2, 4))	# 2行2列

example_plot(ax1)
example_plot(ax2)
example_plot(ax3)
plt.suptitle('Homogenous nrows, ncols')
Homogenous nrows, ncols, Title, Title, Title

但以下情况会导致布局不佳:

fig = plt.figure(layout="constrained")

ax1 = plt.subplot(2, 2, 1)
ax2 = plt.subplot(2, 2, 3)
ax3 = plt.subplot(1, 2, 2)

example_plot(ax1)
example_plot(ax2)
example_plot(ax3)
plt.suptitle('Mixed nrows, ncols')
Mixed nrows, ncols, Title, Title, Title

其他注意事项

  • 约束布局仅考虑tick labels、axis labels、title和legends。因此,其他Artist可能会被剪切,也可能重叠。
  • 它假定tick labels、axis labels和title所需的额外空间与 Axes 的原始位置无关。这通常是正确的,但在极少数情况下并非如此。
  • 如果Artist使用的 Axes 坐标超出 Axes 边界,则在添加到 Axes 时将导致不寻常的布局。

Tight布局

如何使用tight布局将绘图清晰地融入Figure中。

tight_layout 会自动调整subplots参数,以便subplots适合figure区域。这是一项实验性功能,可能不适用于某些情况。它仅检查tick labels、axis labels和title的范围。

tight_layout 的替代方案是 constrained_layout

简单示例

使用默认的 Axes 布局时,axes title, axis labels, 或 tick labels 有时会超出figure区域,从而被剪切。

import matplotlib.pyplot as plt
import numpy as np

plt.rcParams['savefig.facecolor'] = "0.8"

def example_plot(ax, fontsize=12):
    ax.plot([1, 2])
    ax.locator_params(nbins=3)
    ax.set_xlabel('x-label', fontsize=fontsize)
    ax.set_ylabel('y-label', fontsize=fontsize)
    ax.set_title('Title', fontsize=fontsize)

plt.close('all')
fig, ax = plt.subplots()
example_plot(ax, fontsize=24)
Title

为防止这种情况,需要调整 Axes 的位置。对于subplots,这可以通过使用 Figure.subplots_adjust 调整子图参数来手动完成。Figure.tight_layout 会自动执行此操作。

fig, ax = plt.subplots()
example_plot(ax, fontsize=24)
plt.tight_layout()

当有多个子图时,经常会看到不同 Axes 的标签相互重叠。

plt.close('all')

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)
example_plot(ax1)
example_plot(ax2)
example_plot(ax3)
example_plot(ax4)
Title, Title, Title, Title

tight_layout()还将调整subplots之间的间距以最大程度地减少重叠。

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)
example_plot(ax1)
example_plot(ax2)
example_plot(ax3)
example_plot(ax4)
plt.tight_layout()
Title, Title, Title, Title

可以接受 padw_padh_pad 的关键字参数。这些用于控制Figure边框周围和subplots之间的额外填充,填充以 fontsize 的分数指定。

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)
example_plot(ax1)
example_plot(ax2)
example_plot(ax3)
example_plot(ax4)
plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0)
Title, Title, Title, Title

即使subplots的大小不同,只要它们的网格规范兼容,也可以正常工作。在下面的示例中,ax1ax2 是 2x2 网格的子图,而 ax3 是 1x2 网格的子图。

plt.close('all')
fig = plt.figure()

ax1 = plt.subplot(221)
ax2 = plt.subplot(223)
ax3 = plt.subplot(122)

example_plot(ax1)
example_plot(ax2)
example_plot(ax3)

plt.tight_layout()
Title, Title, Title

注意事项

  • 默认情况下,会考虑 Axes 上的所有Artist。要从布局计算中删除Artist,您可以调用 Artist.set_in_layout
  • 假设Artist所需的额外空间独立于 Axes 的原始位置。这通常是正确的,但在极少数情况下并非如此。

个人示例

Bar

垂直柱状图

创建一个柱状图,并且在每个柱子的上方显示百分比:

image-20240820221102259

代码实现:

import matplotlib.pyplot as plt  
# 示例数据  
categories = ['A', 'B', 'C', 'D']  
values = [0.23, 0.45, 0.56, 0.78]  # 这些值将被转换为百分数  
# 创建一个figure和axes  
fig, ax = plt.subplots()  
# 绘制柱状图  
bars = ax.bar(categories, values)  
# 为每个柱子添加标签,将值转换为百分数  
ax.bar_label(bars, labels=[f' {val*100:.2f}%' for val in values])  
# 设置标题和标签  
ax.set_title('Bar Chart with Percentage Labels')  
ax.set_xlabel('Categories')  
ax.set_ylabel('Values (%)')  
# 显示图表  
plt.show()

水平柱状图

创建一个水平柱状图,并且在柱子的底部显示百分比:

image-20240820220506499
import matplotlib.pyplot as plt  
import numpy as np  

# 示例数据  
categories = ['A', 'B', 'C', 'D']  
values = np.random.rand(4)  # 生成0到100之间的随机小数  
# 创建一个figure和axes  
fig, ax = plt.subplots()  
# 绘制水平柱状图  
bars = ax.barh(categories, values, color='skyblue')  
ax.bar_label(bars, labels=[f' {val*100:.2f}%' for val in values])  
# 设置标题和标签  
ax.set_title('Horizontal Bar Chart with Percent Labels')  
ax.set_xlabel('Percentage')  
# 显示图表  
plt.show()

柏拉图

红色的为累加百分比,柱状图为每个类型的值。

image-20240820222021852

代码:

import matplotlib.pyplot as plt  
import numpy as np  
  
# 示例数据:分类和对应的值  
categories = ['Category A', 'Category B', 'Category C', 'Category D', 'Category E']  
values = [30, 20, 15, 10, 25]  
# 计算累积和  
cumulative_values = np.cumsum(values)
# 计算累积百分比  
cumulative_percentages = cumulative_values / cumulative_values[-1] * 100
percentages = []  
# 遍历列表,计算并打印每个元素的百分比  
for number in values:  
    percentages.append((number / sum(values)))
# 创建图形和坐标轴  
fig, ax1 = plt.subplots()  
# 绘制柱状图  
color = 'tab:blue'  
ax1.set_xlabel('Categories')  
ax1.set_ylabel('Values', color=color)  
bars = ax1.bar(categories, values, color=color)  
ax1.bar_label(bars, labels=[f' {val*100:.2f}%' for val in percentages])  
ax1.tick_params(axis='y', labelcolor=color)  
# 绘制累积百分比的折线图  
ax2 = ax1.twinx()  # 创建共享x轴的第二个y轴  
color = 'tab:red'  
ax2.set_ylabel('Cumulative Percentage (%)', color=color)  
ax2.plot(categories, cumulative_percentages, color=color, marker='D', ms=7, linestyle='-')  
ax2.tick_params(axis='y', labelcolor=color)
# 设置图表标题  
fig.suptitle('Pareto Chart Example')  
# 显示图形  
plt.show()

改进:在柏拉图下方显示一个table

image-20240908151122019
import matplotlib.pyplot as plt  
import numpy as np  

# 示例数据:分类和对应的值  
categories = ['Category A', 'Category B', 'Category C', 'Category D', 'Category E']  
values = [30, 20, 15, 10, 25]  
cumulative_values = np.cumsum(values)   # 计算累积和
cumulative_percentages = cumulative_values / cumulative_values[-1] * 100    # 计算累积百分比
percentages = []
for number in values:   # 遍历列表,计算并打印每个元素的百分比
    percentages.append((number / sum(values)))
# 创建图形和坐标轴  
fig, ax1 = plt.subplots()  
# 绘制柱状图  
color = 'tab:blue'
ax1.set_ylabel('Values', color=color)  
bars = ax1.bar(categories, values, color=color)  
ax1.bar_label(bars, labels=[f' {val*100:.2f}%' for val in percentages])  
ax1.tick_params(axis='y', labelcolor=color)  
# 绘制累积百分比的折线图  
ax2 = ax1.twinx()  # 创建共享x轴的第二个y轴  
color = 'tab:red'  
ax2.set_ylabel('Cumulative Percentage (%)', color=color)  
ax2.plot(categories, cumulative_percentages, color=color, marker='D', ms=7, linestyle='-')  
ax2.tick_params(axis='y', labelcolor=color)
# 添加table
cell_text = [categories, values, percentages, cumulative_percentages]
columns = ["Type", "Values", "Percentages", "Cumulative"]
ax1.table(cellText=cell_text, rowLabels=columns, loc='bottom', cellLoc='center')
plt.xticks([])
plt.subplots_adjust(left=0.2, bottom=0.1)
fig.tight_layout() 
# 设置图表标题  
fig.suptitle('Pareto Chart Example')
plt.tight_layout()
# 显示图形  
plt.show()

轴坐标

在 Matplotlib 中,轴坐标(也称为归一化坐标)是一种特殊的坐标系统,它用于相对于当前轴(Axes)的位置和大小来定位图形元素。轴坐标的范围是从 0 到 1,其中 (0, 0) 表示轴的左下角,(1, 1) 表示轴的右上角。这种坐标系统不依赖于数据的实际值或图形的物理尺寸,而是基于轴的可视化区域。

使用轴坐标可以方便地放置图形元素,如标题、图例、注释或自定义的图形(如矩形、圆形等),而无需担心数据范围或图形窗口大小的变化。这是因为轴坐标是相对于轴的可视化区域进行解释的,而不是相对于数据或图形窗口的像素尺寸。

在 Matplotlib 中,你可以通过传递 transform=ax.transAxes 参数给图形元素(如 RectangleText 等)来指定使用轴坐标。ax 是你的 Axes 对象,而 transAxes 是该轴对象的一个属性,表示轴坐标变换。

在table上绘制图形和线条

ax.table创建的时候,使用的是轴坐标系统,但是ax.table的get_window_extent()方法返回的是像素坐标,而不是轴坐标:

bbox = the_table.get_window_extent()
x0, y0 = bbox.x0, bbox.y0  				# 获取左下角的坐标  
width, height = bbox.width, bbox.height # 获取宽度和高度

下面讲解在ax.table上绘制各种图形时的坐标系统:

  • Rectangle:绘制矩形,使用的是轴坐标系统

    # :                +------------------+
    # :                |                  |
    # :              height               |
    # :                |                  |
    # :               (xy)---- width -----+
    class matplotlib.patches.Rectangle(xy, width, height, *, angle=0.0, rotation_point='xy', **kwargs)
    
  • axhline:绘制水平直线,使用的是轴坐标系统

    '''
    y: 水平线的数据坐标中的y位置, 使用的是像素坐标系
    xmin: 应介于0和1之间,0表示绘图的最左侧,1表示绘图的最右侧, 使用的是轴坐标系。
    xmax: 应介于0和1之间,0表示绘图的最左侧,1表示绘图的最右侧, 使用的是轴坐标系。
    '''
    Axes.axhline(y=0, xmin=0, xmax=1, **kwargs)
    
  • axvline:绘制垂直线,使用的是轴坐标系统

    '''
    x: 垂直线的数据坐标中的x位置, 使用的是像素坐标系
    ymin: 应介于0和1之间,0是绘图的底部,1是绘图的顶部, 使用的是轴坐标系。
    ymax: 应介于0和1之间,0是绘图的底部,1是绘图的顶部, 使用的是轴坐标系。
    '''
    Axes.axvline(x=0, ymin=0, ymax=1, **kwargs)
    
  • Ellipse:绘制椭圆,使用的是像素坐标系统

    # xy: 椭圆中心的 xy 坐标
    # width: 长轴的总长度
    # height: 短轴的总长度
    class matplotlib.patches.Ellipse(xy, width, height, *, angle=0, **kwargs)
    

以下是一个举例,创建一个table,然后在上面绘制水平直线、垂直直线、矩形、椭圆形:

image-20240908165124140

实现代码:

import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from matplotlib.patches import Ellipse
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas

cell_width_px = 0
cell_height_px = 0
width_px = 0
height_px = 0
# 对于像素坐标系统,需要计算每个Cell的宽度和高度
def calculate_cell_size(fig, num_rows, num_cols):
    global width_px
    global height_px
    global cell_width_px
    global cell_height_px
    # 绘制椭圆图形(这一步是必需的,以便FigureCanvas可以捕获渲染后的图形)
    fig.canvas.draw()
    # 获取FigureCanvas对象(这里我们假设使用的是Agg后端,但你可以根据你的后端进行调整)  
    canvas = FigureCanvas(fig)
    # 获取图形的宽度和高度(以像素为单位)  
    width_px, height_px = canvas.get_width_height()
    # 估算每个单元格的像素宽度和高度, 减去边距、标签等
    cell_width_px = width_px / num_cols - 16    # 16只是人为估计出来的误差
    cell_height_px = height_px / num_rows + 19  # 19只是人为估计出来的误差
    print(f"Estimated width in pixels: {width_px}")
    print(f"Estimated height in pixels: {height_px}")
    print(f"Estimated cell width in pixels: {cell_width_px}")
    print(f"Estimated cell height in pixels: {cell_height_px}")


# x0, y0, x1, y1代表的是cell的位置
# :      +------------------+
# :      |                  |
# :    height               |
# :      |                  |
# :     (xy)---- width -----+
def draw_rectangle(ax, rows, columns, xy, width, heights):
    # 由于创建表时已经设置了轴坐标范围为(0, 0)->(1, 1), 因此计算单个cell的宽和高就很方便
    cell_width = 1 / columns
    cell_height = 1 / rows
    # 由于表格中央被我们当作坐标原点, 但是极坐标的坐标原点默认为左下角, 因此需要添加一个偏移
    start_x = (xy[1] + columns / 2 )* cell_width
    start_y = (xy[0] + rows / 2) * cell_height
    rect_width = width * cell_width
    rect_height = heights * cell_height
    print (start_x, start_y, rect_width, rect_height)
    rect = Rectangle((start_x, start_y), rect_width, rect_height, linewidth=1.5,
                    edgecolor='blue', facecolor='none', transform=ax.transAxes)
    ax.add_patch(rect)


# 绘制椭圆形
# xy: 椭圆原点(x, y)
# width: 水平轴直径
# height: 垂直轴执行
def draw_ellipse(ax, xy, width, height):
    ellipse_width = cell_width_px * width       # 水平轴
    ellipse_height = cell_height_px * height    # 垂直轴
    x1 = xy[0] * cell_width_px
    y1 = xy[1] * cell_height_px
    ellipse = Ellipse((x1, y1), ellipse_width, ellipse_height, edgecolor='red', facecolor='none')
    ax.add_patch(ellipse)


# 在轴上添加一个水平线: matplotlib.pyplot.axhline(y=0, xmin=0, xmax=1, **kwargs)
# '''
#  :       +--------------------+
#  :       |                    |
# (x0, y0) +----------- y1 -----+
#  :       |                    |
#  :       +--------------------+
# '''
def draw_axhline(ax, columns, x0, y0, y1):
    cell_width = 1 / columns                # 创建表时已经设置了轴坐标范围为(0, 0)->(1, 1)
    y = x0 * cell_height_px                 # 使用的是数据(像素)坐标系
    x_min = (y0 + columns / 2) * cell_width # 使用的是轴坐标系
    x_max = (y1 + columns / 2) * cell_width # 使用的是轴坐标系
    print (y, x_min, x_max)
    ax.axhline(y, x_min, x_max, linewidth=1.5, color='red')


# 在轴上添加一条垂直线: Axes.axvline(x=0, ymin=0, ymax=1, **kwargs)
# '''
#   :         y1-------------------+
#   :         |                    |
#   :         |--------------------+
#   :         |                    |
#   :(x0, y0) +--------------------+
# '''
def draw_axvline(ax, rows, y0, x0, x1):
    cell_height = 1 / rows                  # 创建表时已经设置了轴坐标范围为(0, 0)->(1, 1)
    x = y0 * cell_width_px                  # 使用的是数据(像素)坐标系
    y_min = (x0 + rows / 2) * cell_height   # 使用的是轴坐标系
    y_max = (x1 + rows / 2) * cell_height   # 使用的是轴坐标系
    print (x, y_min, y_max)
    ax.axvline(x, y_min, y_max, linewidth=2, color='cyan')


if __name__ == '__main__':
    rows = 8
    columns = 10
    cell_text = [[1 for x in range(columns)] for y in range(rows)]
    fig, ax = plt.subplots()
    the_table = ax.table(
        cellText=cell_text, 
        loc='center', cellLoc='center',
        bbox=[0, 0, 1, 1],  # 为了方便, 将轴坐标范围设置为[0, 0]->[1, 1]
        edges = 'closed',
    )
    calculate_cell_size(fig, rows, columns)
    # 将坐标原点设置到table的中央
    ax.set_xlim(-height_px/2, height_px/2)  # 设置坐标轴原点
    ax.set_ylim(-width_px/2, width_px/2)    # 设置坐标轴原点
    # 绘图
    draw_rectangle(ax, rows, columns, (1, 1), 2, 2)
    draw_ellipse(ax, (1, 1), 6, 6)
    draw_axhline(ax, columns, -1, 1, 3)
    draw_axvline(ax, rows, -1, 1, 3)

    plt.show()

实现效果:

image-20240908210705243

标签:plot,plt,Python,Axes,Matplotlib,set,fig,ax,绘制
From: https://www.cnblogs.com/mrlayfolk/p/18403483

相关文章

  • 【Python】72行实现代码行数统计,简单实用!
    0.前言最近突然想知道自己总共写了多少行代码,于是做了这样一个小工具……1.准备工作先考虑一下希望得到的效果:Language(语言)Lines(代码行数)Size(代码文件总大小)Files(代码文件总数)A12345300KB193B2345165KB98如上,程序输出一个表格,将代码行数作为关键字......
  • python装饰器\迭代器\生成器
    1.迭代器\生成器#斐波那契数列deffib(n):"""生成斐波那契数列的前n个数字。参数:n--要生成的斐波那契数列的长度返回:生成器,产出斐波那契数列的前n个数字。"""a,b=0,1#初始化斐波那契数列的前两个数字content=......
  • Python函数之lambda函数
    温馨提示:如果读者没有学过def定义函数,请先看这里定义形式<函数名>=lambda<参数列表>:<返回值>等同于:def<函数名>(<参数列表>): return<返回值>也可以定义为匿名函数(没有名字的函数):lambda<参数列表>:<返回值>可以确认lambda函数对象的类型与def定义的一样,都是fu......
  • 【开源推荐】MYScrcpy,不仅仅是python实现的Android投屏工具,更是开发测试新选择
    MYScrcpyV1.5.7python语言实现的一个Scrcpy客户端。包含完整的视频、音频、控制解析及展现,开发友好,引入即用!采用DearPyGui作为主要GUI。支持窗口位置记忆、右键手势控制、断线重连、虚拟摄像头投屏、中文输入,锁屏密码解锁等功能。高速模式使用pygame作为鼠标及键......
  • 【python爬虫】从腾讯API爬取美国疫情数据+制表
    最近(文章撰写时间为2020/6/118:40)疫情在中国情况好转,却在美国暴虐。本篇文章将爬取腾讯提供的美国疫情数据并制表。1.爬取数据调用API接口接口:https://api.inews.qq.com/newsqa/v1/automation/modules/list?modules=FAutoCountryMerge观察得到的数据:{ ..., "data":{ ......
  • Python和MATLAB(Java)及Arduino和Raspberry Pi(树莓派)点扩展函数导图
    ......
  • 【Python使用】嘿马python高级进阶全体系教程第9篇:HTTP 协议,1. HTTP 协议的介绍【附
    本教程的知识点为:操作系统1.常见的操作系统4.小结ls命令选项2.小结mkdir和rm命令选项1.mkdir命令选项压缩和解压缩命令1.压缩格式的介绍2.tar命令及选项的使用3.zip和unzip命令及选项的使用4.小结编辑器vim1.vim的介绍2.vim的工作模式3.vim的末行模......
  • Python毕业设计基于Django的川剧戏剧京剧戏曲科普平台 含选座功能
    文末获取资源,收藏关注不迷路文章目录一、项目介绍1管理员功能模块前台系统功能模块二、主要使用技术三、研究内容四、核心代码五、文章目录一、项目介绍随着我国经济的高速发展与人们生活水平的日益提高,人们对生活质量的追求也多种多样。尤其在人们生活节奏不断加......
  • 【Python】对象(包括类、函数)取名方法
    先上干货,通用的:字母:A-Za-z下划线:_数字:0-9(注意:数字不能在开头)理论上可以使用中文变量名,但强烈不建议使用。合法名字举例abcdef GoodCoder AD_fhrygfuigfrA_a_007 __NAME123 _P_T__123456 Cc_Dd _不合法名字举例666Code C++ 1+1=2 (5)4654ty54F 0.123 [email protected]......
  • Python函数之def定义函数
    链接想研究Python函数?看这里函数怎样取名?看这里有参数的函数还可以怎么传参?看这里一、无参数函数结构def<函数名>():#强制要求 <函数体>#可省略 return<返回值>#可省略程序举例用函数的Helloworld程序:#prints'HelloWorld\nDone'#Author:GoodCoder666d......