Python 绘制直方图 Matplotlib Pyplot figure bar legend gca text

引言

本文主要介绍一下 Python 怎么绘制直方图,涉及到直方图分组绘制,标签,图例,坐标轴的设置。

内容提要:

  1. Matplotlib 简介
    Pyplot 模块
  2. plt.figure 创建画板
  3. plt.bar 绘制直方图
    单组直方图的例子
    分组直方图的例子
  4. plt.text 设置数值标签
  5. plt.legend() 设置图例
  6. plt.gca() 坐标轴的设置
  7. 一个完整的直方图例子

Matplotlib 简介

Matplotlib 是 Python 的绘图库,它能让使用者很轻松地将数据图形化,并且提供多样化的输出格式。 Matplotlib 是一个非常强大的 Python 画图工具,我们可以使用该工具将很多数据通过图表的形式更直观的呈现出来。Matplotlib 可以绘制线图、散点图、等高线图、条形图、柱状图、3D 图形、甚至是图形动画等等。

要使用 Matplotlib,我们需要先安装再导入
pip install matplotlib
import matplotlib

Pyplot

Pyplot 是 Matplotlib 的子库,提供了和 MATLAB 类似的绘图 API。Pyplot 是常用的绘图模块,能很方便让用户绘制 2D 图表。Pyplot 包含一系列绘图函数的相关函数,每个函数会对当前的图像进行一些修改,例如:给图像加上标记,生新的图像,在图像中产生新的绘图区域等等。更多信息,请考官网

使用的时候,我们可以使用 import 导入 pyplot 库,并设置一个别名 plt:
import matplotlib.pyplot as plt

plt.figure 函数

创建一个画板

matplotlib.pyplot.figure(num=None, figsize=None, dpi=None, facecolor=None, edgecolor=None, frameon=True, FigureClass=<class ‘matplotlib.figure.Figure’>, clear=False, **kwargs)

参数 表述
num 指定创建的 figure 名称,默认按创建的顺序构建数字,文本类型;
figsize 以英寸为单位的宽高(1英寸等于2.54 厘米),用元组表示 figsize=(15,3);默认创建一个大小为 432x288 大小的画板(单位是像素)
dpi 指定绘图对象的分辨率,即每英寸多少个像素,缺省值为 72
facecolor 背景颜色
edgecolor 边框颜色
frameon 默认值True为绘制边框,如果为False则不绘制边框;
FigureClass 可以选择使用自定义图形实例;
clear 重建figure实例;
**kwargs 允许将自定义的图类绑定到pylab接口中,额外的kwargs将被传递给figure init函数。

下面通过例子来说明一下 figsize 和 dpi 怎么影响画板尺寸的,为了更形象,我们在画本里添加一个线性图。

import matplotlib.pyplot as plt

def plot(fs, dpi_set):
    plt.figure(figsize=fs, dpi=dpi_set)
    plt.title("size:{}, dpi:{}".format(fs, dpi_set))
    plt.plot([0, 1, 2, 3], [3, 4, 2, 5])
    plt.savefig(str(fs) + "-" + str(dpi_set) + ".png")


if __name__ == "__main__":
    figsize = (2, 2)
    for i in range(1, 4):
        plot(figsize, i*72)

    for i in [2, 4, 6]:
        plot((i, i), 72)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

plt.bar 函数

绘制直方图

matplotlib.pyplot.bar(x, height, width=0.8, bottom=None, *, align=‘center’, data=None, **kwargs)

