• Python - Matplot 绘制多图 直方图和折线图并存 共用 X 轴


    引言

    前面一篇文章 Python 绘制直方图 Matplotlib Pyplot figure bar legend gca text 有介绍如何用 Python 绘制直方图,但现实应用中需要在直方图的基础上再绘制折线图。比如测试报告中,就可以用图形反映测试用例的运行情况,直方图可以反映测试用例 pass 了多少,失败了多少,如果想反映 pass rate 趋势,就可以用折线图,本文介绍如何绘制多个图,以及最后应用在同一图上绘制直方图和折线图并存。

    内容提要:

    1. subplot 和 subplots 函数
    2. twinx 函数
    3. 直方图和折线图并存例子

    subplot 和 subplots 函数

    plt.figure 的作用是定义一个大的图纸,可以设置图纸的大小、分辨率等。
    例如:
    初始化一张画布
    fig = plt.figure(figsize=(7,10),dpi=100)

    直接在当前活跃的的 axes 上面作图,注意是当前活跃的
    plt.plot()plt.bar()

    那么现在来看 subplotsubplots ,两者的区别在于 suplots 绘制多少图已经指定了,所以 ax 提前已经准备好了,而 subplot 函数调用一次就绘制一次,没有指定。

    subplot 函数

    subplot 函数是添加一个指定位置的子图到当前画板中。

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

    函数签名:

    subplot(nrows, ncols, index, **kwargs) 括号里的数值依次表示行数、列数、第几个
    subplot(pos, **kwargs)
    subplot(ax) 这个我没用应用成功

    如果需要自定义画板大小,使用 subplot 这个函数时需要先定义一个自定义大小的画板,因为 subplot 函数无法更改画板的大小和分辨率等信息;所以必须通过 fig = plt.figure(figsize=(12, 4), dpi=200) 来定义画板相关设置;不然就按默认画板的大小; 同时,后续对于这个函数便捷的操作就是直接用 plt,获取当前活跃的图层。

    例如:添加 4 个子图
    没有自定义画板,所以是在默认大小的画板上添加子图的。
    plt.subplot(221) 中的 221 表示子图区域划分为 2 行 2 列,取其第一个子图区域。子区编号从左上角为 1 开始,序号依次向右递增。

    import matplotlib.pyplot as plt
    
        # equivalent but more general than plt.subplot(221)
        ax1 = plt.subplot(2, 2, 1)
    
        # add a subplot with no frame
        ax2 = plt.subplot(222, frameon=False)
    
        # add a polar subplot
        plt.subplot(223, projection='polar')
    
        # add a red subplot that shares the x-axis with ax1
        plt.subplot(224, sharex=ax1, facecolor='red')
    
        plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    效果:
    在这里插入图片描述

    subplots 函数

    subplots 函数主要是创建一个画板和一系列子图。

    matplotlib.pyplot.subplots(nrows=1, ncols=1, sharex=False, sharey=False, squeeze=True, subplot_kw=None, gridspec_kw=None, **fig_kw)

    该函数返回两个变量,一个是 Figure 实例,另一个 AxesSubplot 实例 。Figure 代表整个图像也就是画板整个容器,AxesSubplot 代表坐标轴和画的子图,通过下标获取需要的子区域。

    例 1:一个画板中只有 1 个子图
    plt.subplots() 默认 1 行 1 列

    	import numpy as np
        import matplotlib.pyplot as plt
        # First create some toy data:
        x = np.linspace(0, 2*np.pi, 400)
        y = np.sin(x**2)
        # Creates just a figure and only one subplot
        fig, ax = plt.subplots()
        ax.plot(x, y)
        ax.set_title('Simple plot')
        plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    例 2:一个画板中只有 2 个子图
    plt.subplots(1, 2)1 行 2 列

    	import numpy as np
        import matplotlib.pyplot as plt
    
        # First create some toy data:
        x = np.linspace(0, 2*np.pi, 400)
        y = np.sin(x**2)
    
        # Creates two subplots and unpacks the output array immediately
        f, (ax1, ax2) = plt.subplots(1, 2, sharey=True)
        ax1.plot(x, y)
        ax1.set_title('Sharing Y axis')
        ax2.scatter(x, y)
        plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述
    例 3:一个画板中只有 4 个子图
    plt.subplots(2, 2)2 行 2 列
    通过索引来访问子图:
    axes[0, 0] 第一个图位置
    axes[0, 1] 第二个图位置
    axes[1, 0] 第三个图位置
    axes[1, 1] 第四个图位置

    	import numpy as np
        import matplotlib.pyplot as plt
    
        # First create some toy data:
        x = np.linspace(0, 2*np.pi, 400)
        y = np.sin(x**2)
    
        # Creates four polar axes, and accesses them through the returned array
        fig, axes = plt.subplots(2, 2, subplot_kw=dict(polar=True))
        axes[0, 0].plot(x, y)
        axes[1, 1].scatter(x, y)
        plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述
    例 4:子图共享 X 轴

    sharex=‘col’

       	import numpy as np
        import matplotlib.pyplot as plt
    
        # Share a X axis with each column of subplots
        plt.subplots(2, 2, sharex='col')
        plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述
    例 5:子图共享 Y 轴

    sharey=‘row’

       	import numpy as np
        import matplotlib.pyplot as plt
    
        # Share a X axis with each column of subplots
        plt.subplots(2, 2, sharey='row')
        plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述
    例 6:子图共享 X 和 Y 轴
    sharex=‘all’, sharey=‘all’

       	import numpy as np
        import matplotlib.pyplot as plt
    
        # Share a X axis with each column of subplots
        plt.subplots(2, 2, sharex='all', sharey='all')
        # Note that this is the same as
        # plt.subplots(2, 2, sharex=True, sharey=True)
        plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    例 7:创建制定编号的画图
    编号为 10 的画板,如果该编号已经存在,则删除编号。

       	import numpy as np
        import matplotlib.pyplot as plt
    
    	# Creates figure number 10 with a single subplot
        # and clears it if it already exists.
        fig, ax=plt.subplots(num=10, clear=True)
        plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    twinx 函数

    了解了 subplots 函数,对 twinx 函数的理解就容易多了。

    twinx 返回一个新的子图并共用当前子图的 X 轴。新的子图会覆盖 X 轴(如果新子图 X 轴是 None 就会用当前子图的 X 轴数据),其 Y 轴会在右侧。同理 twiny 函数是共享 Y 轴的。

    例:我们先画了一个红色线子图,再画一个蓝色线子图,蓝色线子图是共用红色线子图的 X 轴的,那么就可以实样实现:

    fig, ax1 = plt.subplots() 生成一个画板 fig,并默认第一个子图 ax1.
    ax1.plot(t, data1, color=color) ax1 第一子图区域画折线图
    ax2 = ax1.twinx() 生成一个新的子图 ax2 ,并共享 ax1 第一子图的 X 轴
    ax2.plot(t, data2, color=color) 在新的子图 ax2 区域画折线图

    效果图:

    在这里插入图片描述

    代码:

    	import numpy as np
        import matplotlib.pyplot as plt
    
        # Create some mock data
        t = np.arange(0.01, 10.0, 0.01)
        data1 = np.exp(t)
        data2 = np.sin(2 * np.pi * t)
    
        fig, ax1 = plt.subplots()
    
        color = 'tab:red'
        ax1.set_xlabel('time (s)')
        ax1.set_ylabel('exp', color=color)
        ax1.plot(t, data1, color=color)
        ax1.tick_params(axis='y', labelcolor=color)
    
        ax2 = ax1.twinx()  # instantiate a second axes that shares the same x-axis
    
        color = 'tab:blue'
        ax2.set_ylabel('sin', color=color)  # we already handled the x-label with ax1
        ax2.plot(t, data2, color=color)
        ax2.tick_params(axis='y', labelcolor=color)
    
        fig.tight_layout()  # otherwise the right y-label is slightly clipped
        plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    应用:直方图和折线图并存

    有了前面的基础,我们来画一个直方图和折线图并存。基于前面一篇文章 Python 绘制直方图 Matplotlib Pyplot figure bar legend gca text 直方图的例子基础上再画折线图。

    应用场景是测试用例运行结果图,直方图描述 Smoke 和 Regressin 用例pass 和 fail 数量,折线图描述总的用例 Pass Rate。把 Mock 的数字换一下就可以直接应用了,具体代码细节就不详细介绍了,可以参考 文章 Python 绘制直方图 Matplotlib Pyplot figure bar legend gca text

    效果图:
    在这里插入图片描述

    代码:

    import matplotlib.pyplot as plt
    import matplotlib.font_manager as font_manager
    import numpy as np
    from matplotlib.ticker import FuncFormatter
    
    def to_percent(num, position):
        return str(num) + '%'
        
    def draw_trend_image_test(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)   
    
        total_pass_rate = [round((s_p+r_p)/(s_p+r_p+s_f+r_f), 2)*100 for s_p, r_p, s_f, r_f in zip(
                smoke_total_count_pass, reg_total_count_pass, smoke_total_count_fail, reg_total_count_fail)]       
    
        if len(date_list) > 5:
            fig, ax1 = plt.subplots(figsize=(10.8, 4.8))
        else:
            fig, ax1 = plt.subplots()
             
        # draw bar
        x = np.arange(len(date_list))
        ax1.bar(x - bar_width/2, smoke_total_count_pass, color=smoke_pass_color, edgecolor=smoke_pass_color, width= bar_width, label="Smoke Passed")
        ax1.bar(x - bar_width/2, smoke_total_count_fail, color="red", edgecolor=smoke_fail_color, width= bar_width, bottom=smoke_total_count_pass)
        ax1.bar(x + bar_width/2, reg_total_count_pass, color=regression_pass_color, edgecolor=regression_pass_color, width= bar_width, label="Regression Passed")
        ax1.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
        ax1.set_title("Test Result Trend", fontsize=title_size, fontname=font_name)
        ax1.set_xticks(x, date_list,fontsize=label_size, fontname=font_name)
        ax1.set_ylabel("Count",fontsize=label_size, fontname=font_name)
    
        # set bar text
        for i in x:
            if smoke_total_count_fail[i] > 0:
                ax1.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')   
              
            ax1.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')
            ax1.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:
                ax1.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')   
    
        # draw plot
        ax2 = ax1.twinx()
        ax2.plot(x, total_pass_rate, label='Total Pass Rate',
        linewidth=2, color='#FFB90F')
        ax2.yaxis.set_major_formatter(FuncFormatter(to_percent))
        ax2.set_ylim(0, 100)
        ax2.set_ylabel("Percent",fontsize=label_size, fontname=font_name)
        # 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
        legend_font = font_manager.FontProperties(family=font_name, weight='normal',style='normal', size=label_size)    
        fig.legend(loc="lower center", ncol=4, frameon=False, prop=legend_font)
        fig.savefig(image_file_name,dpi = 100)
        
    if  __name__ == '__main__':
        reg_total_count_pass = [916,916,916,906,916,716,916,916,916,916]
        reg_total_count_fail = [73,73,73,83,73,273,73,73,73,73]
        smoke_total_count_pass = [420, 420, 420,420, 420, 400,420, 420, 420,420] 
        smoke_total_count_fail = [5,5,5,5,5,25,5,5,5,5]
        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_test(reg_total_count_pass, reg_total_count_fail, smoke_total_count_pass, smoke_total_count_fail, date_list)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
  • 相关阅读:
    秋招攻略秘籍,吃透25个技术栈Offer拿到手软
    贪心算法 之会议安排
    Java基础之常用API
    分布式二级缓存组件实战(Redis+Caffeine实现)
    关于linux与android传输代码tcp -传文件
    云原生|kubernetes|kubeadm部署高可用集群(二)---kube-apiserver高可用+etcd外部集群
    品牌监控用到API接口,可以实现以下功能:
    快速实现本地数据备份与FTP远程数据迁移
    中国移动 OneNET 免费的物联网平台
    网络安全概述
  • 原文地址:https://blog.csdn.net/wumingxiaoyao/article/details/125458300