【1】引言
前述python画图学习中,已经探索过X轴和Y轴的共享,可通过下述链接直达:
但现实的画图实践中总会有新的要求,之前将所有轴合到一起的形式可能不再适用,因此,很有必要探索多个Y轴分列的绘图方法。
【2】官网教程
点击下述链接直达官网教程:
官网教程非常简洁,为此增加了注释。
【3】代码解读
首先引入画图模块matplotlib:
import matplotlib.pyplot as plt #引入画图模块
然后定义了要画图,并约定了第一个图的右边线在整个图片中的位置:
fig, ax = plt.subplots() #定义要画图 fig.subplots_adjust(right=0.75) #设置图形右侧边线所处位置,是图形宽度的函数
关于 fig.subplots_adjust()的解释可参考下述链接:
fig.subplots_adjust()实际可设置以下内容:
Figure.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
left, bottom, right和top 代表图像左下右上边线在整个图形中的位置,相当于直接约定了图像的大小,wspace和hspace则约定了图像的填充。
为此,在【3.1】节展开测试。
接下来使用twinx()函数对X轴进行共享:
twin1 = ax.twinx() #twin1和ax共享X轴 twin2 = ax.twinx() #twin2和ax共享X轴
然后对twin2的Y轴进行了位置设定:将Y轴设定在图像右侧120%位置处
twin2.spines.right.set_position(("axes", 1.2))
然后分别绘制p1、p2和p3:
p1, = ax.plot([0, 1, 2], [0, 1, 2], "C0", label="Density") p2, = twin1.plot([0, 1, 2], [0, 3, 2], "C1", label="Temperature") p3, = twin2.plot([0, 1, 2], [50, 30, 15], "C2", label="Velocity")
然后对坐标范围和标签进行设置:
ax.set(xlim=(0, 2), ylim=(0, 2), xlabel="Distance", ylabel="Density") #设置坐标范围和标签 twin1.set(ylim=(0, 4), ylabel="Temperature") #设置坐标范围和标签 twin2.set(ylim=(1, 65), ylabel="Velocity") #设置坐标范围和标签
然后是颜色设置,这里是一个巧妙的嵌套:先设置文本的颜色,然后要求线条颜色和文本颜色一致:
ax.yaxis.label.set_color(p1.get_color()) #设置线条颜色和文本颜色 twin1.yaxis.label.set_color(p2.get_color()) #设置线条颜色和文本颜色 twin2.yaxis.label.set_color(p3.get_color()) #设置线条颜色和文本颜色
之后设置了Y轴凸出的小短线标签的颜色,使其和线条文本的颜色保持一致:
ax.tick_params(axis='y', colors=p1.get_color()) #设置Y轴凸出标签的颜色 twin1.tick_params(axis='y', colors=p2.get_color()) #设置Y轴凸出标签的颜色 twin2.tick_params(axis='y', colors=p3.get_color()) #设置Y轴凸出标签的颜色
最后设置图例和输出图形:
ax.legend(handles=[p1, p2, p3]) plt.show()
ax.legend()中的handles直接指向p1, p2, p3,实现图例的集中输出。
至此的完整代码为:
import matplotlib.pyplot as plt #引入画图模块
fig, ax = plt.subplots() #定义要画图
fig.subplots_adjust(right=0.75) #设置图形右侧边线所处位置,是图形宽度的函数
twin1 = ax.twinx() #twin1和ax共享X轴
twin2 = ax.twinx() #twin2和ax共享X轴
# Offset the right spine of twin2. The ticks and label have already been
# placed on the right by twinx above.
twin2.spines.right.set_position(("axes", 1.2)) #设置twin2的Y轴位于图像右侧120%处
p1, = ax.plot([0, 1, 2], [0, 1, 2], "C0", label="Density") #绘图
p2, = twin1.plot([0, 1, 2], [0, 3, 2], "C1", label="Temperature") #绘图
p3, = twin2.plot([0, 1, 2], [50, 30, 15], "C2", label="Velocity") #绘图
ax.set(xlim=(0, 2), ylim=(0, 2), xlabel="Distance", ylabel="Density") #设置坐标范围和标签
twin1.set(ylim=(0, 4), ylabel="Temperature") #设置坐标范围和标签
twin2.set(ylim=(1, 65), ylabel="Velocity") #设置坐标范围和标签
ax.yaxis.label.set_color(p1.get_color()) #设置线条颜色和文本颜色
twin1.yaxis.label.set_color(p2.get_color()) #设置线条颜色和文本颜色
twin2.yaxis.label.set_color(p3.get_color()) #设置线条颜色和文本颜色
ax.tick_params(axis='y', colors=p1.get_color()) #设置Y轴凸出标签的颜色
twin1.tick_params(axis='y', colors=p2.get_color()) #设置Y轴凸出标签的颜色
twin2.tick_params(axis='y', colors=p3.get_color()) #设置Y轴凸出标签的颜色
ax.legend(handles=[p1, p2, p3]) #集中输出图例
plt.show() #输出图形
输出图像为:
图1
【3】代码修改
【3.1】横向测试
输入以下代码,对fig.subplots_adjust()函数进行测试。此处的测试方案是横向输出子图,所以hspace的设置没有任何影响。
import matplotlib.pyplot as plt #引入画图模块
fig, ax = plt.subplots(1,2 ) #定义要画图
fig.subplots_adjust(left=0.5,bottom=0.5,right=0.8,top=0.8,wspace=3,hspace=1) #设置图形右侧边线所处位置,是图形宽度的函数
ax[0].plot([0, 1, 2], [0, 1, 2], "C0", label="Density")
ax[1].plot([0, 1, 2], [0, 1, 2], "C01", label="Density1")
ax[0].legend()
ax[0].set(xlim=(0, 2), ylim=(0, 2), xlabel="Distance", ylabel="Density")
ax[1].set(xlim=(0, 2), ylim=(0, 2), xlabel="Distance", ylabel="Density")
ax[1].legend()
plt.show()
运行代码后的输出图像为:
图2
图2中的两个子图位置由下述代码提前约定:
fig.subplots_adjust(left=0.5,bottom=0.5,right=0.8,top=0.8,wspace=3,hspace=1)
其中left=0.5,bottom=0.5约定了第一个子图的左边线和下边线分别位于整个图形横轴和纵轴的中部;right=0.8,top=0.8约定了右边线和上边线分别位于整个图形横轴和纵轴的80%位置处。这里面引用的所有数据起始点都位于图像的左下角(0,0)位置处。
wspace=3约定了两个子图的横向间距是3个坐标轴宽度。
【3.2】纵向测试
输入以下代码,对fig.subplots_adjust()函数进行测试。此处的测试方案是横向输出子图,所以wspace的设置没有任何影响。
import matplotlib.pyplot as plt #引入画图模块
fig, ax = plt.subplots(2,1 ) #定义要画图
fig.subplots_adjust(left=0.5,bottom=0.5,right=0.8,top=0.8,wspace=3,hspace=6) #设置图形右侧边线所处位置,是图形宽度的函数
ax[0].plot([0, 1, 2], [0, 1, 2], "C0", label="Density")
ax[1].plot([0, 1, 2], [0, 1, 2], "C01", label="Density1")
ax[0].legend()
ax[0].set(xlim=(0, 2), ylim=(0, 2), xlabel="Distance", ylabel="Density")
ax[1].set(xlim=(0, 2), ylim=(0, 2), xlabel="Distance", ylabel="Density")
ax[1].legend()
plt.show()
运行代码后的输出图像为:
图3
图2中的两个子图位置由下述代码提前约定:
fig.subplots_adjust(left=0.5,bottom=0.5,right=0.8,top=0.8,wspace=3,hspace=6)
其中left=0.5,bottom=0.5约定了第一个子图的左边线和下边线分别位于整个图形横轴和纵轴的中部;right=0.8,top=0.8约定了右边线和上边线分别位于整个图形横轴和纵轴的80%位置处。这里面引用的所有数据起始点都位于图像的左下角(0,0)位置处。
hspace=6约定了两个子图的纵向间距是6个坐标轴宽度。
【4】代码优化
引入计算模块,将变量变成三角函数,改后代码为:
import matplotlib.pyplot as plt #引入画图模块
import numpy as np #引入计算模块
fig, ax = plt.subplots() #定义要画图
fig.subplots_adjust(right=0.75) #设置图形右侧边线所处位置,是图形宽度的函数
t=np.linspace(0,10,100)
twin1 = ax.twinx() #twin1和ax共享X轴
twin2 = ax.twinx() #twin2和ax共享X轴
# Offset the right spine of twin2. The ticks and label have already been
# placed on the right by twinx above.
twin2.spines.right.set_position(("axes", 1.2)) #设置twin2的Y轴位于图像右侧120%处
p1, = ax.plot(t, np.sin(t)+1, "C0", label="Density") #绘图
p2, = twin1.plot(t, np.cos(t)+1, "C1", label="Temperature") #绘图
p3, = twin2.plot(t, np.sin(t)+np.cos(t)+1, "C2", label="Velocity") #绘图
ax.set(xlim=(0, 10), ylim=(-0.25, 3), xlabel="Distance", ylabel="Density") #设置坐标范围和标签
twin1.set(ylim=(-0.5, 3), ylabel="Temperature") #设置坐标范围和标签
twin2.set(ylim=(-1.5, 3), ylabel="Velocity") #设置坐标范围和标签
ax.yaxis.label.set_color(p1.get_color()) #设置线条颜色和文本颜色
twin1.yaxis.label.set_color(p2.get_color()) #设置线条颜色和文本颜色
twin2.yaxis.label.set_color(p3.get_color()) #设置线条颜色和文本颜色
ax.tick_params(axis='y', colors=p1.get_color()) #设置Y轴凸出标签的颜色
twin1.tick_params(axis='y', colors=p2.get_color()) #设置Y轴凸出标签的颜色
twin2.tick_params(axis='y', colors=p3.get_color()) #设置Y轴凸出标签的颜色
ax.legend(handles=[p1, p2, p3]) #集中输出图例
plt.show() #输出图形
运行后的图像为:
图4
可见,twins()函数确实有良好的适应性。
【5】动态输出
在先前的学习进程中,已经掌握动态输出技能,参考下述链接:
此处也尝试一下,制作Y轴分列的动态输出图,首先实现动态输出,在编辑器输入以下代码:
import matplotlib.pyplot as plt #引入画图模块
import numpy as np #引入计算模块
import matplotlib.animation as animation #引入动画制作模块
fig, ax = plt.subplots() #定义画图
x = np.arange(0, 2*np.pi, 0.01) #定义自变量
twin1 = ax.twinx() #twin1和ax共享X轴
twin2 = ax.twinx() #twin2和ax共享X轴
line, = ax.plot(x, np.sin(x)) #定义因变量
line1, = twin1.plot(x, np.cos(x)) #定义因变量
line2, = twin2.plot(x, np.cos(x)+np.sin(x)) #定义因变量
def animate(i): #自定义函数
line.set_ydata(np.sin(x + i / 50)) # update the data. 更新Y轴数据,实现动态输出
line1.set_ydata(np.cos(x + i / 50)) # update the data. 更新Y轴数据,实现动态输出
line2.set_ydata(np.cos(x + i / 50)+np.sin(x + i / 50))
return line,line1,line2
ani = animation.FuncAnimation( #绘制动画
fig, animate, interval=20, blit=True, save_count=50)
ani.save('animations-3lines.gif') #保存动画
plt.show() #输出图形
获得输出图形为:
图5
由图5可见,在未设置分列Y轴位置的时候,所有轴重合在一起。
为此,继续增加代码,让不同的Y轴分列显示,同时给线条设置不同的颜色。
先设置图形右侧边线位置:
fig, ax = plt.subplots() #定义画图 fig.subplots_adjust(right=0.75)
再设置第二个分列轴的位置:
twin2.spines.right.set_position(("axes", 1.2))
统一轴和线条的颜色:
ax.yaxis.label.set_color(line.get_color()) #设置线条颜色和文本颜色 twin1.yaxis.label.set_color(line1.get_color()) #设置线条颜色和文本颜色 twin2.yaxis.label.set_color(line2.get_color()) #设置线条颜色和文本颜色 ax.tick_params(axis='y', colors=line.get_color()) #设置Y轴凸出标签的颜色 twin1.tick_params(axis='y', colors=line1.get_color()) #设置Y轴凸出标签的颜色 twin2.tick_params(axis='y', colors=line2.get_color()) #设置Y轴凸出标签的颜色
获得的图形为:
图6
对应的完整代码为:
import matplotlib.pyplot as plt #引入画图模块
import numpy as np #引入计算模块
import matplotlib.animation as animation #引入动画制作模块
fig, ax = plt.subplots() #定义画图
fig.subplots_adjust(right=0.75)
x = np.arange(0, 10, 0.01) #定义自变量
twin1 = ax.twinx() #twin1和ax共享X轴
twin2 = ax.twinx() #twin2和ax共享X轴
twin2.spines.right.set_position(("axes", 1.2))
line, = ax.plot(x, np.sin(x),"C0",label="Density") #定义因变量
line1, = twin1.plot(x, np.cos(x),"C1",label="Temperature") #定义因变量
line2, = twin2.plot(x, np.cos(x)+np.sin(x), "C2",label="Velocity") #定义因变量
def animate(i): #自定义函数
line.set_ydata(np.sin(x + i / 50)) # update the data. 更新Y轴数据,实现动态输出
line1.set_ydata(np.cos(x + i / 50)) # update the data. 更新Y轴数据,实现动态输出
line2.set_ydata(np.cos(x + i / 50)+np.sin(x + i / 50))
return line,line1,line2
ani = animation.FuncAnimation( #绘制动画
fig, animate, interval=20, blit=True, save_count=50)
ax.set(xlim=(0, 10), ylim=(-1.5, 1.5), xlabel="Distance", ylabel="Density") #设置坐标范围和标签
twin1.set(ylim=(-1.5, 1.5), ylabel="Temperature") #设置坐标范围和标签
twin2.set(ylim=(-2.5, 3), ylabel="Velocity") #设置坐标范围和标签
ax.yaxis.label.set_color(line.get_color()) #设置线条颜色和文本颜色
twin1.yaxis.label.set_color(line1.get_color()) #设置线条颜色和文本颜色
twin2.yaxis.label.set_color(line2.get_color()) #设置线条颜色和文本颜色
ax.tick_params(axis='y', colors=line.get_color()) #设置Y轴凸出标签的颜色
twin1.tick_params(axis='y', colors=line1.get_color()) #设置Y轴凸出标签的颜色
twin2.tick_params(axis='y', colors=line2.get_color()) #设置Y轴凸出标签的颜色
ax.legend(handles=[line, line1, line2]) #集中输出图例
ani.save('animations-3lines.gif') #保存动画
plt.show() #输出图形
【6】总结
学习了多个Y轴分列右侧的操作技巧,在此基础上实现了动态曲线输出。