用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评测跑分图

大家好!前段时间刷新闻看到一张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)

其中 MulticoreSinglecore 的数据都除以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
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的颜色没有改成渐变色。


欢迎小伙伴对代码进行优化和讨论!
也欢迎小伙伴向我进行咨询!



本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部