记录瞬间
在实际的工作中总会遇到一些报表的展示问题,echarts作为百度的开源工具针对作图来说是不二选择。
教程链接: https://echarts.apache.org/zh/tutorial.html#5%20%E5%88%86%E9%92%9F%E4%B8%8A%E6%89%8B%20ECharts
参数设置:https://echarts.apache.org/zh/option.html#title
本文主要介绍使用echarts时关于一批关联数据,展示到一张图上的方法
主要应用:flask+bootstrap+echarts
由于使用flask的三方插件(pyecharts)在灵活处理非图表数据时较为复杂,所以需要考虑,单独使用echarts进行考虑
先将flask的pyecharts实现简单代码作以展示
def render_result(data): from pyecharts.charts import Bar, Grid, Timeline from pyecharts import options as opts # 内置主题类型可查看 pyecharts.globals.ThemeType from pyecharts.globals import ThemeType from pyecharts.globals import CurrentConfig from jinja2 import Markup, Environment, FileSystemLoader from pyecharts.commons.utils import JsCode # 关于 CurrentConfig,可参考 [基本使用-全局变量] CurrentConfig.GLOBAL_ENV = Environment(loader=FileSystemLoader("./templates/echarts")) # js代码 js_code_str = ''' function(params){ return params.data.text; } ''' all_bar_keys = [] # 获取所有的key数据 result是从执行结果的数据信息中获取的数据,不是最完整的数据结果 for get_data in data: all_bar_keys += data[get_data]['result'].keys() # 去重 get_bar_keys = list(set(all_bar_keys)) # 所有模块名称去重后的结果 get_bar_keys.sort() # 排序保证每次显示的结果一致 all_keys = list(data.keys()) # 获取执行的所有时间区间的值 all_keys.sort(reverse=True) # 按照大小进行排序 bar = ( Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT)) .add_xaxis([i for i in range(1, len(all_keys))]) .set_global_opts(title_opts=opts.TitleOpts(title="结果展示", subtitle="统计当前展示结果"), tooltip_opts=opts.TooltipOpts(formatter=JsCode(js_code_str)), toolbox_opts=opts.ToolboxOpts(is_show=True, feature={"saveAsImage": {}, "dataZoom": {"yAxisIndex": "none"}, "restore": {}, "magicType": {"show": True, "type": ["line", "bar", "stack"]}}) ) ) for bar_key in get_bar_keys: # 遍历所有的模块数据 get_list = [] # 保存执行结果中的,未出现模块个数数据,但不是最准确的数据结果 temp_dict = {"value": 0, "text": ""} for data_key in all_keys: # 遍历所有时间区间,筛选出符合相关模块的query词 temp_dict["text"] = data_key + "<br>" + bar_key if bar_key in data[data_key]['result']: # 结果是从执行query词的结果中获取的,不是最准确的结果,可参考 temp_dict["value"] = len(data[data_key]['result'][bar_key]) get_list.append(copy.deepcopy(temp_dict)) else: temp_dict["value"] = 0 get_list.append(copy.deepcopy(temp_dict)) bar_ = ( Bar() .add_xaxis([i for i in range(1, len(all_keys))]) .add_yaxis(bar_key, get_list) ) bar.overlap(bar_) get_all_order_dict = {} all_bar = [] # 保存所有模块下的柱状图 row_list = [] # 记录行数据 count = 1 # 遍历所有模块,每增加一个模块进行 +1 处理 revise = 0 # 数据校正标记 for bar_key in get_bar_keys: # 遍历所有的模块数据 get_all_order_dict[bar_key] = [] for data_key in all_keys: if bar_key in data[data_key]['result']: for word in data[data_key]['result'][bar_key]: if word not in get_all_order_dict[bar_key]: get_all_order_dict[bar_key].append(word) get_len = len(str(get_all_order_dict[bar_key])) # 获取所有词的长度 rows = int(get_len / 85) + 1 # 除以95后,获取需要分几行,每行在乘以30就可以获取到词位置的高度了 title_pos = 460 + 330 * (count - 1) + revise legend_pos = 510 + 330 * (count - 1) + revise row_list.append(rows) if row_list[count - 1] > 3: revise += (row_list[count - 1] - 3) * 30 print(title_pos, legend_pos, revise) key_bar = ( Bar(init_opts=opts.InitOpts(theme=ThemeType.DARK)) .add_xaxis([i for i in range(1, len(all_keys))]) .set_global_opts(title_opts=opts.TitleOpts(title="关键字统计展示-{}".format(bar_key), subtitle="统计当前出现了关键字的相关词展示结果", pos_top="{}px".format(title_pos)), tooltip_opts=opts.TooltipOpts(formatter=JsCode(js_code_str)), legend_opts=opts.LegendOpts(pos_top="{}px".format(legend_pos), is_show=True, selected_mode='single')) # selected_mode 可以使用“single”、“multiple”使用单选或多选模式,默认为multiple ) count += 1 for word in get_all_order_dict[bar_key]: get_keys = [] # 保存所有模块的对应query词的个数数据,准确的结果 temp_dict = {"value": 0, "text": ""} for data_key in all_keys: # 遍历结果数据 temp_dict["text"] = word + '<br>' + data_key + '<br>' + bar_key if bar_key in data[data_key]["keys_order"]: if word in data[data_key]["keys_order"][bar_key]: temp_dict['value'] = 1 get_keys.append(copy.deepcopy(temp_dict)) else: temp_dict['value'] = 0 get_keys.append(copy.deepcopy(temp_dict)) else: temp_dict['value'] = 1 get_keys.append(copy.deepcopy(temp_dict)) key_bar_ = ( Bar() .add_xaxis([i for i in range(1, len(all_keys))]) .add_yaxis(word, get_keys) ) key_bar.overlap(key_bar_) all_bar.append(copy.deepcopy(key_bar)) grid = Grid(init_opts=opts.InitOpts(theme=ThemeType.LIGHT, width='1100px', height='{}px'.format(700+len(row_list)*365))) grid.add(bar, grid_opts=opts.GridOpts(pos_left="5%", pos_right="1%", height="350px")) # grid.add(key_bar, grid_opts=opts.GridOpts(pos_top="520px", pos_left="5%", pos_right="1%", height="30%")) count = 1 # 循环展示模块的柱形图 revise = 0 # 校正数据 for key_bar in all_bar: # grid_pos = 600 + (240 + (sum(row_list[:count])) * 30) * (count - 1) if row_list[count - 1] > 3: revise += (row_list[count - 1] - 3) * 30 grid_pos = 600 + 330 * (count - 1) + revise grid.add(key_bar, grid_opts=opts.GridOpts(pos_top="{}px".format(grid_pos), pos_left="5%", pos_right="1%", height="150px")) count += 1 # return Markup(bar.render_embed()) return grid.render_embed()
方法调用如下:
1 @ots.route('aisi_summary', methods=["GET"]) 2 def aisi_summary(): 3 get_time = get_time_section(96, flag=True)[1] 4 get_objs = TempData.objects(CREATE_TIME__gte=get_time).all() 5 if get_objs and len(get_objs) > 85: 6 data = sfo.aisi_summary() 7 from jinja2 import Markup 8 get_render = render_result(data=data) 9 return Markup(get_render) # 直接渲染结果 10 else: 11 # 准备数据过程中 12 try: 13 executor.submit(sfo.aisi_summary, flag=True) 14 except: 15 pass 16 return render_template("aisi_summary.html", error="数据修整中,请稍后重试!")
当然使用前提是需要安装pyecharts,并将pyecharts下面的模板放到templates目录下,才可以正常展示结果。
此结果是按照既定的pyecharts的模板进行渲染展示的,所以很大程度上无法进行非图表数据的展示
# 引入echarts
我们希望在展示图表数据的过程中,还要展示其他的数据,比如:判断结果,表格,说明等等
过程分为:1、数据准备;2、渲染页面;3、展示结果
数据准备代码
1 def total_result_for_data(data): 2 result = {"height": 0, "x": [], "y": [], "title": ['结果概览'], "count": 0} 3 all_bar_keys = [] 4 # 获取所有的key数据 result是从执行结果的数据信息中获取的数据,不是最完整的数据结果 5 for get_data in data: 6 all_bar_keys += data[get_data]['result'].keys() 7 # 去重 8 get_bar_keys = list(set(all_bar_keys)) # 所有模块名称去重后的结果 9 get_bar_keys.sort() # 排序保证每次显示的结果一致 10 result['title'] += get_bar_keys # 主标题设置 11 result['count'] = len(result['title']) # 记录显示的柱形图的个数 12 all_keys = list(data.keys()) # 获取执行的所有时间区间的值 13 all_keys.sort(reverse=True) # 按照大小进行排序 14 result['all_keys'] = all_keys 15 result['x'] = [i for i in range(1, len(all_keys)+1)] # 生成x轴刻度 16 # 总体数据展示结果 17 get_all_order_dict = {} 18 row_list = [] # 记录行数据 19 count = 1 # 遍历所有模块,每增加一个模块进行 +1 处理 20 result['结果概览_source'] = [] 21 result['结果概览'] = get_bar_keys 22 result['grid_pos'] = [100] 23 for bar_key in get_bar_keys: 24 set_date_key = {"name": bar_key, "type": "bar", "data": [], "label": {"show": "true"}} 25 for data_key in all_keys: # 遍历所有时间区间,筛选出符合相关模块的query词 26 temp_dict = {"value": 0, "text": data_key + '===' + bar_key} 27 get_all_order_dict[bar_key] = [] 28 if bar_key in data[data_key]['result']: # 结果是从执行query词的结果中获取的 29 data_value = len(data[data_key]['result'][bar_key]) 30 temp_dict["value"] = len(data[data_key]['result'][bar_key]) 31 else: 32 temp_dict["value"] = 0 33 data_value = 0 34 set_date_key["data"].append(copy.deepcopy(temp_dict)) 35 if bar_key in data[data_key]['result']: 36 for word in data[data_key]['result'][bar_key]: 37 if word not in get_all_order_dict[bar_key]: 38 get_all_order_dict[bar_key].append(word) 39 result['结果概览_source'].append(copy.deepcopy(set_date_key)) 40 print(result['结果概览_source']) 41 for bar_key in get_bar_keys: # 遍历所有的模块数据 42 result[bar_key + "_source"] = [] 43 get_all_order_dict[bar_key] = [] 44 for data_key in all_keys: # 遍历所有时间区间,筛选出符合相关模块的query词 45 if bar_key in data[data_key]['result']: 46 for word in data[data_key]['result'][bar_key]: 47 if word not in get_all_order_dict[bar_key]: 48 get_all_order_dict[bar_key].append(word) 49 result[bar_key] = copy.deepcopy(get_all_order_dict[bar_key]) 50 51 get_len = len(str(get_all_order_dict[bar_key])) # 获取所有词的长度 52 rows = int(get_len / 85) + 1 # 除以85后,获取需要分几行,每行在乘以30就可以获取到词位置的高度了 53 54 result['grid_pos'].append(70 + 30 * rows) 55 56 count += 1 57 for word in get_all_order_dict[bar_key]: 58 set_date_key = {"name": word, "type": "bar", "data": [], "label": {"show": "true"}} 59 for data_key in all_keys: # 遍历结果数据 60 temp_dict = {"value": 0, "text": word + '===' + data_key + '===' + bar_key} 61 if bar_key in data[data_key]["keys_order"]: 62 if word in data[data_key]["keys_order"][bar_key]: 63 temp_dict["value"] = 1 64 else: 65 temp_dict["value"] = 0 66 else: 67 temp_dict["value"] = 1 68 set_date_key["data"].append(copy.deepcopy(temp_dict)) 69 result[bar_key + "_source"].append(copy.deepcopy(set_date_key)) 70 print(result[bar_key + "_source"]) 71 72 result['height'] = (len(row_list) + 1) * 365 73 74 return result
前端代码如下
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <meta name="description" content=""> 8 <meta name="generator" content="Hugo 0.84.0"> 9 <title>数据分析</title> 10 <link href="{{ url_for('static', filename='bs/css/bootstrap.min.css') }}" rel="stylesheet"> 11 <link href="{{ url_for('static', filename='bs/css/carousel.css') }}" rel="stylesheet"> 12 <!-- 引入echarts --> 13 <script src="/static/js/echarts.min.js"></script> 14 </head> 15 <body> 16 {% include "common/header.html" %} 17 18 <div class="container"> 19 <main> 20 <div style="margin-top:25px"> 21 <nav aria-label="breadcrumb"> 22 <ol class="breadcrumb"> 23 <li class="breadcrumb-item"><a href="#">监控</a></li> 24 <li class="breadcrumb-item active" aria-current="page">数据分析</li> 25 </ol> 26 </nav> 27 </div> 28 <div class="px-4 py-5 my-5 text-left"> 29 {% if error %} 30 <span style="color: #e4393c">{{ error }}</span> 31 {% endif %} 32 <table class="table table-hover"> 33 <tr><th style="text-align: center;font-size: 0.8cm;background-color: #c8e0cf">结果一览</th></tr> 34 <tr> 35 <td> 36 <div id="main_picture"></div> 37 </td> 38 </tr> 39 {% for foo in range(data.count) %} 40 <tr> 41 <td> 42 <div id="main_picture_{{ foo }}"></div> 43 </td> 44 </tr> 45 {% endfor %} 46 </table> 47 48 </div> 49 </main> 50 </div> 51 52 {% include "common/footer.html" %} 53 54 <script src="/static/bs/js/bootstrap.bundle.min.js"></script> 55 56 <script type="text/javascript"> 57 let temp_str = "{{ data }}".replace(/'/g, '"'); 58 // 将接受的数据转换为json对象 59 let obj = eval("("+temp_str+")"); 60 {% if data %} 61 init(obj); 62 {% endif %} 63 64 function init(obj){ 65 console.log(obj); 66 let num = obj.count; 67 let xdata = obj.x; 68 let title_data = obj.title; 69 let grid_pos = obj.grid_pos 70 for (let mpnum=0; mpnum<num; mpnum++){ 71 let main_picture = document.getElementById('main_picture_' + mpnum); 72 //计算所需要的高度 73 if (mpnum === 0) { 74 main_picture.style.height = "440px"; 75 } else { 76 main_picture.style.height = 150 + grid_pos[mpnum] + "px"; 77 } 78 // 基于准备好的dom,初始化echarts实例 79 let myChart = echarts.init(main_picture); 80 let toolbox = []; // 设置 81 let legend = []; // 图例显示 82 83 //通过配置xAxi和yAxis的gridIndex series的xAxisIndex和yAxisIndex 来配套格子 84 let option = { 85 title: { 86 textAlign: "left", 87 text: title_data[mpnum], 88 subtext: title_data[mpnum], 89 top: "0px" 90 }, 91 xAxis: { 92 type: "category", 93 data: xdata 94 }, 95 yAxis: { 96 type: "value", 97 inverse: false, 98 splitLine: { 99 show: true 100 } 101 }, 102 series: obj[title_data[mpnum]+"_source"], 103 grid: { 104 left: "3%", 105 right: "1%", 106 "95%", 107 top: grid_pos[mpnum] + "px" 108 } 109 }; 110 // 工具栏设置 111 if (mpnum === 0) { 112 toolbox.push({ 113 show: true, 114 feature: { 115 mark: {show: true}, 116 dataZoom: {show: true}, 117 dataView: {show: true,readOnly: false}, 118 magicType: {show: true,type: ['line', 'bar', 'stack']}, 119 restore: {show: true}, 120 saveAsImage: {show: true} 121 } 122 }); 123 } else { 124 toolbox.push({ 125 show: false 126 }); 127 } 128 option["toolbox"] = toolbox[0]; 129 option["tooltip"] = { 130 position: "top", 131 formatter: function (params) { 132 return params.data.text.replace(/===/g, "<br>"); 133 } 134 }; 135 136 // 图例设置 137 if (mpnum === 0){ 138 legend.push({ 139 selectedMode: 'multiple', 140 top: "60px", 141 show: true 142 }); 143 } else { 144 legend.push({ 145 selectedMode: 'single', 146 top: "60px", 147 show: true 148 }); 149 } 150 option["legend"] = legend[0]; 151 // 使用刚指定的配置项和数据显示图表。 152 myChart.setOption(option); 153 } 154 } 155 </script> 156 </body> 157 </html>
展示结果如图
当然图表都是动态加载的,整图下面还有其他的图表,由于截屏关系只截取当前两张
注意:当前使用的都是最新的版本(bootstrap-v5.0、echarts-5.1.2)
供参考---结束了
===