绘图提高篇 | Python/R 双Y轴绘制

最近有很多小伙伴私信我关于双Y轴图的绘制方法? 这里我就直接给出Python-matplotlib绘制方法和R-ggplot2的绘制方法,主要的知识点如下:

  • Matplotlib-Axes.twinx()方法添加副轴

  • ggplot2-sec.axis()绘制双轴

  • 所有内容都已免费新增到我们的系统可视化课程中,有需要可以咨询呀!

Matplotlib-Axes.twinx()方法添加副轴

这里我们直接就给出数据预览和可视化设计的代码,图中部分代码我们再做详细解释,数据预览如下:

自定义的颜色字典year_color构造代码如下:

color = ("#51C1C8", "#E96279", "#44A2D6", "#536D84",
         "#51C1C8", "#E96279", "#44A2D6", "#536D84")
year = artist_01.index.to_list()
year_color = dict(zip(year,color))
year_color

可视化代码如下:

plt.style.use('fivethirtyeight')
fig,ax = plt.subplots(figsize=(8,4),dpi=200,facecolor='white',edgecolor='white')
ax.set_facecolor('white')

x = np.arange(0,len(artist_01),1)
y = artist_01['data01'].values

#绘制连接点的线
line = ax.plot(x,y,color='#333333',lw=1.,zorder=2)
#绘制不同散点图
scatter_out = ax.scatter(x,y,s=500,zorder=1,color='white',ec='grey',alpha=.7,lw=.5)
for i in artist_01.index.to_list():
    scatter = ax.scatter(x[i],y[i],s=180,zorder=3,ec='k',lw=.4,color=year_color[i])
scatter_in = ax.scatter(x,y,s=30,zorder=3,color="#333333")

#定制化绘制(设置图表风格)
ax.grid(color='gray',lw=.5,alpha=.5) #设置网格
ax.tick_params(left=False,bottom=False,labelbottom=False,labelsize=10,colors='gray')#设置刻度
ax.set_ylim(bottom=-3,top=43)#设置轴范围
ax.set_yticks(np.arange(0, 45, step=5)) #设置刻度标签
ax.set_xticks(np.arange(-.5, 8, step=.5))
#添加横线(修饰)
ax.axhline(y=0,color='#45627C',lw=3)
#添加数字标签
label_text = {"size":13,"color":"k",'weight':'semibold'}
for a,b in zip(x,y):
    ax.text(a, b+2.5, '%.0f' % b, ha='center', va= 'bottom',fontdict=label_text,color=year_color[a])
#设置轴脊(spine)
for spine in ['top','bottom','left','right']:
    ax.spines[spine].set_color("#FFFFFF") #设置颜色/set_visible()设置显示与否
for i in artist_01.index.to_list()[:3]:
    axins.scatter(x[i],y[i],s=80,color=year_color[i],zorder=2)
    
#添加标题处小图
#添加小散点图:重点掌握   
axins = inset_axes(ax, width=.4, height=.4,loc='upper left',
                   bbox_to_anchor=(0.01, 0.22, 1, 1),
                   bbox_transform=ax.transAxes,
                   borderpad=0)
axins.set_ylim(bottom=8,top=35)
axins.set_xlim(left=-.5,right=2.5)
axins.plot(x[:3],y[:3],color='#333333',lw=1.,zorder=1)
for i in artist_01.index.to_list()[:3]:
    axins.scatter(x[i],y[i],s=80,color=year_color[i],zorder=2)
axins.axis('off')
#绘制小横线:原理同上
line = inset_axes(ax,width=5.3, height=.4,loc='upper left',
                  bbox_to_anchor=(-0.015, 0.15, 1, 1),
                  bbox_transform=ax.transAxes,
                  borderpad=0)
line.plot([.1,.7],[.1,.1],color='#45627C',lw=2)
line.axis('off')

#添加阴影效果
for i in artist_01.index.to_list():
    ax.axvspan(i-.35, i+.35, facecolor='gray',alpha=.1,zorder=0)
    
#添加双y轴:使用Axes.twinx()方法绘制
second_plot = ax.twinx()
second_plot.set_ylim(bottom=-3,top=43)
second_plot.set_yticks(np.arange(0, 50, step=10))
second_plot.set_xticks(np.arange(-.5, 8, step=.5))
second_plot.tick_params(left=False,bottom=False,labelbottom=False,labelsize=10,colors='k')
second_plot.grid(color="none",zorder=0)
second_plot.set_axisbelow(True)
for spine in ['top','bottom','left','right']:
    second_plot.spines[spine].set_visible(False) #("#FFFFFF")

y2 = artist_01['data02'].values
label_text = {"size":28,"color":"white",'weight':'light'}
for x,y2 in zip(np.arange(len(artist_01)).tolist(),artist_01['data02'].to_list()):
    second_plot.plot([x,x],[0,y2],lw=20,color=color[x],solid_capstyle='round')
    #绘制空心圆
    second_plot.scatter(x,0,s=150,c='white',zorder=3)
    second_plot.scatter(x,0,s=60,c=color[x],zorder=4)
    second_plot.scatter(x,0,s=15,c='white',zorder=5)
   
# 添加文本信息
label_font = {"size":15,'weight':'bold'}
for i,x,text in zip(artist_01.index.to_list(),np.arange(0,len(artist_01),1),artist_01['year'].values):
    ax.text(x, -8,text ,ha='center', va= 'bottom',fontdict=label_font,color=year_color[i],zorder=2)