参数 描述
x 为一个标量序列,确定 x 轴刻度数目
height 确定 y 轴的刻度, 可以是一个标量序列或一个标量
width 单个直方图的宽度,可以是一个标量序列或一个标量,默认 0.8
bottom 设置 y 边界坐标轴起点,可以是一个标量序列或一个标量
color 设置直方图颜色(只给出一个值表示全部使用该颜色,若赋值颜色列表则会逐一染色,若给出颜色列表数目少于直方图数目则会循环利用
edgecolor 直方图边框颜色

单组直方图的例子:

   import matplotlib.pyplot as plt

    x=[1,2,3,4,5]  
    y=[5,7,4,3,1]  
    color=['red','black','peru','orchid','deepskyblue']
    x_label=['One','Two','Three','Four','Five']
    plt.xticks(x, x_label)
    plt.bar(x, y,color=color)

    plt.show()

在这里插入图片描述
改变柱形图的宽度

plt.bar(x, y,width=[0.1,0.2,0.3,0.4,0.5],color=color)

在这里插入图片描述

将颜色设置成只有两个:会循环使用

color=['red','black']

在这里插入图片描述

分组直方图

有时我们进行不同类数据对比,例如统计不同时间 regression test cases 和 smoke test cases 运行情况情况。

    import matplotlib.pyplot as plt
    import numpy as np


    date = ['2022/2/1', '2022/2/2', '2022/2/3', '2022/2/4', '2022/2/5']
    regression_test_cases_pass_total = [10, 20, 30, 40, 50]
    smoke_test_cases_pass_total = [11, 12, 13, 14, 15]

    x = np.arange(len(date))  # the label locations
    width = 0.35  # the width of the bars

    plt.bar(x - width/2, regression_test_cases_pass_total, width, label='Regression passed')
    plt.bar(x + width/2, smoke_test_cases_pass_total, width, label='Smoke passed')

    # Add some text for labels, title and custom x-axis tick labels, etc.
    plt.ylabel('Count')
    plt.xlabel('Date')
    plt.title('Test Result Trend')
    plt.xticks(x,date)
    plt.legend()
    plt.show()

这里的亮点在于:调整每个刻度上的两个直方图,使其分列刻度两边
这里通过左移和右移柱状图,达到互相“礼让”的目的
x - width/2 在 x 处,左移直方图宽度一半的距离
x + width/2 在 x 处,右移直方图宽度一半的距离

  plt.bar(x - width/2, regression_test_cases_pass_total, width, label='Regression passed')
  plt.bar(x + width/2, smoke_test_cases_pass_total, width, label='Smoke passed')

在这里插入图片描述
如果不调整柱状图的位置,会造成同一刻度的柱状图重叠
后赋值的(Smoke 的)会遮盖先赋值绘图的 Regression 的

plt.bar(x, regression_test_cases_pass_total, width, label='Regression passed')
plt.bar(x, smoke_test_cases_pass_total, width, label='Smoke passed')

在这里插入图片描述
通过设置 bottom 来设置 Y 轴的位置,默认的 Y 轴位置是从 0 开始,如下面通过 bottom=regression_test_cases_pass_total 将 smoke 的数据绘制在 regression 数据的上方,这里可不是重叠,只是将 smoke Y 轴的位置抬高了。

plt.bar(x, regression_test_cases_pass_total, width, label='Regression passed')
plt.bar(x, smoke_test_cases_pass_total, width, bottom=regression_test_cases_pass_total,label='Smoke passed')

在这里插入图片描述

plt.text 设置数值标签

给直方图标上数值标签,使得数据一目了然。

matplotlib.pyplot.text(x, y, s, fontdict=None, withdash=, **kwargs)

参数 描述
x,y 表示标签添加的位置,默认是根据坐标轴的数据来度量的,是绝对值,也就是说图中点所在位置的对应的值
s 显示内容
fontdict 一个定义s格式的dict
fontsize 字体大小
color str or tuple, 设置字体颜色 ,单个字符候选项{‘b’, ‘g’, ‘r’, ‘c’, ‘m’, ‘y’, ‘k’, ‘w’},也可以’black’,'red’等,tuple时用[0,1]之间的浮点型数据,RGB或者RGBA, 如: (0.1, 0.2, 0.5)、(0.1, 0.2, 0.5, 0.3)等
backgroundcolor 字体背景颜色
horizontalalignment(ha) 设置垂直对齐方式,可选参数:left,right,center
verticalalignment(va) 设置水平对齐方式 ,可选参数 : ‘center’ , ‘top’ , ‘bottom’ ,‘baseline’
rotation(旋转角度) 可选参数为:vertical,horizontal 也可以为数字
alpha 透明度,参数值0至1之间

看一下效果图:
在这里插入图片描述
通过下面语句便可实现,为了标签显示更美观,可以在 Y 轴的高度上适当调整一下,离柱状图高一点距离

    for i in x:
        plt.text(i - width/2, regression_test_cases_pass_total[i] + 1,regression_test_cases_pass_total[i], ha='center', fontsize=8,family='Calibri')
        plt.text(i + width/2, smoke_test_cases_pass_total[i] + 1,smoke_test_cases_pass_total[i], ha='center', fontsize=8,family='Calibri')

完整代码:

    import matplotlib.pyplot as plt
    import numpy as np


    date = ['2022/2/1', '2022/2/2', '2022/2/3', '2022/2/4', '2022/2/5']
    regression_test_cases_pass_total = [10, 20, 30, 40, 50]
    smoke_test_cases_pass_total = [11, 12, 13, 14, 15]

    x = np.arange(len(date))  # the label locations
    width = 0.35  # the width of the bars

    plt.bar(x - width/2, regression_test_cases_pass_total, width, label='Regression passed')
    plt.bar(x + width/2, smoke_test_cases_pass_total, width, label='Smoke passed')

    # Add some text for labels, title and custom x-axis tick labels, etc.
    plt.ylabel('Count')
    plt.xlabel('Date')
    plt.title('Test Result Trend')
    plt.xticks(x,date)

    for i in x:
        plt.text(i - width/2, regression_test_cases_pass_total[i] + 1,regression_test_cases_pass_total[i], ha='center', fontsize=8,family='Calibri')
        plt.text(i + width/2, smoke_test_cases_pass_total[i] + 1,smoke_test_cases_pass_total[i], ha='center', fontsize=8,family='Calibri')

    plt.legend()
    plt.show()

plt.legend() 设置图例

上例中 plt.bar( ) 中参数 label=’'传入字符串类型的值",也就是图例的名称, 使用 plt.legend( ) 使上述代码产生效果,显示出图例。

matplotlib.pyplot.legend(*args, **kwargs)

主要参数 handles、labels loc, bbox_to_anchor 四个参数,其中:
handles 需要传入你所画线条的实例对象,例如 bar_1 = plt.bar(…), bar_2 = plt.bar(…), handles = (bar_1, bar_2)

labels 是图例的名称(能够覆盖在plt.bar( )中 label 参数值)

loc 代表了图例在整个坐标轴平面中的位置(一般选取 ‘best’ 这个参数值,也是默认的值), 图例自动显示在一个坐标面内的数据图表最少的位置.

Location String Location Code
‘best’ 0
‘upper right’ 1
‘upper left’ 2
‘lower left’ 3
‘lower right’ 4
‘right’ 5
‘center left’ 6
‘center right’ 7
‘lower center’ 8
‘upper center’ 9
‘center’ 10

loc的分布图:
在这里插入图片描述
例如将上例中,设置如下: 图例就显示在正中心

plt.legend(loc='center')

在这里插入图片描述
bbox_to_anchor=(x0, y0) 自定义图例的起始坐标位置, 要结合 loc 这个参数使用, 它所处的方向就有 loc 这个参数来提供.

首先将 X, Y 轴看成是 (0,0) -> (1,1), 即 X, Y 轴看成长度分别是 1. 那么bbox_to_anchor(0.5, 0.5) 就是坐标轴中心的点.

还是拿上面的例子为例:

首先legend是一个bbox类型,是一个由4条边框围成的区域,轴域(axes)也是由4条边框围成的区域(x, y 轴,上边缘线,右边缘线)

plt.legend(bbox_to_anchor=(0.5, 1), loc='center')

坐标点 (0.5, 1), 它的方向是正中心,就是红点那个位置.
在这里插入图片描述
改变一下方向: 作上角,坐标点(0.5, 1) 的点位于 图例矩形的左上角.

plt.legend(bbox_to_anchor=(0.5, 1), loc='upper left')

在这里插入图片描述
改变左边轴和方向, 坐标点(1, 0.5) 就位于图例矩形左边中心位置.由于画板默认大小显示不全,你也可以通过 figure设置画板大小.

plt.legend(bbox_to_anchor=(1, 0.5), loc='center left')

在这里插入图片描述

plt.gca() 坐标轴的设置

只画一个坐标轴,没有数据填充。

import matplotlib.pyplot as plt
plt.figure(figsize = (5,5))
plt.plot()
plt.show()

生成的图看起来有点不数据,因为不是标准的过原点 (0, 0)的坐标轴。
在这里插入图片描述
gca 就是 get current axes 获取当前坐标轴,坐标轴是由左 Left,右 right,顶 top,底 bottom 4 个方向组成的。X 轴就是底部 bottom 的那根线,Y 轴就是左边 Left 的那根线。要移动某跟轴,需要先锁定某个方向。

例如,将 X 轴平移到 y=0 的位置上

import matplotlib.pyplot as plt
plt.figure(figsize = (5,5))
plt.plot()
ax = plt.gca()
#  要挪动底部的X轴,所以先目光锁定底部!
ax.xaxis.set_ticks_position('bottom')  
# 'data'表示按数值挪动,其后数字代表挪动到Y轴的刻度值
ax.spines['bottom'].set_position(('data',0))
plt.show()

在这里插入图片描述
同理移动 Y 轴

import matplotlib.pyplot as plt
plt.figure(figsize = (5,5))
plt.plot()
ax = plt.gca()
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0))
plt.show()

