Python(十七)- Excel操作:xlsxwriter绘制图表

上一篇用一个简单实例描述了openpyxlxlsxwriter对Excel表格的操作,本文则实例介绍一下xlsxwriter图表的绘制。

一、Chart类简介

Chart类是用于在 XlsxWriter中实现图表的模块基础类。支持的图表类型有:面积、条形图、柱形图、折线图、饼图、散点图、股票和雷达等。

1.图表创建:

Chart通过指定图表类型的工作簿的add_chart()方法来创建图表对象。

# e.g.
 chart = workbook.add_chart({'type': 'column'})

2.图表类型

type类型字段值的选择有:

  • area: 创建区域样式(面积图)的图表。
  • bar:创建条形图(转换直方图)的图表。
  • column:创建列样式(直方图)的图表。
  • line:创建折线图表。
  • pie:创建饼图样式的图表。
  • doughnut:创建旭日图样式的图表。
  • scatter:创建散射样式(散点图)的图表。
  • stock:创建股票样式的图表。
  • radar:创建雷达样式的图表。

3.图表插入

# e.g.
worksheet.insert_chart('A4', chart, {'x_offset': 25, 'y_offset': 10})

4.图表方法

图表的常用方法:

  • add_series(options): 定义图表系列选项(如:值、轴标签和格式)。
    • name: 系列的名称。
    • categories: 类别标签。
    • values: 取值列表。
    • line: 系列行类型的属性,如颜色和宽度。
    • fill: 系列的填充属性,如颜色。
    • gradient: 系列的渐变填充属性。
    • marker: 系列标记的属性,如样式和颜色。
    • data_labels: 系列设置数据标签。
    • overlap: 设置系列之间的重叠,范围为+/-100。
    • gap: 设置系列之间的间隙,范围为 0 到 500。
  • set_title(options): 设置图表标题选项。
    • name: 设置图表标题。
    • name_font: 设置图表标题的字体属性。
    • layout: 设置标题位置。
  • set_x_axis(options): 设置图表 X 轴选项。
    • name: 设置轴标题。
    • name_font: 设置轴名的字体属性。
    • num_font: 设置轴标的字体属性。
  • set_y_axis(options): 设置图表 Y 轴选项(同上)。
  • set_y2_axis(options): 设置图表 Y 副轴选项(同上)。
  • set_legend(options): 设置图表图例选项。
    • position: 设置图表图例的位置。
    • font: 设置图表图例的字体属性。
    • pattern: 设置图例的模式填充属性。
  • set_size(options): 设置图表的尺寸。
    • width: 设置宽度。
    • height: 设置高度。
  • set_style(style_id): 设置图表样式类型。
  • set_table(options): 设置轴为数据表格形式。

二、案例Demo

1.构造数据

国内-整体维度
国内-运营商维度

def construct_test_data(dimension='all'):
    """
    构造数据
    """
    if dimension == 'all':
    	sheetName = '国内_整体'
        data = [
            ['指标', '加速线路', '安全加速', '精品EIP', '普通EIP', '腾讯', 'AWS', '阿里云'],
            ['延时(ms)', 51.22, 56.12, 65.61, 92.76, 116.02, 76.02, 66.02],
            ['丢包率(%)', 1.45, 3.21, 0.74, 6.18, 2.32, 1.32, 0.62]
        ]
    else:
    	sheetName = '国内_运营商'
        data = [
            ['运营商', '加速线路', None, '安全加速', None, '精品EIP', None, '普通EIP', None, '腾讯', None, 'AWS', None, '阿里云', None],
            [None, '延时(ms)', '丢包率(%)', '延时(ms)', '丢包率(%)', '延时(ms)', '丢包率(%)', '延时(ms)', '丢包率(%)', '延时(ms)', '丢包率(%)', '延时(ms)', '丢包率(%)', '延时(ms)', '丢包率(%)'],
            ['中国电信', 52.68, 2, 42.1, 0.82, 24.21, 1.08, 52.32, 0.12, 50.97, 2, 121.2, 6.8, 90.33, 0.54],
            ['中国移动', 53.92, 0.3, 56.12, 2, 34, 0.27, 68.21, 1.45, 89.21, 0.42, 77.8, 0.52, 44.3, 0],
            ['中国联通', 67.31, 0.69, 65.15, 0.38, 88, 0.52, 55.52, 0.66, 80.2, 0.22, 92.3, 0.44, 210.2, 2.21]
        ]
    return sheetName, data

