前言
在数据可视化中,很多时候需要对某一区间的数据进行局部放大,以获得对比度更高的可视化效果。下面利用Python语言的Matplotlib库实现一个简单的局部放大图效果。
依赖库
matplotlib:绘图库
numpy:支持大量的维度数组、矩阵运算和数学函数的扩展库
步骤
1. 导入依赖库
1 2 3 4 | import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1.inset_locator import mark_inset from mpl_toolkits.axes_grid1.inset_locator import inset_axes |
2. 准备数据
1 2 3 4 | x = np.linspace(-0.1*np.pi, 2*np.pi, 30) y_1 = np.sinc(x)+0.7 y_2 = np.tanh(x) y_3 = np.exp(-np.sinc(x)) |
3. 绘图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | fig, ax = plt.subplots(1, 1, figsize=(6, 4)) ax.plot(x, y_1, color='k', linestyle=':', linewidth=1, marker='o', markersize=5, markeredgecolor='black', markerfacecolor='C0') ax.plot(x, y_2, color='k', linestyle=':', linewidth=1, marker='o', markersize=5, markeredgecolor='black', markerfacecolor='C3') ax.plot(x, y_3, color='k', linestyle=':', linewidth=1, marker='o', markersize=5, markeredgecolor='black', markerfacecolor='C2') ax.legend(labels=["y_1", "y_2","y_3"], ncol=3) |
4. 嵌入绘制局部放大图的坐标系
1 2 3 | axins = inset_axes(ax, width="40%", height="30%", loc='lower left', bbox_to_anchor=(0.1, 0.1, 1, 1), bbox_transform=ax.transAxes) |
参数说明
ax:父坐标系
width, height:子坐标系的宽度和高度(百分比形式或者浮点数个数)
loc:子坐标系的位置
bbox_to_anchor:边界框,四元数组(x0, y0, width, height)
bbox_transform:从父坐标系到子坐标系的几何映射
axins:子坐标系固定坐标系的宽度和高度以及边界框,分别设置loc为左上、左下、右上(默认)、右下和中间,
效果图如下:
关于mpl_toolkits.axes_grid1.inset_locator.inset_axes的详细使用说明可以参考官方文档
1 | https://link.zhihu.com/?target=https%3A//matplotlib.org/3.2.1/api/_as_gen/mpl_toolkits.axes_grid1.inset_locator.inset_axes.html |
在本例子中,将子坐标系嵌入到合适的位置:
1 2 3 | axins = inset_axes(ax, width="40%", height="30%", loc='lower left', bbox_to_anchor=(0.5, 0.1, 1, 1), bbox_transform=ax.transAxes) |
效果图如下:
另外有一种更加简洁的子坐标系嵌入方法:
1 | axins = ax.inset_axes((0.2, 0.2, 0.4, 0.3)) |
ax为父坐标系,后面四个参数同样是(x0, y0, width, height),上述代码的含义是:以父坐标系中的x0=0.2x,y0=0.2y为左下角起点,嵌入一个宽度为0.2x,高度为0.3y的子坐标系,其中x和y分别为父坐标系的坐标轴范围。效果如下图所示:
5. 在子坐标系中绘制原始数据
1 2 3 4 5 6 7 8 9 10 11 | axins.plot(x, y_1, color='k', linestyle=':', linewidth=1, marker='o', markersize=5, markeredgecolor='black', markerfacecolor='C0') axins.plot(x, y_2, color='k', linestyle=':', linewidth=1, marker='o', markersize=5, markeredgecolor='black', markerfacecolor='C3') axins.plot(x, y_3, color='k', linestyle=':', linewidth=1, marker='o', markersize=5, markeredgecolor='black', markerfacecolor='C2') |
效果图如下:
6. 设置放大区间,调整子坐标系的显示范围
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # 设置放大区间 zone_left = 11 zone_right = 12 # 坐标轴的扩展比例(根据实际数据调整) x_ratio = 0.5 # x轴显示范围的扩展比例 y_ratio = 0.5 # y轴显示范围的扩展比例 # X轴的显示范围 xlim0 = x[zone_left]-(x[zone_right]-x[zone_left])*x_ratio xlim1 = x[zone_right]+(x[zone_right]-x[zone_left])*x_ratio # Y轴的显示范围 y = np.hstack((y_1[zone_left:zone_right], y_2[zone_left:zone_right], y_3[zone_left:zone_right])) ylim0 = np.min(y)-(np.max(y)-np.min(y))*y_ratio ylim1 = np.max(y)+(np.max(y)-np.min(y))*y_ratio # 调整子坐标系的显示范围 axins.set_xlim(xlim0, xlim1) axins.set_ylim(ylim0, ylim1) |
效果图如下:
7. 建立父坐标系与子坐标系的连接线
1 2 3 | # loc1 loc2: 坐标系的四个角 # 1 (右上) 2 (左上) 3(左下) 4(右下) mark_inset(ax, axins, loc1=3, loc2=1, fc="none", ec='k', lw=1) |
loc1和loc2分别为父坐标系和子坐标系的四个角,取值为1,2,3,4,对应的四个角分别为:右上,左上,左下,右下。以loc1=3, loc2=1为例,实现的功能为:父坐标系的左下角和子坐标系的左下角相连,父坐标系的右上角和子坐标系的右上角相连。效果图如下:
以上就是利用Matplotlib实现局部放大图画法的例子,关键之处在于bbox_to_anchor参数的设定,利用这个参数可以实现任意位置的坐标系嵌入。完整的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | #-*- coding: utf-8 -*- import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1.inset_locator import mark_inset from mpl_toolkits.axes_grid1.inset_locator import inset_axes #准备数据 x = np.linspace(-0.1*np.pi, 2*np.pi, 30) y_1 = np.sinc(x)+0.7 y_2 = np.tanh(x) y_3 = np.exp(-np.sinc(x)) #绘图 fig, ax = plt.subplots(1, 1, figsize=(6, 4)) ax.plot(x, y_1, color='k', linestyle=':', linewidth=1, marker='o', markersize=5, markeredgecolor='black', markerfacecolor='C0') ax.plot(x, y_2, color='k', linestyle=':', linewidth=1, marker='o', markersize=5, markeredgecolor='black', markerfacecolor='C3') ax.plot(x, y_3, color='k', linestyle=':', linewidth=1, marker='o', markersize=5, markeredgecolor='black', markerfacecolor='C2') ax.legend(labels=["y_1", "y_2","y_3"], ncol=3) #嵌入绘制局部放大图的坐标系 axins = inset_axes(ax, width="40%", height="30%",loc='lower left', bbox_to_anchor=(0.5, 0.1, 1, 1), bbox_transform=ax.transAxes) #在子坐标系中绘制原始数据 axins.plot(x, y_1, color='k', linestyle=':', linewidth=1, marker='o', markersize=5, markeredgecolor='black', markerfacecolor='C0') axins.plot(x, y_2, color='k', linestyle=':', linewidth=1, marker='o', markersize=5, markeredgecolor='black', markerfacecolor='C3') axins.plot(x, y_3, color='k', linestyle=':', linewidth=1, marker='o', markersize=5, markeredgecolor='black', markerfacecolor='C2') #设置放大区间 zone_left = 11 zone_right = 12 #坐标轴的扩展比例(根据实际数据调整) x_ratio = 0.5 # x轴显示范围的扩展比例 y_ratio = 0.5 # y轴显示范围的扩展比例 #X轴的显示范围 xlim0 = x[zone_left]-(x[zone_right]-x[zone_left])*x_ratio xlim1 = x[zone_right]+(x[zone_right]-x[zone_left])*x_ratio #Y轴的显示范围 y = np.hstack((y_1[zone_left:zone_right], y_2[zone_left:zone_right], y_3[zone_left:zone_right])) ylim0 = np.min(y)-(np.max(y)-np.min(y))*y_ratio ylim1 = np.max(y)+(np.max(y)-np.min(y))*y_ratio #调整子坐标系的显示范围 axins.set_xlim(xlim0, xlim1) axins.set_ylim(ylim0, ylim1) #建立父坐标系与子坐标系的连接线 #loc1 loc2: 坐标系的四个角 #1 (右上) 2 (左上) 3(左下) 4(右下) mark_inset(ax, axins, loc1=3, loc2=1, fc="none", ec='k', lw=1) #显示 plt.show() |