|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
引言
Matplotlib是Python中最流行的数据可视化库之一,它提供了丰富的功能来创建各种类型的图表。在数据分析、科学研究和商业报告中,我们经常需要在一个图形中展示多个图表,以便进行对比分析或全面展示数据的不同方面。网格化图表布局是实现这一目标的关键技术,它允许我们在一个画布上创建多个子图,并灵活控制它们的位置、大小和间距。
掌握Matplotlib的网格化布局技巧,不仅能大幅提升数据分析效率,还能让我们的可视化作品更加专业、美观,从而更有效地传达信息。本文将详细介绍Matplotlib中各种网格化布局的方法和技巧,从基础的subplot到高级的GridSpec,帮助读者轻松创建专业的数据可视化作品。
Matplotlib基础回顾
在深入了解网格化布局之前,让我们先回顾一些Matplotlib的基础概念。Matplotlib中的图形由几个关键组件组成:
• Figure:整个图形窗口或画布,可以包含一个或多个子图。
• Axes:子图,是实际绘制数据的区域,包括坐标轴、标题、标签等。
• Axis:坐标轴,包括x轴、y轴以及它们的刻度和标签。
• Artist:图形中的所有可见元素,包括文本、线条、图例等。
在Matplotlib中创建一个基本的图形非常简单:
- import matplotlib.pyplot as plt
- import numpy as np
- # 创建数据
- x = np.linspace(0, 10, 100)
- y = np.sin(x)
- # 创建图形和子图
- fig, ax = plt.subplots()
- # 绘制数据
- ax.plot(x, y)
- # 添加标题和标签
- ax.set_title('正弦函数')
- ax.set_xlabel('x')
- ax.set_ylabel('sin(x)')
- # 显示图形
- plt.show()
复制代码
现在,让我们开始探索如何在一个图形中创建多个子图。
使用plt.subplot()创建简单网格
plt.subplot()是Matplotlib中最基本的创建网格布局的方法。它允许我们将图形分割成规则的网格,并在指定的位置创建子图。
基本用法
plt.subplot(nrows, ncols, index)创建一个具有nrows行和ncols列的网格,并在index指定的位置创建子图。索引从1开始,从左到右、从上到下递增。
- import matplotlib.pyplot as plt
- import numpy as np
- # 创建数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.tan(x)
- y4 = np.exp(x/10)
- # 创建2x2的网格
- plt.figure(figsize=(10, 8))
- # 第一个子图(左上)
- plt.subplot(2, 2, 1)
- plt.plot(x, y1)
- plt.title('正弦函数')
- # 第二个子图(右上)
- plt.subplot(2, 2, 2)
- plt.plot(x, y2)
- plt.title('余弦函数')
- # 第三个子图(左下)
- plt.subplot(2, 2, 3)
- plt.plot(x, y3)
- plt.title('正切函数')
- plt.ylim(-5, 5) # 限制y轴范围,因为正切函数在某些点趋向于无穷大
- # 第四个子图(右下)
- plt.subplot(2, 2, 4)
- plt.plot(x, y4)
- plt.title('指数函数')
- # 调整子图间距
- plt.tight_layout()
- # 显示图形
- plt.show()
复制代码
使用逗号分隔的简写形式
plt.subplot()也接受一个三位数的整数作为参数,其中百位数表示行数,十位数表示列数,个位数表示索引。例如,plt.subplot(221)等同于plt.subplot(2, 2, 1)。
- import matplotlib.pyplot as plt
- import numpy as np
- # 创建数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- # 创建2x1的网格
- plt.figure(figsize=(10, 6))
- # 第一个子图(上)
- plt.subplot(211)
- plt.plot(x, y1)
- plt.title('正弦函数')
- # 第二个子图(下)
- plt.subplot(212)
- plt.plot(x, y2)
- plt.title('余弦函数')
- # 调整子图间距
- plt.tight_layout()
- # 显示图形
- plt.show()
复制代码
创建不规则网格
plt.subplot()还可以创建不规则的网格,通过指定rowspan和colspan参数,让一个子图跨越多个行或列。
- import matplotlib.pyplot as plt
- import numpy as np
- # 创建数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.tan(x)
- # 创建图形
- plt.figure(figsize=(10, 8))
- # 第一个子图,跨越第一行的两列
- plt.subplot(2, 2, (1, 2))
- plt.plot(x, y1)
- plt.title('正弦函数')
- # 第二个子图,位于第二行第一列
- plt.subplot(2, 2, 3)
- plt.plot(x, y2)
- plt.title('余弦函数')
- # 第三个子图,位于第二行第二列
- plt.subplot(2, 2, 4)
- plt.plot(x, y3)
- plt.title('正切函数')
- plt.ylim(-5, 5)
- # 调整子图间距
- plt.tight_layout()
- # 显示图形
- plt.show()
复制代码
使用plt.subplots()创建更复杂的网格
plt.subplots()是另一种创建网格布局的方法,与plt.subplot()不同,它一次性创建所有子图,并返回一个包含图形和子图数组的元组。这种方法更适合创建规则的网格,并且可以更方便地对子图进行操作。
基本用法
- import matplotlib.pyplot as plt
- import numpy as np
- # 创建数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.tan(x)
- y4 = np.exp(x/10)
- # 创建2x2的网格
- fig, axes = plt.subplots(2, 2, figsize=(10, 8))
- # 绘制第一个子图(左上)
- axes[0, 0].plot(x, y1)
- axes[0, 0].set_title('正弦函数')
- # 绘制第二个子图(右上)
- axes[0, 1].plot(x, y2)
- axes[0, 1].set_title('余弦函数')
- # 绘制第三个子图(左下)
- axes[1, 0].plot(x, y3)
- axes[1, 0].set_title('正切函数')
- axes[1, 0].set_ylim(-5, 5)
- # 绘制第四个子图(右下)
- axes[1, 1].plot(x, y4)
- axes[1, 1].set_title('指数函数')
- # 调整子图间距
- plt.tight_layout()
- # 显示图形
- plt.show()
复制代码
共享x轴和y轴
plt.subplots()的一个强大功能是可以让子图共享x轴或y轴,这在比较相似数据时非常有用。
- import matplotlib.pyplot as plt
- import numpy as np
- # 创建数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.sin(x) + 0.5 * np.cos(x)
- y4 = np.cos(x) + 0.5 * np.sin(x)
- # 创建2x2的网格,共享x轴和y轴
- fig, axes = plt.subplots(2, 2, figsize=(10, 8), sharex=True, sharey=True)
- # 绘制子图
- axes[0, 0].plot(x, y1)
- axes[0, 0].set_title('正弦函数')
- axes[0, 1].plot(x, y2)
- axes[0, 1].set_title('余弦函数')
- axes[1, 0].plot(x, y3)
- axes[1, 0].set_title('sin(x) + 0.5*cos(x)')
- axes[1, 1].plot(x, y4)
- axes[1, 1].set_title('cos(x) + 0.5*sin(x)')
- # 设置x轴和y轴标签
- for ax in axes.flat:
- ax.set_xlabel('x')
- ax.set_ylabel('y')
- # 调整子图间距
- plt.tight_layout()
- # 显示图形
- plt.show()
复制代码
创建一维子图数组
如果只需要一行或一列子图,plt.subplots()会返回一个一维数组,这使得操作更加简单。
- import matplotlib.pyplot as plt
- import numpy as np
- # 创建数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.tan(x)
- # 创建1x3的网格
- fig, axes = plt.subplots(1, 3, figsize=(15, 5))
- # 绘制子图
- axes[0].plot(x, y1)
- axes[0].set_title('正弦函数')
- axes[1].plot(x, y2)
- axes[1].set_title('余弦函数')
- axes[2].plot(x, y3)
- axes[2].set_title('正切函数')
- axes[2].set_ylim(-5, 5)
- # 调整子图间距
- plt.tight_layout()
- # 显示图形
- plt.show()
复制代码
使用GridSpec进行高级布局控制
GridSpec是Matplotlib中最灵活的网格布局工具,它允许我们创建复杂的、不规则的网格布局,并精确控制每个子图的位置和大小。
基本用法
- import matplotlib.pyplot as plt
- import matplotlib.gridspec as gridspec
- import numpy as np
- # 创建数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.tan(x)
- y4 = np.exp(x/10)
- # 创建图形
- fig = plt.figure(figsize=(10, 8))
- # 创建GridSpec
- gs = gridspec.GridSpec(3, 3)
- # 创建子图
- # 第一个子图,跨越第一行的三列
- ax1 = fig.add_subplot(gs[0, :])
- ax1.plot(x, y1)
- ax1.set_title('正弦函数')
- # 第二个子图,位于第二行第一列
- ax2 = fig.add_subplot(gs[1, 0])
- ax2.plot(x, y2)
- ax2.set_title('余弦函数')
- # 第三个子图,位于第二行第二列和第三列
- ax3 = fig.add_subplot(gs[1, 1:])
- ax3.plot(x, y3)
- ax3.set_title('正切函数')
- ax3.set_ylim(-5, 5)
- # 第四个子图,位于第三行的三列
- ax4 = fig.add_subplot(gs[2, :])
- ax4.plot(x, y4)
- ax4.set_title('指数函数')
- # 调整子图间距
- plt.tight_layout()
- # 显示图形
- plt.show()
复制代码
使用GridSpec调整子图大小比例
GridSpec允许我们通过width_ratios和height_ratios参数来调整子图的宽高比例。
- import matplotlib.pyplot as plt
- import matplotlib.gridspec as gridspec
- import numpy as np
- # 创建数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.tan(x)
- # 创建图形
- fig = plt.figure(figsize=(10, 8))
- # 创建GridSpec,设置宽度比例和高度比例
- gs = gridspec.GridSpec(2, 2, width_ratios=[1, 2], height_ratios=[2, 1])
- # 创建子图
- # 第一个子图,位于第一行第一列
- ax1 = fig.add_subplot(gs[0, 0])
- ax1.plot(x, y1)
- ax1.set_title('正弦函数')
- # 第二个子图,位于第一行第二列
- ax2 = fig.add_subplot(gs[0, 1])
- ax2.plot(x, y2)
- ax2.set_title('余弦函数')
- # 第三个子图,跨越第二行的两列
- ax3 = fig.add_subplot(gs[1, :])
- ax3.plot(x, y3)
- ax3.set_title('正切函数')
- ax3.set_ylim(-5, 5)
- # 调整子图间距
- plt.tight_layout()
- # 显示图形
- plt.show()
复制代码
使用GridSpec创建嵌套布局
GridSpec还可以创建嵌套布局,即在一个子图中再创建一个更小的网格。
- import matplotlib.pyplot as plt
- import matplotlib.gridspec as gridspec
- import numpy as np
- # 创建数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.tan(x)
- y4 = np.exp(x/10)
- # 创建图形
- fig = plt.figure(figsize=(12, 10))
- # 创建主GridSpec
- gs = gridspec.GridSpec(3, 3)
- # 创建大子图,跨越前两行的两列
- ax_big = fig.add_subplot(gs[:2, :2])
- ax_big.plot(x, y1)
- ax_big.set_title('正弦函数(大图)')
- # 创建小GridSpec,位于第一行第三列
- gs_small = gridspec.GridSpecFromSubplotSpec(2, 1, subplot_spec=gs[0, 2])
- # 在小GridSpec中创建子图
- ax_small1 = fig.add_subplot(gs_small[0])
- ax_small1.plot(x, y2)
- ax_small1.set_title('余弦函数')
- ax_small2 = fig.add_subplot(gs_small[1])
- ax_small2.plot(x, y3)
- ax_small2.set_title('正切函数')
- ax_small2.set_ylim(-5, 5)
- # 创建另一个子图,位于第二行第三列
- ax3 = fig.add_subplot(gs[1, 2])
- ax3.plot(x, y4)
- ax3.set_title('指数函数')
- # 创建底部子图,跨越第三行的三列
- ax_bottom = fig.add_subplot(gs[2, :])
- ax_bottom.plot(x, y1 + y2)
- ax_bottom.set_title('正弦函数 + 余弦函数')
- # 调整子图间距
- plt.tight_layout()
- # 显示图形
- plt.show()
复制代码
使用subplot2grid进行灵活布局
subplot2grid是另一种创建不规则网格布局的方法,它比GridSpec更简单,但仍然提供了很大的灵活性。
基本用法
subplot2grid(shape, loc, rowspan=1, colspan=1)接受四个参数:
• shape:网格的形状,表示为(rows, cols)
• loc:子图的起始位置,表示为(row, col)
• rowspan:子图跨越的行数(默认为1)
• colspan:子图跨越的列数(默认为1)
- import matplotlib.pyplot as plt
- import numpy as np
- # 创建数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.tan(x)
- y4 = np.exp(x/10)
- # 创建图形
- fig = plt.figure(figsize=(10, 8))
- # 创建子图
- # 第一个子图,位于(0,0),跨越2行
- ax1 = plt.subplot2grid((3, 3), (0, 0), rowspan=2)
- ax1.plot(x, y1)
- ax1.set_title('正弦函数')
- # 第二个子图,位于(0,1),跨越1行2列
- ax2 = plt.subplot2grid((3, 3), (0, 1), colspan=2)
- ax2.plot(x, y2)
- ax2.set_title('余弦函数')
- # 第三个子图,位于(1,1)
- ax3 = plt.subplot2grid((3, 3), (1, 1))
- ax3.plot(x, y3)
- ax3.set_title('正切函数')
- ax3.set_ylim(-5, 5)
- # 第四个子图,位于(1,2)
- ax4 = plt.subplot2grid((3, 3), (1, 2))
- ax4.plot(x, y4)
- ax4.set_title('指数函数')
- # 第五个子图,位于(2,0),跨越1行3列
- ax5 = plt.subplot2grid((3, 3), (2, 0), colspan=3)
- ax5.plot(x, y1 + y2)
- ax5.set_title('正弦函数 + 余弦函数')
- # 调整子图间距
- plt.tight_layout()
- # 显示图形
- plt.show()
复制代码
创建复杂的仪表板布局
subplot2grid特别适合创建复杂的仪表板布局,让我们看一个更复杂的例子:
- import matplotlib.pyplot as plt
- import numpy as np
- # 创建数据
- np.random.seed(42)
- data = np.random.randn(100, 5)
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.cumsum(np.random.randn(100))
- # 创建图形
- fig = plt.figure(figsize=(15, 10))
- # 创建标题
- fig.suptitle('数据分析仪表板', fontsize=16)
- # 创建主图表,位于(0,0),跨越2行2列
- ax_main = plt.subplot2grid((4, 4), (0, 0), colspan=2, rowspan=2)
- ax_main.plot(x, y1, label='正弦函数')
- ax_main.plot(x, y2, label='余弦函数')
- ax_main.set_title('主要趋势')
- ax_main.legend()
- # 创建柱状图,位于(0,2)
- ax_bar = plt.subplot2grid((4, 4), (0, 2))
- ax_bar.bar(range(5), np.mean(data, axis=0))
- ax_bar.set_title('平均值')
- # 创建饼图,位于(0,3)
- ax_pie = plt.subplot2grid((4, 4), (0, 3))
- ax_pie.pie(np.abs(np.mean(data, axis=0)), labels=['A', 'B', 'C', 'D', 'E'], autopct='%1.1f%%')
- ax_pie.set_title('比例分布')
- # 创建箱线图,位于(1,2)
- ax_box = plt.subplot2grid((4, 4), (1, 2))
- ax_box.boxplot(data)
- ax_box.set_title('数据分布')
- # 创建散点图,位于(1,3)
- ax_scatter = plt.subplot2grid((4, 4), (1, 3))
- ax_scatter.scatter(data[:, 0], data[:, 1])
- ax_scatter.set_title('相关性')
- # 创建累积和图,位于(2,0),跨越1行4列
- ax_cumsum = plt.subplot2grid((4, 4), (2, 0), colspan=4)
- ax_cumsum.plot(y3)
- ax_cumsum.set_title('累积和')
- # 创建直方图,位于(3,0),跨越1行2列
- ax_hist = plt.subplot2grid((4, 4), (3, 0), colspan=2)
- ax_hist.hist(data.flatten(), bins=20)
- ax_hist.set_title('数据分布')
- # 创建表格,位于(3,2),跨越1行2列
- ax_table = plt.subplot2grid((4, 4), (3, 2), colspan=2)
- ax_table.axis('off')
- table_data = [
- ['统计量', '值'],
- ['平均值', f'{np.mean(data):.2f}'],
- ['标准差', f'{np.std(data):.2f}'],
- ['最小值', f'{np.min(data):.2f}'],
- ['最大值', f'{np.max(data):.2f}']
- ]
- table = ax_table.table(cellText=table_data, loc='center')
- table.auto_set_font_size(False)
- table.set_fontsize(10)
- ax_table.set_title('数据摘要')
- # 调整子图间距
- plt.tight_layout(rect=[0, 0, 1, 0.96]) # 为标题留出空间
- # 显示图形
- plt.show()
复制代码
嵌套图表和复杂布局
有时,我们需要在一个子图中创建另一个子图,或者创建更复杂的布局。Matplotlib提供了几种方法来实现这一点。
使用inset_axes创建嵌套子图
inset_axes方法允许我们在一个现有的子图中创建一个嵌套的子图。
- import matplotlib.pyplot as plt
- import numpy as np
- from mpl_toolkits.axes_grid1.inset_locator import inset_axes
- # 创建数据
- x = np.linspace(0, 10, 1000)
- y = np.sin(x) * np.exp(-x/10)
- # 创建图形
- fig, ax = plt.subplots(figsize=(10, 6))
- # 绘制主图
- ax.plot(x, y)
- ax.set_title('阻尼振荡')
- ax.set_xlabel('x')
- ax.set_ylabel('y')
- # 创建嵌套子图
- axins = inset_axes(ax, width="40%", height="30%", loc='upper right', borderpad=2)
- axins.plot(x, y)
- axins.set_xlim(0, 2)
- axins.set_ylim(0.9, 1.0)
- axins.set_title('放大视图')
- # 添加连接线
- from mpl_toolkits.axes_grid1.inset_locator import mark_inset
- mark_inset(ax, axins, loc1=2, loc2=4, fc="none", ec="0.5")
- # 显示图形
- plt.show()
复制代码
使用make_axes_locatable创建颜色条
make_axes_locatable是一个有用的工具,可以创建与主图对齐的颜色条。
- import matplotlib.pyplot as plt
- import numpy as np
- from mpl_toolkits.axes_grid1 import make_axes_locatable
- # 创建数据
- x = np.linspace(-3, 3, 100)
- y = np.linspace(-3, 3, 100)
- X, Y = np.meshgrid(x, y)
- Z = np.exp(-(X**2 + Y**2))
- # 创建图形
- fig, ax = plt.subplots(figsize=(8, 6))
- # 绘制等高线图
- c = ax.contourf(X, Y, Z, levels=20, cmap='viridis')
- ax.set_title('二维高斯分布')
- # 创建颜色条
- divider = make_axes_locatable(ax)
- cax = divider.append_axes("right", size="5%", pad=0.1)
- plt.colorbar(c, cax=cax)
- # 显示图形
- plt.show()
复制代码
创建复杂的嵌套布局
我们可以结合使用多种技术来创建非常复杂的嵌套布局。
- import matplotlib.pyplot as plt
- import matplotlib.gridspec as gridspec
- import numpy as np
- from mpl_toolkits.axes_grid1.inset_locator import inset_axes
- # 创建数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.tan(x)
- y4 = np.exp(x/10)
- data = np.random.randn(100, 5)
- # 创建图形
- fig = plt.figure(figsize=(15, 10))
- fig.suptitle('复杂数据分析仪表板', fontsize=16)
- # 创建主GridSpec
- gs = gridspec.GridSpec(3, 3)
- # 创建大子图,跨越前两行的两列
- ax_main = fig.add_subplot(gs[:2, :2])
- ax_main.plot(x, y1, label='正弦函数')
- ax_main.plot(x, y2, label='余弦函数')
- ax_main.set_title('主要趋势')
- ax_main.legend()
- # 在大子图中创建嵌套子图
- ax_inset = inset_axes(ax_main, width="30%", height="30%", loc='lower right')
- ax_inset.plot(x, y3)
- ax_inset.set_title('正切函数')
- ax_inset.set_ylim(-5, 5)
- # 创建右上角的子图
- ax_top_right = fig.add_subplot(gs[0, 2])
- ax_top_right.bar(range(5), np.mean(data, axis=0))
- ax_top_right.set_title('平均值')
- # 创建中右角的子图
- ax_middle_right = fig.add_subplot(gs[1, 2])
- ax_middle_right.boxplot(data)
- ax_middle_right.set_title('数据分布')
- # 创建底部的子图,跨越第三行的三列
- ax_bottom = fig.add_subplot(gs[2, :])
- ax_bottom.plot(x, y4)
- ax_bottom.set_title('指数函数')
- # 在底部子图中创建嵌套子图
- ax_bottom_inset = inset_axes(ax_bottom, width="30%", height="40%", loc='center left')
- ax_bottom_inset.hist(np.random.randn(1000), bins=20)
- ax_bottom_inset.set_title('正态分布')
- # 调整子图间距
- plt.tight_layout(rect=[0, 0, 1, 0.96]) # 为标题留出空间
- # 显示图形
- plt.show()
复制代码
调整子图间距和大小
在创建多个子图时,调整它们之间的间距和大小是非常重要的,这可以使图形更加美观和易读。
使用tight_layout自动调整
tight_layout函数可以自动调整子图参数,以避免标签重叠。
- import matplotlib.pyplot as plt
- import numpy as np
- # 创建数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.tan(x)
- y4 = np.exp(x/10)
- # 创建2x2的网格
- fig, axes = plt.subplots(2, 2, figsize=(10, 8))
- # 绘制子图
- axes[0, 0].plot(x, y1)
- axes[0, 0].set_title('正弦函数')
- axes[0, 0].set_xlabel('x')
- axes[0, 0].set_ylabel('sin(x)')
- axes[0, 1].plot(x, y2)
- axes[0, 1].set_title('余弦函数')
- axes[0, 1].set_xlabel('x')
- axes[0, 1].set_ylabel('cos(x)')
- axes[1, 0].plot(x, y3)
- axes[1, 0].set_title('正切函数')
- axes[1, 0].set_xlabel('x')
- axes[1, 0].set_ylabel('tan(x)')
- axes[1, 0].set_ylim(-5, 5)
- axes[1, 1].plot(x, y4)
- axes[1, 1].set_title('指数函数')
- axes[1, 1].set_xlabel('x')
- axes[1, 1].set_ylabel('exp(x/10)')
- # 自动调整子图间距
- plt.tight_layout()
- # 显示图形
- plt.show()
复制代码
使用subplots_adjust手动调整
subplots_adjust函数允许我们手动调整子图之间的间距。
- import matplotlib.pyplot as plt
- import numpy as np
- # 创建数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.tan(x)
- y4 = np.exp(x/10)
- # 创建2x2的网格
- fig, axes = plt.subplots(2, 2, figsize=(10, 8))
- # 绘制子图
- axes[0, 0].plot(x, y1)
- axes[0, 0].set_title('正弦函数')
- axes[0, 0].set_xlabel('x')
- axes[0, 0].set_ylabel('sin(x)')
- axes[0, 1].plot(x, y2)
- axes[0, 1].set_title('余弦函数')
- axes[0, 1].set_xlabel('x')
- axes[0, 1].set_ylabel('cos(x)')
- axes[1, 0].plot(x, y3)
- axes[1, 0].set_title('正切函数')
- axes[1, 0].set_xlabel('x')
- axes[1, 0].set_ylabel('tan(x)')
- axes[1, 0].set_ylim(-5, 5)
- axes[1, 1].plot(x, y4)
- axes[1, 1].set_title('指数函数')
- axes[1, 1].set_xlabel('x')
- axes[1, 1].set_ylabel('exp(x/10)')
- # 手动调整子图间距
- plt.subplots_adjust(
- left=0.1, # 左边界
- right=0.9, # 右边界
- bottom=0.1, # 下边界
- top=0.9, # 上边界
- wspace=0.4, # 水平间距
- hspace=0.4 # 垂直间距
- )
- # 显示图形
- plt.show()
复制代码
使用constrained_layout进行高级布局控制
constrained_layout是Matplotlib 2.2版本引入的新功能,它提供了更高级的布局控制。
- import matplotlib.pyplot as plt
- import numpy as np
- # 创建数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.tan(x)
- y4 = np.exp(x/10)
- # 创建图形,启用constrained_layout
- fig, axes = plt.subplots(2, 2, figsize=(10, 8), constrained_layout=True)
- # 绘制子图
- axes[0, 0].plot(x, y1)
- axes[0, 0].set_title('正弦函数')
- axes[0, 0].set_xlabel('x')
- axes[0, 0].set_ylabel('sin(x)')
- axes[0, 1].plot(x, y2)
- axes[0, 1].set_title('余弦函数')
- axes[0, 1].set_xlabel('x')
- axes[0, 1].set_ylabel('cos(x)')
- axes[1, 0].plot(x, y3)
- axes[1, 0].set_title('正切函数')
- axes[1, 0].set_xlabel('x')
- axes[1, 0].set_ylabel('tan(x)')
- axes[1, 0].set_ylim(-5, 5)
- axes[1, 1].plot(x, y4)
- axes[1, 1].set_title('指数函数')
- axes[1, 1].set_xlabel('x')
- axes[1, 1].set_ylabel('exp(x/10)')
- # 设置constrained_layout参数
- fig.set_constrained_layout_pads(
- w_pad=0.2, # 水平间距
- h_pad=0.2, # 垂直间距
- wspace=0.2, # 子图间水平间距
- hspace=0.2 # 子图间垂直间距
- )
- # 显示图形
- plt.show()
复制代码
实际案例:创建专业仪表板
让我们通过一个实际案例,综合运用前面学到的技巧,创建一个专业的数据分析仪表板。
- import matplotlib.pyplot as plt
- import matplotlib.gridspec as gridspec
- import numpy as np
- import pandas as pd
- from mpl_toolkits.axes_grid1.inset_locator import inset_axes
- from mpl_toolkits.axes_grid1 import make_axes_locatable
- # 设置随机种子以确保结果可重现
- np.random.seed(42)
- # 创建模拟数据
- dates = pd.date_range('2023-01-01', periods=100)
- values = np.cumsum(np.random.randn(100)) + 100
- categories = ['A', 'B', 'C', 'D', 'E']
- category_values = np.random.randint(10, 100, size=(100, 5))
- correlation_data = np.random.randn(100, 2)
- # 创建图形
- fig = plt.figure(figsize=(20, 15))
- fig.suptitle('业务数据分析仪表板', fontsize=20, fontweight='bold')
- # 创建主GridSpec
- gs = gridspec.GridSpec(4, 4, figure=fig)
- # 1. 创建主趋势图(跨越前两行的前两列)
- ax_main = fig.add_subplot(gs[0:2, 0:2])
- ax_main.plot(dates, values, linewidth=2, color='#1f77b4')
- ax_main.set_title('主要业务指标趋势', fontsize=14, fontweight='bold')
- ax_main.set_xlabel('日期')
- ax_main.set_ylabel('指标值')
- ax_main.grid(True, linestyle='--', alpha=0.7)
- # 在主趋势图中添加嵌套图,显示最近30天的详细数据
- ax_inset = inset_axes(ax_main, width="40%", height="30%", loc='upper right', borderpad=2)
- ax_inset.plot(dates[-30:], values[-30:], linewidth=2, color='#ff7f0e')
- ax_inset.set_title('最近30天', fontsize=10)
- ax_inset.grid(True, linestyle='--', alpha=0.7)
- # 2. 创建分类柱状图(位于第一行第三列)
- ax_bar = fig.add_subplot(gs[0, 2])
- bar_colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
- ax_bar.bar(categories, np.mean(category_values, axis=0), color=bar_colors)
- ax_bar.set_title('各分类平均值', fontsize=14, fontweight='bold')
- ax_bar.set_xlabel('分类')
- ax_bar.set_ylabel('平均值')
- # 3. 创建饼图(位于第一行第四列)
- ax_pie = fig.add_subplot(gs[0, 3])
- pie_data = np.mean(category_values, axis=0)
- ax_pie.pie(pie_data, labels=categories, autopct='%1.1f%%', colors=bar_colors)
- ax_pie.set_title('分类占比', fontsize=14, fontweight='bold')
- # 4. 创建箱线图(位于第二行第三列)
- ax_box = fig.add_subplot(gs[1, 2])
- ax_box.boxplot(category_values, labels=categories)
- ax_box.set_title('分类数据分布', fontsize=14, fontweight='bold')
- ax_box.set_xlabel('分类')
- ax_box.set_ylabel('值')
- # 5. 创建散点图(位于第二行第四列)
- ax_scatter = fig.add_subplot(gs[1, 3])
- ax_scatter.scatter(correlation_data[:, 0], correlation_data[:, 1], alpha=0.6)
- ax_scatter.set_title('相关性分析', fontsize=14, fontweight='bold')
- ax_scatter.set_xlabel('变量1')
- ax_scatter.set_ylabel('变量2')
- # 6. 创建分类趋势图(跨越第三行的前两列)
- ax_category = fig.add_subplot(gs[2, 0:2])
- for i, cat in enumerate(categories):
- ax_category.plot(dates, category_values[:, i], label=cat, color=bar_colors[i])
- ax_category.set_title('各分类趋势', fontsize=14, fontweight='bold')
- ax_category.set_xlabel('日期')
- ax_category.set_ylabel('值')
- ax_category.legend()
- ax_category.grid(True, linestyle='--', alpha=0.7)
- # 7. 创建热力图(位于第三行第三列)
- ax_heatmap = fig.add_subplot(gs[2, 2])
- heatmap_data = np.corrcoef(category_values.T)
- im = ax_heatmap.imshow(heatmap_data, cmap='coolwarm')
- ax_heatmap.set_title('分类相关性', fontsize=14, fontweight='bold')
- ax_heatmap.set_xticks(np.arange(len(categories)))
- ax_heatmap.set_yticks(np.arange(len(categories)))
- ax_heatmap.set_xticklabels(categories)
- ax_heatmap.set_yticklabels(categories)
- # 添加颜色条
- divider = make_axes_locatable(ax_heatmap)
- cax = divider.append_axes("right", size="5%", pad=0.1)
- plt.colorbar(im, cax=cax)
- # 8. 创建面积图(位于第三行第四列)
- ax_area = fig.add_subplot(gs[2, 3])
- ax_area.stackplot(dates, category_values.T, labels=categories, colors=bar_colors, alpha=0.7)
- ax_area.set_title('分类累积', fontsize=14, fontweight='bold')
- ax_area.set_xlabel('日期')
- ax_area.set_ylabel('累积值')
- ax_area.legend(loc='upper left')
- # 9. 创建数据摘要表(跨越第四行的四列)
- ax_table = fig.add_subplot(gs[3, :])
- ax_table.axis('off')
- # 计算统计数据
- stats = []
- for i, cat in enumerate(categories):
- stats.append([
- cat,
- f'{np.mean(category_values[:, i]):.2f}',
- f'{np.std(category_values[:, i]):.2f}',
- f'{np.min(category_values[:, i]):.2f}',
- f'{np.max(category_values[:, i]):.2f}',
- f'{np.median(category_values[:, i]):.2f}'
- ])
- # 创建表格
- table_data = [['分类', '平均值', '标准差', '最小值', '最大值', '中位数']] + stats
- table = ax_table.table(
- cellText=table_data,
- cellLoc='center',
- loc='center',
- colColours=['#f3f3f3']*6
- )
- table.auto_set_font_size(False)
- table.set_fontsize(12)
- table.scale(1, 1.5)
- # 设置表格样式
- for i in range(len(table_data[0])):
- table[(0, i)].set_facecolor('#4bacc6')
- table[(0, i)].set_text_props(weight='bold', color='white')
- for i in range(1, len(table_data)):
- for j in range(len(table_data[0])):
- if i % 2 == 0:
- table[(i, j)].set_facecolor('#f3f3f3')
- ax_table.set_title('数据摘要统计', fontsize=14, fontweight='bold', pad=20)
- # 调整布局
- plt.tight_layout(rect=[0, 0, 1, 0.96]) # 为标题留出空间
- # 保存图形
- plt.savefig('business_dashboard.png', dpi=300, bbox_inches='tight')
- # 显示图形
- plt.show()
复制代码
这个仪表板展示了多种类型的图表,包括趋势图、柱状图、饼图、箱线图、散点图、热力图、面积图和表格,它们共同构成了一个全面的数据分析视图。通过使用GridSpec和嵌套布局,我们能够创建一个结构清晰、信息丰富的专业仪表板。
最佳实践和技巧总结
在使用Matplotlib创建网格化图表布局时,以下最佳实践和技巧可以帮助你提高效率和创建更专业的可视化作品:
1. 选择合适的布局方法
• 简单规则网格:使用plt.subplots(),它简单直观,适合创建规则的网格布局。
• 不规则网格:使用subplot2grid,它比GridSpec更简单,但仍然提供了很大的灵活性。
• 复杂布局:使用GridSpec,它提供了最灵活的控制,适合创建复杂的、不规则的网格布局。
• 嵌套图表:使用inset_axes,它允许你在现有的子图中创建嵌套的子图。
2. 合理设置图形大小
- # 根据子图数量和复杂性设置合适的图形大小
- fig, axes = plt.subplots(2, 3, figsize=(15, 10)) # 宽15英寸,高10英寸
复制代码
3. 使用共享轴提高可比性
- # 共享x轴和y轴,使子图之间的比较更加直观
- fig, axes = plt.subplots(2, 2, figsize=(10, 8), sharex=True, sharey=True)
复制代码
4. 调整子图间距
- # 使用tight_layout自动调整子图间距
- plt.tight_layout()
- # 或者使用subplots_adjust手动调整
- plt.subplots_adjust(
- left=0.1, # 左边界
- right=0.9, # 右边界
- bottom=0.1, # 下边界
- top=0.9, # 上边界
- wspace=0.4, # 水平间距
- hspace=0.4 # 垂直间距
- )
复制代码
5. 使用一致的样式和颜色
- # 定义颜色方案
- colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
- # 在所有子图中使用相同的样式
- for ax, color in zip(axes.flat, colors):
- ax.plot(x, y, color=color, linewidth=2)
- ax.grid(True, linestyle='--', alpha=0.7)
复制代码
6. 添加适当的标题和标签
- # 为每个子图添加描述性标题和标签
- ax.set_title('销售趋势', fontsize=12, fontweight='bold')
- ax.set_xlabel('日期')
- ax.set_ylabel('销售额')
复制代码
7. 使用图例说明数据系列
- # 添加图例,并设置位置
- ax.plot(x, y1, label='产品A')
- ax.plot(x, y2, label='产品B')
- ax.legend(loc='upper right')
复制代码
8. 考虑数据密度和比例
- # 对于数据密度高的区域,使用嵌套图进行放大显示
- ax_inset = inset_axes(ax, width="40%", height="30%", loc='upper right')
- ax_inset.plot(x_zoom, y_zoom)
复制代码
9. 添加网格线提高可读性
- # 添加网格线,并设置样式
- ax.grid(True, linestyle='--', alpha=0.7)
复制代码
10. 保存高质量图形
- # 保存高分辨率图形,适合打印或出版
- plt.savefig('visualization.png', dpi=300, bbox_inches='tight')
复制代码
11. 使用面向对象的API
- # 使用面向对象的API,而不是pyplot的状态机接口
- fig, ax = plt.subplots()
- ax.plot(x, y) # 而不是 plt.plot(x, y)
复制代码
12. 创建可重用的函数
- def create_dashboard(data, figsize=(15, 10)):
- """创建数据分析仪表板"""
- fig = plt.figure(figsize=figsize)
- gs = gridspec.GridSpec(3, 3)
-
- # 创建子图
- ax1 = fig.add_subplot(gs[0, :2])
- ax2 = fig.add_subplot(gs[0, 2])
- # ... 更多子图
-
- # 绘制数据
- ax1.plot(data['dates'], data['values'])
- # ... 更多绘图代码
-
- # 调整布局
- plt.tight_layout()
-
- return fig
- # 使用函数
- fig = create_dashboard(data)
- plt.show()
复制代码
通过遵循这些最佳实践和技巧,你可以创建出专业、美观且信息丰富的数据可视化作品,大幅提升你的数据分析效率和展示效果。
结论
Matplotlib提供了多种创建网格化图表布局的方法,从简单的subplot到高级的GridSpec,每种方法都有其适用的场景。通过掌握这些技巧,你可以轻松创建专业的数据可视化作品,不仅能够更有效地分析数据,还能更清晰地传达你的发现。
在实际应用中,选择合适的布局方法、合理设置图形大小、调整子图间距、使用一致的样式和颜色、添加适当的标题和标签等,都是创建高质量可视化作品的关键。通过不断实践和探索,你将能够熟练运用Matplotlib的网格化布局技巧,创建出令人印象深刻的数据可视化作品。
希望本文能够帮助你更好地理解和掌握Matplotlib的网格化图表布局技巧,从而在数据分析、科学研究和商业报告中创建出更加专业和有效的可视化作品。 |
|