在这里插入图片描述
也可以设置坐标轴在整个画板的比例,通过设置宽度和高度的比例,特别是当设置图例在画板两测是,图例可能显示不完整,所以适当的缩放坐标轴的占比

如原图,坐标轴在画板中的占比
在这里插入图片描述
将宽度设置成只占 90%

import matplotlib.pyplot as plt
plt.figure(figsize = (5,5))
plt.plot()
ax = plt.gca()
box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.9, box.height])
plt.show()

在这里插入图片描述

一个完整的直方图例子

结合上面的知识点,来实践一下. 统计一下最近10天时间 regression 和 smoke 测试用例的运行趋势.
需求:
fail 的数据分别显示在 pass 的上方法,并用红色显示
图例位于画板右侧中部位置
直方图加上数据标签

先看一下效果图:
颜色灵感来自第 24 界北京冬奥会开幕式《立春》的绿色,哈哈!

在这里插入图片描述

小窍门:

如果数据悬殊比较大,比如大的数字非常大,小的数字非常小,在直方图上就显示不明显,绘制直方图的时候用巧妙用 edgecolor 这个属性。

plt.bar(x - bar_width/2, smoke_total_count_fail, color="red", edgecolor=smoke_fail_color, width= bar_width, bottom=smoke_total_count_pass)

