本文主要是介绍用Matplotlib绘制柱状图(仿照极客湾的Mate60 Pro评测跑分图),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
用Matplotlib绘制柱状图(仿照极客湾的Mate60 Pro评测跑分图)
- 1、基础绘制
- 2、进阶版本
- 2.1 颜色的设置
- 2.1.1、背景颜色的设置
- 2.1.2、字体颜色的设置
- 2.2 bar数值显示
- 3、高级版本
- 3.1 带圆角的bar
- 3.2 标题和Logo
- 3.2.1 标题
- 3.2.2 Logo
- 最终完整代码
大家好!前段时间刷新闻看到一张Mate60 Pro的一张测评图,感觉挺有这种风格的数据展示方式挺有科技感。然后有个想法,想用Matplotlib去复现一下。这两天闲了下来,写个blog,打算从入门、进阶到高级三个阶段绘制这个柱状图。
注意,如果你用的是版本比较低的matplotlib和python,直接拿我的代码使用的话,有些是可能会报错的哦!
#我的matplotlib的版本
import matplotlib
print(matplotlib.__version__)
3.6.3
1、基础绘制
仔细观察这个图,图中内容比较简单,主要是两组数据即多核与单核的跑分。这两组数据比较好的表现方式就是柱状图(当然,横向和纵向的表达方式都是可以的),例图中呈现的是横向柱状图。
所以,咱们从最简单barh函数开始吧。
废话不多说,直接上代码
import numpy as np
import matplotlib.pyplot as pltplt.rcParams['font.family'] = 'Microsoft Yahei'
plt.rcParams['axes.unicode_minus'] = FalseMulticore = np.array([4019,3770,5150,3830,3765,3395,3820])
Singlecore = np.array([1005,1019,1508,1255,1127,903,907])
name = ['麒麟9000S\n华为Mate 60Pro','麒麟9000\n华为Mate 40Pro','骁龙8 Gen2\n小米13','骁龙8 Gen1\nMoto edge X30','骁龙888\n魅族18','骁龙865\n一加8 Pro','天玑8100\n红米K50']
#多核的数据
Multicore = np.array([4019,3770,5150,3830,3765,3395,3820])
#单核的数据
Singlecore = np.array([1005,1019,1508,1255,1127,903,907])
x = np.arange(len(Multicore))fig,ax = plt.subplots(figsize=(12,5))
height = 0.25
ax.barh(x-0.7*height,Multicore ,height=height,label='多核')
ax.barh(x+0.7*height,Singlecore,height=height,label='单核')
ax.set_yticks(x,labels=name,rotation=0)
ax.legend()
ax.invert_yaxis()
plt.show()
显示结果:
基础版本比较好理解,就是两个barh就可以画出来,唯一需要注意的就是柱子的位置和height大小。
2、进阶版本
有了基础版本,就有了复现例图的基础框架了。
进阶版本就是在此基础上,美化图片中的元素,在细节上尽可能做到一致。
2.1 颜色的设置
2.1.1、背景颜色的设置
图中的背景色是黑色,因此
fig,ax = plt.subplots(figsize=(12,5),facecolor='k') #图片的背景设置为黑色
ax.set_facecolor('k') #axes的背景颜色设置为黑色
与背景无关的元素就不能有黑色的存在,否则就混在一起,看不出来。
2.1.2、字体颜色的设置
(1)基础版本中bar的颜色是默认色,这里我们需要对其进行修改。
p1 = ax.barh(x-0.2,Multicore ,height=width,color='#5A8FFF',label='多核')
p2 = ax.barh(x+0.2,Singlecore,height=width,color='#CEFD89',label='单核')
(2)同时,左侧的y轴的刻度标签字体,根据处理器品牌的不同也是不同的颜色。因此也需要进行处理。
#这里首先要重新定义一下名称列表,将芯片与对应型号分开
#目的是不再用yticks和yticklabels,而是用ax.text去实现。
name1 = ['麒麟9000S','华为Mate 60Pro','麒麟9000','华为Mate 40Pro','骁龙8 Gen2','小米13','骁龙8 Gen1','Moto edge X30','骁龙888','魅族18','骁龙865','一加8 Pro','天玑8100','红米K50']
for i in range(len(Multicore)):ax.text(-750,i-1.5*height,s=name1[0::2][i],va='top',ha='left',fontsize=12,color=colors[i],transform = ax.transData)ax.text(-750,i+0.5*height,s=name1[1::2][i],va='top',ha='left',fontsize=9,color='w',transform = ax.transData)
2.2 bar数值显示
这个直接用bar_label函数就可以啦
for p in [p1,p2]:ax.bar_label(p,color='w',padding=10)
上完整代码
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'Microsoft Yahei'Multicore = np.array([4019,3770,5150,3830,3765,3395,3820])
Singlecore = np.array([1005,1019,1508,1255,1127,903,907])
name1 = ['麒麟9000S','华为Mate 60Pro','麒麟9000','华为Mate 40Pro','骁龙8 Gen2','小米13','骁龙8 Gen1','Moto edge X30','骁龙888','魅族18','骁龙865','一加8 Pro','天玑8100','红米K50']
x = np.arange(len(Multicore))height = 0.25colors=['r','r','lightskyblue','lightskyblue','lightskyblue','lightskyblue','orange']
fig,ax = plt.subplots(figsize=(12,5),facecolor='k')
ax.set_facecolor('k')p1 = ax.barh(x-0.2,Multicore ,height=height,color='#5A8FFF',label='多核')
p2 = ax.barh(x+0.2,Singlecore,height=height,color='#CEFD89',label='单核')for p in [p1,p2]:ax.bar_label(p,color='w',padding=10)ax.legend(frameon= False,labelcolor='w',loc=(0.9,0.98),ncol=2)
ax.invert_yaxis()
ax.set_xticks([])
ax.set_yticks([])
ax.set_xlim(-800,5400)
for i in range(len(Multicore)):ax.text(-750,i-1.5*height,s=name1[0::2][i],va='top',ha='left',fontsize=12,color=colors[i],transform = ax.transData)ax.text(-750,i+0.5*height,s=name1[1::2][i],va='top',ha='left',fontsize=9,color='w',transform = ax.transData)
plt.show()
显示结果:
怎么样,是不是有点点像了捏?
3、高级版本
虽然实现了进阶版本,但是也是大面上实现了这样一个样式。仍然有些元素或者说细节没有实现。
抱着追求完美的态度,总结一下进阶版本仍然存在的问题:
1、bar的形状是带圆角的长方形,这个难以用bar函数实现
2、图形的上方有个炫酷的标题和一个logo
如何解决上述问题呢?
很遗憾,基础代码和进阶代码的思路在这里可能用不了。为了实现这个功能,需要重新转换思路。
3.1 带圆角的bar
其实bar对象是class matplotlib.patches.Rectangle 的一个实例。那么圆角矩阵就对应class matplotlib.patches.FancyBboxPatch。
for i in range(len(Multicore)):mcp.append(mpatches.FancyBboxPatch((0,i-1.5*height),Multicore[i],height,ec="none",color='#5A8FFF',boxstyle=mpatches.BoxStyle("Round4", pad=0.03)))scp.append(mpatches.FancyBboxPatch((0,i+0.5*height),Singlecore[i],height,ec="none",color='#CEFD89',boxstyle=mpatches.BoxStyle("Round4", pad=0.03)))ax.text(-0.4,i-1.5*height,s=name1[0::2][i],va='top',ha='left',fontsize=12,color=colors[i],transform = ax.transData)ax.text(-0.4,i+0.5*height,s=name1[1::2][i],va='top',ha='left',fontsize=9,color='w',transform = ax.transData)
for mp,sp in zip(mcp,scp):ax.add_artist(mp)
其中 Multicore 和 Singlecore 的数据都除以3000。原因就是FancyBboxPatch长宽比例差距过大的时候round几乎看不出来。
Multicore = np.array([4019,3770,5150,3830,3765,3395,3820])/3000
Singlecore = np.array([1005,1019,1508,1255,1127,903,907])/3000
3.2 标题和Logo
3.2.1 标题
废话不多说,直接上代码
>||<这个多边形的形状是我一点点找坐标调出来的,太费劲了 >||<
verts = [[-0.58,-2.3],[-0.32,-2.4],[-0.3, -2.2], [1., -2.2], [0.98, -1.3], [0.95, -1.1],[-0.3, -1.1], [-0.3, -1.1],[-0.32,-0.9],[-0.52,-0.9],[-0.55,-1.1],[-0.52,-0.9],
]
# 坐标对应的路径代码
codes = [Path.MOVETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.CLOSEPOLY,
]
path1 = Path(verts, codes)
patch1 = mpatches.PathPatch(path1, facecolor='lightgrey', zorder=0,lw=2,alpha=0.7)
ax.add_patch(patch1)
3.2.2 Logo
由于Logo这个我没有现成的图片,不知道会不会涉及版权啥的,我就用一个自己电脑里的logo图标替代一下。
myfig = mpimg.imread(r'E:/Desktop/Logo.png')
axin = ax.inset_axes([0,0.85,0.12,0.12])
axin.imshow(myfig)
axin.set_axis_off()
我的logo
最终完整代码
上完整代码
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.path import Path
import matplotlib.image as mpimg
plt.rcParams['font.family'] = 'Microsoft Yahei'height= 0.2
mcp = []
scp = []
colors=['r','r','lightskyblue','lightskyblue','lightskyblue','lightskyblue','orange']
name1 = ['麒麟9000S','华为Mate 60Pro','麒麟9000','华为Mate 40Pro','骁龙8 Gen2','小米13','骁龙8 Gen1','Moto edge X30','骁龙888','魅族18','骁龙865','一加8 Pro','天玑8100','红米K50']
Multicore = np.array([4019,3770,5150,3830,3765,3395,3820])/3000
Singlecore = np.array([1005,1019,1508,1255,1127,903,907])/3000
x = np.arange(len(Multicore))fig,ax = plt.subplots(figsize=(12,6),facecolor='k',dpi=600)for i in range(len(Multicore)):mcp.append(mpatches.FancyBboxPatch((0,i-1.5*height),Multicore[i],height,ec="none",color='#5A8FFF',boxstyle=mpatches.BoxStyle("Round4", pad=0.03)))scp.append(mpatches.FancyBboxPatch((0,i+0.5*height),Singlecore[i],height,ec="none",color='#CEFD89',boxstyle=mpatches.BoxStyle("Round4", pad=0.03)))ax.text(-0.4,i-1.5*height,s=name1[0::2][i],va='top',ha='left',fontsize=12,color=colors[i],transform = ax.transData)ax.text(-0.4,i+0.5*height,s=name1[1::2][i],va='top',ha='left',fontsize=9,color='w',transform = ax.transData)
for mp,sp in zip(mcp,scp):ax.add_artist(mp)ax.add_artist(sp)
p1 = ax.barh(x-0.2,Multicore ,height=height,color='none')
p2 = ax.barh(x+0.2,Singlecore,height=height,color='none')
for p in [p1,p2]:ax.bar_label(p,labels=[int(np.round(i)) for i in p.datavalues*2000],color='w',padding=10)
ax.set_facecolor('k')ax.set_xlim(-0.6,2.)
ax.set_ylim(-2.5,6.5)
ax.set_xticks([])
ax.set_yticks([])
ax.invert_yaxis()myfig = mpimg.imread(r'E:/Desktop/Logo.png')
axin = ax.inset_axes([0,0.85,0.12,0.12])
axin.imshow(myfig)
axin.set_axis_off()
verts = [[-0.58,-2.3],[-0.32,-2.4],[-0.3, -2.2], [1., -2.2], [0.98, -1.3], [0.95, -1.1],[-0.3, -1.1], [-0.3, -1.1],[-0.32,-0.9],[-0.52,-0.9],[-0.55,-1.1],[-0.52,-0.9],
]# 坐标对应的路径代码
codes = [Path.MOVETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.CLOSEPOLY,
]path1 = Path(verts, codes)
patch1 = mpatches.PathPatch(path1, facecolor='lightgrey', zorder=0,lw=2,alpha=0.7)
ax.add_patch(patch1)
ax.text(-0.2, -1.6,'Geekbench 5 CPU 测试',fontstyle='italic',color='w',va='center',ha='left',fontsize=20,fontweight='bold')
ax.legend(handles=[mp,sp],labels=['多核','单核'],frameon=False,labelcolor='w',ncol=2,loc=(0.78,0.84),fontsize=12,markerfirst=False)plt.show()
附图
已经十分相似了。哈哈哈哈!
不过仍然有两个小问题可以再进行优化:
1、legend中的矩形没有做到圆角。
2、bar的颜色没有改成渐变色。
欢迎小伙伴对代码进行优化和讨论!
也欢迎小伙伴向我进行咨询!
这篇关于用Matplotlib绘制柱状图(仿照极客湾的Mate60 Pro评测跑分图)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!