Python 数据可视化:Matplotlib 基础教程
引言:数据可视化的力量
在当今数据爆炸的时代,我们处理的数据量呈几何级数增长。然而,原始的数据本身往往是枯燥、难以理解的。为了从复杂的数据中提取有价值的洞见、发现隐藏的模式、趋势和异常,数据可视化成为了不可或缺的工具。通过将数据转化为图表、图形和地图等视觉元素,我们能够更直观、更快速地感知数据,从而做出更明智的决策。
Python 作为当前最受欢迎的数据科学编程语言之一,拥有众多功能强大且易于使用的数据可视化库。其中,Matplotlib 是历史最悠久、应用最广泛的基础库。它提供了极其灵活的接口,能够创建从简单的线图到复杂的科学图表,几乎涵盖了所有常见的可视化需求。虽然有时 Matplotlib 的语法可能略显繁琐,但其高度的可定制性使其成为构建任何类型静态图表的强大基石。掌握 Matplotlib,就像掌握了数据可视化的通用语言,为学习其他更高级、更专业的可视化库(如 Seaborn、Plotly 等)打下了坚实的基础。
本教程将带您深入了解 Matplotlib 的基础知识,从安装、核心概念,到创建各种常见图表以及进行基本的图表定制。通过学习本教程,您将能够使用 Matplotlib 绘制出清晰、美观、富有信息量的数据可视化图表。
第一章:准备工作——安装 Matplotlib
在使用 Matplotlib 之前,您需要先确保它已经安装在您的 Python 环境中。如果您使用的是 Anaconda 或 Miniconda 发行版,Matplotlib 通常已经预装。如果没有,或者您使用的是标准的 Python 安装,可以通过 pip 包管理器轻松安装。
打开您的终端或命令提示符,执行以下命令:
bash
pip install matplotlib
如果您还需要处理数值计算,通常会与 NumPy 库一起使用,所以也建议安装 NumPy:
bash
pip install numpy
安装完成后,您就可以在 Python 脚本或交互式环境中导入 Matplotlib 了。Matplotlib 通常与 matplotlib.pyplot
模块一起使用,并且按照约定俗成,通常将其别名为 plt
。
python
import matplotlib.pyplot as plt
import numpy as np # 通常也会用到
导入 matplotlib.pyplot
后,您就可以开始绘制图表了。在某些环境中(如 Jupyter Notebook),您可能需要在代码的开头加上 %matplotlib inline
或 %matplotlib notebook
来确保图表能够直接显示在 Notebook 中。%matplotlib inline
会生成静态图片,而%matplotlib notebook
则提供交互式功能(如缩放、平移)。对于普通 Python 脚本,运行脚本后图表会显示在一个单独的窗口中,您需要调用 plt.show()
来显示它。
第二章:Matplotlib 的核心概念——Figure 和 Axes
理解 Matplotlib 的核心概念对于高效使用它至关重要。最重要的两个概念是 Figure (图) 和 Axes (坐标系/子图)。
-
Figure (图):Figure 是最顶层的容器,可以理解为一张画布或者一个空白的画框。它包含了所有的 Axes、图形元素(如图例、标题等)以及边框、背景等。您可以创建一个或多个 Figure 对象。Figure 对象本身不绘制任何数据,它只是一个容器。
-
Axes (坐标系/子图):Axes 是 Matplotlib 中实际进行绘图的区域。每个 Axes 对象都代表一个独立的坐标系,您可以在其中绘制数据(如线、点、柱状图等)。一个 Figure 中可以包含一个或多个 Axes。在二维绘图中,Axes 通常由 x 轴和 y 轴定义(对于三维绘图,还包括 z 轴)。标题、轴标签、刻度线、图例等都与特定的 Axes 相关联。
理解 Figure 和 Axes 的关系是掌握 Matplotlib 的关键。您可以将 Figure 想象成一个房间,而 Axes 则是房间里的一扇窗户,您通过窗户看外面的风景(数据)。一个房间(Figure)可以有多扇窗户(Axes),每扇窗户看到的风景(绘制的数据)可能是不同的。
Matplotlib 提供了两种主要的接口风格来创建和管理 Figure 和 Axes:
- Pyplot API (基于状态的接口):这是一个更简单、更像 MATLAB 的接口。它通过
matplotlib.pyplot
模块(通常导入为plt
)提供一系列函数(如plt.plot()
,plt.title()
,plt.xlabel()
,plt.show()
)。这种接口会隐式地创建 Figure 和 Axes,并维护当前活动的 Axes,所有函数调用都作用于这个当前的 Axes。对于简单的、单个图表的绘制,这种方法非常方便。 - Object-Oriented (OO) API (面向对象接口):这是一种更强大、更灵活的接口。您显式地创建 Figure 和 Axes 对象,然后调用这些对象的方法来绘制和定制图表(如
ax.plot()
,ax.set_title()
,ax.set_xlabel()
)。对于绘制多个子图、更复杂的布局或需要精细控制图表元素的场景,OO API 是首选。
在本教程中,我们将主要使用 OO API 来讲解,因为它更具扩展性,也是 Matplotlib 官方推荐的更清晰的用法。当然,也会穿插介绍 Pyplot API 的便捷用法。
创建 Figure 和 Axes 的常用方法:plt.subplots()
最常用的创建 Figure 和一个或多个 Axes 的方法是使用 plt.subplots()
函数。
python
fig, ax = plt.subplots() # 创建一个包含一个Axes的Figure
plt.subplots()
会返回一个元组,第一个元素是 Figure 对象,第二个元素是 Axes 对象(如果创建了多个Axes,则返回一个Axes对象的数组)。
如果您想创建包含多个 Axes 的 Figure,可以指定行数和列数:
python
fig, axes = plt.subplots(nrows=2, ncols=2) # 创建一个2x2的Axes网格
此时 axes
是一个 NumPy 数组,您可以通过索引访问每个 Axes 对象,例如 axes[0, 0]
、axes[0, 1]
等。
创建了 fig
和 ax
(或 axes
) 对象后,您就可以开始在 Axes 上绘制数据了。
第三章:绘制常见图表类型
掌握了 Figure 和 Axes 的概念后,我们可以开始绘制具体的图表了。
3.1 线图 (Line Plot)
线图常用于展示数据随时间或另一个连续变量的变化趋势。
使用 OO API 在 Axes 对象上绘制线图:
“`python
import matplotlib.pyplot as plt
import numpy as np
准备数据
x = np.linspace(0, 10, 100) # 生成0到10之间的100个点
y = np.sin(x) # 计算每个点的sin值
创建 Figure 和 Axes
fig, ax = plt.subplots()
在 Axes 上绘制线图
ax.plot(x, y)
显示图表 (在脚本中需要)
plt.show()
“`
使用 Pyplot API 绘制线图(更简洁):
“`python
import matplotlib.pyplot as plt
import numpy as np
准备数据
x = np.linspace(0, 10, 100)
y = np.sin(x)
直接使用 plt 绘制,它会自动创建 Figure 和 Axes
plt.plot(x, y)
显示图表
plt.show()
“`
可以看到,Pyplot API 更直接,但隐藏了 Figure 和 Axes 的创建过程。
绘制多条线:您可以在同一个 Axes 上多次调用 plot()
方法来绘制多条线。
“`python
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
fig, ax = plt.subplots()
ax.plot(x, y1, label=’sin(x)’) # 添加 label 用于图例
ax.plot(x, y2, label=’cos(x)’)
ax.set_title(“Sin and Cosine Functions”) # 设置标题
ax.set_xlabel(“X-axis”) # 设置 x 轴标签
ax.set_ylabel(“Y-axis”) # 设置 y 轴标签
ax.legend() # 添加图例
plt.show()
“`
这里我们引入了设置标题、轴标签和图例的方法,这在所有图表类型中都是通用的。
3.2 散点图 (Scatter Plot)
散点图用于显示两个数值变量之间的关系,每个数据点由一对 (x, y) 坐标表示。
“`python
import matplotlib.pyplot as plt
import numpy as np
准备数据
np.random.seed(0) # 为了结果可复现
x = np.random.rand(50) * 10 # 50个0-10之间的随机数
y = np.random.rand(50) * 10 # 50个0-10之间的随机数
创建 Figure 和 Axes
fig, ax = plt.subplots()
在 Axes 上绘制散点图
ax.scatter(x, y)
ax.set_title(“Simple Scatter Plot”)
ax.set_xlabel(“Feature 1”)
ax.set_ylabel(“Feature 2”)
plt.show()
“`
scatter()
方法可以接受额外的参数来控制点的大小 (s
)、颜色 (c
) 和透明度 (alpha
) 等,这对于可视化更多维度的数据非常有用。
“`python
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(0)
x = np.random.rand(100) * 10
y = np.random.rand(100) * 10
sizes = np.random.rand(100) * 500 # 点的大小
colors = np.random.rand(100) # 点的颜色 (映射到颜色条)
fig, ax = plt.subplots(figsize=(8, 6)) # 设置 Figure 大小
scatter = ax.scatter(x, y, s=sizes, c=colors, alpha=0.7, cmap=’viridis’) # 使用不同的颜色和大小,设置透明度,指定颜色映射
ax.set_title(“Scatter Plot with Size and Color Variation”)
ax.set_xlabel(“X-axis Data”)
ax.set_ylabel(“Y-axis Data”)
fig.colorbar(scatter, ax=ax, label=’Color Value’) # 添加颜色条
plt.show()
“`
3.3 柱状图 (Bar Plot)
柱状图用于比较不同类别之间的数据。柱子的高度代表了该类别的值。
“`python
import matplotlib.pyplot as plt
import numpy as np
准备数据
categories = [‘A’, ‘B’, ‘C’, ‘D’, ‘E’]
values = [25, 40, 30, 35, 20]
创建 Figure 和 Axes
fig, ax = plt.subplots()
在 Axes 上绘制柱状图
ax.bar(categories, values)
ax.set_title(“Bar Plot of Category Values”)
ax.set_xlabel(“Category”)
ax.set_ylabel(“Value”)
plt.show()
“`
对于水平柱状图,可以使用 ax.barh()
方法,此时传入的参数顺序是 ax.barh(categories, values)
。
分组柱状图:绘制多个系列的数据时,需要更精细地控制柱子的位置和宽度。
“`python
import matplotlib.pyplot as plt
import numpy as np
categories = [‘A’, ‘B’, ‘C’, ‘D’, ‘E’]
values1 = [25, 40, 30, 35, 20]
values2 = [30, 35, 25, 30, 40]
x = np.arange(len(categories)) # 类别对应的数值位置
width = 0.35 # 柱子的宽度
fig, ax = plt.subplots()
rects1 = ax.bar(x – width/2, values1, width, label=’Series 1′) # 绘制第一个系列
rects2 = ax.bar(x + width/2, values2, width, label=’Series 2′) # 绘制第二个系列
设置 x 轴刻度和标签
ax.set_xticks(x)
ax.set_xticklabels(categories)
ax.set_title(“Grouped Bar Plot”)
ax.set_xlabel(“Category”)
ax.set_ylabel(“Value”)
ax.legend()
plt.show()
“`
3.4 直方图 (Histogram)
直方图用于展示连续型数据的分布。它将数据范围分割成若干个“ bins”(箱),然后统计落入每个箱子中的数据点的数量。
“`python
import matplotlib.pyplot as plt
import numpy as np
准备数据 (例如:1000个符合标准正态分布的随机数)
data = np.random.randn(1000)
创建 Figure 和 Axes
fig, ax = plt.subplots()
在 Axes 上绘制直方图
ax.hist(data, bins=30) # bins 参数指定箱子的数量或边界
ax.set_title(“Histogram of Random Data”)
ax.set_xlabel(“Value”)
ax.set_ylabel(“Frequency”)
plt.show()
“`
hist()
方法有很多选项可以控制直方图的外观和计算方式,例如 density=True
可以将频率标准化为概率密度,color
设置柱子颜色,edgecolor
设置柱子边缘颜色等。
第四章:图表元素的定制与美化
Matplotlib 提供了丰富的选项来定制图表的各个方面,使其更具表现力和易读性。我们已经在前面的例子中看到了一些基本的定制,下面详细介绍一些常用的定制技巧。
4.1 设置标题、轴标签和图例
这是图表最基本的元素,对于解释图表内容至关重要。
使用 OO API (推荐):
python
ax.set_title("My Plot Title")
ax.set_xlabel("X-axis Label")
ax.set_ylabel("Y-axis Label")
ax.legend() # 显示图例,需要在plot/scatter/bar等函数中设置 label 参数
使用 Pyplot API:
python
plt.title("My Plot Title")
plt.xlabel("X-axis Label")
plt.ylabel("Y-axis Label")
plt.legend()
4.2 控制颜色、线型和标记
在绘制线图和散点图时,可以控制线条和点的外观。
对于 ax.plot()
:
* color
: 线条颜色 (例如: ‘blue’, ‘red’, ‘#FF5733’, ‘C0’, ‘C1’…)
* linestyle
: 线条风格 (例如: ‘-‘, ‘–‘, ‘-.’, ‘:’)
* marker
: 数据点标记 (例如: ‘o’ 圆形, ‘x’ 叉, ‘*’ 星形, ‘.’ 点, ‘,’ 像素, ‘+’ 加号…)
* linewidth
: 线条宽度
* markersize
: 标记大小
这些参数可以单独设置,也可以组合在一个格式字符串中(Pyplot风格常用,但OO API 也支持):
ax.plot(x, y, 'ro--')
表示红色 (‘r’) 圆形标记 (‘o’) 虚线 (‘–‘)。
对于 ax.scatter()
:
* s
: 标记大小 (可以是单个数值或与数据点数量相同的数组)
* c
: 标记颜色 (可以是单个颜色、颜色名称数组、数值数组用于颜色映射)
* marker
: 标记形状
* alpha
: 透明度 (0.0 完全透明 到 1.0 完全不透明)
示例:
“`python
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 50)
y1 = np.sin(x)
y2 = np.cos(x)
fig, ax = plt.subplots()
定制第一条线
ax.plot(x, y1, color=’red’, linestyle=’–‘, marker=’o’, label=’Sin’, linewidth=2, markersize=5)
定制第二条线
ax.plot(x, y2, color=’blue’, linestyle=’-‘, marker=’x’, label=’Cos’, linewidth=1, markersize=8)
ax.legend()
ax.set_title(“Customized Line Plot”)
plt.show()
“`
4.3 设置轴范围和刻度
有时需要调整 x 轴或 y 轴的显示范围,或者自定义刻度位置和标签。
设置轴范围:
python
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
例如:
python
ax.set_xlim(0, 2 * np.pi) # 将 x 轴范围限制在 0 到 2pi
ax.set_ylim(-1.5, 1.5) # 将 y 轴范围设置为 -1.5 到 1.5
设置刻度位置和标签(稍微高级,这里只做简单介绍):
python
ax.set_xticks([0, np.pi/2, np.pi, 3*np.pi/2, 2*np.pi]) # 指定刻度位置
ax.set_xticklabels(['0', r'$\pi/2$', r'$\pi$', r'$3\pi/2$', r'$2\pi$']) # 指定刻度标签 (支持 LaTeX 语法)
4.4 添加网格线
网格线可以帮助读者更容易地读取图表上的值。
“`python
ax.grid(True) # 添加网格线
ax.grid(axis=’y’, linestyle=’–‘) # 只添加水平网格线,使用虚线
“`
4.5 设置图表大小
创建 Figure 时可以指定图表的尺寸(英寸):
python
fig, ax = plt.subplots(figsize=(width_inches, height_inches))
例如:
python
fig, ax = plt.subplots(figsize=(10, 6)) # 创建一个 10x6 英寸的图表
4.6 文本和注解
可以在图表中的特定位置添加文本说明或注解。
python
ax.text(x, y, 'Text', ...) # 在坐标 (x, y) 处添加文本
ax.annotate('Annotation', xy=(arrow_x, arrow_y), xytext=(text_x, text_y),
arrowprops=dict(facecolor='black', shrink=0.05)) # 添加注解和箭头
示例:
python
fig, ax = plt.subplots()
ax.plot(x, y)
ax.text(5, 0.5, 'Peak Value', fontsize=12, color='green') # 在(5, 0.5)添加文本
ax.annotate('Min Value', xy=(7.85, -1), xytext=(6, -0.8),
arrowprops=dict(facecolor='black', shrink=0.05)) # 添加注解带箭头
plt.show()
4.7 使用不同的颜色映射 (Colormaps)
在绘制散点图、热力图等需要将数值映射到颜色的图表时,颜色映射非常有用。Matplotlib 提供了大量的内置颜色映射。
“`python
ax.scatter(…, c=values, cmap=’viridis’) # 将 values 数组映射到 viridis 颜色映射
或者使用其他颜色映射,如 ‘plasma’, ‘inferno’, ‘magma’, ‘cividis’ (感知均匀)
或 ‘Blues’, ‘Greens’, ‘Reds’ (单色系)
或 ‘coolwarm’, ‘bwr’ (发散系)
“`
颜色映射列表可以在 Matplotlib 官方文档中找到。
第五章:处理多个子图 (Subplots)
前面提到,一个 Figure 可以包含多个 Axes,也就是多个子图。plt.subplots()
是创建子图的最常用方法。
“`python
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(8, 6)) # 创建一个2×2的子图网格
axes 是一个二维数组,通过索引访问每个子图
ax1 = axes[0, 0]
ax2 = axes[0, 1]
ax3 = axes[1, 0]
ax4 = axes[1, 1]
在每个 Axes 上绘制不同的图
ax1.plot(x, y1)
ax1.set_title(‘Subplot 1’)
ax2.scatter(x, y2)
ax2.set_title(‘Subplot 2’)
… 在 ax3, ax4 上绘制其他图
自动调整子图之间的间距,避免重叠
plt.tight_layout()
plt.show()
“`
plt.tight_layout()
函数会自动调整子图参数,使之填充整个 figure 区域,并解决子图之间的标签、标题等重叠问题,强烈推荐使用。
对于简单的子图布局,也可以使用 Pyplot API 的 plt.subplot()
函数(注意是 subplot
而不是 subplots
)。它的参数是 nrows, ncols, index
。例如 plt.subplot(2, 2, 1)
表示一个 2×2 网格中的第一个子图(索引从1开始,按行排列)。每次调用 plt.subplot()
都会激活指定的子图,后续的 plt.plot()
, plt.title()
等函数都会作用于这个子图。这种方法对于少量子图且无需精细控制的场景比较方便,但在更复杂的布局或需要对象引用的场景下不如 OO API 灵活。
“`python
使用 Pyplot API 创建子图
plt.figure(figsize=(8, 6)) # 创建一个 Figure (可选,plt会自动创建)
plt.subplot(2, 2, 1) # 激活第一个子图
plt.plot(x, y1)
plt.title(‘Subplot 1’)
plt.subplot(2, 2, 2) # 激活第二个子图
plt.scatter(x, y2)
plt.title(‘Subplot 2’)
… 激活并绘制其他子图
plt.tight_layout()
plt.show()
“`
第六章:保存图表
绘制好的图表通常需要保存到文件中以便分享或在文档中使用。plt.savefig()
或 Figure 对象的 savefig()
方法可以完成这个任务。
“`python
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set_title(“My Plot”)
保存到文件
plt.savefig(“my_plot.png”) # 保存为PNG格式
fig.savefig(“my_plot_fig.png”) # 使用 Figure 对象的 savefig 方法
可以指定格式、分辨率等
plt.savefig(“my_plot.pdf”, format=’pdf’) # 保存为PDF
plt.savefig(“my_plot_high_res.png”, dpi=300) # 保存为高分辨率 PNG (300 dots per inch)
plt.savefig(“my_plot.svg”, format=’svg’) # 保存为SVG (矢量图)
“`
常用的格式包括:png (位图,适合网页), jpg (位图,有损压缩), pdf (矢量图,适合文档), svg (矢量图,适合 웹), eps (矢量图,适合科学出版)。
在调用 plt.savefig()
之后,如果图表窗口仍然是打开的,您可能还需要调用 plt.show()
来显示它(取决于您的环境和设置)。但如果您只是想保存而不显示,可以直接调用 savefig
后结束程序。注意:在调用 plt.show()
之后再调用 plt.savefig()
可能会得到一张空白图片,因为 plt.show()
会清除当前的 Figure。因此,通常先保存,再显示。
第七章:一些进阶和最佳实践建议
- 优先使用 OO API: 对于任何非简单的图表,OO API (
fig, ax = plt.subplots()
) 提供了更清晰、更可控的方式来构建和定制图表。它使得代码更容易理解和维护,特别是在处理多个子图或复杂布局时。 - 添加标题、标签和图例: 务必为您的图表添加描述性的标题、清晰的轴标签和必要的图例,这能极大地提高图表的可读性。
- 选择合适的图表类型: 根据您数据的类型和想要传达的信息,选择最合适的图表类型(趋势用线图,比较用柱状图,分布用直方图,关系用散点图等)。
- 颜色和风格的选择: 使用颜色、线型和标记来区分不同的数据集,但避免使用过多或过于鲜艳的颜色,以免分散注意力。考虑色盲读者,选择感知上均匀的颜色映射。
- 调整 Figure 和 Axes 大小: 合理设置 Figure 的
figsize
可以让图表元素有足够的空间展开,避免拥挤。 - 使用
plt.tight_layout()
: 在绘制多个子图时,几乎总是需要调用plt.tight_layout()
来自动调整间距。 - 高分辨率保存: 当需要将图表用于演示文稿或出版物时,使用较高的
dpi
(dots per inch) 来保存,确保图像清晰。 - 探索 Matplotlib 文档: Matplotlib 的官方文档非常详尽,包含了各种图表类型、定制选项和示例。遇到问题时,查阅文档是最好的方法。
- 学习 Seaborn: 一旦掌握了 Matplotlib 基础,学习 Seaborn 会让您的可视化工作事半功倍。Seaborn 是构建在 Matplotlib 之上的统计数据可视化库,提供了更高级的接口和更美观的默认风格,特别适合处理 Pandas DataFrame 数据。但 Seaborn 的底层仍然是 Matplotlib,理解 Matplotlib 会让您更好地定制 Seaborn 图表。
结论
Matplotlib 是一个功能强大且灵活的 Python 数据可视化库。本教程涵盖了其基础知识,包括安装、Figure 和 Axes 的核心概念、绘制线图、散点图、柱状图、直方图等常见图表类型,以及如何对图表进行定制和美化,最后介绍了如何处理多个子图和保存图表。
虽然 Matplotlib 的语法有时可能显得底层,但正是这种底层控制能力赋予了它无限的灵活性。掌握了 Matplotlib 的基础,您就已经具备了创建各种静态图表的能力,并为进一步学习其他高级可视化库奠定了坚实的基础。
数据可视化是一门艺术也是一门科学。通过不断实践和探索 Matplotlib 的更多功能,您将能够创建出既能准确传达数据信息又具有视觉吸引力的优秀图表。现在,就开始您的可视化之旅吧!