數(shù)據(jù)可視化是數(shù)據(jù)處理中的重要部分,前面我們了解了 Flask 的開發(fā)和部署,如何用 Flask 做數(shù)據(jù)可視化呢?今天我們來了解一下。
Python/ target=_blank class=infotextkey>Python 語言極富表達(dá)力,并且擁有眾多的數(shù)據(jù)分析庫和框架,是數(shù)據(jù)分析的首選;
echarts,最初由百度團(tuán)隊(duì)開發(fā),現(xiàn)在已獨(dú)立成 Apache 旗下一款國際化產(chǎn)品,是基于 Web 的數(shù)據(jù)可視化框架,API 簡單明了,應(yīng)用極為廣泛;
Python 和 echarts 的完美結(jié)合就是 pyecharts
pyecharts 簡介
pyecharts 使得可以用 Python 語言,完成 echarts 中對(duì)圖表的各種操作,并且讓編寫代碼更便利
pyecharts 中的概念和 echarts 是想通的,對(duì)于剛接觸的同學(xué),無論從 pyecharts 還是 echarts 開始了解都可以
圖表類
pyecharts 中的圖表都是類,都繼承自 Base 基類,構(gòu)造函數(shù)接受一個(gè) init_opts 參數(shù),用于設(shè)置圖表的屬性
意下是常用 API 接口:
- add_js_func:將 js 腳本附加在圖表 html 中
- set_global_opts:設(shè)置圖表屬性
- render:渲染出圖表的 Html 文件
- dump_options_with_quotes:將圖表所有設(shè)置導(dǎo)出為 json,用于前后分離
全局配置
pyecharts 將圖表中和數(shù)據(jù)無關(guān)的屬性,集中在全局配置中,也就是這些配置是服務(wù)于整個(gè)圖表的,比如 標(biāo)題、圖例、工具欄、數(shù)據(jù)提示框、區(qū)域縮放等,每種配置項(xiàng),都是一個(gè) BasicOpts 的子類,通過圖標(biāo)對(duì)象的 set_global_opts 方法設(shè)置,例如:
from pyecharts.charts import Bar
bar = Bar()
bar.set_global_opts(
title_opts=opts.TitleOpts(
title="Bar-基本示例",
subtitle="我是副標(biāo)題",
pos_left= "center",
pos_top="top"),
legend_opts=opts.LegendOpts(
pos_top="60"
))
系列配置
系列(series)是很常見的名詞。在 echarts 里,系列(series)是指:一組數(shù)值以及他們映射成的圖。“系列”這個(gè)詞原本可能來源于“一系列的數(shù)據(jù)”,而在 echarts 中取其擴(kuò)展的概念,不僅表示數(shù)據(jù),也表示數(shù)據(jù)映射成為的圖。所以,一個(gè) 系列 包含的要素至少有:一組數(shù)值、圖表類型(series.type)、以及其他的關(guān)于這些數(shù)據(jù)如何映射成圖的參數(shù)。
pyecharts 系列配置 和 全局配置 類似,用于對(duì)圖表中 系列 進(jìn)行設(shè)置,比如設(shè)置 系列 樣式、坐標(biāo)系、顏色、形狀、特殊點(diǎn),以及等。
例如,柱狀圖上不顯示標(biāo)簽:
from pyecharts.charts import Bar
bar = Bar()
bar.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
pyecharts 安裝
首先安裝 pyecharts:
pip install pyecharts
安裝完后,在 Python 交互式環(huán)境( REPL )中,可以查看版本信息:
>>> import pyecharts
>>> print(pyecharts.__version__)
1.7.0
Flask 集成
前面我們了解了 Flask 的開發(fā),對(duì)于一個(gè)應(yīng)用來說,需要有 視圖函數(shù) , 模板、和 路由,echarts 是一個(gè)前臺(tái)框架,只要將頁面做成模板,然后將數(shù)據(jù)寫入模板就好,這樣確實(shí)是可以做的,不過 pyecharts 已經(jīng)處理了大部分工作,只要在 Python 中開發(fā)代碼就好了。
pyecharts 和 Flask 集成,四種形式,分別是 模板渲染、前后分離、定時(shí)全集更新 和 增量數(shù)據(jù)更新
模板渲染
模板渲染是比較方便的,可以不用寫前臺(tái)頁面,因?yàn)?pyecharts 已經(jīng)定義了很多模板,以及模板宏,調(diào)用很方便。
第一步 下載 pyecharts 的模板
可以從 github 的 pyecharts 項(xiàng)目中獲取,
https://github.com/pyecharts/pyecharts
如果用 pip 安裝的 pyecharts ,可以在安裝環(huán)境中的模塊目錄下找到,即 Python home 中的
Lib/site-packages/pyecharts/render/templates
第二步 將模板放入項(xiàng)目目錄下
在我們的 Flask 應(yīng)用的目錄的 templates 模板下,創(chuàng)建 pyecharts 目錄,來存放復(fù)制的 pyecharts 模板。
這樣可以避免與 Flask 應(yīng)用中我們自建的模板混淆。
第三步 渲染圖表
我們將業(yè)務(wù)邏輯寫入都寫在 Flask 啟動(dòng)腳本 App.py 中:
from flask import Flask # 引入 Flask
from jinja2 import Markup, Environment, FileSystemLoader
from pyecharts.globals import CurrentConfig
CurrentConfig.GLOBAL_ENV = Environment(loader=FileSystemLoader("./templates/pyecharts"))
from pyecharts import options as opts
from pyecharts.charts import Bar
app = Flask(__name__)
def bar_base() -> Bar: # -> 表示要返回的是類型
c = (
Bar()
.add_xaxis(["襯衫", "羊毛衫", "雪紡衫", "褲子", "高跟鞋", "襪子"])
.add_yaxis("商家A", [5, 20, 36, 10, 75, 90])
.add_yaxis("商家B", [15, 25, 16, 55, 48, 8])
.set_global_opts(
title_opts=opts.TitleOpts(
title="Bar-基本示例",
subtitle="我是副標(biāo)題"
)
)
)
return c
@app.route("/")
def index():
c = bar_base()
return Markup(c.render_embed())
- 首先引入 Flask、jinjia2 和 pyecharts
- 為全局變量設(shè)置 jinjia2 環(huán)境,指定模板路徑為 /templates/pyecharts 即我們存放 pyecharts 模板的路徑。這樣不會(huì)影響 Flask 的默認(rèn)模板路徑
- 定義圖表工廠方法,返回一個(gè)圖表實(shí)例,圖表實(shí)例支持點(diǎn)串聯(lián)操作
- add_xaxis 添加 X 軸顯示的項(xiàng)目
- add_yaxis 添加 Y 軸數(shù)據(jù)分類和數(shù)值,相當(dāng)于分組,可以添加多個(gè)
- set_global_opts 設(shè)置圖標(biāo)的全局配置
- 視圖函數(shù)中,用圖表工廠方法 bar_base 創(chuàng)建一個(gè)圖表實(shí)體,返回 render_embed 經(jīng)過 jinjia2 的渲染結(jié)果
- render_embed 返回的是合成好的 html 可以直接返回給前臺(tái)做展示
前后分離
模板渲染雖然方便,但是不透靈活,比如要修改已有頁面,加上一個(gè)圖表,這是可以考慮用前后分離的方式
前兩步和 模板渲染 中的一樣
第三步 創(chuàng)建前臺(tái)頁面
創(chuàng)建一個(gè) html 文件 index.html,存放在 templates 文件夾下,內(nèi)容和 echarts 一樣,主要是需要引用 echarts 框架,和 jQuery 框架(其他的Ajax框架均可),定義顯示圖表的 Dom,最后在頁面加載完成回調(diào)方法中,通過 ajax 請(qǐng)求后臺(tái)數(shù)據(jù),異步將獲取到的圖標(biāo)數(shù)據(jù)設(shè)置到圖表中:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>我的圖表</title>
<script src="https://cdn.bootcss.com/jquery/3.0.0/jquery.min.js"></script>
<script type="text/JAVAscript" src="https://assets.pyecharts.org/assets/echarts.min.js"></script>
</head>
<body>
<div id="bar" style="width:1000px; height:600px;"></div>
<script>
$(
function () {
var chart = echarts.init(document.getElementById('bar'), 'white', {renderer: 'canvas'});
$.ajax({
type: "GET",
url: "/barChart",
dataType: 'json',
success: function (result) {
chart.setOption(result);
}
});
}
)
</script>
</body>
</html>
第四步 編寫后臺(tái)相應(yīng)方法
前臺(tái)頁面中定義了 ajax 請(qǐng)求路徑是 barChart,我們就寫一個(gè)處理該請(qǐng)求的視圖方法:
@app.route("/barChart")
def bar_chart():
c = bar_base()
return c.dump_options_with_quotes()
- 定義圖表的方式和 模板渲染一樣
- 視圖方法中,用工廠方法創(chuàng)建視圖對(duì)象,返回 dump_options_with_quotes 的結(jié)果
- dump_options_with_quotes 將圖標(biāo)的配置集成為前臺(tái)需要的格式,返回 JSON 數(shù)據(jù)
最后啟動(dòng) Flask 應(yīng)用,在 <localhost:5000> 就能看到效果
前后分離的方式更常用,可以讓前臺(tái)的展示發(fā)揮最大的優(yōu)勢,F(xiàn)lask 后臺(tái)提供圖表需要的數(shù)據(jù)和設(shè)置
定時(shí)全量更新
有很多場景需要實(shí)時(shí)更新圖表內(nèi)容,實(shí)現(xiàn)方式是將 前后分離 的方式,獲取后臺(tái)圖標(biāo)配置的請(qǐng)求寫成定時(shí)調(diào)用的,將得到的圖標(biāo)數(shù)據(jù)通過 setOption 設(shè)置到圖表對(duì)象中。
后臺(tái)視圖方法每次重新根據(jù)查詢條件,獲取新的數(shù)據(jù),設(shè)置到圖表對(duì)象中,再用 dump_options_with_quotes 將設(shè)置導(dǎo)出,返回給前臺(tái)
定時(shí)增量更新
增量更新在數(shù)據(jù)監(jiān)控的場景中很常用,實(shí)現(xiàn)方式和全量更新有些差別
首先需要得到一個(gè)圖表的設(shè)置,這個(gè)和全量更新一樣
然后將獲取增量數(shù)據(jù)的方法作為定時(shí)的,在回調(diào)函數(shù)中,為圖標(biāo)設(shè)置增量數(shù)據(jù),與全量更新不同的是只更新 系列數(shù)據(jù),echarts 會(huì)處理好圖表的變化,包括動(dòng)畫效果
前臺(tái)獲取增量數(shù)據(jù)并更新的方法:
function getDynamicData() {
$.ajax({
type: "GET",
url: "/lineDynamicData",
dataType: "json",
success: function (result) {
old_data.push([result.name, result.value]);
chart.setOption({
series: [{data: old_data}]
});
}
});
}
old_data 圖表數(shù)據(jù)的應(yīng)用:
old_data = chart.getOption().series[0].data;
如果需要同時(shí)將最早的數(shù)據(jù)清除掉,只需要將需要去除的數(shù)據(jù)從 old_data 中刪除就行:
old_data.shift(); // 清楚最早的一個(gè)數(shù)據(jù)
后臺(tái)數(shù)據(jù)處理
根據(jù)圖表數(shù)據(jù)要求,每次前臺(tái)請(qǐng)求增量數(shù)據(jù)時(shí),將最新的數(shù)據(jù)返回
這里需要注意到是增量數(shù)據(jù)范圍,即怎么確定增量數(shù)據(jù)
常用數(shù)據(jù)產(chǎn)生時(shí)間 或者 數(shù)據(jù) id 作為增量條件,例如圖表展示的是在線用戶數(shù)變化曲線,在線用戶數(shù),會(huì)定時(shí)存放在庫表中,每條記錄都有個(gè) id,每次請(qǐng)求增量數(shù)據(jù)時(shí),將已經(jīng)獲取到數(shù)據(jù)的最大的 id 值作為請(qǐng)求參數(shù),后臺(tái)就可以獲取該主鍵值后面的數(shù)據(jù),作為增量數(shù)據(jù)。
渲染圖片
在有些場景下,需要生成圖表圖片,Python 有很多圖表處理工具,可以做圖像生成。
對(duì) echarts 來說,也有生成圖片的功能,不過需要在瀏覽器中,pyecharts 作為 Python 和 echarts 的橋梁,支持后端生成圖表圖片。
pyecharts 提供了 selenium, phantomjs 和 pyppeteer 三種方式渲染圖片,其原理是用無頭瀏覽器,渲染圖表頁面后,用 echarts 生成圖片功能,生成圖片。
這里我們用 selenium 做演示
安裝 snapshot-selenium
snapshot-selenium 是 pyecharts + selenium 渲染圖片的擴(kuò)展,selenium 需要配置 browser driver,推薦使用 Chrome 瀏覽器,可以開啟 headless 模式,具體配置可參考 selenium-python 相關(guān)介紹。
使用
pyecharts 使用 make_snapshot 直接生成圖片,支持生成圖片相關(guān)的配置,如 echarts html 文件名,輸出文件名,瀏覽器種類等:
from pyecharts import options as opts
from pyecharts.charts import Bar
from pyecharts.render import make_snapshot
from snapshot_selenium import snapshot
def bar_chart() -> Bar:
c = (
Bar()
.add_xaxis(["襯衫", "毛衣", "領(lǐng)帶", "褲子", "風(fēng)衣", "高跟鞋", "襪子"])
.add_yaxis("商家A", [114, 55, 27, 101, 125, 27, 105])
.add_yaxis("商家B", [57, 134, 137, 129, 145, 60, 49])
.reversal_axis()
.set_series_opts(label_opts=opts.LabelOpts(position="right"))
.set_global_opts(title_opts=opts.TitleOpts(title="Bar-測試渲染圖片"))
)
return c
make_snapshot(snapshot, bar_chart().render(), "bar0.png")
- 先引入 make_snapshot 和 snapshot
- 定義圖表工廠方法
- 調(diào)用 make_snapshot 導(dǎo)出圖片,第一個(gè)參數(shù)是渲染擴(kuò)展工具,第二個(gè)是生成的 Html 文件路徑,第三個(gè)參數(shù)是生成的圖片文件路徑
- 由于是通過無頭瀏覽器中模擬的,圖表復(fù)雜或者數(shù)據(jù)多時(shí),渲染可能較慢,可以通過 make_snapshot 命名參數(shù) delay 來設(shè)置等待時(shí)間,默認(rèn)為 2 秒
總結(jié)
今天介紹了使用 pyecharts 實(shí)現(xiàn)數(shù)據(jù)可視化的方法,并描述了如何與 Flask 集成,以及幾種生成圖表的方式,可以嘗試一下,以便做出更好玩更有用的 Flask 應(yīng)用。






