matplotlib可视化:局部放大折线图+阴影区间绘制【原理+代码】

该博客介绍了如何使用matplotlib进行数据可视化,特别是如何绘制包含主图和子图的黄金比特币价格折线图,以及如何设置阴影区间、双轴图例和局部放大。主要内容包括数据准备、主图和子图的绘制,以及子图边界框的连线。文章提供了详细的代码示例,展示了如何处理多条折线及其误差区间的填充,以及如何在不同坐标轴间设置图例。
摘要由CSDN通过智能技术生成

系列文章目录

matplotlib可视化:基础绘图函数使用【函数功能+案例代码】
matplotlib可视化:基础图像绘制
matplotlib可视化:局部放大折线图+阴影区间绘制【原理+代码】



一、效果展示

首先放效果图。图像由主图和子图组成,包括两条折线,没一条折线均被上下误差线组成的区间包围,并通过颜色填充。
在这里插入图片描述

二、图像绘制

2.1 准备工作

2.1.1 依赖库导入

采用2022年美赛C题数据集,通过pandas读取数据,matplotlib绘制图像。

import matplotlib.pyplot as plt
import pandas as pd
from mpl_toolkits.axes_grid1.inset_locator import mark_inset

2.1.2 字体设置和乱码消除

#字体设置和乱码消除
plt.rcParams['font.sans-serif'] = ['SongNTR']
plt.rcParams["axes.unicode_minus"] = False

采用的SongNTR字体(中文:宋体;英文:Times New Roman),具体本机的字体配置参考博文:字体设置

2.1.3 数据准备

分析图像,上面共存在2条折线,以及他们的上下区间折线。这里采用黄金比特币的价格作为主要的2条折线;价格±5天的标准差*n作为上下区间折线。

#数据
mkpruData=pd.read_csv(r"D:\data\BCHAIN-MKPRU.csv")
goldData=pd.read_csv(r"D:\data\LBMA-GOLD.csv")
gold_price=goldData["USD (PM)"]
mkpru_price=mkpruData["Value"]
#标准差计算
goldStd=gold_price.rolling(5).std()
mkpruStd=mkpru_price.rolling(5).std()

2.2 主图绘制

主图绘制过程中,主要遇到了两个问题:1、阴影区间如何填充。2、双轴图的图例如何绘制。

2.2.1 阴影区间绘制

主要采用函数如下:

matplotlib.pyplot.fill_between(x, y1, y2=0, where=None, interpolate=False, step=None, *, data=None, **kwargs)

其中主要的参数为x,y1,y2。x表示的为x轴数据,y1,y2表示上界,下界,其中y2默认为0。
在图像中,我们采用如下语法绘制:

plt.fill_between(range(len(goldStd)),
                 gold_price-5*goldStd,
                 gold_price+5*goldStd,
                 color='#1111FF',
                 alpha=0.2)
plt.fill_between(range(len(mkpruStd)),
                mkpru_price-8*mkpruStd,
                 mkpru_price+8*mkpruStd,
                 color='#FF1010',
                 alpha=0.2)              

注:alpha为透明度设置。

2.2.2 双轴图的图例绘制

双轴图的相关介绍在matplotlib可视化:基础图像绘制的第二章。这里不再赘述。

对于单轴的图像来说,直接通过ax.legend()便可以设置图例的相关属性;但是双轴图存在两个axes对象,如何让两个axes对象生成的图例位于一个方框中。
通过查阅博客发现,plot函数返回值是一个存放Line2D类型的元素的列表,legend方法中可以通过传递图像列表以及标签列表,然后绘制不同axes中的图例。

line1,=ax1.plot(gold_price,color='#1111FF',linewidth=1.3)
line2,=ax1_2.plot(mkpru_price,color='#FF1010',linewidth=1.3)
ax1.legend([line1,line2],["黄金","比特币"],loc='upper left',ncol=2,prop={"size":9})

由此,可以得到[line1,line2]图像对应的图例名称为[“黄金”,“比特币”],将其传入legend中即可。其余的legend参数可以参考博客:matplotlib可视化:基础绘图函数使用第2.5小节坐标轴设置。

2.2.3 主图绘制代码

主图绘制的所有代码如下:

