溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何利用python數(shù)據(jù)可視化的Bokeh和Bottle.py在網(wǎng)頁(yè)上展示你的數(shù)據(jù)

發(fā)布時(shí)間:2021-10-08 09:14:41 來(lái)源:億速云 閱讀:189 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本篇內(nèi)容介紹了“如何利用python數(shù)據(jù)可視化的Bokeh和Bottle.py在網(wǎng)頁(yè)上展示你的數(shù)據(jù)”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

目錄
  • 1. 文章重點(diǎn)和項(xiàng)目介紹

  • 2. 數(shù)據(jù)集研究和圖表準(zhǔn)備

    • 2.1 導(dǎo)入數(shù)據(jù)集

    • 2.2 繪制圖表

      • 圖表1:2019年上海,北京,深圳三地的每天AQI變化曲線

      • 圖表2:2019年上海,北京,深圳三地的每月平均AQI對(duì)比

      • 圖表3:2017年到2019年北京每月平均AQI對(duì)比

  • 3. Bottle網(wǎng)頁(yè)應(yīng)用

    • 3.1 文件夾結(jié)構(gòu)

      • 3.2 路由

        • 3.3 模板實(shí)現(xiàn)

          • 3.4 啟動(dòng)網(wǎng)頁(yè)服務(wù)

          • 4. 將Bokeh和Bottle集成在一起

            • 4.1 模板修改

              • 4.2 Python代碼集成

              • 5. 部署應(yīng)用到Heroku

                • 6. 參考文檔

                  在數(shù)據(jù)科學(xué)中,通過(guò)圖表將數(shù)據(jù)可視化是一個(gè)很重要的工作,在開(kāi)始數(shù)據(jù)分析之前,通過(guò)數(shù)據(jù)可視化可以幫助我們理解數(shù)據(jù),而更重要的是,在完成分析、預(yù)測(cè)等等過(guò)程之后,我們需要通過(guò)數(shù)據(jù)可視化講結(jié)論展示出來(lái)。通過(guò)網(wǎng)頁(yè)創(chuàng)建可以交互的圖表是展示數(shù)據(jù)的一個(gè)重要手段。

                  1. 文章重點(diǎn)和項(xiàng)目介紹

                  本文的重點(diǎn)將是展示如何將bokeh和bottle集成在一起,并部署到服務(wù)器上,供他人訪問(wèn)查閱,因此不會(huì)在bokeh和bottle,以及pandas的相關(guān)代碼具體實(shí)現(xiàn)細(xì)節(jié)上面面俱到,但是對(duì)于我們實(shí)現(xiàn)的代碼,還是會(huì)進(jìn)行講解(可能不會(huì)那么深入)。本文將選取中國(guó)2017到2019年的AQI數(shù)據(jù)作為項(xiàng)目的數(shù)據(jù)集,然后利用這些數(shù)據(jù)繪制3張表格(一張折線圖和兩張帶分組的柱狀圖),然后通過(guò)bottle和bootstrap前端模板建立一個(gè)展示網(wǎng)頁(yè),最后會(huì)將這個(gè)網(wǎng)頁(yè)應(yīng)用部署到Heroku上邊(這一步作為參考,你可以通過(guò)localhost訪問(wèn)本機(jī)服務(wù),或者選取其他云服務(wù)商的服務(wù)器)。

                  本文使用的數(shù)據(jù)集和代碼實(shí)現(xiàn)都可以在下邊這個(gè)github倉(cāng)庫(kù)中找到:
                  https://github.com/pythonlibrary/bokeh-bottlepy
                  我已經(jīng)將數(shù)據(jù)集進(jìn)行過(guò)清理,數(shù)據(jù)集中包含規(guī)整的從2017年到2019年的各個(gè)城市的日AQI平均值。

                  2. 數(shù)據(jù)集研究和圖表準(zhǔn)備

                  在本節(jié)中,和大多數(shù)數(shù)據(jù)分析項(xiàng)目一樣,我們將使用jupyter notebook作為我們的環(huán)境,因?yàn)檫@個(gè)工具能夠方便的實(shí)現(xiàn)代碼修改和及時(shí)的代碼結(jié)果展示。
                  首先完成最重要的事情,導(dǎo)入必要的python庫(kù)

                  import numpy as np
                  import pandas as pd
                  
                  from bokeh.plotting import figure, show
                  from bokeh.models import ColumnDataSource, HoverTool
                  from bokeh.transform import dodge
                  
                  from bokeh.io import output_notebook

                  然后在notebook中運(yùn)行,下邊代碼來(lái)初始化bokeh,bokeh可以將圖標(biāo)輸出成不同的格式文件,如html等,但是要在notebook中顯示,則需要在最開(kāi)始的時(shí)候指明。

                  output_notebook()

                  成功執(zhí)行完以后,notebook會(huì)提示成功加載bokeh環(huán)境,如下:

                  2.1 導(dǎo)入數(shù)據(jù)集

                  我們使用pandas的read_csv方法讀入數(shù)據(jù)集,并將一些城市的數(shù)據(jù)拿出來(lái),因?yàn)樽x入以后date一列的數(shù)據(jù)格式不是pandas datatime,我們?cè)谶@里做一個(gè)轉(zhuǎn)換,方便后邊繪圖使用,因?yàn)閿?shù)據(jù)集中還有一些其他空氣質(zhì)量指標(biāo)例如PM2.5等,我們僅僅選取AQI作為關(guān)注重點(diǎn)形成新的數(shù)據(jù)幀 df

                  cities = ['上海', '北京', '杭州', '寧波', '保定', '南京', '蘇州', '深圳', '廈門(mén)', '廣州']
                  df = pd.read_csv('AQI_merged.csv')
                  df['date'] = pd.to_datetime(df['date'])
                  df = df.sort_values(by='date').reset_index(drop=True)
                  df = df[df['type']=='AQI']

                  我們的基礎(chǔ)數(shù)據(jù)幀(Dataframe)創(chuàng)建好以后,讓我們來(lái)看一下它里邊包含了什么數(shù)據(jù),我們使用如下代碼提取2019年的數(shù)據(jù),并且在notebook中展示前5條記錄。

                  df_2019_day = df[df['date']>='2019-01-01']
                  df_2019_day.head()

                  如何利用python數(shù)據(jù)可視化的Bokeh和Bottle.py在網(wǎng)頁(yè)上展示你的數(shù)據(jù)

                  可以看出,在數(shù)據(jù)幀中,按照每一天為行,記錄了當(dāng)天幾個(gè)城市的AQI值。

                  2.2 繪制圖表

                  利用導(dǎo)入的數(shù)據(jù),我們將繪制3張圖表:

                  2019年上海,北京,深圳三地的每天AQI變化曲線(曲線圖)
                  2019年上海,北京,深圳三地的每月平均AQI對(duì)比(柱狀圖)
                  2017年到2019年北京每月平均AQI對(duì)比(柱狀圖)

                  圖表1:2019年上海,北京,深圳三地的每天AQI變化曲線

                  利用剛剛我們創(chuàng)建的df_2019_day數(shù)據(jù)幀,使用如下代碼繪制圖表,注意,我們使用了Bokeh提供的ColumnDataSource的方式來(lái)給bokeh圖表傳遞數(shù)據(jù)。

                  簡(jiǎn)單說(shuō)明一下:我們先使用figure創(chuàng)建了一個(gè)空的圖畫(huà),然后用line方法畫(huà)了上海的數(shù)據(jù),然后重復(fù)line方法兩次在圖畫(huà)上添加了另外兩個(gè)城市的數(shù)據(jù),最后,通過(guò)add_tools方法添加了一個(gè)鼠標(biāo)懸停提示,用于顯示鼠標(biāo)位置的AQI值。

                  source = ColumnDataSource(df_2019_day)
                  
                  p = figure(x_axis_type="datetime", title="2019年AQI日均平均變化曲線", plot_width=900, plot_height=400)
                  p.line('date', '上海', line_color='blue', legend_label='上海', source=source)
                  p.line('date', '北京', line_color='green', legend_label='北京', source=source)
                  p.line('date', '深圳', line_color='orange', legend_label='深圳', source=source)
                  
                  p.legend.location = "top_right"
                  p.add_tools(HoverTool(tooltips=[("AQI", "$y")]))
                      
                  show(p)

                  如何利用python數(shù)據(jù)可視化的Bokeh和Bottle.py在網(wǎng)頁(yè)上展示你的數(shù)據(jù)

                  圖表2:2019年上海,北京,深圳三地的每月平均AQI對(duì)比

                  我們想要畫(huà)出每月平均AQI,而數(shù)據(jù)幀中包含的是每日的AQI,因此,利用dataframe的groupby方法,可以求得每月的平均值。并新建了一列month來(lái)存放月信息。最后通過(guò)head方法查看下我們獲得的新的數(shù)據(jù)幀是否包含了按月平均的AQI信息。

                  pd.options.mode.chained_assignment = None
                  df_2019_day['month'] = df_2019_day['date'].apply(lambda x: x.strftime('%Y-%m'))
                  df_2019_month = df_2019_day.groupby(by='month').mean().reset_index()
                  df_2019_month.head()

                  如何利用python數(shù)據(jù)可視化的Bokeh和Bottle.py在網(wǎng)頁(yè)上展示你的數(shù)據(jù)

                  數(shù)據(jù)集處理結(jié)果符合我們的預(yù)期,接下來(lái)使用這個(gè)數(shù)據(jù)集繪制第二張圖表。因?yàn)槲覀兿胍容^不同城市同一個(gè)月的AQI,因此我們的柱狀圖需要分組顯示,這里使用了bokeh中的dodge方式,每一個(gè)dodge為一個(gè)城市的數(shù)據(jù),并指明了在圖表上的相對(duì)位置。

                  source = ColumnDataSource(df_2019_month)
                  
                  p = figure(x_range=list(df_2019_month['month']), title="2019年AQI", plot_width=900, plot_height=400)
                  
                  p.vbar(x=dodge('month', -0.25, range=p.x_range), top='上海', width=0.2, color="#c9d9d3", legend_label="上海", source=source)
                  p.vbar(x=dodge('month', 0, range=p.x_range), top='北京', width=0.2, color="#718dbf", legend_label="北京", source=source)
                  p.vbar(x=dodge('month', 0.25, range=p.x_range), top='深圳', width=0.2, color="#e84d60", legend_label="深圳", source=source)
                  
                  p.xgrid.grid_line_color = None
                  p.y_range.start = 0
                  
                  p.add_tools(HoverTool(tooltips=[("時(shí)間", "@month"), ("上海平均AQI", "@{上海}"), ("北京平均AQI", "@{北京}"), ("深圳平均AQI", "@{深圳}")]))
                  
                  show(p)

                  如何利用python數(shù)據(jù)可視化的Bokeh和Bottle.py在網(wǎng)頁(yè)上展示你的數(shù)據(jù)

                  圖表3:2017年到2019年北京每月平均AQI對(duì)比

                  跟圖表2 類似我們對(duì)數(shù)據(jù)幀進(jìn)行必要的處理,同時(shí)因?yàn)槲覀円@示不同的年月的對(duì)比,所以講年份和月份單獨(dú)放置到y(tǒng)ear和month列中。

                  df['date_ym'] = df['date'].apply(lambda x: x.strftime('%Y-%m'))
                  df_month = df.groupby(by='date_ym').mean().reset_index()
                  df_month['month'] = df_month['date_ym'].apply(lambda x: x.split('-')[-1])
                  df_month['year'] = df_month['date_ym'].apply(lambda x: x.split('-')[0])
                  df_month.head()

                  如何利用python數(shù)據(jù)可視化的Bokeh和Bottle.py在網(wǎng)頁(yè)上展示你的數(shù)據(jù)

                  然后創(chuàng)建3個(gè)數(shù)據(jù)幀,每個(gè)僅包含一年的數(shù)據(jù)

                  df_2017 = df_month[df_month['year']=='2017'][['month', '北京']]
                  df_2018 = df_month[df_month['year']=='2018'][['month', '北京']]
                  df_2019 = df_month[df_month['year']=='2019'][['month', '北京']]

                  最后,還是通過(guò)相同的bokeh方法,繪制新的柱狀圖。

                  source_2017 = ColumnDataSource(df_2017)
                  source_2018 = ColumnDataSource(df_2018)
                  source_2019 = ColumnDataSource(df_2019)
                  
                  p = figure(x_range=list(df_2017['month']), title="2017-2019年北京AQI對(duì)比", plot_width=900, plot_height=400)
                  
                  p.vbar(x=dodge('month', -0.25, range=p.x_range), top='北京', width=0.2, color="#c9d9d3", legend_label="2017", source=source_2017)
                  p.vbar(x=dodge('month', 0, range=p.x_range), top='北京', width=0.2, color="#718dbf", legend_label="2018", source=source_2018)
                  p.vbar(x=dodge('month', 0.25, range=p.x_range), top='北京', width=0.2, color="#e84d60", legend_label="2019", source=source_2019)
                  
                  p.xgrid.grid_line_color = None
                  p.y_range.start = 0
                  
                  p.add_tools(HoverTool(tooltips=[("時(shí)間", "@month"), ("AQI", "@{北京}")]))
                  
                  show(p)

                  如何利用python數(shù)據(jù)可視化的Bokeh和Bottle.py在網(wǎng)頁(yè)上展示你的數(shù)據(jù)

                  到這里我們的3張圖表已經(jīng)準(zhǔn)備好了,但是他們都是在notebook中運(yùn)行的,后邊我們將對(duì)這些代碼進(jìn)行簡(jiǎn)單的轉(zhuǎn)化,并嵌入到bottle網(wǎng)頁(yè)應(yīng)用中。

                  3. Bottle網(wǎng)頁(yè)應(yīng)用

                  bottle是一個(gè)超輕量級(jí)的python web框架,我們?cè)诒疚闹羞x擇了bottle而沒(méi)有選擇flask或者Django的原因就在于它的超輕量級(jí),可以快速的搭建網(wǎng)頁(yè)應(yīng)用,對(duì)于以僅僅做數(shù)據(jù)展示為目的的網(wǎng)頁(yè)應(yīng)用,使用bottle可以讓你快速上手,讓你更專注于數(shù)據(jù)分析。

                  我們將采用bootstrap前端模板加bottle內(nèi)置的模板引擎的方式來(lái)實(shí)現(xiàn)這個(gè)應(yīng)用,為了快速實(shí)現(xiàn)這個(gè)目標(biāo),我們選取了https://github.com/arsho/bottle-bootstrap這個(gè)項(xiàng)目作為我們的初始代碼,所以,本文項(xiàng)目中使用到的網(wǎng)頁(yè)應(yīng)用代碼99%的實(shí)現(xiàn)來(lái)自于這個(gè)項(xiàng)目,我們僅僅做了一點(diǎn)改動(dòng)。在本節(jié)內(nèi)容中,我們會(huì)講解一下bottle應(yīng)用的重點(diǎn)代碼和概念。

                  本文對(duì)應(yīng)的代碼可以在 https://github.com/pythonlibrary/bokeh-bottlepy 這個(gè)倉(cāng)庫(kù)中找到。

                  3.1 文件夾結(jié)構(gòu)

                  我們的bokeh-bottlepy項(xiàng)目目錄結(jié)構(gòu)如下,其中

                  dataset文件夾:包含了數(shù)據(jù)集csv文件
                  static文件夾:包含了bootstrap前端框架代碼,包括css,JavaScript,以及fonts等,用于以bootstrap的主題來(lái)展現(xiàn)html頁(yè)面
                  views文件夾中:包含我們要如何展示數(shù)據(jù)的模板,本項(xiàng)目作為入門(mén)項(xiàng)目,其中僅僅包含了一個(gè)index.tpl文件,作為我們僅有的一個(gè)單頁(yè)面網(wǎng)頁(yè)的模板,該模板會(huì)由bottle應(yīng)用導(dǎo)入數(shù)據(jù)來(lái)渲染,最總形成用戶看到的頁(yè)面
                  app.py:為我們的入口文件,我們所有的python代碼將在這個(gè)里邊實(shí)現(xiàn),最終運(yùn)行也是通過(guò):python app.py來(lái)啟動(dòng)服務(wù)
                  Procfile:涉及到Heroku部署,后邊我們會(huì)提到

                  如何利用python數(shù)據(jù)可視化的Bokeh和Bottle.py在網(wǎng)頁(yè)上展示你的數(shù)據(jù)

                  3.2 路由

                  用python web框架實(shí)現(xiàn)的是動(dòng)態(tài)的網(wǎng)頁(yè),也就是說(shuō)網(wǎng)頁(yè)是在用戶訪問(wèn)的時(shí)候生成的,路由這個(gè)概念對(duì)于第一次接觸網(wǎng)頁(yè)應(yīng)用的人比較陌生,不過(guò)其實(shí)很簡(jiǎn)單,通俗的講,用戶在點(diǎn)擊一個(gè)網(wǎng)頁(yè)上的鏈接或按鈕,或在瀏覽器地址欄中訪問(wèn)一個(gè)鏈接的時(shí)候,網(wǎng)頁(yè)服務(wù)器端會(huì)根據(jù)鏈接的不同做不同的動(dòng)作,并將結(jié)果組織成html并呈現(xiàn)給用戶,這一個(gè)過(guò)程就是路由。

                  在bottle中實(shí)現(xiàn)路由其實(shí)就是給每一個(gè)url實(shí)現(xiàn)一個(gè)對(duì)應(yīng)的處理方法。下邊的代碼就是本項(xiàng)目用到的所有相關(guān)的部分

                  dirname = '.'
                  
                  app = Bottle()
                  debug(True)
                  
                  @app.route('/static/<filename:re:.*\.css>')
                  def send_css(filename):
                      return static_file(filename, root=dirname+'/static/asset/css')
                  
                  @app.route('/static/<filename:re:.*\.js>')
                  def send_js(filename):
                      return static_file(filename, root=dirname+'/static/asset/js')
                  
                  @app.route('/')
                  def index():
                      data = {
                              "developer_organization":"pythonlibrary.net"}
                      return template('index', data = data)

                  所有bottle網(wǎng)頁(yè)應(yīng)用需要實(shí)例化一個(gè)Bottle對(duì)像,作為服務(wù)本身,這里我們起名叫app,同時(shí)打開(kāi)了debug模式,即當(dāng)訪問(wèn)url的時(shí)候,Bottle應(yīng)用會(huì)打印一些調(diào)試信息輔助開(kāi)發(fā)人員定位問(wèn)題。

                  路由函數(shù)的指定是通過(guò)@app.route裝飾器實(shí)現(xiàn)的,這個(gè)裝飾器的參數(shù)就是相對(duì)url,例如index函數(shù)的路由地址為/,如果本地服務(wù)端口為8080,則絕對(duì)url為:http://localhost:8080/,用戶在訪問(wèn)這個(gè)地址的時(shí)候index函數(shù)將會(huì)被調(diào)用,而它的返回值就是用戶看到的頁(yè)面,這里是使用了template方法來(lái)使用data數(shù)據(jù)渲染模板,模板的概念我們下一章節(jié)會(huì)進(jìn)行介紹。

                  要做出一個(gè)漂亮的頁(yè)面,需要使用到復(fù)雜的JavaScript和css,所幸的是我們選擇的bootstrap框架為我們實(shí)現(xiàn)了這些復(fù)雜部分,我們只需要應(yīng)用它提供的模組就可以搭建出一個(gè)漂亮的網(wǎng)站。

                  在html中,JavaScript和css也是通過(guò)url來(lái)訪問(wèn)到的,因此如果要使模板生效,需要告知bottle這些JavaScript和css需要從本地哪個(gè)路徑中去找,代碼中的send_css和send_js函數(shù)就是利用bottle 中的static_file函數(shù)來(lái)通知應(yīng)用本地的資源在什么位置,而上邊的路由地址則是用戶訪問(wèn)網(wǎng)頁(yè)的時(shí)候再html中的地址,因此這兩個(gè)函數(shù)實(shí)現(xiàn)了,url和本地資源的連接。

                  3.3 模板實(shí)現(xiàn)

                  所有的Python網(wǎng)頁(yè)框架,在不使用前后端分離的方式開(kāi)發(fā)網(wǎng)頁(yè)應(yīng)用的時(shí)候,都會(huì)包含一個(gè)模板的概念,這些框架大部分都繼承了自己的模板引擎,bottle中也集成了一個(gè)他們稱為SimpleTemplate的簡(jiǎn)單模板引擎,當(dāng)然你可以選擇使用其他第三方的模板引擎,如nijia2,mako等。

                  所謂模板引擎其實(shí)即使基于模板關(guān)鍵字的替換,引擎提供了一系列的語(yǔ)法,引擎可以解析這些語(yǔ)法,做出相應(yīng)的動(dòng)作,例如根據(jù)不同的情況填入不同的數(shù)據(jù),做循環(huán),判斷等等,然后其余的內(nèi)容將保持不變的放到輸出中,可以通過(guò)python的stringtemplate來(lái)類比。

                  我們這個(gè)項(xiàng)目中,index.tpl就是模板,里邊包含了SimpleTemplate可以識(shí)別的語(yǔ)法以及其他內(nèi)容,當(dāng)SimpleTemplate解析index.tpl總的語(yǔ)法,并填入合適的數(shù)據(jù),則最終會(huì)得到完整的html內(nèi)容,因此模板是 html + 引擎語(yǔ)法的集合,至于文件后綴tpl則無(wú)關(guān)緊要,可以使任何你定義的后綴,只是一般tpl代表template。

                  我們對(duì)原始代碼的該文件進(jìn)行一些修改:將head標(biāo)簽中的信息,按照我們的項(xiàng)目進(jìn)行修改

                  <meta name="description" content="Deploy Bokeh Data Visualization with Bottlepy">
                  
                  <title>China AQI</title>

                  然后將導(dǎo)航條 navbar div按照我們的要求修改成我們自己的鏈接,將網(wǎng)頁(yè)主體container中最上邊的文字框改成我們的項(xiàng)目描述。

                  <div id="navbar" class="navbar-collapse collapse">
                    <ul class="nav navbar-nav navbar-right">
                    	<li><a href="../" rel="external nofollow"   >Home</a></li>
                    	<li><a href="https://github.com/pythonlibrary/bokeh-bottlepy" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"       >On Github</a></li>
                    </ul>
                  </div><!--/.nav-collapse -->
                  <div class="row">
                    <div class="jumbotron">
                    <h3>中國(guó)AQI數(shù)據(jù)可視化</h3>
                  	  <p>這是一個(gè)基于bottlepy, bokeh和Bootstrap的一個(gè)數(shù)據(jù)可視化部署的示例項(xiàng)目,采用了中國(guó)從2017年到2019年的AQI信息數(shù)據(jù)作為項(xiàng)目的演示數(shù)據(jù)。</p>
                    </div>
                  </div>

                  回到app.py中,在這個(gè)文件中下邊這段代碼,通過(guò)template方法實(shí)現(xiàn)了對(duì)index模板的渲染,這個(gè)方法的參數(shù)data,將作為數(shù)據(jù)動(dòng)態(tài)的傳入到模板中,相對(duì)應(yīng)的模板中有一個(gè) {{data[“developer_organization”]}} 的語(yǔ)句,這就是模板語(yǔ)法,跟python語(yǔ)法類似,通過(guò)dict的方式訪問(wèn)了data變量中的developer_organization鍵對(duì)應(yīng)的值。

                  @app.route('/')
                  def index():
                      data = {
                              "developer_organization":"pythonlibrary.net"}
                      return template('index', data = data)

                  3.4 啟動(dòng)網(wǎng)頁(yè)服務(wù)

                  我們?cè)赼pp.py實(shí)現(xiàn)了類似下邊這樣的入口,如果在終端中運(yùn)行python app.py,這段代碼將被執(zhí)行,也就可以啟動(dòng)網(wǎng)頁(yè)服務(wù),服務(wù)的端口為8080,同時(shí)將host設(shè)置為0.0.0.0意思是其他電腦可以訪問(wèn)這臺(tái)電腦上的服務(wù),如果僅想本機(jī)本地訪問(wèn)可以設(shè)置為localhost

                  if __name__ == "__main__":
                      port = 8080
                      app.run(host="0.0.0.0", port=port, debug=True)

                  4. 將Bokeh和Bottle集成在一起

                  4.1 模板修改

                  首先我們想要在html中顯示bokeh生成的圖表,需要加載bokeh的JavaScript,通過(guò)在index.tpl中添加下邊幾個(gè)CDN的方式來(lái)導(dǎo)入。

                  <script src="https://cdn.bokeh.org/bokeh/release/bokeh-1.4.0.min.js"></script>
                  <script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.4.0.min.js"></script>
                  <script src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-1.4.0.min.js"></script>

                  然后我們要添加數(shù)據(jù)圖表的占位符(相關(guān)的引擎語(yǔ)法代碼),當(dāng)進(jìn)行模板渲染的時(shí)候,會(huì)被動(dòng)態(tài)的替換為python代碼中提供的內(nèi)容。

                  在頁(yè)面主體container中添加三個(gè)圖表的占位符

                  注意:有別于其他數(shù)據(jù)傳入語(yǔ)法,這里在data[“l(fā)ot1_div”]前邊有一個(gè)感嘆號(hào)(?。?,這個(gè)非常重要,如果沒(méi)有感嘆號(hào)意味著,傳入的數(shù)據(jù)將被認(rèn)為是字符串,在渲染的時(shí)候會(huì)被引號(hào)括起來(lái),而我們實(shí)際想要填充在這里的是html代碼,而不是被雙引號(hào)括起來(lái)的html代碼,感嘆號(hào)就是告知引擎,我們傳入是的瀏覽器可以處理的html或者JavaScript或者css代碼。

                  <div class="row">
                    {{!data["plot1_div"]}}
                  </div>
                  </br></br></br></br>
                  <div class="row">
                    {{!data["plot2_div"]}}
                  </div>
                  </br></br></br></br>
                  <div class="row">
                    {{!data["plot3_div"]}}
                  </div>

                  在body標(biāo)簽后邊添加繪制圖表使用的JavaScript腳本占位符

                  {{!data["plot_script"]}}

                  這里模板中未來(lái)用到的圖表div和JavaScript腳本將會(huì)由bokeh生成,并有bottle渲染,我們會(huì)在加下來(lái)這一章節(jié)說(shuō)明。

                  4.2 Python代碼集成

                  將 2.2 章節(jié)中在notebook中調(diào)試成功的代碼轉(zhuǎn)換為函數(shù),并實(shí)現(xiàn)到app.py中,注意原本在notebook中顯示圖表我們使用了show(p)的方法,在網(wǎng)頁(yè)應(yīng)用中我們僅僅是通過(guò)return p將圖表對(duì)象返回,返回值將通過(guò)bottle提供的方法進(jìn)行處理。

                  def get_df_from_source():
                      ''' get dataframes from the source dataset, only take the data of some big cities
                      '''
                      cities = ['上海', '北京', '杭州', '寧波', '保定', '南京', '蘇州', '深圳', '廈門(mén)', '廣州']
                      df = pd.read_csv(dirname+'/dataset/AQI_merged.csv')
                      df['date'] = pd.to_datetime(df['date'])
                      df = df.sort_values(by='date').reset_index(drop=True)
                      df = df[df['type']=='AQI']
                      return df
                  
                  def draw_daily_AQI(mini_date, df):
                      year = mini_date.split('-')[0]
                      df_day = df[df['date']>=mini_date]
                      source = ColumnDataSource(df_day)
                  
                      p = figure(x_axis_type="datetime", title="{}年AQI日均平均變化曲線".format(year), plot_width=1150, plot_height=400)
                      p.line('date', '上海', line_color='blue', legend_label='上海', source=source)
                      p.line('date', '北京', line_color='green', legend_label='北京', source=source)
                      p.line('date', '深圳', line_color='orange', legend_label='深圳', source=source)
                  
                      p.legend.location = "top_right"
                      p.add_tools(HoverTool(tooltips=[("AQI", "$y")]))
                  
                      return p
                          
                  def draw_month_AQI(mini_date, df):
                      year = mini_date.split('-')[0]
                      df_day = df[df['date']>=mini_date]
                  
                      df_day['month'] = df_day['date'].apply(lambda x: x.strftime('%Y-%m'))
                      df_month = df_day.groupby(by='month').mean().reset_index()
                  
                      source = ColumnDataSource(df_month)
                  
                      p = figure(x_range=list(df_month['month']), title="2019年AQI", plot_width=1150, plot_height=400)
                      p.vbar(x=dodge('month', -0.25, range=p.x_range), top='上海', width=0.2, color="#c9d9d3", legend_label="上海", source=source)
                      p.vbar(x=dodge('month', 0, range=p.x_range), top='北京', width=0.2, color="#718dbf", legend_label="北京", source=source)
                      p.vbar(x=dodge('month', 0.25, range=p.x_range), top='深圳', width=0.2, color="#e84d60", legend_label="深圳", source=source)
                      p.xgrid.grid_line_color = None
                      p.y_range.start = 0
                      p.add_tools(HoverTool(tooltips=[("時(shí)間", "@month"), ("上海平均AQI", "@{上海}"), ("北京平均AQI", "@{北京}"), ("深圳平均AQI", "@{深圳}")]))
                          
                      return p
                  
                  def draw_year_AQI(df):
                      df['date_ym'] = df['date'].apply(lambda x: x.strftime('%Y-%m'))
                      df_month = df.groupby(by='date_ym').mean().reset_index()
                      df_month['month'] = df_month['date_ym'].apply(lambda x: x.split('-')[-1])
                      df_month['year'] = df_month['date_ym'].apply(lambda x: x.split('-')[0])
                  
                      df_2017 = df_month[df_month['year']=='2017'][['month', '北京']]
                      df_2018 = df_month[df_month['year']=='2018'][['month', '北京']]
                      df_2019 = df_month[df_month['year']=='2019'][['month', '北京']]
                  
                      source_2017 = ColumnDataSource(df_2017)
                      source_2018 = ColumnDataSource(df_2018)
                      source_2019 = ColumnDataSource(df_2019)
                  
                      p = figure(x_range=list(df_2017['month']), title="2017-2019年北京AQI對(duì)比", plot_width=1150, plot_height=400)
                  
                      p.vbar(x=dodge('month', -0.25, range=p.x_range), top='北京', width=0.2, color="#c9d9d3", legend_label="2017", source=source_2017)
                      p.vbar(x=dodge('month', 0, range=p.x_range), top='北京', width=0.2, color="#718dbf", legend_label="2018", source=source_2018)
                      p.vbar(x=dodge('month', 0.25, range=p.x_range), top='北京', width=0.2, color="#e84d60", legend_label="2019", source=source_2019)
                  
                      p.xgrid.grid_line_color = None
                      p.y_range.start = 0
                  
                      p.add_tools(HoverTool(tooltips=[("時(shí)間", "@month"), ("AQI", "@{北京}")]))
                  
                      return p

                  三個(gè)繪圖函數(shù)返回了圖表對(duì)象p,我們?nèi)绻軌蜃宐ottle來(lái)渲染圖表對(duì)象,從而實(shí)現(xiàn)在網(wǎng)頁(yè)中的圖表展示呢?bokeh提供了一個(gè)components方法,可以接收?qǐng)D表對(duì)象作為參數(shù),而返回繪圖使用的JavaScript腳本和圖表div,因此修改我們的index路由函數(shù)為:

                  @app.route('/')
                  def index():
                      df = get_df_from_source()
                      plot1 = draw_daily_AQI('2019-01-01', df=df)
                      plot2 = draw_month_AQI('2019-01-01', df=df)
                      plot3 = draw_year_AQI(df=df)
                      plots_data = components((plot1, plot2, plot3))
                  
                      data = {
                              "plot_script":plots_data[0],
                              "plot1_div":plots_data[1][0],
                              "plot2_div":plots_data[1][1],
                              "plot3_div":plots_data[1][2],
                              "developer_organization":"pythonlibrary.net"}
                      return template('index', data = data)

                  在這里,index.tpl模板中的data字典中的plot1_div,plot2_div,plot3_div以及plot_script將被動(dòng)態(tài)的渲染替換。最終實(shí)現(xiàn)了將圖表展示在網(wǎng)頁(yè)上的目的。

                  你可以clone本項(xiàng)目的倉(cāng)庫(kù)來(lái)嘗試運(yùn)行,或者直接訪問(wèn)http://china-aqi-data-visulazition.herokuapp.com/來(lái)查看效果

                  如何利用python數(shù)據(jù)可視化的Bokeh和Bottle.py在網(wǎng)頁(yè)上展示你的數(shù)據(jù)

                  5. 部署應(yīng)用到Heroku

                  這部分內(nèi)容跟怎么將數(shù)據(jù)圖表展示在網(wǎng)頁(yè)上沒(méi)有直接的關(guān)系,僅僅是一種可選的免費(fèi)云服務(wù),可以供你來(lái)共享你的頁(yè)面,或者了解網(wǎng)頁(yè)部署。但其實(shí)不同的服務(wù)可能部署的方式并不相同,因此如果你要部署你的網(wǎng)頁(yè)到其他服務(wù)提供商,可能這里的知識(shí)完全不適用。

                  在Heroku上用戶可以免費(fèi)部署有限的網(wǎng)絡(luò)應(yīng)用,同時(shí)過(guò)程也非常的簡(jiǎn)單,只需要實(shí)現(xiàn)一個(gè)Procfile文件,Heroku系統(tǒng)就知道怎么運(yùn)行你的服務(wù)了。我們的項(xiàng)目中Procfile使用如下代碼,跟我們本地運(yùn)行服務(wù)類似。

                  web: python app.py

                  而針對(duì)app.py的入口代碼,需要將port改為從環(huán)境變量讀取,因?yàn)镠eroku會(huì)動(dòng)態(tài)的為應(yīng)用分配端口,如果指定一個(gè)固定值,則會(huì)因?yàn)镠eroku沒(méi)有打開(kāi)其的對(duì)外訪問(wèn),而導(dǎo)致用戶無(wú)法訪問(wèn)該服務(wù)。

                  if __name__ == "__main__":
                      port=int(os.environ.get("PORT", 8080))
                      app.run(host="0.0.0.0", port=port, debug=True

                  最后用戶可以在Heroku頁(yè)面上選擇將github倉(cāng)庫(kù)和應(yīng)用連接在一起,那么系統(tǒng)會(huì)自動(dòng)的從github拉取最新代碼然后啟動(dòng)服務(wù)。

                  “如何利用python數(shù)據(jù)可視化的Bokeh和Bottle.py在網(wǎng)頁(yè)上展示你的數(shù)據(jù)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

                  向AI問(wèn)一下細(xì)節(jié)

                  免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

                  AI