2.绘制图表

import xlsxwriter

color_lst = [
   ['#000000', '#312D30', '#444245'],  # 黑色
   ['#DDEBCF', '#9CB86E', '#156B13'],  # 绿色
   ['#0000FF', '#6A98CC', '#46A7F5'],  # 蓝色
   ['#800000', '#633915', '#A45D2D'],  # 棕色
   ['#00FFFF', '#29CFCD', '#006054'],  # 青色
   ['#808080', '#76797E', '#878785'],  # 灰色
   ['#FF0000', '#801801', '#D70F19'],  # 红色
   ['#C0C0C0', '#C2C2C2', '#8A8687'],  # 银色
   ['#800080', '#621C9B', '#46024F'],  # 紫色
   ['#FFFF00', '#F2BF04', '#FFE057'],  # 黄色
   ['#FFFFFF', '#FCFAFB', '#CECECC'],  # 白色
]

def export_simple_excel(filename=None, sheetLst=None, contents=None, dimensions=None):
    filename = filename if filename else '竟对_xlsxwriter_Test.xlsx'
    sheetLst = sheetLst if sheetLst else ['竟对分析测试']
    contents = contents if contents else []
    dimensions = dimensions if dimensions else ['all', 'isp']
    wb = xlsxwriter.Workbook(filename)

    # 设置风格
    style1 = wb.add_format({
        "bold": True,
        'font_name': '仿宋',
        'font_size': 12,
        'bg_color': '#4DCFF6',
        "align": 'center',
        "valign": 'vcenter',
        'text_wrap': 1
    })
    style2 = wb.add_format({
        'font_size': 11,
        'font_color': '#217346',
        'bg_color': '#E6EDEC',
        "align": 'center',
        "valign": 'vcenter',
    })

    for index_, sheetName in enumerate(sheetLst):
        ws = wb.add_worksheet(name=sheetName)
        dimension = dimensions[index_]
        # ws.set_default_row(35)  # 设置默认行高
        ws.set_column(0, len(contents[index_][0])-1, 20)  # 设置列宽
        # 写入
        if dimension == 'all':
            for i, row_lst in enumerate(contents[index_]):
                style = style2 if i != 0 else style1
                ws.write_row(f'A{i+1}', row_lst, style)
        else:
            line1, line2 = list(filter(lambda col: col, contents[index_][0])), list(filter(lambda col: col, contents[index_][1]))
            isp, titleLine1 = line1[0], line1[1:]
            titleLine2 = line2
            ws.set_column(0, 0, 20)  # 设置列宽
            ws.set_column(1, len(titleLine2), 15)  # 设置列宽
            # 标题
            ws.merge_range(0, 0, 1, 0, isp, style1)
            [ws.merge_range(0, 2*i+1, 0, 2*(i+1), name, style1) for i, name in enumerate(titleLine1)]
            [ws.write(1, i+1, name, style1) for i, name in enumerate(titleLine2)]
            # 内容
            for i, row_lst in enumerate(contents[index_][2:]):
                ws.write_row(f'A{i+3}', row_lst, style2)
        
        # 画图
        y1_name = '延时(ms)'
        y2_name = '丢包率(%)'
        x_name = '产品类型'
        title_name = sheetName

        # 柱状图
        column_chart = wb.add_chart({'type': 'column'})
        # 折线图
        line_chart = wb.add_chart({'type': 'line'})

        if dimension == 'all':  # 整体维度
            column_name = f'={sheetName}!A2'
            line_name = f'={sheetName}!A3'
            categories = f'={sheetName}!B1:H1'
            column_values = f'={sheetName}!B2:H2'
            line_values = f'={sheetName}!B3:H3'

            column_chart.add_series({
                'name':       column_name,
                'categories': categories,
                'values':     column_values,
            })
            line_chart.add_series({
                'name':       line_name,
                'categories': categories,
                'values':     line_values,
                'marker':       {'type': 'circle'},  # 系列标记
                'data_labels': {  # 数据标签
                    'value': True,
                    'series_name': True,
                    'position': 'above',
                    'separator': "\n",
                    'font': {'name': 'Consolas', 'color': 'red', 'size': 8}
                },
                'y2_axis':    True,
            })
        else:  # 运营商维度
            # 添加规则
            d_lst = ['B', 'D', 'F', 'H', 'J', 'L', 'N']
            l_lst = ['C', 'E', 'G', 'I', 'K', 'M', 'O']

            for i, c in enumerate(d_lst):
                # 写法一
                categories_str = f'={sheetName}!A3:A5'
                column_name_str = f'={sheetName}!{c}1:{c}2'
                line_name_str = f'={sheetName}!{l_lst[i]}1:{l_lst[i]}2'
                column_values_str = f'={sheetName}!{c}3:{c}5'
                line_values_str = f'={sheetName}!{l_lst[i]}3:{l_lst[i]}5'

                # 添加数据选项
                column_chart.add_series({
                    'name':       column_name_str,
                    'categories': categories_str,
                    'values':     column_values_str,
                    'gap':        50,  # 间隙
                    'overlap':    -5,  # 系列重叠
                    'gradient': {  # 渐变填充
                        'colors': color_lst[i],
                        'type': 'radial'
                    },
                })
                line_chart.add_series({
                    'name':       line_name_str,
                    'categories': categories_str,
                    'values':     line_values_str,
                    'marker':       {'type': 'circle'},  # 系列标记
                    'data_labels': {  # 数据标签
                        'value': True,
                        'series_name': True,
                        'position': 'above',
                        'separator': "\n",
                        'font': {'name': 'Consolas', 'color': 'yellow', 'size': 8}
                    },
                    'y2_axis':    True,
                })

        # 设置副坐标
        line_chart.set_y2_axis({
            'name': y2_name, 
            'name_font': {'name': 'Calibri','color': 'red' },  # 轴标题设置
            'num_font': {'name': 'Arial', 'color': '#00B0F0', 'size': 8},  # 轴参数设置
        })
        # 设置前、背景色
        column_chart.set_plotarea({
            'pattern': {
                'pattern': 'percent_5',
                'fg_color': '#555555',
                'bg_color': '#595959',
            }
        })
        # 组合
        column_chart.combine(line_chart)
        column_chart.set_title({'name': title_name, 'name_font': {'name': 'Calibri','color': 'red' , 'size': 15}}) 
        column_chart.set_x_axis({'name': x_name, 'name_font': {'name': 'Calibri','color': 'green' }, 'num_font': {'name': 'Arial', 'color': 'green', 'size': 9}}) 
        column_chart.set_y_axis({'name': y1_name, 'name_font': {'name': 'Calibri','color': 'blue' }, 'num_font': {'name': 'Arial', 'color': 'blue', 'size': 8}})
        # 样式
        column_chart.set_style(2)
        column_chart.set_legend({'font': {'size': 8, 'bold': False}})
        column_chart.set_size({'width': 720, 'height': 576})
        # 插入
        ws.insert_chart('A4', column_chart, {'x_offset': 25, 'y_offset': 10})  # 在A4单元格插入图表
    # 保存关闭文件
    wb.close()

3.运行测试

import os

FILE_PATH = os.path.join(os.path.dirname(__file__), 'files/')

def simple_test():
    dimension_lst = ['all', 'isp']

    # 方式一:按文件拆分
    for dimension in dimension_lst:
        filename = os.path.join(FILE_PATH, f'竟对分析_SimpleTest_{dimension}.xlsx')
        sheetName, data = construct_test_data(dimension=dimension)
        export_simple_excel(filename=filename, sheetLst=[sheetName], contents=[data], dimensions=[dimension])
    # 方式二:按sheet拆分
    file = os.path.join(FILE_PATH, '竟对分析_SimpleTest.xlsx')
    sheetLst = []
    contents = []
    for dimension in dimension_lst:
        sheetName, data = construct_test_data(dimension=dimension)
        sheetLst.append(sheetName)
        contents.append(data)
    export_simple_excel(filename=file, sheetLst=sheetLst, contents=contents, dimensions=dimension_lst)


if __name__ == '__main__':
    simple_test()

4.图表显示

国内-整体图

国内-运营商图


以上就是利用XlsxWriter包的Chart类绘制的简单的组合图。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值