在数据可视化中,很多时候需要对某一区间的数据进行局部放大,以获得对比度更高的可视化效果。下面利用Python语言的Matplotlib库实现一个简单的局部放大图效果。

依赖:matplotlib和numpy

导入依赖库

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.1, 0.1, 1, 1),
bbox_transform=ax.transAxes)

参数说明

  1. ax:父坐标系
  2. width, height:子坐标系的宽度和高度(百分比形式或者浮点数个数)
  3. loc:子坐标系的位置
  4. bbox_to_anchor:边界框,四元数组(x0, y0, width, height)
  5. bbox_transform:从父坐标系到子坐标系的几何映射
  6. axins:子坐标系

​ 固定坐标系的宽度和高度以及边界框,分别设置loc为左上、左下、右上(默认)、右下和中间

​ 在本例子中,将子坐标系嵌入到合适的位置:

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 = ax.inset_axes((0.2, 0.2, 0.4, 0.3))

​ ax为父坐标系,后面四个参数同样是(x0, y0, width, height),上述代码的含义是:以父坐标系中的x0=0.2x,y0=0.2y为左下角起点,嵌入一个宽度为0.4x,高度为0.3y的子坐标系,其中x和y分别为父坐标系的坐标轴范围。

在子坐标系中绘制原始数据

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)

​ loc1和loc2分别为父坐标系和子坐标系的四个角,取值为1,2,3,4,对应的四个角分别为:右上,左上,左下,右下。以loc1=3, loc2=1为例,实现的功能为:父坐标系的左下角和子坐标系的左下角相连,父坐标系的右上角和子坐标系的右上角相连。

​ 以上就是利用Matplotlib实现局部放大图画法的例子,关键之处在于bbox_to_anchor参数的设定,利用这个参数可以实现任意位置的坐标系嵌入。

​ 完整的代码如下:

# -*- 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()

最常用:

fig = plt.figure(0)
axe = fig.add_subplot(1, 1, 1)

# plt.yscale('log') # Y轴对数显示
# plt.ylim((10, 1e3)) # 设置Y轴范围
axe.plot(SNRdB_TRA, BPG_LDPC_YSPSNR, marker='^', color='blue', linestyle='-.', label='BPG+1/2 LDPC QPSK')
axe.plot(SNRdB, DeepJSCC_YSPSNR, marker='+', color='chocolate', linestyle='--', label='1/8 DeepJSCC')
axe.plot(SNRdB, mae_yolo_YSPSNR, marker='s', color='green', linestyle='-', label='SCViT')
axe.plot(SNRdB, ca_mae_yolo_YSPSNR, marker='*', color='brown', linestyle='-', label='SCViT+Mask')
axe.plot(SNRdB, ft_ca_mae_yolo_YSPSNR, marker='p', color='purple', linestyle='-', label='ASCViT+Mask')

axe.grid(True, which='both', color='black', linestyle='--', linewidth=0.2) # 网格

plt.title("PSNR+CS vs. SNR")
plt.xlabel('SNR[dB]')
plt.ylabel('PSNR+CS')
plt.legend()

# 嵌入绘制局部放大图的坐标系 子坐标系
axins = inset_axes(axe, width="35%", height="25%", loc='lower left', # 调这里
bbox_to_anchor=(0.55, 0.45, 1, 1), # 调这里
bbox_transform=axe.transAxes)
axins.plot(SNRdB_TRA, BPG_LDPC_YSPSNR, marker='^', color='blue', linestyle='-.', label='BPG+1/2 LDPC QPSK') # 调这里
axins.plot(SNRdB, DeepJSCC_YSPSNR, marker='+', color='chocolate', linestyle='--', label='1/8 DeepJSCC')
axins.plot(SNRdB, mae_yolo_YSPSNR, marker='s', color='green', linestyle='-', label='SCViT')
axins.plot(SNRdB, ca_mae_yolo_YSPSNR, marker='*', color='brown', linestyle='-', label='SCViT+Mask')
axins.plot(SNRdB, ft_ca_mae_yolo_YSPSNR, marker='p', color='purple', linestyle='-', label='ASCViT+Mask')
axins.grid(True, which='both', color='black', linestyle='--', linewidth=0.2) # 网格
# 调整子坐标系的显示范围
axins.set_xlim(3, 11) # 调这里
axins.set_ylim(0.74, 0.93) # 调这里
# 建立父坐标系与子坐标系的连接线
# loc1 loc2: 坐标系的四个角
# 1 (右上) 2 (左上) 3(左下) 4(右下)
mark_inset(axe, axins, loc1=3, loc2=1, fc="none", ec='k', lw=1)

plt.show()