ax.text(.39,1.2,'\nSecond Y Axes Plot Exercise',transform = ax.transAxes,
        ha='center', va='center',fontsize = 20,color='k',fontweight="bold")
ax.text(.02,1.04,'Use the Matplotlib axes.Axes.twinx()',
        transform = ax.transAxes,
        ha='left', va='center',fontsize = 9,color='#45627C')

ax.text(.91,.02,'\nVisualization by DataCharm',transform = ax.transAxes,
        ha='center', va='center',fontsize = 7,color='black')
plt.savefig(r'double_y_axis_plot.png',width=6,height=3,
            dpi=900,bbox_inches='tight',facecolor='white')
#ax.set_axisbelow(True)
plt.show()

解释:

1. 添加横线(修饰)

ax.axhline(y=0,color='#45627C',lw=3)

2. 添加标题处小图

axins = inset_axes(ax, width=.4, height=.4,loc='upper left',
                   bbox_to_anchor=(0.01, 0.22, 1, 1),
                   bbox_transform=ax.transAxes,
                   borderpad=0)
axins.set_ylim(bottom=8,top=35)
axins.set_xlim(left=-.5,right=2.5)
axins.plot(x[:3],y[:3],color='#333333',lw=1.,zorder=1)
for i in artist_01.index.to_list()[:3]:
    axins.scatter(x[i],y[i],s=80,color=year_color[i],zorder=2)
axins.axis('off')

3. 添加双y轴:使用Axes.twinx()方法绘制:重点

#添加双y轴:使用Axes.twinx()方法绘制
second_plot = ax.twinx()
second_plot.set_ylim(bottom=-3,top=43)
second_plot.set_yticks(np.arange(0, 50, step=10))
second_plot.set_xticks(np.arange(-.5, 8, step=.5))
second_plot.tick_params(left=False,bottom=False,labelbottom=False,labelsize=10,colors='k')
second_plot.grid(color="none",zorder=0)
second_plot.set_axisbelow(True)
for spine in ['top','bottom','left','right']:
    second_plot.spines[spine].set_visible(False) #("#FFFFFF")

y2 = artist_01['data02'].values
label_text = {"size":28,"color":"white",'weight':'light'}
for x,y2 in zip(np.arange(len(artist_01)).tolist(),artist_01['data02'].to_list()):
    second_plot.plot([x,x],[0,y2],lw=20,color=color[x],solid_capstyle='round')
    #绘制空心圆
    second_plot.scatter(x,0,s=150,c='white',zorder=3)
    second_plot.scatter(x,0,s=60,c=color[x],zorder=4)
    second_plot.scatter(x,0,s=15,c='white',zorder=5)

最终的可视化结果如下:

总结: Python-matplotlib 绘制双Y轴的关键就是使用Axes.twinx()方法再次添加一个绘图对象,再把要绘制的对象在此绘图对象上绘制即可,其他和正常的matplotlib语法一样。

ggplot2-sec.axis()绘制双轴

在介绍完Python-matplotlib 绘制双Y轴后,我们再次介绍R-ggplot2如何绘制双Y轴,由于绘制上面的可视化结果较为繁琐,这里我们直接生成样例数据进行双Y轴的讲解。主要涉及的知识点就是scale_y_continuous() 或scale_x_continuous()中的sec.axis()属性设置。

构建数据

这里我们构建虚拟数据,代码如下:

data <- data.frame(
  day = as.Date("2019-01-01") + 0:99,
  temperature = runif(100) + seq(1,100)^2.5 / 10000,
  price = runif(100) + seq(100,1)^1.5 / 10
)
head(data)

数据预览如下(部分):

可视化绘制

这里我们直接给出代码,大家不懂的可以参考ggplot2官网,代码如下:

coeff <- 10
temperatureColor <- "#75B8D1"
priceColor <- "#D175B8"

double_y <-ggplot(data, aes(x=day)) +
 geom_line(aes(y=temperature), size=1.5, color=temperatureColor) + 
 geom_line(aes(y=price / coeff), size=1.5, color=priceColor) +
 #设置双轴关键代码
 scale_y_continuous(
   # first axis name
   name = "Temperature (C°)",
   # 定制化设置第二个图例属性
   sec.axis = sec_axis(trans = ~.*coeff, name="Price ($)")
 ) + 
 labs(x="",
      title = "R Charts Exercise: <span style='color:#D20F26'>Double Y Axis</span>",
      subtitle = "processed charts with <span style='color:#1A73E8'>scale_y/x_continuous:sec_axis()</span>",
      caption = "Visualization by <span style='color:#DD6449'>DataCharm</span>") +
 theme_ft_rc()+
 theme(
   axis.title.y = element_text(color = temperatureColor, size=13),
   axis.title.y.right = element_text(color = priceColor, size=13),
   plot.title = element_markdown(hjust = 0.5,vjust = .5,color = "black",
                                 size = 20, margin = margin(t = 1, b = 12)),
   plot.subtitle = element_markdown(hjust = 0,vjust = .5,size=15),
   plot.caption = element_markdown(face = 'bold',size = 12),
 ) 

设置双轴代码:

 #设置双轴关键代码
 scale_y_continuous(
   # first axis name
   name = "Temperature (C°)",
   # 定制化设置第二个图例属性
   sec.axis = sec_axis(trans = ~.*coeff, name="Price ($)")
 ) + 

最终可视化代码如下:

总结

本期推文我们简单介绍了Python-matplotlibR-ggplot2 绘制双Y轴的绘制方法,希望可以帮助到有需要的小伙伴。我们基础系列的图表绘制教程还在继续中哦,感谢大家持续支持和关注。