#绘制折线图
fig=plt.figure(dpi=300)
ax1=fig.add_axes([0.1,0.1,0.95,0.5])
#黄金折线
line1,=ax1.plot(gold_price,color='#1111FF',linewidth=1.3)
ax1.set_title("黄金比特币价格折线图",fontsize=14)
ax1.set_xlabel("时间",fontsize=12)
ax1.set_ylabel("黄金价格",fontsize=12,color="#1111FF")
ax1.set_xlim(0,1200)
plt.fill_between(range(len(goldStd)),
                 gold_price-5*goldStd,
                 gold_price+5*goldStd,
                 color='#1111FF',
                 alpha=0.2)
plt.tick_params('y',labelcolor="#1111FF")

#比特币折线
ax1_2=ax1.twinx()
line2,=ax1_2.plot(mkpru_price,color='#FF1010',linewidth=1.3)
ax1_2.set_ylabel("比特币价格",fontsize=12,color="#FF1010")
plt.fill_between(range(len(mkpruStd)),
                mkpru_price-8*mkpruStd,
                 mkpru_price+8*mkpruStd,
                 color='#FF1010',
                 alpha=0.2)
plt.tick_params('y',labelcolor="#FF1010")
ax1.legend([line1,line2],["黄金","比特币"],loc='upper left',ncol=2,prop={"size":9})

2.3 子图绘制

2.3.1 子图坐标轴建立

子图坐标轴的建立,其核心思想主要是通过主图的axes对象创建一个新的axes对象作为子图,通过主图的axes对象和子图的axes对象相互关联。主要通过两种方式实现
【方法一】

mpl_toolkits.axes_grid1.inset_locator.inset_axes(parent_axes, width, height, loc='upper right', bbox_to_anchor=None, bbox_transform=None, axes_class=None, axes_kwargs=None, borderpad=0.5)

主要参数说明:

参数含义
parent_axes主坐标系的axes对象
width,height创建的子图轴的大小。若为浮点数(1.3),则以英寸为单位;若为字符串(‘40%’),则以主图的相对大小为单位。若没有指定bbox_to_anchor和bbox_transform,就是相对主图的大小,否则为相对于bbox_to_anchor的大小。
loc子图边界框的位置,和图例的loc相同
bbox_to_anchor边界框,四元数组(x0, y0, width, height)
bbox_transform一般为parent_axes.transAxes

通过上述函数,构建如下的子图:

childax=inset_axes(ax1, width='60%', height='60%', bbox_to_anchor=(0,0.3,0.55,0.55),bbox_transform=ax1.transAxes)

【方法二】
通过ax.inset_axes(x0,y0,width,height)函数实现。
其中x0,y0为子图左下方坐标原点,width和height为子图的长度和宽度。
例如:

axins = ax.inset_axes((0.2, 0.2, 0.4, 0.3))

上述代码的含义是:以父坐标系中的x0=0.2x,y0=0.2y为左下角起点,嵌入一个宽度为0.4x,高度为0.3y的子坐标系,其中x和y分别为父坐标系的坐标轴范围。效果如下图所示:
在这里插入图片描述
由此,我们基于主图建立子图坐标轴:

#创建子坐标系,并设置区间
childAx1=ax1.inset_axes((0.16, 0.5, 0.4, 0.3))

2.3.2 子图折线图绘制

子图于主图的折线绘制并没有什么区别,主要是对于x轴和y轴的坐标轴区间范围显示发生了变化。传入的数据与主图相同。

childAx1=ax1.inset_axes((0.16, 0.5, 0.4, 0.3))
#黄金子折线图
line3,=childAx1.plot(gold_price,color='#1111FF',linewidth=1.3)
childAx1.fill_between(range(len(goldStd)),
                 gold_price-5*goldStd,
                 gold_price+5*goldStd,
                 color='#1111FF',
                 alpha=0.2)
childAx1.set_xlim(400,650)
childAx1.set_ylim(1100,1400)
childAx1.tick_params('y',labelcolor="#1111FF")
#比特币子折线图
childAx1_2=childAx1.twinx()
line2,=childAx1_2.plot(range(len(mkpru_price)),mkpru_price,color='#FF1010',linewidth=1.3)
childAx1_2.fill_between(range(len(mkpruStd)),
                mkpru_price-8*mkpruStd,
                 mkpru_price+8*mkpruStd,
                 color='#FF1010',
                 alpha=0.2)
childAx1_2.tick_params('y',labelcolor="#FF1010")
childAx1_2.set_ylim(0,21000)

在这里,我选择了x轴范围在400-650之间的数据绘制子图;然后分别设置y轴的范围为1100-1400,0-21000。采用的命令:

childAx1.set_xlim(400,650)
childAx1.set_ylim(1100,1400)
childAx1_2.set_ylim(0,21000)