当要保存图片时,需要注释掉 plt.show(),不然保存的图片会是空白。

plt.show() # should comment it if save the pic as a file, or the saved pic is blank
# dpi: image resolution, better resolution, bigger image size
# plt.savefig(image_file_name, bbox_inches='tight', dpi = 600)   

完整代码

import matplotlib.pyplot as plt
import matplotlib.font_manager as font_manager
import numpy as np

def draw_trend_image(reg_total_count_pass, reg_total_count_fail, smoke_total_count_pass, smoke_total_count_fail, date_list, image_file_name = 'test_result_trend.png'):
    
    # attributes
    bar_width = 0.3
    regression_pass_color = '#0ac00a'
    smoke_pass_color = '#068606'
    font_name = 'Calibri'
    label_size = 10
    text_size = 8
    title_size = 14

    # set the color for failure
    smoke_fail_color = []
    for item in smoke_total_count_fail:
       if item > 0:
           smoke_fail_color.append("red")
       else:
           smoke_fail_color.append(smoke_pass_color)   

    reg_fail_color = []
    for item in reg_total_count_fail:
       if item > 0:
           reg_fail_color.append("red")
       else:
           reg_fail_color.append(regression_pass_color)   

    if len(date_list) == 10:
        plt.figure(figsize=(10.8, 4.8))   
         
    # draw bar
    x = np.arange(len(date_list))
    plt.bar(x - bar_width/2, smoke_total_count_pass, color=smoke_pass_color, edgecolor=smoke_pass_color, width= bar_width, label="Smoke Passed")
    plt.bar(x - bar_width/2, smoke_total_count_fail, color="red", edgecolor=smoke_fail_color, width= bar_width, bottom=smoke_total_count_pass)
    plt.bar(x + bar_width/2, reg_total_count_pass, color=regression_pass_color, edgecolor=regression_pass_color, width= bar_width, label="Regression Passed")
    plt.bar(x + bar_width/2, reg_total_count_fail, color="red", edgecolor=reg_fail_color, width= bar_width, label="Failed", bottom=reg_total_count_pass)
  
    # set title, labels
    plt.title("Test Result Trend", fontsize=title_size, fontname=font_name)
    plt.xlabel("Date",fontsize=label_size, fontname=font_name)
    plt.ylabel("Count",fontsize=label_size, fontname=font_name)
    plt.xticks(x, date_list,fontsize=label_size, fontname=font_name)   

    # get current axes and set width ratio
    ax = plt.gca()
    box = ax.get_position()
    ax.set_position([box.x0, box.y0, box.width * 0.9, box.height])

    # loc: the position of the legend, bbox_to_anchor: start position of the legend
    legend_font = font_manager.FontProperties(family=font_name, weight='normal',style='normal', size=label_size)    
    plt.legend(loc='center left', bbox_to_anchor=(1, 0.5), prop= legend_font)

    # set bar text
    for i in x:
        if smoke_total_count_fail[i] > 0:
            plt.text(i-bar_width/2, smoke_total_count_fail[i] + smoke_total_count_pass[i], smoke_total_count_fail[i],horizontalalignment = 'center', verticalalignment='bottom',fontsize=text_size,family=font_name,color='red',weight='bold')   
          
        plt.text(i-bar_width, smoke_total_count_pass[i], smoke_total_count_pass[i],horizontalalignment = 'right', verticalalignment='top',fontsize=text_size,family=font_name,color=smoke_pass_color,weight='bold')
        plt.text(i, reg_total_count_pass[i], reg_total_count_pass[i], horizontalalignment = 'right', verticalalignment='top',fontsize=text_size,family=font_name,color=regression_pass_color,weight='bold')
 
        if reg_total_count_fail[i] > 0:
            plt.text(i+ bar_width/2, reg_total_count_fail[i] + reg_total_count_pass[i], reg_total_count_fail[i],horizontalalignment = 'center', verticalalignment='bottom',fontsize=text_size,family=font_name,color='red',weight='bold')   
       

    plt.show() # should comment it if save the pic as a file, or the saved pic is blank
    # dpi: image resolution, better resolution, bigger image size
    # plt.savefig(image_file_name, bbox_inches='tight', dpi = 600)


if  __name__ == '__main__':
    #    reg_total_count_pass = [11641, 11641, 11641,11641,11641,11641,11641,11641,11641,11641]
    reg_total_count_pass = [1000, 1000, 1000,1000,1000,1000,1000,1000,1000,1000]
    reg_total_count_fail = [30, 5, 6,10,2,1,10,10,0,0]
    #    smoke_total_count_pass = [963, 963, 963,963, 963, 963,963, 963, 963,963] 
    smoke_total_count_pass = [100, 100, 100,100, 100, 100,100, 100, 100,100] 
    smoke_total_count_fail = [3, 5, 6,0,0,0,0,0,0,0]
    date_list = ['2022/1/1', '2022/1/2', '2022/1/3','2022/1/4','2022/1/5', '2022/1/6', '2022/1/7','2022/1/8', '2022/1/9', '2022/1/10']
    draw_trend_image(reg_total_count_pass, reg_total_count_fail, smoke_total_count_pass, smoke_total_count_fail, date_list)

猜你喜欢

转载自blog.csdn.net/wumingxiaoyao/article/details/122769640