echarts使用多圖的表達
阿新 • • 發佈:2021-08-10
記錄瞬間
在實際的工作中總會遇到一些報表的展示問題,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 importMarkup, 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 width: "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)
供參考---結束了
===