2.3.3 主图和子图之间边界框连线绘制

采用mark_inset函数实现

mpl_toolkits.axes_grid1.inset_locator.mark_inset(parent_axes, inset_axes, loc1, loc2, **kwargs)

参数含义如下:

参数含义
parent_axes主坐标系的axes对象
inset_axes子坐标系的axes对象
loc1其中一个连接线的位置 ,坐标系的四个角( 1 (右上) 2 (左上) 3(左下) 4(右下))
loc2另外一个连接线的位置

由此,我们绘制子图边界框:

mark_inset(ax1, childAx1, loc1=3, loc2=4, fc="none", ec='k', lw=1)

三、完整代码

完整代码如下:

import matplotlib.pyplot as plt
import pandas as pd
from mpl_toolkits.axes_grid1.inset_locator import mark_inset

#字体设置和乱码消除
plt.rcParams['font.sans-serif'] = ['SongNTR']
plt.rcParams["axes.unicode_minus"] = False

#数据
mkpruData=pd.read_csv(r"D:\paper\图像复刻\折线图\data\BCHAIN-MKPRU.csv")
goldData=pd.read_csv(r"D:\paper\图像复刻\折线图\data\LBMA-GOLD.csv")
gold_price=goldData["USD (PM)"]
mkpru_price=mkpruData["Value"]
#标准差计算
goldStd=gold_price.rolling(5).std()
mkpruStd=mkpru_price.rolling(5).std()


plt.style.use("default")
#绘制折线图
fig=plt.figure(dpi=300)
ax1=fig.add_axes([0.1,0.1,0.95,0.5])
#黄金折线
line1,=ax1.plot(gold_price,color='#1111FF',linewidth=1.3)
ax1.set_title("黄金比特币价格折线图",fontsize=14)
ax1.set_xlabel("时间",fontsize=12)
ax1.set_ylabel("黄金价格",fontsize=12,color="#1111FF")
ax1.set_xlim(0,1200)
plt.fill_between(range(len(goldStd)),
                 gold_price-5*goldStd,
                 gold_price+5*goldStd,
                 color='#1111FF',
                 alpha=0.2)
plt.tick_params('y',labelcolor="#1111FF")


#比特币折线
ax1_2=ax1.twinx()
line2,=ax1_2.plot(mkpru_price,color='#FF1010',linewidth=1.3)
ax1_2.set_ylabel("比特币价格",fontsize=12,color="#FF1010")
plt.fill_between(range(len(mkpruStd)),
                mkpru_price-8*mkpruStd,
                 mkpru_price+8*mkpruStd,
                 color='#FF1010',
                 alpha=0.2)
plt.tick_params('y',labelcolor="#FF1010")
ax1.legend([line1,line2],["黄金","比特币"],loc='upper left',ncol=2,prop={"size":9})

#创建子坐标系,并设置区间
# childax=inset_axes(ax1, width='60%', height='60%', bbox_to_anchor=(0,0.3,0.55,0.55),bbox_transform=ax1.transAxes)
childAx1=ax1.inset_axes((0.16, 0.5, 0.4, 0.3))
#黄金子折线图
line3,=childAx1.plot(gold_price,color='#1111FF',linewidth=1.3)
childAx1.fill_between(range(len(goldStd)),
                 gold_price-5*goldStd,
                 gold_price+5*goldStd,
                 color='#1111FF',
                 alpha=0.2)
childAx1.set_xlim(400,650)
childAx1.set_ylim(1100,1400)
childAx1.tick_params('y',labelcolor="#1111FF")
#比特币子折线图
childAx1_2=childAx1.twinx()
line2,=childAx1_2.plot(range(len(mkpru_price)),mkpru_price,color='#FF1010',linewidth=1.3)
childAx1_2.fill_between(range(len(mkpruStd)),
                mkpru_price-8*mkpruStd,
                 mkpru_price+8*mkpruStd,
                 color='#FF1010',
                 alpha=0.2)
childAx1_2.tick_params('y',labelcolor="#FF1010")
childAx1_2.set_ylim(0,21000)

# loc1 loc2: 坐标系的四个角
# 1 (右上) 2 (左上) 3(左下) 4(右下)
mark_inset(ax1, childAx1, loc1=3, loc2=4, fc="none", ec='k', lw=1)

# plt.show()
plt.savefig("test.png",dpi=300,transparent=False,bbox_inches='tight')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oax_knud

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值