├── tutorial ├── assets │ ├── __init__.py │ ├── bokeh-transparent.png │ ├── 5_gaussians_3_ways.png │ ├── 5_gaussians_dsblue.png │ ├── 5_gaussians_labeled.png │ ├── datashader_examples.png │ └── document.svg ├── .gitignore ├── environment.yml ├── README.md ├── A4 - Additional Resources.ipynb ├── 11 - Running Bokeh Applications.ipynb ├── 08 - Graph and Network Plots.ipynb ├── 09 - Geographic Plots.ipynb └── 05 - Presentation Layouts.ipynb └── read_me ├── Bokeh 探索頻道(2)_客製化技術圖表升級.md └── Bokeh 探索頻道(1)_Python互動式圖表函數庫初體驗.md /tutorial/assets/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tutorial/.gitignore: -------------------------------------------------------------------------------- 1 | plot.html 2 | plot.png 3 | plot.svg 4 | -------------------------------------------------------------------------------- /tutorial/assets/bokeh-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benbilly3/bokeh_explore/HEAD/tutorial/assets/bokeh-transparent.png -------------------------------------------------------------------------------- /tutorial/assets/5_gaussians_3_ways.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benbilly3/bokeh_explore/HEAD/tutorial/assets/5_gaussians_3_ways.png -------------------------------------------------------------------------------- /tutorial/assets/5_gaussians_dsblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benbilly3/bokeh_explore/HEAD/tutorial/assets/5_gaussians_dsblue.png -------------------------------------------------------------------------------- /tutorial/assets/5_gaussians_labeled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benbilly3/bokeh_explore/HEAD/tutorial/assets/5_gaussians_labeled.png -------------------------------------------------------------------------------- /tutorial/assets/datashader_examples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benbilly3/bokeh_explore/HEAD/tutorial/assets/datashader_examples.png -------------------------------------------------------------------------------- /tutorial/environment.yml: -------------------------------------------------------------------------------- 1 | name: bokeh-notebooks 2 | dependencies: 3 | - python 4 | - bokeh=0.12.2 5 | - pandas 6 | - jupyter 7 | - nodejs 8 | - ipywidgets 9 | -------------------------------------------------------------------------------- /tutorial/README.md: -------------------------------------------------------------------------------- 1 | ## Clone or download the repo 2 | First get local copies of the tutorial notebooks: 3 | 4 | ``` 5 | $ git clone https://github.com/bokeh/bokeh-notebooks.git 6 | ``` 7 | 8 | Or download from: https://github.com/bokeh/bokeh-notebooks/archive/master.zip 9 | 10 | ## Install the dependencies 11 | 12 | This tutorial has been tested on: 13 | 14 | * bokeh 1.4.0 15 | * pandas 0.25.2 16 | * notebook 6.0.1 17 | * phantomjs 2.1.1 18 | * pillow 6.1.0 19 | * selenium 3.8.0 20 | 21 | Other combinations may work also. 22 | 23 | The quickest, easiest way to install is to use Anaconda (or Miniconda): 24 | 25 | #### Installing with anaconda 26 | 27 | Install [anaconda](http://anaconda.com/downloads) 28 | 29 | Anaconda should come with all the dependencies included, but you may need to update your versions. 30 | 31 | #### Installing with miniconda 32 | 33 | Install [miniconda](http://conda.pydata.org/miniconda.html). 34 | 35 | Use the command line to create an environment and install the packages: 36 | 37 | ```bash 38 | $ conda env create 39 | $ source activate bokeh-notebooks 40 | ``` 41 | 42 | NOTE: Run this in the `tutorial` directory where `environment.yml` file is. 43 | 44 | ---- 45 | 46 | Once you've got a base install, you can install the remaining dependencies with: 47 | 48 | ```bash 49 | conda install phantomjs pillow selenium 50 | ``` 51 | 52 | ## Get the sample data 53 | 54 | Bokeh has a [sample data](https://docs.bokeh.org/en/latest/docs/installation.html#sample-data) download that gives us some data to build demo visualizations. To get 55 | it run the following command at your command line: 56 | 57 | ```bash 58 | $ bokeh sampledata 59 | ``` 60 | 61 | or run the following from within a Python interpreter: 62 | 63 | ```python 64 | import bokeh.sampledata 65 | bokeh.sampledata.download() 66 | ``` 67 | 68 | ### Install Datashader and Holoviews (optional) 69 | 70 | Some optional sections require the additional packages Flask, Datashader, and Holoviews. 71 | These can be installed with: 72 | 73 | ```bash 74 | $ conda install -c pyviz datashader holoviews flask 75 | ``` 76 | 77 | ## Run the Jupyter notebook 78 | 79 | From this folder run jupyter notebook, and open the [00 - Introduction and Setup.ipynb](00 - Introduction and Setup.ipynb) notebook. 80 | 81 | ``` 82 | $ jupyter notebook 83 | ``` 84 | -------------------------------------------------------------------------------- /read_me/Bokeh 探索頻道(2)_客製化技術圖表升級.md: -------------------------------------------------------------------------------- 1 | # Bokeh 探索頻道(2)~客製化技術圖表升級 2 | 3 | ## 改造動機 4 | 5 | 上次介紹了Bokeh厲害的地方,那可不可以應用到投資圖表的繪製呢? 6 | 在Hahow課程:『Python 理財:打造小資族選股策略』的單元20中,韓老師用Talib和matplotlib套件示範了如何提取資料和客製化技術圖表繪圖,點出應用方向,然而課程重點是放在選股,不是在視覺化,圖表功能比較簡單,當時Bokeh也沒那麼厲害。 7 | ![](https://i.imgur.com/Q8hEt30.png) 8 | 9 | 有些同學希望能有更精美的圖表可使用,搭配課程精彩的選股程式技巧,不就更完美了?這個願望今天就可以實現,後面有銜接課程的程式碼直接提供使用。 10 | 現在有Bokeh的幫忙,用Python也能打造不錯的互動圖表效果,接下來就要綜合小資族課程、Bokeh、網路上各路技巧,來打造適用於Hahow課程的新圖表。沒參與課程的可以用github 連結裡的demo_json檔來試試,只要資料格式對應,都可使用。 11 | 12 | #### github:https://github.com/benbilly3/bokeh_explore 13 | 使用的程式檔為technical_chart。 14 | 15 | 16 | ## 繪圖技巧說明 17 | 18 | 寫Python就要利用其他魔法師的咒語,省時有效率,先來Bokeh gallery看看沒有範例。 19 | 結果有現成的K線圖範例,太好了! 20 | 21 | ![](https://i.imgur.com/wjTomBS.png) 22 | 23 | 雖然仍是陽春的靜態圖,但至少有了改造藍圖,從官網的程式碼: 24 | https://docs.bokeh.org/en/latest/docs/gallery/candlestick.html 25 | 26 | 可以發現bokeh輕鬆地用pandas分類紅黑棒資料,再用segment畫引線和用vbar畫長棒圖。眼尖的人可以發現股價碰到假日的時候會有空值,這造成閱讀上有些礙眼,能否有讓時間序列日期資料的解決辦法呢? 27 | 28 | 在還不熟bokeh的時候,stackoverflow也是我們好朋友...處理關鍵在讓日期轉為文字,不用會自動補假日日期的datetime。 29 | 30 | ``` 31 | fig.xaxis.major_label_overrides = { 32 | i: date.strftime('%Y/%m/%d') for i, date in enumerate(pd.to_datetime(df["date"])) 33 | # pd.date_range(start='3/1/2000', end='1/08/2018') 34 | } 35 | ``` 36 | 37 | bokeh是物件導向繪圖庫,都封裝相當好,基本上沒啥程式技巧,就像玩樂高積木一樣,搜尋工具來堆,不難,比較繁瑣,內容頗多。 38 | 接著主要會實踐下列功能到圖表,有興趣學的可以看連結。 39 | 40 | #### 1. figure圖紙設定,bokeh各種models應用 41 | https://docs.bokeh.org/en/latest/docs/reference/plotting.html 42 | 43 | #### 2. 處理假日日期造成的資料不連續問題,x_range overwrite技巧 44 | https://stackoverflow.com/questions/23585545/how-do-i-make-bokeh-omit-missing-dates-when-using-datetime-as-x-axis 45 | 46 | #### 3. hover互動資料顯示 47 | https://docs.bokeh.org/en/latest/docs/user_guide/tools.html 48 | 49 | #### 4. legend物件控制,從label控制線圖開關。將legend移到圖表外,讓版面清晰。 50 | https://stackoverflow.com/questions/26254619/position-of-the-legend-in-a-bokeh-plot 51 | https://docs.bokeh.org/en/latest/docs/user_guide/interaction/legends.html 52 | 53 | #### 5. 位移、縮放、十字線、重置、存檔工具 54 | 55 | https://docs.bokeh.org/en/latest/docs/user_guide/tools.html 56 | 57 | #### 6. second y_ranges繪製(使用雙軸) 58 | https://docs.bokeh.org/en/latest/docs/user_guide/plotting.html#userguide-plotting-twin-axes 59 | 60 | #### 7. 利用vbar和segment快速繪製k線,並將均線帶入。 61 | https://docs.bokeh.org/en/latest/docs/gallery/candlestick.html 62 | 63 | #### 8. 建立技術線子圖組 64 | https://docs.bokeh.org/en/latest/docs/user_guide/layout.html#userguide-layout 65 | 66 | #### 經過神奇的魔法就有hahow課程的進化版,包含以上的功能,實踐了不錯的效果!加入output_file('檔名')就可產生html檔在瀏覽器使用,只要再寫一個傳導查詢變數的API,就能串接做一個服務出來,之後會講用FastApi寫後端來串Bokeh。 67 | 68 | ![](https://i.imgur.com/4BY8lBt.png) 69 | 70 | 71 | ## 課程相容資料提取工具 72 | 73 | #### 從python小資族的sqlite提取資料 74 | 若DB為pickle檔,須將pd.read_sql那行修改為read_pickle,並要注意格式。 75 | 76 | 如果有上過韓老師python小資族,可將course11.ipynb上頭用read_sql取資料的程式改為下部份取資料,輸入股號和資料開始日期,產出的data丟入後面的繪圖程式,即可產生互動式圖表。 77 | ``` 78 | import pandas as pd 79 | import sqlite3 80 | import os 81 | import json 82 | from talib import abstract 83 | 84 | 85 | def get_price_data(stock_id,date): 86 | # connect to sql 87 | conn = sqlite3.connect(os.path.join('data', "data.db")) 88 | # read data from sql 89 | df = pd.read_sql(f"select stock_id,證券名稱, date, 開盤價, 收盤價, 最高價, 最低價, 成交股數 from price where stock_id='{stock_id}' and date > '{date}'", conn, 90 | index_col=['date']) 91 | # rename the columns of dataframe 92 | df.index=df.index.astype(str).str.split(" ").str[0] 93 | df.rename(columns={'收盤價':'close','證券名稱':'name', '開盤價':'open', '最高價':'high', '最低價':'low', '成交股數':'volume'}, inplace=True) 94 | df['MA5']=df['close'].rolling(5).mean() 95 | df['MA10']=df['close'].rolling(10).mean() 96 | df['MA20']=df['close'].rolling(20).mean() 97 | df['MA60']=df['close'].rolling(60).mean() 98 | df['MA120']=df['close'].rolling(120).mean() 99 | df['volume']=df['volume']/1000 100 | 101 | 102 | RSI = pd.DataFrame(abstract.RSI(df, timeperiod=12),columns=['RSI_12']) 103 | RSI['RSI_36']=abstract.RSI(df, timeperiod=36) 104 | RSI=RSI.to_dict() 105 | STOCH = abstract.STOCH(df).to_dict() 106 | MACD=abstract.MACD(df).to_dict() 107 | basic=df.iloc[-1,:2].to_dict() 108 | df=df.drop(columns=['stock_id','name']).to_dict() 109 | data={'basic':basic,'price_df':df,'RSI':RSI,'STOCH' :STOCH,'MACD':MACD } 110 | 111 | return data 112 | 113 | ``` 114 | ## 程式碼下載 115 | 116 | 使用的繪圖程式檔為technical_chart,將get_price_data(stock_id,date)帶入technical_chart(json_df)即可繪圖,可到gitlab下載bokeh_explore。 117 | #### github:https://github.com/benbilly3/bokeh_explore 118 | 119 | {%gist benbilly3/ac18625f06545cff7ab46256ceaccbbc %} 120 | 121 | ###### tags: `bokeh` -------------------------------------------------------------------------------- /read_me/Bokeh 探索頻道(1)_Python互動式圖表函數庫初體驗.md: -------------------------------------------------------------------------------- 1 | # Bokeh 探索頻道(1)~Python互動式圖表函數庫初體驗 2 | ![](https://i.imgur.com/05S59sz.png) 3 | 4 | ## Python 視覺化套件使用經驗 5 | Python套件多,品質不一。用module之前,我習慣先看github星星確認認可度,有1000個以上多是品質保證,再看release history,看是否有被持續維護,那種2年以上沒新版本的,採坑機率高。Bokeh看來沒問題。 6 | 7 | ![](https://i.imgur.com/y3p5KBo.png) 8 | 9 | 平常python視覺化的主力套件是matplotlib和seaborn,前者是20歲老套件,是視覺化元祖,許多套件基礎都是建構在matplotlib上,像seaborn就是,有更簡潔的寫法和精美圖例,兩者都以靜態視覺居多,缺乏變化性(或是要費點功)。 10 | Python守備範圍廣,但在網頁前端仍難以與Java Script抗衡,而D3.js函數庫也要花不少學習成本,有沒有辦法用Python做出JS動態視覺的效果? 11 | 12 | Bokeh的出世,提供了Python與D3.js之間的橋梁,讓你可以用Python做出D3.js常用功能的效果,減少學習成本,高效開發,輕鬆描述統計結果。 13 | 14 | ## 厲害在哪裡? 15 | 先來看Gallory,python視覺化套件很多,範例圖不夠精美的,可考慮跳過。 16 | 1. 化學元素週期表 17 | 18 | ![化學元素週期表](https://i.imgur.com/81XiMAU.png) 19 | 20 | 2. 雷達圖 21 | 22 | ![](https://i.imgur.com/hMPxBgq.png) 23 | 24 | 3. 互動式wiget散點圖表 25 | 26 | ![](https://i.imgur.com/9vnv8yZ.png) 27 | 28 | 逛了一圈,發現Bokeh圖表精美、範例多、都有附原始碼,官方教學也完整,有tutorial jupyter檔案教學。 29 | 看來比matplotlib強大,也比plotly漂亮...,函數庫使用看來也不難,且可鑲嵌到Django等Web框架,無論是local或遠端的前端呈現都可以效率開發。 30 | 31 | ## 開箱試玩時間 32 | 33 | 要注意的是bokeh會預設連BokehJS cdn,但連線有時不是很穩定,這時可多加"INLINE"環境變數設定,讓BokehJS驅動於local python env。 34 | 35 | ``` 36 | from bokeh.resources import INLINE 37 | bokeh.io.output_notebook(INLINE) 38 | ``` 39 | 40 | Bokeh可以在Jupyter呈現開發也可以跳轉出html檔,可自由設定,預設是跳轉html檔(output_file())。若要更改預設,必須加上`bokeh.io.reset_output()`重設環境預設。 41 | 42 | ``` 43 | output_notebook() #jupyter呈現 44 | output_file() #html呈現 45 | ``` 46 | 47 | fig物件可設定tools參數,圖表會自帶縮放、重整、儲存等功能。 48 | 以下是參考官方範例後微調的程式。 49 | ``` 50 | from bokeh.plotting import figure, output_file, show, output_notebook 51 | import bokeh.io 52 | from bokeh.resources import INLINE 53 | 54 | # env settings 環境變數設定 55 | bokeh.io.reset_output() 56 | bokeh.io.output_notebook(INLINE) 57 | 58 | # prepare some data 59 | x = [0.1, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0] 60 | y0 = [i**2 for i in x] 61 | y1 = [10**i for i in x] 62 | y2 = [10**(i**2) for i in x] 63 | 64 | # output to static HTML file 65 | # output_file("log_lines.html") 66 | 67 | # create a new plot 68 | p = figure( 69 | tools="pan,box_zoom,reset,save", 70 | y_axis_type="log", y_range=[0.001, 10**11], title="log axis example", 71 | x_axis_label='sections', y_axis_label='particles' 72 | ) 73 | 74 | # add some renderers 75 | p.line(x, x, legend_label="y=x") 76 | p.circle(x, x, legend_label="y=x", fill_color="white", size=8) 77 | p.line(x, y0, legend_label="y=x^2", line_width=3) 78 | p.line(x, y1, legend_label="y=10^x", line_color="red") 79 | p.circle(x, y1, legend_label="y=10^x", fill_color="red", line_color="red", size=6) 80 | p.line(x, y2, legend_label="y=10^x^2", line_color="orange", line_dash="4 4") 81 | 82 | # # show the results 83 | show(p) 84 | output_notebook() 85 | ``` 86 | 87 | ![](https://i.imgur.com/cK9ZFBB.png) 88 | 89 | ## 投資圖表試玩 90 | 91 | Bokeh官方有提供sample_data給大家練習,gallery豐富的範例都取自sample_data,對比官方的資料格式就能輕鬆模仿應用,沒啥高深程式技巧。下載sample_data指令為` bokeh.sampledata.download()`,直接貼在jupyter執行。檔案會下載到bokeh module裡。 92 | 93 | ### 檢查蘋果電腦範例資料(json) 94 | from bokeh.sampledata.stocks import AAPL 95 | ``` 96 | 檢查欄位 97 | AAPL.keys() 98 | #dict_keys(['date', 'open', 'high', 'low', 'close', 'volume', 'adj_close']) 99 | ``` 100 | 101 | ### ColumnDataSource物件為Bokeh資料驅動渲染核心 102 | 103 | dataframe要傳入ColumnDataSource才能驅動js。 104 | providing the data that is visualized by the glyphs of the plot 105 | https://docs.bokeh.org/en/latest/docs/user_guide/data.html 106 | 107 | ### HoverTool 108 | 109 | 游標滑過時顯示資料 110 | 111 | ### Click_policy 112 | 113 | 藉由標籤控制數值顯示 114 | hide為隱藏,mute為切換自訂顯示模式 115 | 可在muted_color控制顏色, muted_alpha控制顏色濃淡 116 | 117 | ### 程式範例 118 | ``` 119 | 120 | import bokeh.io 121 | from bokeh.resources import INLINE 122 | from bokeh.models import HoverTool 123 | from bokeh.palettes import Spectral4 124 | from bokeh.plotting import figure, output_file, show, output_notebook, ColumnDataSource 125 | from bokeh.sampledata.stocks import AAPL, GOOG, IBM, MSFT 126 | import pandas as pd 127 | 128 | # env settings 129 | bokeh.io.reset_output() 130 | bokeh.io.output_notebook(INLINE) 131 | 132 | 133 | # set hover 134 | ## HoverTool 135 | # 游標滑過時顯示資料,date格式需要轉換,不然會是timestamp 136 | hover = HoverTool( 137 | tooltips = [ 138 | ("date", "@date{%F}"), 139 | ("close", "@open"), 140 | ("close", "@close"), 141 | ("high", "@high"), 142 | ("low", "@low"), 143 | ("volume","@volume") 144 | ], 145 | formatters={"@date":"datetime"} 146 | ) 147 | 148 | # set figure 149 | p = figure( 150 | plot_width=1000, 151 | plot_height=400, 152 | x_axis_type="datetime", 153 | tools=[hover,"pan,box_zoom,reset,save"], 154 | ) 155 | p.title.text = 'Stock_Price--Click on legend entries to mute the corresponding lines and show daily details in hover' 156 | 157 | # use ColumnDataSource to control 158 | # click_policy 159 | # 藉由標籤控制數值顯示 160 | # hide為隱藏,mute為切換自訂顯示模式 161 | # 可在muted_color控制顏色, muted_alpha控制顏色濃淡 162 | 163 | for data, name, color in zip([AAPL, IBM, MSFT, GOOG], ["AAPL", "IBM", "MSFT", "GOOG"], Spectral4): 164 | df = pd.DataFrame(data) 165 | df['date'] = pd.to_datetime(df['date']) 166 | source = ColumnDataSource(df) 167 | p.line(x="date",y="close", line_width=2, color=color, alpha=0.8, 168 | muted_color=color, muted_alpha=0.2, legend_label=name,source=source) 169 | 170 | 171 | p.legend.location = "top_left" 172 | # use hide or mute 173 | p.legend.click_policy="mute" 174 | 175 | # output_file("interactive_legend.html", title="interactive_legend.py example") 176 | 177 | show(p) 178 | output_notebook() 179 | ``` 180 | 181 | ### 兩種模式結果比較 182 | 1. Mute 183 | ![](https://i.imgur.com/xkQhfUt.png) 184 | 185 | 2. Hide 186 | ![](https://i.imgur.com/fKmVrH1.png) 187 | 188 | 189 | ### 小結 190 | 191 | 這篇主要是來體會一下Bokeh的效果,不用會JS,隨便玩就有這種效果,蠻滿意的,值得再深入一下細節,把以前matplotlib的圖表都轉來bokeh,無論是在工作上的資料報告還是看盤需要,都蠻方便的。 192 | 193 | ### 程式檔案連結 194 | https://github.com/benbilly3/bokeh_explore 195 | 196 | ###### tags: `bokeh` 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | -------------------------------------------------------------------------------- /tutorial/A4 - Additional Resources.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\n", 8 | " \n", 9 | " \n", 17 | " \n", 20 | " \n", 21 | "
\n", 10 | " \n", 11 | " \n", 15 | " \n", 16 | " \n", 18 | "

Bokeh Tutorial

\n", 19 | "
\n", 22 | "

A4. Additional resources

" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "# Additional resources\n", 30 | "\n", 31 | "There are lots of things we haven't had time to tell you about. In general to learn more about bokeh, the following resources will hopefully be helpful:\n", 32 | "\n" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "## Doumentation\n", 40 | "\n", 41 | "##### Main Page - https://bokeh.org \n", 42 | "\n", 43 | "The main front page, with links to many other resources\n", 44 | "\n", 45 | "---\n", 46 | "\n", 47 | "##### Documentation - https://docs.bokeh.org/en/latest \n", 48 | "\n", 49 | "The documentation toplevel page\n", 50 | "\n", 51 | "---\n", 52 | "\n", 53 | "##### User's Guide - https://docs.bokeh.org/en/latest/docs/user_guide.html\n", 54 | "\n", 55 | "The user's guide has many top-oriented subsections, for example \"Plotting with Basic Glyphs\", \"Configuring Plot Tools\", or \"Adding Interactions\". Each user's guide section typically example code and corresponding live plots that demonstrate how to accomplish various tasks. \n", 56 | "\n", 57 | "---\n", 58 | "\n", 59 | "##### Gallery - https://docs.bokeh.org/en/latest/docs/gallery.html\n", 60 | "\n", 61 | "One of the best ways to learn is to find an existing example similar to what you want, and to study it and then use it as a starting place. Starting from a known working example can often save time and effort when getting started by allowing you to make small, incremental changes and observing the outcome. The Bokeh docs have a large thumbnail gallery that links to live plots and apps with corresponding code. \n", 62 | "\n", 63 | "\n", 64 | "---\n", 65 | "\n", 66 | "##### Reference Guide - https://docs.bokeh.org/en/latest/docs/reference.html\n", 67 | "\n", 68 | "If you are already familiar with Bokeh and have questions about specific details of the obejcts you are already using, the reference guide is a good resource for finding information. The reference guide is automatically generated from the project source code and is a complete resources for all bokeh models and their properties. \n", 69 | "\n", 70 | "---\n", 71 | "\n", 72 | "\n", 73 | "\n", 74 | "##### Issue tracker - https://github.com/bokeh/bokeh/issues\n", 75 | "\n", 76 | "The GitHub issue tracker is the place to go to submit ***bug reports*** and ***feature requests***. It it NOT the right place for general support questions (see the *General Community Support* links below).\n", 77 | "\n" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "## Example apps and Scripts\n", 85 | "\n", 86 | "In addition to all the live gallery examples, Bokeh has many additional scripts and apps that can be instructive to study and emulate. \n", 87 | "\n", 88 | "##### Examples folder - https://github.com/bokeh/bokeh/tree/master/examples/\n", 89 | "\n", 90 | "The `examples` directory has many subfolders dedicated to different kinds of topics. Some of the hightlights are:\n", 91 | "\n", 92 | "* `app` - example Bokeh apps, run with \"`bokeh serve`\"\n", 93 | "* `howto` - some examples arranged around specific topics such as layout or notebook comms\n", 94 | "* `models` - examples that demonstrate the low-level `bokeh.models` API\n", 95 | "* `plotting` - a large collections of examples using the `bokeh.plotting` interface\n", 96 | "* `webgl` - some examples demonstrating WebGL usage\n", 97 | "\n", 98 | "---\n", 99 | "\n" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "## General Commnity Support\n", 107 | "\n", 108 | "Bokeh has a large and growing community. The best place to go for general support questions (either to ask, or to answer!) is https://discourse.bokeh.org\n", 109 | "\n" 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "metadata": {}, 115 | "source": [ 116 | "## Contributor Resources\n", 117 | "\n", 118 | "Bokeh has a small but growing developer community. We are always looking to have new contributors. Below are some resources for people involved in working on Bokeh itself.\n", 119 | "\n", 120 | "##### Source code - https://github.com/bokeh/bokeh\n", 121 | "\n", 122 | "Go here to clone the GitHub repo (in order to contribute or get the examples), or to submit issues to the issue tracker \n", 123 | "\n", 124 | "---\n", 125 | "\n", 126 | "##### Issue tracker - https://github.com/bokeh/bokeh/issues\n", 127 | "\n", 128 | "The GitHub issue tracker is the place to go to submit ***bug reports*** and ***feature requests***. For general support questions, see the *General Community Support* links above.\n", 129 | "\n", 130 | "---\n", 131 | "\n", 132 | "#### Developer's Guide - https://bokeh.pydata.org/en/latest/docs/dev_guide.html\n", 133 | "\n", 134 | "If you are interesting in becoming a contributor to Bokeh, the developer's guide is the place to start. It has information about getting a development environment set up, the library architecture, writing and running tests, \n", 135 | "\n", 136 | "---\n", 137 | "\n", 138 | "#### Dev Channel in Discourse - https://discourse.bokeh.org/c/development\n", 139 | "\n", 140 | "Come here for assistance with any questions about developing Bokeh itself. \n" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "# Next Section" 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "This is the last section of the appendices.\n", 155 | "\n", 156 | "To go back to the overview, click [here](00%20-%20Introduction%20and%20Setup.ipynb)." 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": null, 162 | "metadata": {}, 163 | "outputs": [], 164 | "source": [] 165 | } 166 | ], 167 | "metadata": { 168 | "kernelspec": { 169 | "display_name": "Python 3", 170 | "language": "python", 171 | "name": "python3" 172 | }, 173 | "language_info": { 174 | "codemirror_mode": { 175 | "name": "ipython", 176 | "version": 3 177 | }, 178 | "file_extension": ".py", 179 | "mimetype": "text/x-python", 180 | "name": "python", 181 | "nbconvert_exporter": "python", 182 | "pygments_lexer": "ipython3", 183 | "version": "3.7.6" 184 | } 185 | }, 186 | "nbformat": 4, 187 | "nbformat_minor": 4 188 | } 189 | -------------------------------------------------------------------------------- /tutorial/11 - Running Bokeh Applications.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\n", 8 | " \n", 9 | " \n", 17 | " \n", 20 | " \n", 21 | "
\n", 10 | " \n", 11 | " \n", 15 | " \n", 16 | " \n", 18 | "

Bokeh Tutorial

\n", 19 | "
\n", 22 | "\n", 23 | "

11. Running Bokeh Applications

" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "The architecture of Bokeh is such that high-level “model objects” (representing things like plots, ranges, axes, glyphs, etc.) are created in Python, and then converted to a JSON format that is consumed by the client library, BokehJS. Using the Bokeh Server, it is possible to keep the “model objects” in python and in the browser in sync with one another, creating powerful capabilities:\n", 31 | "\n", 32 | "* respond to UI and tool events generated in a browser with computations or queries using the full power of python\n", 33 | "* automatically push updates the UI (i.e. widgets or plots), in a browser\n", 34 | "* use periodic, timeout, and asychronous callbacks drive streaming updates\n", 35 | "\n", 36 | "***This capability to synchronize between python and the browser is the main purpose of the Bokeh Server.***" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "from bokeh.io import output_notebook, show\n", 46 | "output_notebook()" 47 | ] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "metadata": {}, 52 | "source": [ 53 | "## Bokeh Apps in Notebooks\n", 54 | "\n", 55 | "The easiest way to embed a Bokeh application in a notebook is to make a function `modify_doc(doc)` that creates Bokeh content, and adds it to the document. This function can be passed to `show`, and the app defined by the function will be displayed inline. A short complete example is below" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [ 64 | "from bokeh.layouts import column\n", 65 | "from bokeh.models import TextInput, Button, Paragraph\n", 66 | "\n", 67 | "def modify_doc(doc):\n", 68 | " \n", 69 | " # create some widgets\n", 70 | " button = Button(label=\"Say HI\")\n", 71 | " input = TextInput(value=\"Bokeh\")\n", 72 | " output = Paragraph()\n", 73 | "\n", 74 | " # add a callback to a widget\n", 75 | " def update():\n", 76 | " output.text = \"Hello, \" + input.value\n", 77 | " button.on_click(update)\n", 78 | "\n", 79 | " # create a layout for everything\n", 80 | " layout = column(button, input, output)\n", 81 | "\n", 82 | " # add the layout to curdoc\n", 83 | " doc.add_root(layout)\n", 84 | " \n", 85 | "# In the notebook, just pass the function that defines the app to show\n", 86 | "# You may need to supply notebook_url, e.g notebook_url=\"http://localhost:8889\" \n", 87 | "show(modify_doc) " 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "metadata": {}, 94 | "outputs": [], 95 | "source": [ 96 | "# EXERCISE: add a Select widget to this example that offers several different greetings\n", 97 | "\n" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "## Bokeh Apps with `bokeh serve`\n", 105 | "\n", 106 | "It's also possible to define Bokeh applications by creating a standard Python script. In this case, there is no need to make a function like `modify_doc`. Typically, the script should simply create all the bokeh cotent, then add it to the doc with a line like\n", 107 | "```python\n", 108 | "curdoc().add_root(layout)\n", 109 | "```\n", 110 | "\n", 111 | "To try out the example below, copy the code into a file ``hello.py`` and then execute:\n", 112 | "```bash\n", 113 | "bokeh serve --show hello.py \n", 114 | "```\n", 115 | "\n", 116 | "
NOTE: The exercise below require work outside the notebook
" 117 | ] 118 | }, 119 | { 120 | "cell_type": "markdown", 121 | "metadata": {}, 122 | "source": [ 123 | "```python\n", 124 | "# hello.py \n", 125 | "\n", 126 | "from bokeh.io import curdoc\n", 127 | "from bokeh.layouts import column\n", 128 | "from bokeh.models.widgets import TextInput, Button, Paragraph\n", 129 | "\n", 130 | "# create some widgets\n", 131 | "button = Button(label=\"Say HI\")\n", 132 | "input = TextInput(value=\"Bokeh\")\n", 133 | "output = Paragraph()\n", 134 | "\n", 135 | "# add a callback to a widget\n", 136 | "def update():\n", 137 | " output.text = \"Hello, \" + input.value\n", 138 | "button.on_click(update)\n", 139 | "\n", 140 | "# create a layout for everything\n", 141 | "layout = column(button, input, output)\n", 142 | "\n", 143 | "# add the layout to curdoc\n", 144 | "curdoc().add_root(layout)\n", 145 | "```" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": {}, 151 | "source": [ 152 | "Copy this code to a script `hello.py` and run it with the Bokeh server." 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": {}, 158 | "source": [ 159 | "## Linking Plots and Widgets\n", 160 | "\n", 161 | "Lets take a look at a more involved example that links several widgets to a plot. " 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": null, 167 | "metadata": {}, 168 | "outputs": [], 169 | "source": [ 170 | "from numpy.random import random\n", 171 | "\n", 172 | "from bokeh.layouts import column, row\n", 173 | "from bokeh.plotting import figure\n", 174 | "from bokeh.models import ColumnDataSource, Select, TextInput\n", 175 | "\n", 176 | "def get_data(N):\n", 177 | " return dict(x=random(size=N), y=random(size=N), r=random(size=N) * 0.03)\n", 178 | "\n", 179 | "COLORS = [\"black\", \"firebrick\", \"navy\", \"olive\", \"goldenrod\"]\n", 180 | "\n", 181 | "def modify_doc(doc):\n", 182 | " source = ColumnDataSource(data=get_data(200))\n", 183 | "\n", 184 | " p = figure(tools=\"\", toolbar_location=None)\n", 185 | " r = p.circle(x='x', y='y', radius='r', source=source,\n", 186 | " color=\"navy\", alpha=0.6, line_color=\"white\")\n", 187 | "\n", 188 | " \n", 189 | " select = Select(title=\"Color\", value=\"navy\", options=COLORS)\n", 190 | " input = TextInput(title=\"Number of points\", value=\"200\")\n", 191 | "\n", 192 | " def update_color(attrname, old, new):\n", 193 | " r.glyph.fill_color = select.value\n", 194 | " select.on_change('value', update_color)\n", 195 | "\n", 196 | " def update_points(attrname, old, new):\n", 197 | " N = int(input.value)\n", 198 | " source.data = get_data(N)\n", 199 | " input.on_change('value', update_points)\n", 200 | "\n", 201 | " layout = column(row(select, input, width=400), row(p))\n", 202 | "\n", 203 | " doc.add_root(layout)\n", 204 | "\n", 205 | "show(modify_doc)" 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": null, 211 | "metadata": {}, 212 | "outputs": [], 213 | "source": [ 214 | "# EXERCISE: add more widgets to change more aspects of this plot\n" 215 | ] 216 | }, 217 | { 218 | "cell_type": "markdown", 219 | "metadata": {}, 220 | "source": [ 221 | "# Streaming Data\n", 222 | "\n", 223 | "It is possible to efficiently stream new data to column data sources by using the ``stream`` method. This method accepts two argmuments:\n", 224 | "* ``new_data`` — a dictionary with the same structure as the column data source\n", 225 | "* ``rollover`` — a maximum column length on the client (earlier data is dropped) *[optional]*\n", 226 | "\n", 227 | "If no ``rollover`` is specified, data is never dropped on the client and columns grow without bound.\n", 228 | "\n", 229 | "It is often useful to use periodic callbacks in conjuction with streaming data The ``add_periodic_callback`` method of ``curdoc()`` accepts a callback function, and a time interval (in ms) to repeatedly execute the callback. \n" 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": null, 235 | "metadata": {}, 236 | "outputs": [], 237 | "source": [ 238 | "from math import cos, sin\n", 239 | "\n", 240 | "from bokeh.models import ColumnDataSource\n", 241 | "\n", 242 | "def modify_doc(doc):\n", 243 | " p = figure(match_aspect=True)\n", 244 | " p.circle(x=0, y=0, radius=1, fill_color=None, line_width=2)\n", 245 | " \n", 246 | " # this is just to help the auto-datarange\n", 247 | " p.rect(0, 0, 2, 2, alpha=0)\n", 248 | "\n", 249 | " # this is the data source we will stream to\n", 250 | " source = ColumnDataSource(data=dict(x=[1], y=[0]))\n", 251 | " p.circle(x='x', y='y', size=12, fill_color='white', source=source)\n", 252 | "\n", 253 | " def update():\n", 254 | " x, y = source.data['x'][-1], source.data['y'][-1]\n", 255 | "\n", 256 | " # construct the new values for all columns, and pass to stream\n", 257 | " new_data = dict(x=[x*cos(0.1) - y*sin(0.1)], y=[x*sin(0.1) + y*cos(0.1)])\n", 258 | " source.stream(new_data, rollover=8)\n", 259 | "\n", 260 | " doc.add_periodic_callback(update, 150)\n", 261 | " doc.add_root(p)\n", 262 | " \n", 263 | "show(modify_doc)" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": null, 269 | "metadata": {}, 270 | "outputs": [], 271 | "source": [ 272 | "### EXERCISE: starting with the above example, create your own streaming plot\n" 273 | ] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "metadata": {}, 278 | "source": [ 279 | "Bokeh column data sources also support a `patch` method that can be used to efficiently update subsets of data." 280 | ] 281 | }, 282 | { 283 | "cell_type": "markdown", 284 | "metadata": {}, 285 | "source": [ 286 | "## Directory Format Apps and Templates\n", 287 | "\n", 288 | "Bokeh apps can also be defined with a directory format. This format affords the use of extra modules, data files, templates, theme files, and other features. The directory should contain a `main.py` which is the \"entry point\" for the app, but ay contain extra parts:\n", 289 | "```\n", 290 | "myapp\n", 291 | " |\n", 292 | " +---main.py\n", 293 | " +---server_lifecycle.py\n", 294 | " +---static\n", 295 | " +---theme.yaml\n", 296 | " +---templates\n", 297 | " +---index.html\n", 298 | "```\n", 299 | "The [Directory Format](https://bokeh.pydata.org/en/latest/docs/user_guide/server.html#directory-format) section of the User's Guide has more information. \n", 300 | "\n", 301 | "See a complete sophisticated example at: https://github.com/bokeh/bokeh/tree/master/examples/app/dash\n", 302 | "\n", 303 | "" 304 | ] 305 | }, 306 | { 307 | "cell_type": "markdown", 308 | "metadata": {}, 309 | "source": [ 310 | "## Tips and Tricks\n", 311 | "\n", 312 | " \n", 313 | "* Real Python callbacks *require* a Bokeh server application. They cannot work with `output_file`, `components` or other functions that generate standalone output. Standalone content can only use `CustomJS` callbacks. \n", 314 | "\n", 315 | "\n", 316 | "* Try to update data sources \"all at once\" whenever possible, i.e. prefer this:\n", 317 | " ```python\n", 318 | " source.data = new_data_dict # GOOD\n", 319 | " ```\n", 320 | " rather then updating individual columns sequentially:\n", 321 | " ```python\n", 322 | " # LESS GOOD\n", 323 | " source.data['foo'] = new_foo_column\n", 324 | " source.data['bar'] = new_bar_column \n", 325 | " ```\n", 326 | " If the new columns are exactly the same length as the old ones, then updating sequentially will trigger extra updates and may result in bad visual effects. \n", 327 | " If the new columns are a different length than the old ones, then updating \"all at once\" is **mandatory**.\n", 328 | " \n", 329 | "\n", 330 | "* Each time a session is started, the Bokeh server runs the script (or `modify_doc`) function, and the code that is run ***must return completely new Bokeh objects every time***. It is not possible to share Bokeh objects between sessions. As a concrete example, this is what NOT to do:\n", 331 | " ```python\n", 332 | " source = ColumnDataSource(data) # VERY BAD - global outside modify_doc\n", 333 | " \n", 334 | " def modify_doc(doc):\n", 335 | " p = figure()\n", 336 | " p.circle('x', 'y', source=source)\n", 337 | " doc.add_root(p)\n", 338 | " \n", 339 | " ```\n", 340 | " The analogous situation would occur with a script if the script imports a global Bokeh object from a separate module (due to the way Python caches imports)." 341 | ] 342 | }, 343 | { 344 | "cell_type": "markdown", 345 | "metadata": {}, 346 | "source": [ 347 | "# Next Section" 348 | ] 349 | }, 350 | { 351 | "cell_type": "markdown", 352 | "metadata": {}, 353 | "source": [ 354 | "This is the last section of the main tutorial. To explore some extra topics, see the appendices:\n", 355 | "\n", 356 | "[A1 - Models and Primitives](A1%20-%20Models%20and%20Primitives.ipynb)
\n", 357 | "[A2 - Visualizing Big Data with Datashader](A2%20-%20Visualizing%20Big%20Data%20with%20Datashader.ipynb)
\n", 358 | "[A3 - High-Level Charting with Holoviews](A3%20-%20High-Level%20Charting%20with%20Holoviews.ipynb)
\n", 359 | "[A4 - Additional Resources](A4%20-%20Additional%20Resources.ipynb)\n", 360 | "\n", 361 | "To go back to the overview, click [here](00%20-%20Introduction%20and%20Setup.ipynb)." 362 | ] 363 | }, 364 | { 365 | "cell_type": "code", 366 | "execution_count": null, 367 | "metadata": {}, 368 | "outputs": [], 369 | "source": [] 370 | } 371 | ], 372 | "metadata": { 373 | "kernelspec": { 374 | "display_name": "Python 3", 375 | "language": "python", 376 | "name": "python3" 377 | }, 378 | "language_info": { 379 | "codemirror_mode": { 380 | "name": "ipython", 381 | "version": 3 382 | }, 383 | "file_extension": ".py", 384 | "mimetype": "text/x-python", 385 | "name": "python", 386 | "nbconvert_exporter": "python", 387 | "pygments_lexer": "ipython3", 388 | "version": "3.8.2" 389 | } 390 | }, 391 | "nbformat": 4, 392 | "nbformat_minor": 4 393 | } 394 | -------------------------------------------------------------------------------- /tutorial/assets/document.svg: -------------------------------------------------------------------------------- 1 | 2 |
Document
<font style="font-size: 39px">Document</font>
roots
[Not supported by viewer]

Select

[Not supported by viewer]

Slider



[Not supported by viewer]
Plot
Plot
+ tools
+ tools
+ renderers
+ renderers

PanTool
[Not supported by viewer]

SaveTool
[Not supported by viewer]
Axis
Axis
+ formatter
+ formatter
+ ticker
+ ticker
GlyphRenderer

[Not supported by viewer]
+ glyph
+ glyph<br>
+ hover_glyph

+ selection_glyph

+ nonselection_glyph
[Not supported by viewer]


BasicTicker

[Not supported by viewer]


BasicTickformatter

[Not supported by viewer]

Circle
[Not supported by viewer]

Circle
[Not supported by viewer]

Circle
[Not supported by viewer]

Circle
[Not supported by viewer]

ResetTool
[Not supported by viewer]
Grid
Grid
+ ticker
+ ticker
Column
Column
+ children
+ children
-------------------------------------------------------------------------------- /tutorial/08 - Graph and Network Plots.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\n", 8 | " \n", 9 | " \n", 17 | " \n", 20 | " \n", 21 | "
\n", 10 | " \n", 11 | " \n", 15 | " \n", 16 | " \n", 18 | "

Bokeh Tutorial

\n", 19 | "
\n", 22 | "\n", 23 | "

08. Graph and Network Plots

" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "This chapter will cover how to plot network node/link graphs in Bokeh using NetwortkX. For information on creating graph renderers from a low level, see [Visualizing Network Graphs](https://docs.bokeh.org/en/latest/docs/user_guide/graph.html)\n" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 1, 36 | "metadata": {}, 37 | "outputs": [ 38 | { 39 | "data": { 40 | "text/html": [ 41 | "\n", 42 | "
\n", 43 | " \n", 44 | " Loading BokehJS ...\n", 45 | "
" 46 | ] 47 | }, 48 | "metadata": {}, 49 | "output_type": "display_data" 50 | }, 51 | { 52 | "data": { 53 | "application/javascript": [ 54 | "\n", 55 | "(function(root) {\n", 56 | " function now() {\n", 57 | " return new Date();\n", 58 | " }\n", 59 | "\n", 60 | " var force = true;\n", 61 | "\n", 62 | " if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n", 63 | " root._bokeh_onload_callbacks = [];\n", 64 | " root._bokeh_is_loading = undefined;\n", 65 | " }\n", 66 | "\n", 67 | " var JS_MIME_TYPE = 'application/javascript';\n", 68 | " var HTML_MIME_TYPE = 'text/html';\n", 69 | " var EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n", 70 | " var CLASS_NAME = 'output_bokeh rendered_html';\n", 71 | "\n", 72 | " /**\n", 73 | " * Render data to the DOM node\n", 74 | " */\n", 75 | " function render(props, node) {\n", 76 | " var script = document.createElement(\"script\");\n", 77 | " node.appendChild(script);\n", 78 | " }\n", 79 | "\n", 80 | " /**\n", 81 | " * Handle when an output is cleared or removed\n", 82 | " */\n", 83 | " function handleClearOutput(event, handle) {\n", 84 | " var cell = handle.cell;\n", 85 | "\n", 86 | " var id = cell.output_area._bokeh_element_id;\n", 87 | " var server_id = cell.output_area._bokeh_server_id;\n", 88 | " // Clean up Bokeh references\n", 89 | " if (id != null && id in Bokeh.index) {\n", 90 | " Bokeh.index[id].model.document.clear();\n", 91 | " delete Bokeh.index[id];\n", 92 | " }\n", 93 | "\n", 94 | " if (server_id !== undefined) {\n", 95 | " // Clean up Bokeh references\n", 96 | " var cmd = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n", 97 | " cell.notebook.kernel.execute(cmd, {\n", 98 | " iopub: {\n", 99 | " output: function(msg) {\n", 100 | " var id = msg.content.text.trim();\n", 101 | " if (id in Bokeh.index) {\n", 102 | " Bokeh.index[id].model.document.clear();\n", 103 | " delete Bokeh.index[id];\n", 104 | " }\n", 105 | " }\n", 106 | " }\n", 107 | " });\n", 108 | " // Destroy server and session\n", 109 | " var cmd = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n", 110 | " cell.notebook.kernel.execute(cmd);\n", 111 | " }\n", 112 | " }\n", 113 | "\n", 114 | " /**\n", 115 | " * Handle when a new output is added\n", 116 | " */\n", 117 | " function handleAddOutput(event, handle) {\n", 118 | " var output_area = handle.output_area;\n", 119 | " var output = handle.output;\n", 120 | "\n", 121 | " // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n", 122 | " if ((output.output_type != \"display_data\") || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", 123 | " return\n", 124 | " }\n", 125 | "\n", 126 | " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", 127 | "\n", 128 | " if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n", 129 | " toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n", 130 | " // store reference to embed id on output_area\n", 131 | " output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", 132 | " }\n", 133 | " if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", 134 | " var bk_div = document.createElement(\"div\");\n", 135 | " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", 136 | " var script_attrs = bk_div.children[0].attributes;\n", 137 | " for (var i = 0; i < script_attrs.length; i++) {\n", 138 | " toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n", 139 | " toinsert[toinsert.length - 1].firstChild.textContent = bk_div.children[0].textContent\n", 140 | " }\n", 141 | " // store reference to server id on output_area\n", 142 | " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", 143 | " }\n", 144 | " }\n", 145 | "\n", 146 | " function register_renderer(events, OutputArea) {\n", 147 | "\n", 148 | " function append_mime(data, metadata, element) {\n", 149 | " // create a DOM node to render to\n", 150 | " var toinsert = this.create_output_subarea(\n", 151 | " metadata,\n", 152 | " CLASS_NAME,\n", 153 | " EXEC_MIME_TYPE\n", 154 | " );\n", 155 | " this.keyboard_manager.register_events(toinsert);\n", 156 | " // Render to node\n", 157 | " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", 158 | " render(props, toinsert[toinsert.length - 1]);\n", 159 | " element.append(toinsert);\n", 160 | " return toinsert\n", 161 | " }\n", 162 | "\n", 163 | " /* Handle when an output is cleared or removed */\n", 164 | " events.on('clear_output.CodeCell', handleClearOutput);\n", 165 | " events.on('delete.Cell', handleClearOutput);\n", 166 | "\n", 167 | " /* Handle when a new output is added */\n", 168 | " events.on('output_added.OutputArea', handleAddOutput);\n", 169 | "\n", 170 | " /**\n", 171 | " * Register the mime type and append_mime function with output_area\n", 172 | " */\n", 173 | " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", 174 | " /* Is output safe? */\n", 175 | " safe: true,\n", 176 | " /* Index of renderer in `output_area.display_order` */\n", 177 | " index: 0\n", 178 | " });\n", 179 | " }\n", 180 | "\n", 181 | " // register the mime type if in Jupyter Notebook environment and previously unregistered\n", 182 | " if (root.Jupyter !== undefined) {\n", 183 | " var events = require('base/js/events');\n", 184 | " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", 185 | "\n", 186 | " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", 187 | " register_renderer(events, OutputArea);\n", 188 | " }\n", 189 | " }\n", 190 | "\n", 191 | " \n", 192 | " if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n", 193 | " root._bokeh_timeout = Date.now() + 5000;\n", 194 | " root._bokeh_failed_load = false;\n", 195 | " }\n", 196 | "\n", 197 | " var NB_LOAD_WARNING = {'data': {'text/html':\n", 198 | " \"
\\n\"+\n", 199 | " \"

\\n\"+\n", 200 | " \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n", 201 | " \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n", 202 | " \"

\\n\"+\n", 203 | " \"\\n\"+\n", 207 | " \"\\n\"+\n", 208 | " \"from bokeh.resources import INLINE\\n\"+\n", 209 | " \"output_notebook(resources=INLINE)\\n\"+\n", 210 | " \"\\n\"+\n", 211 | " \"
\"}};\n", 212 | "\n", 213 | " function display_loaded() {\n", 214 | " var el = document.getElementById(\"1001\");\n", 215 | " if (el != null) {\n", 216 | " el.textContent = \"BokehJS is loading...\";\n", 217 | " }\n", 218 | " if (root.Bokeh !== undefined) {\n", 219 | " if (el != null) {\n", 220 | " el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n", 221 | " }\n", 222 | " } else if (Date.now() < root._bokeh_timeout) {\n", 223 | " setTimeout(display_loaded, 100)\n", 224 | " }\n", 225 | " }\n", 226 | "\n", 227 | "\n", 228 | " function run_callbacks() {\n", 229 | " try {\n", 230 | " root._bokeh_onload_callbacks.forEach(function(callback) {\n", 231 | " if (callback != null)\n", 232 | " callback();\n", 233 | " });\n", 234 | " } finally {\n", 235 | " delete root._bokeh_onload_callbacks\n", 236 | " }\n", 237 | " console.debug(\"Bokeh: all callbacks have finished\");\n", 238 | " }\n", 239 | "\n", 240 | " function load_libs(css_urls, js_urls, callback) {\n", 241 | " if (css_urls == null) css_urls = [];\n", 242 | " if (js_urls == null) js_urls = [];\n", 243 | "\n", 244 | " root._bokeh_onload_callbacks.push(callback);\n", 245 | " if (root._bokeh_is_loading > 0) {\n", 246 | " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", 247 | " return null;\n", 248 | " }\n", 249 | " if (js_urls == null || js_urls.length === 0) {\n", 250 | " run_callbacks();\n", 251 | " return null;\n", 252 | " }\n", 253 | " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", 254 | " root._bokeh_is_loading = css_urls.length + js_urls.length;\n", 255 | "\n", 256 | " function on_load() {\n", 257 | " root._bokeh_is_loading--;\n", 258 | " if (root._bokeh_is_loading === 0) {\n", 259 | " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", 260 | " run_callbacks()\n", 261 | " }\n", 262 | " }\n", 263 | "\n", 264 | " function on_error() {\n", 265 | " console.error(\"failed to load \" + url);\n", 266 | " }\n", 267 | "\n", 268 | " for (var i = 0; i < css_urls.length; i++) {\n", 269 | " var url = css_urls[i];\n", 270 | " const element = document.createElement(\"link\");\n", 271 | " element.onload = on_load;\n", 272 | " element.onerror = on_error;\n", 273 | " element.rel = \"stylesheet\";\n", 274 | " element.type = \"text/css\";\n", 275 | " element.href = url;\n", 276 | " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", 277 | " document.body.appendChild(element);\n", 278 | " }\n", 279 | "\n", 280 | " const hashes = {\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\": \"JpP8FXbgAZLkfur7LiK3j9AGBhHNIvF742meBJrjO2ShJDhCG2I1uVvW+0DUtrmc\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\": \"xZlADit0Q04ISQEdKg2k3L4W9AwQBAuDs9nJL9fM/WwzL1tEU9VPNezOFX0nLEAz\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\": \"4BuPRZkdMKSnj3zoxiNrQ86XgNw0rYmBOxe7nshquXwwcauupgBF2DHLVG1WuZlV\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\": \"Dv1SQ87hmDqK6S5OhBf0bCuwAEvL5QYL0PuR/F1SPVhCS/r/abjkbpKDYL2zeM19\"};\n", 281 | "\n", 282 | " for (var i = 0; i < js_urls.length; i++) {\n", 283 | " var url = js_urls[i];\n", 284 | " var element = document.createElement('script');\n", 285 | " element.onload = on_load;\n", 286 | " element.onerror = on_error;\n", 287 | " element.async = false;\n", 288 | " element.src = url;\n", 289 | " if (url in hashes) {\n", 290 | " element.crossOrigin = \"anonymous\";\n", 291 | " element.integrity = \"sha384-\" + hashes[url];\n", 292 | " }\n", 293 | " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", 294 | " document.head.appendChild(element);\n", 295 | " }\n", 296 | " };var element = document.getElementById(\"1001\");\n", 297 | " if (element == null) {\n", 298 | " console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1001' but no matching script tag was found. \")\n", 299 | " return false;\n", 300 | " }\n", 301 | "\n", 302 | " function inject_raw_css(css) {\n", 303 | " const element = document.createElement(\"style\");\n", 304 | " element.appendChild(document.createTextNode(css));\n", 305 | " document.body.appendChild(element);\n", 306 | " }\n", 307 | "\n", 308 | " \n", 309 | " var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\"];\n", 310 | " var css_urls = [];\n", 311 | " \n", 312 | "\n", 313 | " var inline_js = [\n", 314 | " function(Bokeh) {\n", 315 | " Bokeh.set_log_level(\"info\");\n", 316 | " },\n", 317 | " function(Bokeh) {\n", 318 | " \n", 319 | " \n", 320 | " }\n", 321 | " ];\n", 322 | "\n", 323 | " function run_inline_js() {\n", 324 | " \n", 325 | " if (root.Bokeh !== undefined || force === true) {\n", 326 | " \n", 327 | " for (var i = 0; i < inline_js.length; i++) {\n", 328 | " inline_js[i].call(root, root.Bokeh);\n", 329 | " }\n", 330 | " if (force === true) {\n", 331 | " display_loaded();\n", 332 | " }} else if (Date.now() < root._bokeh_timeout) {\n", 333 | " setTimeout(run_inline_js, 100);\n", 334 | " } else if (!root._bokeh_failed_load) {\n", 335 | " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", 336 | " root._bokeh_failed_load = true;\n", 337 | " } else if (force !== true) {\n", 338 | " var cell = $(document.getElementById(\"1001\")).parents('.cell').data().cell;\n", 339 | " cell.output_area.append_execute_result(NB_LOAD_WARNING)\n", 340 | " }\n", 341 | "\n", 342 | " }\n", 343 | "\n", 344 | " if (root._bokeh_is_loading === 0) {\n", 345 | " console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n", 346 | " run_inline_js();\n", 347 | " } else {\n", 348 | " load_libs(css_urls, js_urls, function() {\n", 349 | " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", 350 | " run_inline_js();\n", 351 | " });\n", 352 | " }\n", 353 | "}(window));" 354 | ], 355 | "application/vnd.bokehjs_load.v0+json": "\n(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n\n if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n root._bokeh_onload_callbacks = [];\n root._bokeh_is_loading = undefined;\n }\n\n \n\n \n if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n var NB_LOAD_WARNING = {'data': {'text/html':\n \"
\\n\"+\n \"

\\n\"+\n \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n \"

\\n\"+\n \"\\n\"+\n \"\\n\"+\n \"from bokeh.resources import INLINE\\n\"+\n \"output_notebook(resources=INLINE)\\n\"+\n \"\\n\"+\n \"
\"}};\n\n function display_loaded() {\n var el = document.getElementById(\"1001\");\n if (el != null) {\n el.textContent = \"BokehJS is loading...\";\n }\n if (root.Bokeh !== undefined) {\n if (el != null) {\n el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(display_loaded, 100)\n }\n }\n\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n\n root._bokeh_onload_callbacks.push(callback);\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls == null || js_urls.length === 0) {\n run_callbacks();\n return null;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n root._bokeh_is_loading = css_urls.length + js_urls.length;\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n\n function on_error() {\n console.error(\"failed to load \" + url);\n }\n\n for (var i = 0; i < css_urls.length; i++) {\n var url = css_urls[i];\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n }\n\n const hashes = {\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\": \"JpP8FXbgAZLkfur7LiK3j9AGBhHNIvF742meBJrjO2ShJDhCG2I1uVvW+0DUtrmc\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\": \"xZlADit0Q04ISQEdKg2k3L4W9AwQBAuDs9nJL9fM/WwzL1tEU9VPNezOFX0nLEAz\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\": \"4BuPRZkdMKSnj3zoxiNrQ86XgNw0rYmBOxe7nshquXwwcauupgBF2DHLVG1WuZlV\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\": \"Dv1SQ87hmDqK6S5OhBf0bCuwAEvL5QYL0PuR/F1SPVhCS/r/abjkbpKDYL2zeM19\"};\n\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n if (url in hashes) {\n element.crossOrigin = \"anonymous\";\n element.integrity = \"sha384-\" + hashes[url];\n }\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n };var element = document.getElementById(\"1001\");\n if (element == null) {\n console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1001' but no matching script tag was found. \")\n return false;\n }\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n \n var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\"];\n var css_urls = [];\n \n\n var inline_js = [\n function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\n function(Bokeh) {\n \n \n }\n ];\n\n function run_inline_js() {\n \n if (root.Bokeh !== undefined || force === true) {\n \n for (var i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }\n if (force === true) {\n display_loaded();\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n } else if (force !== true) {\n var cell = $(document.getElementById(\"1001\")).parents('.cell').data().cell;\n cell.output_area.append_execute_result(NB_LOAD_WARNING)\n }\n\n }\n\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n run_inline_js();\n } else {\n load_libs(css_urls, js_urls, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n}(window));" 356 | }, 357 | "metadata": {}, 358 | "output_type": "display_data" 359 | } 360 | ], 361 | "source": [ 362 | "from bokeh.io import show, output_notebook\n", 363 | "from bokeh.plotting import figure\n", 364 | "\n", 365 | "output_notebook()" 366 | ] 367 | }, 368 | { 369 | "cell_type": "markdown", 370 | "metadata": {}, 371 | "source": [ 372 | "## Plotting from NetworkX\n", 373 | "\n", 374 | "The easiest way to plot network graphs with Bokeh is to use the `from_networkx` function. This function accepts any NetworkX graph and returns a Bokeh `GraphRenderer` that can be added to a plot. The `GraphRenderer` has `node_renderer` and `edge_renderer` properties that contain the Bokeh renderers that draw the nodes and edges, respectively. \n", 375 | "\n", 376 | "The example below shows a Bokeh plot of `nx.desargues_graph()`, setting some of the node and edge properties." 377 | ] 378 | }, 379 | { 380 | "cell_type": "code", 381 | "execution_count": 2, 382 | "metadata": {}, 383 | "outputs": [ 384 | { 385 | "data": { 386 | "text/html": [ 387 | "\n", 388 | "\n", 389 | "\n", 390 | "\n", 391 | "\n", 392 | "\n", 393 | "
\n" 394 | ] 395 | }, 396 | "metadata": {}, 397 | "output_type": "display_data" 398 | }, 399 | { 400 | "data": { 401 | "application/javascript": [ 402 | "(function(root) {\n", 403 | " function embed_document(root) {\n", 404 | " \n", 405 | " var docs_json = {\"d4dbe452-03f6-4209-8bc7-57d2c5e76be5\":{\"roots\":{\"references\":[{\"attributes\":{\"renderers\":[{\"id\":\"1007\"}],\"title\":{\"id\":\"1026\"},\"toolbar\":{\"id\":\"1028\"},\"x_range\":{\"id\":\"1002\"},\"x_scale\":{\"id\":\"1025\"},\"y_range\":{\"id\":\"1003\"},\"y_scale\":{\"id\":\"1027\"}},\"id\":\"1004\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1042\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1041\",\"type\":\"UnionRenderers\"},{\"attributes\":{},\"id\":\"1025\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1029\",\"type\":\"NodesOnly\"},{\"attributes\":{\"end\":2,\"start\":-2},\"id\":\"1003\",\"type\":\"Range1d\"},{\"attributes\":{\"end\":2,\"start\":-2},\"id\":\"1002\",\"type\":\"Range1d\"},{\"attributes\":{},\"id\":\"1027\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1038\",\"type\":\"NodesOnly\"},{\"attributes\":{\"data\":{\"end\":[1,19,5,2,16,3,11,4,14,5,9,6,7,15,8,18,9,13,10,11,19,12,13,17,14,15,16,17,18,19],\"start\":[0,0,0,1,1,2,2,3,3,4,4,5,6,6,7,7,8,8,9,10,10,11,12,12,13,14,15,16,17,18]},\"selected\":{\"id\":\"1040\"},\"selection_policy\":{\"id\":\"1039\"}},\"id\":\"1013\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"data_source\":{\"id\":\"1013\"},\"glyph\":{\"id\":\"1012\"},\"hover_glyph\":null,\"muted_glyph\":null,\"view\":{\"id\":\"1015\"}},\"id\":\"1014\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"fill_color\":{\"value\":\"orange\"},\"size\":{\"units\":\"screen\",\"value\":20}},\"id\":\"1008\",\"type\":\"Circle\"},{\"attributes\":{\"line_dash\":[2,2]},\"id\":\"1012\",\"type\":\"MultiLine\"},{\"attributes\":{\"graph_layout\":{\"0\":[-1.1042342270583827,-1.1358203033156933],\"1\":[-0.32528538882698,-1.7994753213588925],\"10\":[-1.625670493253707,0.8619054556698852],\"11\":[-0.9937202605143635,0.18131834718760223],\"12\":[0.22787948925992138,0.36722672691041464],\"13\":[1.1168440806216449,1.1183473694056227],\"14\":[1.6157242120675364,0.1547889930386424],\"15\":[1.61261258064872,-0.8611365542446111],\"16\":[0.7754992820567755,-1.6394034299170748],\"17\":[0.3146902597398338,-0.7525670875693947],\"18\":[-0.49184158969092406,0.06231601538197338],\"19\":[-1.616014837453719,-0.17249304477551114],\"2\":[-0.4659406014608107,-0.8636140545286619],\"3\":[0.4815995343541902,-0.0836919751599053],\"4\":[-0.31234089232066437,0.7291976828651057],\"5\":[-0.19796639546974296,-0.3605636780163431],\"6\":[0.9946709677016442,-0.11662921985543172],\"7\":[0.4435326518309118,0.8939251155381592],\"8\":[0.3302811789520167,1.8],\"9\":[-0.7803195511838985,1.6163689627441111]}},\"id\":\"1016\",\"type\":\"StaticLayoutProvider\"},{\"attributes\":{\"data\":{\"index\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]},\"selected\":{\"id\":\"1042\"},\"selection_policy\":{\"id\":\"1041\"}},\"id\":\"1009\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"source\":{\"id\":\"1009\"}},\"id\":\"1011\",\"type\":\"CDSView\"},{\"attributes\":{\"source\":{\"id\":\"1013\"}},\"id\":\"1015\",\"type\":\"CDSView\"},{\"attributes\":{\"text\":\"\"},\"id\":\"1026\",\"type\":\"Title\"},{\"attributes\":{\"edge_renderer\":{\"id\":\"1014\"},\"inspection_policy\":{\"id\":\"1029\"},\"layout_provider\":{\"id\":\"1016\"},\"node_renderer\":{\"id\":\"1010\"},\"selection_policy\":{\"id\":\"1038\"}},\"id\":\"1007\",\"type\":\"GraphRenderer\"},{\"attributes\":{},\"id\":\"1039\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\"},\"id\":\"1028\",\"type\":\"Toolbar\"},{\"attributes\":{},\"id\":\"1040\",\"type\":\"Selection\"},{\"attributes\":{\"data_source\":{\"id\":\"1009\"},\"glyph\":{\"id\":\"1008\"},\"hover_glyph\":null,\"muted_glyph\":null,\"view\":{\"id\":\"1011\"}},\"id\":\"1010\",\"type\":\"GlyphRenderer\"}],\"root_ids\":[\"1004\"]},\"title\":\"Bokeh Application\",\"version\":\"2.0.1\"}};\n", 406 | " var render_items = [{\"docid\":\"d4dbe452-03f6-4209-8bc7-57d2c5e76be5\",\"root_ids\":[\"1004\"],\"roots\":{\"1004\":\"287e2448-5aa8-4de4-879b-11b6f8280e46\"}}];\n", 407 | " root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n", 408 | "\n", 409 | " }\n", 410 | " if (root.Bokeh !== undefined) {\n", 411 | " embed_document(root);\n", 412 | " } else {\n", 413 | " var attempts = 0;\n", 414 | " var timer = setInterval(function(root) {\n", 415 | " if (root.Bokeh !== undefined) {\n", 416 | " clearInterval(timer);\n", 417 | " embed_document(root);\n", 418 | " } else {\n", 419 | " attempts++;\n", 420 | " if (attempts > 100) {\n", 421 | " clearInterval(timer);\n", 422 | " console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n", 423 | " }\n", 424 | " }\n", 425 | " }, 10, root)\n", 426 | " }\n", 427 | "})(window);" 428 | ], 429 | "application/vnd.bokehjs_exec.v0+json": "" 430 | }, 431 | "metadata": { 432 | "application/vnd.bokehjs_exec.v0+json": { 433 | "id": "1004" 434 | } 435 | }, 436 | "output_type": "display_data" 437 | } 438 | ], 439 | "source": [ 440 | "import networkx as nx\n", 441 | "from bokeh.models import Range1d, Plot\n", 442 | "from bokeh.plotting import from_networkx\n", 443 | "\n", 444 | "G = nx.desargues_graph()\n", 445 | "\n", 446 | "# We could use figure here but don't want all the axes and titles\n", 447 | "plot = Plot(x_range=Range1d(-2, 2), y_range=Range1d(-2, 2))\n", 448 | "\n", 449 | "# Create a Bokeh graph from the NetworkX input using nx.spring_layout\n", 450 | "graph = from_networkx(G, nx.spring_layout, scale=1.8, center=(0,0))\n", 451 | "plot.renderers.append(graph)\n", 452 | "\n", 453 | "# Set some of the default node glyph (Circle) properties\n", 454 | "graph.node_renderer.glyph.update(size=20, fill_color=\"orange\")\n", 455 | "\n", 456 | "# Set some edge properties too\n", 457 | "graph.edge_renderer.glyph.line_dash = [2,2]\n", 458 | "\n", 459 | "show(plot)" 460 | ] 461 | }, 462 | { 463 | "cell_type": "code", 464 | "execution_count": 3, 465 | "metadata": {}, 466 | "outputs": [], 467 | "source": [ 468 | "# Exercise: try a different NetworkX layout, and set some properies on `graph.edge_renderer.glyph` \n", 469 | "# and `graph.node_renderer.glyph`\n" 470 | ] 471 | }, 472 | { 473 | "cell_type": "markdown", 474 | "metadata": {}, 475 | "source": [ 476 | "## Adding Extra Data Columns.\n", 477 | "\n", 478 | "The `node_renderer` and `edge_renderer` properties of the graph renderer each have a `data_source` that is a standard `ColumnDataSource` that you can add new data to, e.g. to drive a hover tool, or to specify colors for the renderer. The example below demonstates both." 479 | ] 480 | }, 481 | { 482 | "cell_type": "code", 483 | "execution_count": 4, 484 | "metadata": {}, 485 | "outputs": [ 486 | { 487 | "data": { 488 | "text/html": [ 489 | "\n", 490 | "\n", 491 | "\n", 492 | "\n", 493 | "\n", 494 | "\n", 495 | "
\n" 496 | ] 497 | }, 498 | "metadata": {}, 499 | "output_type": "display_data" 500 | }, 501 | { 502 | "data": { 503 | "application/javascript": [ 504 | "(function(root) {\n", 505 | " function embed_document(root) {\n", 506 | " \n", 507 | " var docs_json = {\"e8d65793-b5c5-4264-a983-2cf19d800507\":{\"roots\":{\"references\":[{\"attributes\":{\"renderers\":[{\"id\":\"1176\"}],\"title\":{\"id\":\"1219\"},\"toolbar\":{\"id\":\"1199\"},\"x_range\":{\"id\":\"1171\"},\"x_scale\":{\"id\":\"1218\"},\"y_range\":{\"id\":\"1172\"},\"y_scale\":{\"id\":\"1220\"}},\"id\":\"1173\",\"type\":\"Plot\"},{\"attributes\":{\"data_source\":{\"id\":\"1182\"},\"glyph\":{\"id\":\"1181\"},\"hover_glyph\":null,\"muted_glyph\":null,\"view\":{\"id\":\"1184\"}},\"id\":\"1183\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"fill_color\":{\"field\":\"colors\"},\"size\":{\"units\":\"screen\",\"value\":20}},\"id\":\"1177\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"1218\",\"type\":\"LinearScale\"},{\"attributes\":{\"edge_renderer\":{\"id\":\"1183\"},\"inspection_policy\":{\"id\":\"1222\"},\"layout_provider\":{\"id\":\"1185\"},\"node_renderer\":{\"id\":\"1179\"},\"selection_policy\":{\"id\":\"1231\"}},\"id\":\"1176\",\"type\":\"GraphRenderer\"},{\"attributes\":{},\"id\":\"1234\",\"type\":\"UnionRenderers\"},{\"attributes\":{},\"id\":\"1235\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1220\",\"type\":\"LinearScale\"},{\"attributes\":{\"text\":\"\"},\"id\":\"1219\",\"type\":\"Title\"},{\"attributes\":{},\"id\":\"1222\",\"type\":\"NodesOnly\"},{\"attributes\":{\"data\":{\"end\":[1,19,5,2,16,3,11,4,14,5,9,6,7,15,8,18,9,13,10,11,19,12,13,17,14,15,16,17,18,19],\"start\":[0,0,0,1,1,2,2,3,3,4,4,5,6,6,7,7,8,8,9,10,10,11,12,12,13,14,15,16,17,18]},\"selected\":{\"id\":\"1233\"},\"selection_policy\":{\"id\":\"1232\"}},\"id\":\"1182\",\"type\":\"ColumnDataSource\"},{\"attributes\":{},\"id\":\"1231\",\"type\":\"NodesOnly\"},{\"attributes\":{\"callback\":null,\"tooltips\":\"index: @index\"},\"id\":\"1198\",\"type\":\"HoverTool\"},{\"attributes\":{\"data_source\":{\"id\":\"1178\"},\"glyph\":{\"id\":\"1177\"},\"hover_glyph\":null,\"muted_glyph\":null,\"view\":{\"id\":\"1180\"}},\"id\":\"1179\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"end\":2,\"start\":-2},\"id\":\"1171\",\"type\":\"Range1d\"},{\"attributes\":{\"data\":{\"colors\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#ffbb78\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\",\"#8c564b\",\"#c49c94\",\"#e377c2\",\"#f7b6d2\",\"#7f7f7f\",\"#c7c7c7\",\"#bcbd22\",\"#dbdb8d\",\"#17becf\",\"#9edae5\"],\"index\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]},\"selected\":{\"id\":\"1235\"},\"selection_policy\":{\"id\":\"1234\"}},\"id\":\"1178\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"end\":2,\"start\":-2},\"id\":\"1172\",\"type\":\"Range1d\"},{\"attributes\":{\"source\":{\"id\":\"1178\"}},\"id\":\"1180\",\"type\":\"CDSView\"},{\"attributes\":{\"source\":{\"id\":\"1182\"}},\"id\":\"1184\",\"type\":\"CDSView\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1198\"}]},\"id\":\"1199\",\"type\":\"Toolbar\"},{\"attributes\":{},\"id\":\"1181\",\"type\":\"MultiLine\"},{\"attributes\":{\"graph_layout\":{\"0\":[1.0515968362707657,-1.255341561322923],\"1\":[0.004529336305524424,-1.5528293849193473],\"10\":[0.17700868741796563,0.8835039305391639],\"11\":[-0.9181924731316187,0.5375484026992642],\"12\":[-1.8000000000000003,0.48995537611432627],\"13\":[-1.0534431239036623,1.2598999721436945],\"14\":[-0.3618883739150459,0.37249250484267554],\"15\":[-0.17880110261021936,-0.8896662248563576],\"16\":[-0.9805627702247748,-1.484911991465559],\"17\":[-1.689784752921127,-0.6388597818793655],\"18\":[-0.7331052032293595,-0.3792878309018295],\"19\":[0.3618672511929743,-0.37518842056369683],\"2\":[-0.15904602080640082,-0.3415316511030135],\"3\":[0.7393935700888642,0.3888562655401655],\"4\":[1.693239258433371,0.6504186501218778],\"5\":[1.7999207404597233,-0.4966205304129801],\"6\":[0.9209952330094453,-0.5391817075928242],\"7\":[0.16903182235239397,0.3429042529331822],\"8\":[-0.014912216313250633,1.5415313824254366],\"9\":[0.9721533015244336,1.4863083476581085]}},\"id\":\"1185\",\"type\":\"StaticLayoutProvider\"},{\"attributes\":{},\"id\":\"1232\",\"type\":\"UnionRenderers\"},{\"attributes\":{},\"id\":\"1233\",\"type\":\"Selection\"}],\"root_ids\":[\"1173\"]},\"title\":\"Bokeh Application\",\"version\":\"2.0.1\"}};\n", 508 | " var render_items = [{\"docid\":\"e8d65793-b5c5-4264-a983-2cf19d800507\",\"root_ids\":[\"1173\"],\"roots\":{\"1173\":\"896a11db-a60e-4aee-8654-2958666ba64c\"}}];\n", 509 | " root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n", 510 | "\n", 511 | " }\n", 512 | " if (root.Bokeh !== undefined) {\n", 513 | " embed_document(root);\n", 514 | " } else {\n", 515 | " var attempts = 0;\n", 516 | " var timer = setInterval(function(root) {\n", 517 | " if (root.Bokeh !== undefined) {\n", 518 | " clearInterval(timer);\n", 519 | " embed_document(root);\n", 520 | " } else {\n", 521 | " attempts++;\n", 522 | " if (attempts > 100) {\n", 523 | " clearInterval(timer);\n", 524 | " console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n", 525 | " }\n", 526 | " }\n", 527 | " }, 10, root)\n", 528 | " }\n", 529 | "})(window);" 530 | ], 531 | "application/vnd.bokehjs_exec.v0+json": "" 532 | }, 533 | "metadata": { 534 | "application/vnd.bokehjs_exec.v0+json": { 535 | "id": "1173" 536 | } 537 | }, 538 | "output_type": "display_data" 539 | } 540 | ], 541 | "source": [ 542 | "from bokeh.models import HoverTool\n", 543 | "from bokeh.palettes import Category20_20\n", 544 | "\n", 545 | "G = nx.desargues_graph() # always 20 nodes\n", 546 | "\n", 547 | "# We could use figure here but don't want all the axes and titles\n", 548 | "plot = Plot(x_range=Range1d(-2, 2), y_range=Range1d(-2, 2))\n", 549 | "\n", 550 | "# Create a Bokeh graph from the NetworkX input using nx.spring_layout\n", 551 | "graph = from_networkx(G, nx.spring_layout, scale=1.8, center=(0,0))\n", 552 | "plot.renderers.append(graph)\n", 553 | "\n", 554 | "# Add some new columns to the node renderer data source\n", 555 | "graph.node_renderer.data_source.data['index'] = list(range(len(G)))\n", 556 | "graph.node_renderer.data_source.data['colors'] = Category20_20\n", 557 | "\n", 558 | "graph.node_renderer.glyph.update(size=20, fill_color=\"colors\")\n", 559 | "\n", 560 | "plot.add_tools(HoverTool(tooltips=\"index: @index\"))\n", 561 | "\n", 562 | "show(plot)" 563 | ] 564 | }, 565 | { 566 | "cell_type": "code", 567 | "execution_count": 5, 568 | "metadata": {}, 569 | "outputs": [], 570 | "source": [ 571 | "# Exercise: Add your own columns for other node or edge properties e.g. fill_alpha or line_color,\n", 572 | "# or to show other fields in a tooltoip\n" 573 | ] 574 | }, 575 | { 576 | "cell_type": "markdown", 577 | "metadata": {}, 578 | "source": [ 579 | "## Inspection and Selection Policies\n", 580 | "\n", 581 | "Bokeh graph renderers have `inspection_policy` and `selection_policy` properties. These can be used to control how hover inspections highlight the graph, or how selection tools make selections. These properties may be set to any of the inpection policies in `bokeh.graphs`. For instance, if a user hovers over a node, you may wish to highlight all the associated edges as well. This can be accomplished by setting the inspection policy:\n", 582 | "\n", 583 | " graph.inspection_policy = NodesAndLinkedEdges()\n", 584 | " \n", 585 | "as the example below demonstrates." 586 | ] 587 | }, 588 | { 589 | "cell_type": "code", 590 | "execution_count": 6, 591 | "metadata": {}, 592 | "outputs": [ 593 | { 594 | "data": { 595 | "text/html": [ 596 | "\n", 597 | "\n", 598 | "\n", 599 | "\n", 600 | "\n", 601 | "\n", 602 | "
\n" 603 | ] 604 | }, 605 | "metadata": {}, 606 | "output_type": "display_data" 607 | }, 608 | { 609 | "data": { 610 | "application/javascript": [ 611 | "(function(root) {\n", 612 | " function embed_document(root) {\n", 613 | " \n", 614 | " var docs_json = {\"5f81c02f-15f1-4b90-8138-2bbe70eafff7\":{\"roots\":{\"references\":[{\"attributes\":{\"renderers\":[{\"id\":\"1369\"}],\"title\":{\"id\":\"1440\"},\"toolbar\":{\"id\":\"1402\"},\"x_range\":{\"id\":\"1364\"},\"x_scale\":{\"id\":\"1439\"},\"y_range\":{\"id\":\"1365\"},\"y_scale\":{\"id\":\"1441\"}},\"id\":\"1366\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1453\",\"type\":\"Selection\"},{\"attributes\":{\"data\":{\"end\":[11,7,1,6,3,12,13,11,2,6,10,4,13,12,10,9,12,11,7,8,12,14,12,9,10,8,12,9,13,12],\"start\":[0,0,0,0,0,0,1,1,1,1,2,2,2,3,3,4,4,4,5,5,5,6,6,6,6,7,7,8,10,10]},\"selected\":{\"id\":\"1453\"},\"selection_policy\":{\"id\":\"1452\"}},\"id\":\"1375\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"line_color\":{\"value\":\"#abdda4\"},\"line_width\":{\"value\":4}},\"id\":\"1394\",\"type\":\"MultiLine\"},{\"attributes\":{\"data\":{\"index\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]},\"selected\":{\"id\":\"1455\"},\"selection_policy\":{\"id\":\"1454\"}},\"id\":\"1371\",\"type\":\"ColumnDataSource\"},{\"attributes\":{},\"id\":\"1454\",\"type\":\"UnionRenderers\"},{\"attributes\":{},\"id\":\"1455\",\"type\":\"Selection\"},{\"attributes\":{\"source\":{\"id\":\"1371\"}},\"id\":\"1373\",\"type\":\"CDSView\"},{\"attributes\":{},\"id\":\"1451\",\"type\":\"NodesOnly\"},{\"attributes\":{},\"id\":\"1439\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1399\",\"type\":\"NodesAndLinkedEdges\"},{\"attributes\":{},\"id\":\"1441\",\"type\":\"LinearScale\"},{\"attributes\":{\"text\":\"\"},\"id\":\"1440\",\"type\":\"Title\"},{\"attributes\":{\"source\":{\"id\":\"1375\"}},\"id\":\"1377\",\"type\":\"CDSView\"},{\"attributes\":{\"fill_color\":{\"value\":\"#2b83ba\"},\"size\":{\"units\":\"screen\",\"value\":25}},\"id\":\"1379\",\"type\":\"Circle\"},{\"attributes\":{\"callback\":null,\"tooltips\":null},\"id\":\"1401\",\"type\":\"HoverTool\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1401\"}]},\"id\":\"1402\",\"type\":\"Toolbar\"},{\"attributes\":{\"data_source\":{\"id\":\"1371\"},\"glyph\":{\"id\":\"1379\"},\"hover_glyph\":{\"id\":\"1389\"},\"muted_glyph\":null,\"view\":{\"id\":\"1373\"}},\"id\":\"1372\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"line_alpha\":{\"value\":0.8},\"line_color\":{\"value\":\"#cccccc\"},\"line_width\":{\"value\":2}},\"id\":\"1384\",\"type\":\"MultiLine\"},{\"attributes\":{\"edge_renderer\":{\"id\":\"1376\"},\"inspection_policy\":{\"id\":\"1399\"},\"layout_provider\":{\"id\":\"1378\"},\"node_renderer\":{\"id\":\"1372\"},\"selection_policy\":{\"id\":\"1451\"}},\"id\":\"1369\",\"type\":\"GraphRenderer\"},{\"attributes\":{\"data_source\":{\"id\":\"1375\"},\"glyph\":{\"id\":\"1384\"},\"hover_glyph\":{\"id\":\"1394\"},\"muted_glyph\":null,\"view\":{\"id\":\"1377\"}},\"id\":\"1376\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"fill_color\":{\"value\":\"#abdda4\"},\"size\":{\"units\":\"screen\",\"value\":25}},\"id\":\"1389\",\"type\":\"Circle\"},{\"attributes\":{\"graph_layout\":{\"0\":[-0.07348979989817135,0.05449454682370959],\"1\":[-0.848577117308267,0.8596777277579513],\"10\":[-0.4014927048022815,0.7441272939202426],\"11\":[-1.3659332223311402,-0.06737469580507789],\"12\":[0.45775864306475894,-0.30527024838462435],\"13\":[-1.3390248110427074,1.5782708110302275],\"14\":[1.453304851718345,1.6561750142885403],\"2\":[-1.4996520553360426,0.6782768558445251],\"3\":[-0.35207983735933435,-0.33571282848173944],\"4\":[-0.8643162264938244,-0.6041710223563577],\"5\":[1.6889571287678822,-1.1127840984328077],\"6\":[0.4617452903795823,0.5749544582766315],\"7\":[1.0784954883428057,-0.8570180733106607],\"8\":[1.2905485461482438,-1.8],\"9\":[0.3137558261501579,-1.063645741170558]}},\"id\":\"1378\",\"type\":\"StaticLayoutProvider\"},{\"attributes\":{\"end\":2,\"start\":-2},\"id\":\"1364\",\"type\":\"Range1d\"},{\"attributes\":{\"end\":2,\"start\":-2},\"id\":\"1365\",\"type\":\"Range1d\"},{\"attributes\":{},\"id\":\"1452\",\"type\":\"UnionRenderers\"}],\"root_ids\":[\"1366\"]},\"title\":\"Bokeh Application\",\"version\":\"2.0.1\"}};\n", 615 | " var render_items = [{\"docid\":\"5f81c02f-15f1-4b90-8138-2bbe70eafff7\",\"root_ids\":[\"1366\"],\"roots\":{\"1366\":\"2638454e-062e-41a6-a49d-f5b673f87110\"}}];\n", 616 | " root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n", 617 | "\n", 618 | " }\n", 619 | " if (root.Bokeh !== undefined) {\n", 620 | " embed_document(root);\n", 621 | " } else {\n", 622 | " var attempts = 0;\n", 623 | " var timer = setInterval(function(root) {\n", 624 | " if (root.Bokeh !== undefined) {\n", 625 | " clearInterval(timer);\n", 626 | " embed_document(root);\n", 627 | " } else {\n", 628 | " attempts++;\n", 629 | " if (attempts > 100) {\n", 630 | " clearInterval(timer);\n", 631 | " console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n", 632 | " }\n", 633 | " }\n", 634 | " }, 10, root)\n", 635 | " }\n", 636 | "})(window);" 637 | ], 638 | "application/vnd.bokehjs_exec.v0+json": "" 639 | }, 640 | "metadata": { 641 | "application/vnd.bokehjs_exec.v0+json": { 642 | "id": "1366" 643 | } 644 | }, 645 | "output_type": "display_data" 646 | } 647 | ], 648 | "source": [ 649 | "from bokeh.models.graphs import NodesAndLinkedEdges\n", 650 | "from bokeh.models import Circle, HoverTool, MultiLine\n", 651 | "\n", 652 | "G = nx.gnm_random_graph(15, 30)\n", 653 | "\n", 654 | "# We could use figure here but don't want all the axes and titles\n", 655 | "plot = Plot(x_range=Range1d(-2, 2), y_range=Range1d(-2 ,2))\n", 656 | "\n", 657 | "# Create a Bokeh graph from the NetworkX input using nx.spring_layout\n", 658 | "graph = from_networkx(G, nx.spring_layout, scale=1.8, center=(0,0))\n", 659 | "plot.renderers.append(graph)\n", 660 | "\n", 661 | "# Blue circles for nodes, and light grey lines for edges\n", 662 | "graph.node_renderer.glyph = Circle(size=25, fill_color='#2b83ba')\n", 663 | "graph.edge_renderer.glyph = MultiLine(line_color=\"#cccccc\", line_alpha=0.8, line_width=2)\n", 664 | "\n", 665 | "# green hover for both nodes and edges\n", 666 | "graph.node_renderer.hover_glyph = Circle(size=25, fill_color='#abdda4')\n", 667 | "graph.edge_renderer.hover_glyph = MultiLine(line_color='#abdda4', line_width=4)\n", 668 | "\n", 669 | "# When we hover over nodes, highlight adjecent edges too\n", 670 | "graph.inspection_policy = NodesAndLinkedEdges()\n", 671 | "\n", 672 | "plot.add_tools(HoverTool(tooltips=None))\n", 673 | "\n", 674 | "show(plot)" 675 | ] 676 | }, 677 | { 678 | "cell_type": "code", 679 | "execution_count": 7, 680 | "metadata": {}, 681 | "outputs": [], 682 | "source": [ 683 | "# Exercise: try a different inspection (or selection) policy like NodesOnly or EdgesAndLinkedNodes\n" 684 | ] 685 | }, 686 | { 687 | "cell_type": "markdown", 688 | "metadata": {}, 689 | "source": [ 690 | "# Next Section" 691 | ] 692 | }, 693 | { 694 | "cell_type": "markdown", 695 | "metadata": {}, 696 | "source": [ 697 | "Click on this link to go to the next notebook: [09 - Geographic Plots](09%20-%20Geographic%20Plots.ipynb).\n", 698 | "\n", 699 | "To go back to the overview, click [here](00%20-%20Introduction%20and%20Setup.ipynb)." 700 | ] 701 | }, 702 | { 703 | "cell_type": "code", 704 | "execution_count": null, 705 | "metadata": {}, 706 | "outputs": [], 707 | "source": [] 708 | } 709 | ], 710 | "metadata": { 711 | "kernelspec": { 712 | "display_name": "Python 3", 713 | "language": "python", 714 | "name": "python3" 715 | }, 716 | "language_info": { 717 | "codemirror_mode": { 718 | "name": "ipython", 719 | "version": 3 720 | }, 721 | "file_extension": ".py", 722 | "mimetype": "text/x-python", 723 | "name": "python", 724 | "nbconvert_exporter": "python", 725 | "pygments_lexer": "ipython3", 726 | "version": "3.8.2" 727 | } 728 | }, 729 | "nbformat": 4, 730 | "nbformat_minor": 4 731 | } 732 | -------------------------------------------------------------------------------- /tutorial/09 - Geographic Plots.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\n", 8 | " \n", 9 | " \n", 17 | " \n", 20 | " \n", 21 | "
\n", 10 | " \n", 11 | " \n", 15 | " \n", 16 | " \n", 18 | "

Bokeh Tutorial

\n", 19 | "
\n", 22 | "\n", 23 | "

09. Geographic Plots

" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 1, 29 | "metadata": {}, 30 | "outputs": [ 31 | { 32 | "data": { 33 | "text/html": [ 34 | "\n", 35 | "
\n", 36 | " \n", 37 | " Loading BokehJS ...\n", 38 | "
" 39 | ] 40 | }, 41 | "metadata": {}, 42 | "output_type": "display_data" 43 | }, 44 | { 45 | "data": { 46 | "application/javascript": [ 47 | "\n", 48 | "(function(root) {\n", 49 | " function now() {\n", 50 | " return new Date();\n", 51 | " }\n", 52 | "\n", 53 | " var force = true;\n", 54 | "\n", 55 | " if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n", 56 | " root._bokeh_onload_callbacks = [];\n", 57 | " root._bokeh_is_loading = undefined;\n", 58 | " }\n", 59 | "\n", 60 | " var JS_MIME_TYPE = 'application/javascript';\n", 61 | " var HTML_MIME_TYPE = 'text/html';\n", 62 | " var EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n", 63 | " var CLASS_NAME = 'output_bokeh rendered_html';\n", 64 | "\n", 65 | " /**\n", 66 | " * Render data to the DOM node\n", 67 | " */\n", 68 | " function render(props, node) {\n", 69 | " var script = document.createElement(\"script\");\n", 70 | " node.appendChild(script);\n", 71 | " }\n", 72 | "\n", 73 | " /**\n", 74 | " * Handle when an output is cleared or removed\n", 75 | " */\n", 76 | " function handleClearOutput(event, handle) {\n", 77 | " var cell = handle.cell;\n", 78 | "\n", 79 | " var id = cell.output_area._bokeh_element_id;\n", 80 | " var server_id = cell.output_area._bokeh_server_id;\n", 81 | " // Clean up Bokeh references\n", 82 | " if (id != null && id in Bokeh.index) {\n", 83 | " Bokeh.index[id].model.document.clear();\n", 84 | " delete Bokeh.index[id];\n", 85 | " }\n", 86 | "\n", 87 | " if (server_id !== undefined) {\n", 88 | " // Clean up Bokeh references\n", 89 | " var cmd = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n", 90 | " cell.notebook.kernel.execute(cmd, {\n", 91 | " iopub: {\n", 92 | " output: function(msg) {\n", 93 | " var id = msg.content.text.trim();\n", 94 | " if (id in Bokeh.index) {\n", 95 | " Bokeh.index[id].model.document.clear();\n", 96 | " delete Bokeh.index[id];\n", 97 | " }\n", 98 | " }\n", 99 | " }\n", 100 | " });\n", 101 | " // Destroy server and session\n", 102 | " var cmd = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n", 103 | " cell.notebook.kernel.execute(cmd);\n", 104 | " }\n", 105 | " }\n", 106 | "\n", 107 | " /**\n", 108 | " * Handle when a new output is added\n", 109 | " */\n", 110 | " function handleAddOutput(event, handle) {\n", 111 | " var output_area = handle.output_area;\n", 112 | " var output = handle.output;\n", 113 | "\n", 114 | " // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n", 115 | " if ((output.output_type != \"display_data\") || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", 116 | " return\n", 117 | " }\n", 118 | "\n", 119 | " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", 120 | "\n", 121 | " if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n", 122 | " toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n", 123 | " // store reference to embed id on output_area\n", 124 | " output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", 125 | " }\n", 126 | " if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", 127 | " var bk_div = document.createElement(\"div\");\n", 128 | " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", 129 | " var script_attrs = bk_div.children[0].attributes;\n", 130 | " for (var i = 0; i < script_attrs.length; i++) {\n", 131 | " toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n", 132 | " toinsert[toinsert.length - 1].firstChild.textContent = bk_div.children[0].textContent\n", 133 | " }\n", 134 | " // store reference to server id on output_area\n", 135 | " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", 136 | " }\n", 137 | " }\n", 138 | "\n", 139 | " function register_renderer(events, OutputArea) {\n", 140 | "\n", 141 | " function append_mime(data, metadata, element) {\n", 142 | " // create a DOM node to render to\n", 143 | " var toinsert = this.create_output_subarea(\n", 144 | " metadata,\n", 145 | " CLASS_NAME,\n", 146 | " EXEC_MIME_TYPE\n", 147 | " );\n", 148 | " this.keyboard_manager.register_events(toinsert);\n", 149 | " // Render to node\n", 150 | " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", 151 | " render(props, toinsert[toinsert.length - 1]);\n", 152 | " element.append(toinsert);\n", 153 | " return toinsert\n", 154 | " }\n", 155 | "\n", 156 | " /* Handle when an output is cleared or removed */\n", 157 | " events.on('clear_output.CodeCell', handleClearOutput);\n", 158 | " events.on('delete.Cell', handleClearOutput);\n", 159 | "\n", 160 | " /* Handle when a new output is added */\n", 161 | " events.on('output_added.OutputArea', handleAddOutput);\n", 162 | "\n", 163 | " /**\n", 164 | " * Register the mime type and append_mime function with output_area\n", 165 | " */\n", 166 | " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", 167 | " /* Is output safe? */\n", 168 | " safe: true,\n", 169 | " /* Index of renderer in `output_area.display_order` */\n", 170 | " index: 0\n", 171 | " });\n", 172 | " }\n", 173 | "\n", 174 | " // register the mime type if in Jupyter Notebook environment and previously unregistered\n", 175 | " if (root.Jupyter !== undefined) {\n", 176 | " var events = require('base/js/events');\n", 177 | " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", 178 | "\n", 179 | " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", 180 | " register_renderer(events, OutputArea);\n", 181 | " }\n", 182 | " }\n", 183 | "\n", 184 | " \n", 185 | " if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n", 186 | " root._bokeh_timeout = Date.now() + 5000;\n", 187 | " root._bokeh_failed_load = false;\n", 188 | " }\n", 189 | "\n", 190 | " var NB_LOAD_WARNING = {'data': {'text/html':\n", 191 | " \"
\\n\"+\n", 192 | " \"

\\n\"+\n", 193 | " \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n", 194 | " \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n", 195 | " \"

\\n\"+\n", 196 | " \"\\n\"+\n", 200 | " \"\\n\"+\n", 201 | " \"from bokeh.resources import INLINE\\n\"+\n", 202 | " \"output_notebook(resources=INLINE)\\n\"+\n", 203 | " \"\\n\"+\n", 204 | " \"
\"}};\n", 205 | "\n", 206 | " function display_loaded() {\n", 207 | " var el = document.getElementById(\"1001\");\n", 208 | " if (el != null) {\n", 209 | " el.textContent = \"BokehJS is loading...\";\n", 210 | " }\n", 211 | " if (root.Bokeh !== undefined) {\n", 212 | " if (el != null) {\n", 213 | " el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n", 214 | " }\n", 215 | " } else if (Date.now() < root._bokeh_timeout) {\n", 216 | " setTimeout(display_loaded, 100)\n", 217 | " }\n", 218 | " }\n", 219 | "\n", 220 | "\n", 221 | " function run_callbacks() {\n", 222 | " try {\n", 223 | " root._bokeh_onload_callbacks.forEach(function(callback) {\n", 224 | " if (callback != null)\n", 225 | " callback();\n", 226 | " });\n", 227 | " } finally {\n", 228 | " delete root._bokeh_onload_callbacks\n", 229 | " }\n", 230 | " console.debug(\"Bokeh: all callbacks have finished\");\n", 231 | " }\n", 232 | "\n", 233 | " function load_libs(css_urls, js_urls, callback) {\n", 234 | " if (css_urls == null) css_urls = [];\n", 235 | " if (js_urls == null) js_urls = [];\n", 236 | "\n", 237 | " root._bokeh_onload_callbacks.push(callback);\n", 238 | " if (root._bokeh_is_loading > 0) {\n", 239 | " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", 240 | " return null;\n", 241 | " }\n", 242 | " if (js_urls == null || js_urls.length === 0) {\n", 243 | " run_callbacks();\n", 244 | " return null;\n", 245 | " }\n", 246 | " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", 247 | " root._bokeh_is_loading = css_urls.length + js_urls.length;\n", 248 | "\n", 249 | " function on_load() {\n", 250 | " root._bokeh_is_loading--;\n", 251 | " if (root._bokeh_is_loading === 0) {\n", 252 | " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", 253 | " run_callbacks()\n", 254 | " }\n", 255 | " }\n", 256 | "\n", 257 | " function on_error() {\n", 258 | " console.error(\"failed to load \" + url);\n", 259 | " }\n", 260 | "\n", 261 | " for (var i = 0; i < css_urls.length; i++) {\n", 262 | " var url = css_urls[i];\n", 263 | " const element = document.createElement(\"link\");\n", 264 | " element.onload = on_load;\n", 265 | " element.onerror = on_error;\n", 266 | " element.rel = \"stylesheet\";\n", 267 | " element.type = \"text/css\";\n", 268 | " element.href = url;\n", 269 | " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", 270 | " document.body.appendChild(element);\n", 271 | " }\n", 272 | "\n", 273 | " const hashes = {\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\": \"JpP8FXbgAZLkfur7LiK3j9AGBhHNIvF742meBJrjO2ShJDhCG2I1uVvW+0DUtrmc\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\": \"xZlADit0Q04ISQEdKg2k3L4W9AwQBAuDs9nJL9fM/WwzL1tEU9VPNezOFX0nLEAz\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\": \"4BuPRZkdMKSnj3zoxiNrQ86XgNw0rYmBOxe7nshquXwwcauupgBF2DHLVG1WuZlV\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\": \"Dv1SQ87hmDqK6S5OhBf0bCuwAEvL5QYL0PuR/F1SPVhCS/r/abjkbpKDYL2zeM19\"};\n", 274 | "\n", 275 | " for (var i = 0; i < js_urls.length; i++) {\n", 276 | " var url = js_urls[i];\n", 277 | " var element = document.createElement('script');\n", 278 | " element.onload = on_load;\n", 279 | " element.onerror = on_error;\n", 280 | " element.async = false;\n", 281 | " element.src = url;\n", 282 | " if (url in hashes) {\n", 283 | " element.crossOrigin = \"anonymous\";\n", 284 | " element.integrity = \"sha384-\" + hashes[url];\n", 285 | " }\n", 286 | " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", 287 | " document.head.appendChild(element);\n", 288 | " }\n", 289 | " };var element = document.getElementById(\"1001\");\n", 290 | " if (element == null) {\n", 291 | " console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1001' but no matching script tag was found. \")\n", 292 | " return false;\n", 293 | " }\n", 294 | "\n", 295 | " function inject_raw_css(css) {\n", 296 | " const element = document.createElement(\"style\");\n", 297 | " element.appendChild(document.createTextNode(css));\n", 298 | " document.body.appendChild(element);\n", 299 | " }\n", 300 | "\n", 301 | " \n", 302 | " var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\"];\n", 303 | " var css_urls = [];\n", 304 | " \n", 305 | "\n", 306 | " var inline_js = [\n", 307 | " function(Bokeh) {\n", 308 | " Bokeh.set_log_level(\"info\");\n", 309 | " },\n", 310 | " function(Bokeh) {\n", 311 | " \n", 312 | " \n", 313 | " }\n", 314 | " ];\n", 315 | "\n", 316 | " function run_inline_js() {\n", 317 | " \n", 318 | " if (root.Bokeh !== undefined || force === true) {\n", 319 | " \n", 320 | " for (var i = 0; i < inline_js.length; i++) {\n", 321 | " inline_js[i].call(root, root.Bokeh);\n", 322 | " }\n", 323 | " if (force === true) {\n", 324 | " display_loaded();\n", 325 | " }} else if (Date.now() < root._bokeh_timeout) {\n", 326 | " setTimeout(run_inline_js, 100);\n", 327 | " } else if (!root._bokeh_failed_load) {\n", 328 | " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", 329 | " root._bokeh_failed_load = true;\n", 330 | " } else if (force !== true) {\n", 331 | " var cell = $(document.getElementById(\"1001\")).parents('.cell').data().cell;\n", 332 | " cell.output_area.append_execute_result(NB_LOAD_WARNING)\n", 333 | " }\n", 334 | "\n", 335 | " }\n", 336 | "\n", 337 | " if (root._bokeh_is_loading === 0) {\n", 338 | " console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n", 339 | " run_inline_js();\n", 340 | " } else {\n", 341 | " load_libs(css_urls, js_urls, function() {\n", 342 | " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", 343 | " run_inline_js();\n", 344 | " });\n", 345 | " }\n", 346 | "}(window));" 347 | ], 348 | "application/vnd.bokehjs_load.v0+json": "\n(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n\n if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n root._bokeh_onload_callbacks = [];\n root._bokeh_is_loading = undefined;\n }\n\n \n\n \n if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n var NB_LOAD_WARNING = {'data': {'text/html':\n \"
\\n\"+\n \"

\\n\"+\n \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n \"

\\n\"+\n \"\\n\"+\n \"\\n\"+\n \"from bokeh.resources import INLINE\\n\"+\n \"output_notebook(resources=INLINE)\\n\"+\n \"\\n\"+\n \"
\"}};\n\n function display_loaded() {\n var el = document.getElementById(\"1001\");\n if (el != null) {\n el.textContent = \"BokehJS is loading...\";\n }\n if (root.Bokeh !== undefined) {\n if (el != null) {\n el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(display_loaded, 100)\n }\n }\n\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n\n root._bokeh_onload_callbacks.push(callback);\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls == null || js_urls.length === 0) {\n run_callbacks();\n return null;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n root._bokeh_is_loading = css_urls.length + js_urls.length;\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n\n function on_error() {\n console.error(\"failed to load \" + url);\n }\n\n for (var i = 0; i < css_urls.length; i++) {\n var url = css_urls[i];\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n }\n\n const hashes = {\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\": \"JpP8FXbgAZLkfur7LiK3j9AGBhHNIvF742meBJrjO2ShJDhCG2I1uVvW+0DUtrmc\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\": \"xZlADit0Q04ISQEdKg2k3L4W9AwQBAuDs9nJL9fM/WwzL1tEU9VPNezOFX0nLEAz\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\": \"4BuPRZkdMKSnj3zoxiNrQ86XgNw0rYmBOxe7nshquXwwcauupgBF2DHLVG1WuZlV\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\": \"Dv1SQ87hmDqK6S5OhBf0bCuwAEvL5QYL0PuR/F1SPVhCS/r/abjkbpKDYL2zeM19\"};\n\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n if (url in hashes) {\n element.crossOrigin = \"anonymous\";\n element.integrity = \"sha384-\" + hashes[url];\n }\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n };var element = document.getElementById(\"1001\");\n if (element == null) {\n console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1001' but no matching script tag was found. \")\n return false;\n }\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n \n var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\"];\n var css_urls = [];\n \n\n var inline_js = [\n function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\n function(Bokeh) {\n \n \n }\n ];\n\n function run_inline_js() {\n \n if (root.Bokeh !== undefined || force === true) {\n \n for (var i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }\n if (force === true) {\n display_loaded();\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n } else if (force !== true) {\n var cell = $(document.getElementById(\"1001\")).parents('.cell').data().cell;\n cell.output_area.append_execute_result(NB_LOAD_WARNING)\n }\n\n }\n\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n run_inline_js();\n } else {\n load_libs(css_urls, js_urls, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n}(window));" 349 | }, 350 | "metadata": {}, 351 | "output_type": "display_data" 352 | } 353 | ], 354 | "source": [ 355 | "from bokeh.io import output_notebook, show\n", 356 | "output_notebook()" 357 | ] 358 | }, 359 | { 360 | "cell_type": "markdown", 361 | "metadata": {}, 362 | "source": [ 363 | "It is often useful to be able to relate datasets with their real-world context. You can plot geographic data just like any other type of data, as in the [Texas Unemployment example](https://docs.bokeh.org/en/latest/docs/gallery/texas.html), but Bokeh also Bokeh provides several specialized mechanisms for plotting data in geographic coordinates:\n", 364 | "\n", 365 | "* [GMapPlot](https://docs.bokeh.org/en/latest/docs/user_guide/geo.html#google-maps-support): Bokeh Plots on top of Google Maps\n", 366 | "\n", 367 | "* [TileSource](https://docs.bokeh.org/en/latest/docs/user_guide/geo.html#tile-providers), especially WMTSTileSource: allows data to be overlaid on data from any map tile server, including \n", 368 | "[Google Maps](http://maps.google.com), \n", 369 | "[Stamen](http://maps.stamen.com), \n", 370 | "[MapQuest](https://www.mapquest.com/), \n", 371 | "[OpenStreetMap](https://www.openstreetmap.org), \n", 372 | "[ESRI](http://www.esri.com), \n", 373 | "and custom servers.\n", 374 | "\n", 375 | "* [GeoJSONDataSource](https://docs.bokeh.org/en/latest/docs/user_guide/geo.html#geojson-datasource): Allows reading data in [GeoJSON](http://geojson.org/) format for use with Bokeh plots and glyphs, similar to `ColumnDataSource`." 376 | ] 377 | }, 378 | { 379 | "cell_type": "markdown", 380 | "metadata": {}, 381 | "source": [ 382 | "## Google Maps plots\n", 383 | "\n", 384 | "Bokeh can render its own interactive glyphs on top of a Google Maps underlay. To use this functionality, call the `gmap` method on a standard figure as shown below. You will need to supply a valid Google API key for the plot to function, see: https://developers.google.com/maps/documentation/javascript/get-api-key" 385 | ] 386 | }, 387 | { 388 | "cell_type": "code", 389 | "execution_count": 4, 390 | "metadata": {}, 391 | "outputs": [ 392 | { 393 | "data": { 394 | "text/html": [ 395 | "\n", 396 | "\n", 397 | "\n", 398 | "\n", 399 | "\n", 400 | "\n", 401 | "
\n" 402 | ] 403 | }, 404 | "metadata": {}, 405 | "output_type": "display_data" 406 | }, 407 | { 408 | "data": { 409 | "application/javascript": [ 410 | "(function(root) {\n", 411 | " function embed_document(root) {\n", 412 | " \n", 413 | " var docs_json = {\"2e5a84df-881d-461d-be5b-4c72a50c312a\":{\"roots\":{\"references\":[{\"attributes\":{\"api_key\":\"QUl6YVN5QU0xT0hWbTZZcl9pNTRLdDAxbXlsZnh5TnhRZHhteEhR\",\"below\":[{\"id\":\"1013\"}],\"left\":[{\"id\":\"1018\"}],\"map_options\":{\"id\":\"1003\"},\"renderers\":[{\"id\":\"1034\"}],\"title\":{\"id\":\"1005\"},\"toolbar\":{\"id\":\"1025\"},\"x_range\":{\"id\":\"1006\"},\"x_scale\":{\"id\":\"1037\"},\"y_range\":{\"id\":\"1007\"},\"y_scale\":{\"id\":\"1036\"}},\"id\":\"1004\",\"subtype\":\"GMap\",\"type\":\"GMapPlot\"},{\"attributes\":{},\"id\":\"1006\",\"type\":\"Range1d\"},{\"attributes\":{\"formatter\":{\"id\":\"1011\"},\"ticker\":{\"id\":\"1012\"}},\"id\":\"1013\",\"type\":\"LinearAxis\"},{\"attributes\":{\"source\":{\"id\":\"1030\"}},\"id\":\"1035\",\"type\":\"CDSView\"},{\"attributes\":{},\"id\":\"1023\",\"type\":\"ResetTool\"},{\"attributes\":{},\"id\":\"1040\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"dimension\":\"lat\"},\"id\":\"1017\",\"type\":\"MercatorTicker\"},{\"attributes\":{\"formatter\":{\"id\":\"1016\"},\"ticker\":{\"id\":\"1017\"}},\"id\":\"1018\",\"type\":\"LinearAxis\"},{\"attributes\":{\"dimension\":\"lon\"},\"id\":\"1012\",\"type\":\"MercatorTicker\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.8},\"fill_color\":{\"value\":\"blue\"},\"line_color\":{\"value\":\"#1f77b4\"},\"size\":{\"units\":\"screen\",\"value\":15},\"x\":{\"field\":\"lon\"},\"y\":{\"field\":\"lat\"}},\"id\":\"1032\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"1021\",\"type\":\"PanTool\"},{\"attributes\":{\"dimension\":\"lat\"},\"id\":\"1016\",\"type\":\"MercatorTickFormatter\"},{\"attributes\":{},\"id\":\"1007\",\"type\":\"Range1d\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1021\"},{\"id\":\"1022\"},{\"id\":\"1023\"},{\"id\":\"1024\"}]},\"id\":\"1025\",\"type\":\"Toolbar\"},{\"attributes\":{\"lat\":30.2861,\"lng\":-97.7394,\"zoom\":11},\"id\":\"1003\",\"type\":\"GMapOptions\"},{\"attributes\":{},\"id\":\"1024\",\"type\":\"HelpTool\"},{\"attributes\":{\"data\":{\"lat\":[30.29,30.2,30.29],\"lon\":[-97.7,-97.74,-97.78]},\"selected\":{\"id\":\"1039\"},\"selection_policy\":{\"id\":\"1040\"}},\"id\":\"1030\",\"type\":\"ColumnDataSource\"},{\"attributes\":{},\"id\":\"1022\",\"type\":\"WheelZoomTool\"},{\"attributes\":{\"text\":\"Austin\"},\"id\":\"1005\",\"type\":\"Title\"},{\"attributes\":{},\"id\":\"1036\",\"type\":\"LinearScale\"},{\"attributes\":{\"dimension\":\"lon\"},\"id\":\"1011\",\"type\":\"MercatorTickFormatter\"},{\"attributes\":{},\"id\":\"1037\",\"type\":\"LinearScale\"},{\"attributes\":{\"data_source\":{\"id\":\"1030\"},\"glyph\":{\"id\":\"1032\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"1033\"},\"selection_glyph\":null,\"view\":{\"id\":\"1035\"}},\"id\":\"1034\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"blue\"},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"#1f77b4\"},\"size\":{\"units\":\"screen\",\"value\":15},\"x\":{\"field\":\"lon\"},\"y\":{\"field\":\"lat\"}},\"id\":\"1033\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"1039\",\"type\":\"Selection\"}],\"root_ids\":[\"1004\"]},\"title\":\"Bokeh Application\",\"version\":\"2.0.1\"}};\n", 414 | " var render_items = [{\"docid\":\"2e5a84df-881d-461d-be5b-4c72a50c312a\",\"root_ids\":[\"1004\"],\"roots\":{\"1004\":\"7bd20ebd-caf2-4919-ad73-ccbdce28eecb\"}}];\n", 415 | " root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n", 416 | "\n", 417 | " }\n", 418 | " if (root.Bokeh !== undefined) {\n", 419 | " embed_document(root);\n", 420 | " } else {\n", 421 | " var attempts = 0;\n", 422 | " var timer = setInterval(function(root) {\n", 423 | " if (root.Bokeh !== undefined) {\n", 424 | " clearInterval(timer);\n", 425 | " embed_document(root);\n", 426 | " } else {\n", 427 | " attempts++;\n", 428 | " if (attempts > 100) {\n", 429 | " clearInterval(timer);\n", 430 | " console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n", 431 | " }\n", 432 | " }\n", 433 | " }, 10, root)\n", 434 | " }\n", 435 | "})(window);" 436 | ], 437 | "application/vnd.bokehjs_exec.v0+json": "" 438 | }, 439 | "metadata": { 440 | "application/vnd.bokehjs_exec.v0+json": { 441 | "id": "1004" 442 | } 443 | }, 444 | "output_type": "display_data" 445 | } 446 | ], 447 | "source": [ 448 | "import os\n", 449 | "from bokeh.models import GMapOptions\n", 450 | "from bokeh.plotting import gmap\n", 451 | "\n", 452 | "map_options = GMapOptions(lat=30.2861, lng=-97.7394, map_type=\"roadmap\", zoom=11)\n", 453 | "\n", 454 | "# Replace the value below with your personal API key:\n", 455 | "api_key = os.environ[\"GOOGLE_API_KEY\"]\n", 456 | "\n", 457 | "p = gmap(api_key, map_options, title=\"Austin\")\n", 458 | "\n", 459 | "data = dict(lat=[ 30.29, 30.20, 30.29],\n", 460 | " lon=[-97.70, -97.74, -97.78])\n", 461 | "\n", 462 | "p.circle(x=\"lon\", y=\"lat\", size=15, fill_color=\"blue\", fill_alpha=0.8, source=data)\n", 463 | "\n", 464 | "show(p)" 465 | ] 466 | }, 467 | { 468 | "cell_type": "markdown", 469 | "metadata": {}, 470 | "source": [ 471 | "## WMTS Tile Source\n", 472 | "\n", 473 | "WTMS is the most common web standard for tiled map data, i.e. maps supplied as standard-sized image patches from which the overall map can be constructed at a given zoom level. WTMS uses Web Mercator format, measuring distances from Greenwich, England as meters north and meters west, which is easy to compute but does distort the global shape. \n", 474 | "\n", 475 | "First let's create an empty Bokeh plot covering the USA, with bounds specified in meters:" 476 | ] 477 | }, 478 | { 479 | "cell_type": "code", 480 | "execution_count": 5, 481 | "metadata": {}, 482 | "outputs": [], 483 | "source": [ 484 | "from bokeh.plotting import figure\n", 485 | "from bokeh.models import WMTSTileSource\n", 486 | "\n", 487 | "# web mercator coordinates\n", 488 | "USA = x_range,y_range = ((-13884029,-7453304), (2698291,6455972))\n", 489 | "\n", 490 | "p = figure(tools='pan, wheel_zoom', x_range=x_range, y_range=y_range, \n", 491 | " x_axis_type=\"mercator\", y_axis_type=\"mercator\")" 492 | ] 493 | }, 494 | { 495 | "cell_type": "markdown", 496 | "metadata": {}, 497 | "source": [ 498 | "A few WTMS tile sources are already defined in `bokeh.tile_providers`, but here we'll show how to specify the interface using a format string showing Bokeh how to request a tile with the required zoom, x, and y values from a given tile provider:" 499 | ] 500 | }, 501 | { 502 | "cell_type": "code", 503 | "execution_count": 6, 504 | "metadata": {}, 505 | "outputs": [ 506 | { 507 | "data": { 508 | "text/html": [ 509 | "
TileRenderer(
id = '1110', …)
alpha = 1.0,
js_event_callbacks = {},
js_property_callbacks = {},
level = 'glyph',
name = None,
render_parents = True,
smoothing = True,
subscribed_events = [],
tags = [],
tile_source = WMTSTileSource(id='1109', ...),
visible = True,
x_range_name = 'default',
y_range_name = 'default')
\n", 510 | "\n" 525 | ], 526 | "text/plain": [ 527 | "TileRenderer(id='1110', ...)" 528 | ] 529 | }, 530 | "execution_count": 6, 531 | "metadata": {}, 532 | "output_type": "execute_result" 533 | } 534 | ], 535 | "source": [ 536 | "url = 'http://a.basemaps.cartocdn.com/rastertiles/voyager/{Z}/{X}/{Y}.png'\n", 537 | "attribution = \"Tiles by Carto, under CC BY 3.0. Data by OSM, under ODbL\"\n", 538 | "\n", 539 | "p.add_tile(WMTSTileSource(url=url, attribution=attribution))" 540 | ] 541 | }, 542 | { 543 | "cell_type": "markdown", 544 | "metadata": {}, 545 | "source": [ 546 | "If you show the figure, you can then use the wheel zoom and pan tools to navigate over any zoom level, and Bokeh will request the appropriate tiles from the server and insert them at the correct locations in the plot:" 547 | ] 548 | }, 549 | { 550 | "cell_type": "code", 551 | "execution_count": 7, 552 | "metadata": {}, 553 | "outputs": [ 554 | { 555 | "data": { 556 | "text/html": [ 557 | "\n", 558 | "\n", 559 | "\n", 560 | "\n", 561 | "\n", 562 | "\n", 563 | "
\n" 564 | ] 565 | }, 566 | "metadata": {}, 567 | "output_type": "display_data" 568 | }, 569 | { 570 | "data": { 571 | "application/javascript": [ 572 | "(function(root) {\n", 573 | " function embed_document(root) {\n", 574 | " \n", 575 | " var docs_json = {\"7f0acfd7-cdb6-45a7-8a52-b6cbdd932ddb\":{\"roots\":{\"references\":[{\"attributes\":{\"below\":[{\"id\":\"1088\"}],\"center\":[{\"id\":\"1095\"},{\"id\":\"1103\"}],\"left\":[{\"id\":\"1096\"}],\"renderers\":[{\"id\":\"1110\"}],\"title\":{\"id\":\"1119\"},\"toolbar\":{\"id\":\"1106\"},\"x_range\":{\"id\":\"1080\"},\"x_scale\":{\"id\":\"1084\"},\"y_range\":{\"id\":\"1082\"},\"y_scale\":{\"id\":\"1086\"}},\"id\":\"1079\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{\"end\":6455972,\"start\":2698291},\"id\":\"1082\",\"type\":\"Range1d\"},{\"attributes\":{\"dimension\":\"lat\"},\"id\":\"1099\",\"type\":\"MercatorTickFormatter\"},{\"attributes\":{\"axis\":{\"id\":\"1088\"},\"ticker\":null},\"id\":\"1095\",\"type\":\"Grid\"},{\"attributes\":{\"dimension\":\"lat\"},\"id\":\"1097\",\"type\":\"MercatorTicker\"},{\"attributes\":{\"dimension\":\"lon\"},\"id\":\"1091\",\"type\":\"MercatorTickFormatter\"},{\"attributes\":{\"end\":-7453304,\"start\":-13884029},\"id\":\"1080\",\"type\":\"Range1d\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1104\"},{\"id\":\"1105\"}]},\"id\":\"1106\",\"type\":\"Toolbar\"},{\"attributes\":{\"formatter\":{\"id\":\"1099\"},\"ticker\":{\"id\":\"1097\"}},\"id\":\"1096\",\"type\":\"MercatorAxis\"},{\"attributes\":{},\"id\":\"1084\",\"type\":\"LinearScale\"},{\"attributes\":{\"axis\":{\"id\":\"1096\"},\"dimension\":1,\"ticker\":null},\"id\":\"1103\",\"type\":\"Grid\"},{\"attributes\":{\"tile_source\":{\"id\":\"1109\"}},\"id\":\"1110\",\"type\":\"TileRenderer\"},{\"attributes\":{\"formatter\":{\"id\":\"1091\"},\"ticker\":{\"id\":\"1089\"}},\"id\":\"1088\",\"type\":\"MercatorAxis\"},{\"attributes\":{},\"id\":\"1105\",\"type\":\"WheelZoomTool\"},{\"attributes\":{},\"id\":\"1086\",\"type\":\"LinearScale\"},{\"attributes\":{\"attribution\":\"Tiles by Carto, under CC BY 3.0. Data by OSM, under ODbL\",\"url\":\"http://a.basemaps.cartocdn.com/rastertiles/voyager/{Z}/{X}/{Y}.png\"},\"id\":\"1109\",\"type\":\"WMTSTileSource\"},{\"attributes\":{\"dimension\":\"lon\"},\"id\":\"1089\",\"type\":\"MercatorTicker\"},{\"attributes\":{\"text\":\"\"},\"id\":\"1119\",\"type\":\"Title\"},{\"attributes\":{},\"id\":\"1104\",\"type\":\"PanTool\"}],\"root_ids\":[\"1079\"]},\"title\":\"Bokeh Application\",\"version\":\"2.0.1\"}};\n", 576 | " var render_items = [{\"docid\":\"7f0acfd7-cdb6-45a7-8a52-b6cbdd932ddb\",\"root_ids\":[\"1079\"],\"roots\":{\"1079\":\"4d2049cc-aaab-442a-a2c9-22f6965c2a0c\"}}];\n", 577 | " root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n", 578 | "\n", 579 | " }\n", 580 | " if (root.Bokeh !== undefined) {\n", 581 | " embed_document(root);\n", 582 | " } else {\n", 583 | " var attempts = 0;\n", 584 | " var timer = setInterval(function(root) {\n", 585 | " if (root.Bokeh !== undefined) {\n", 586 | " clearInterval(timer);\n", 587 | " embed_document(root);\n", 588 | " } else {\n", 589 | " attempts++;\n", 590 | " if (attempts > 100) {\n", 591 | " clearInterval(timer);\n", 592 | " console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n", 593 | " }\n", 594 | " }\n", 595 | " }, 10, root)\n", 596 | " }\n", 597 | "})(window);" 598 | ], 599 | "application/vnd.bokehjs_exec.v0+json": "" 600 | }, 601 | "metadata": { 602 | "application/vnd.bokehjs_exec.v0+json": { 603 | "id": "1079" 604 | } 605 | }, 606 | "output_type": "display_data" 607 | } 608 | ], 609 | "source": [ 610 | "show(p)" 611 | ] 612 | }, 613 | { 614 | "cell_type": "markdown", 615 | "metadata": {}, 616 | "source": [ 617 | "That's all it takes to put map data into your plot! Of course, you'll usually want to show other data as well, or you could just use the tile server's own web address. You can now add anything you would normally use in a Bokeh plot, as long as you can obtain coordinates for it in Web Mercator format. For example:" 618 | ] 619 | }, 620 | { 621 | "cell_type": "code", 622 | "execution_count": 8, 623 | "metadata": {}, 624 | "outputs": [ 625 | { 626 | "data": { 627 | "text/html": [ 628 | "
\n", 629 | "\n", 642 | "\n", 643 | " \n", 644 | " \n", 645 | " \n", 646 | " \n", 647 | " \n", 648 | " \n", 649 | " \n", 650 | " \n", 651 | " \n", 652 | " \n", 653 | " \n", 654 | " \n", 655 | " \n", 656 | " \n", 657 | " \n", 658 | " \n", 659 | " \n", 660 | " \n", 661 | " \n", 662 | " \n", 663 | " \n", 664 | " \n", 665 | " \n", 666 | " \n", 667 | " \n", 668 | " \n", 669 | " \n", 670 | " \n", 671 | "
namelonlatxy
0Austin-97.743130.2672-1.088071e+073.537942e+06
1NYC-74.005940.7128-8.238299e+064.970072e+06
\n", 672 | "
" 673 | ], 674 | "text/plain": [ 675 | " name lon lat x y\n", 676 | "0 Austin -97.7431 30.2672 -1.088071e+07 3.537942e+06\n", 677 | "1 NYC -74.0059 40.7128 -8.238299e+06 4.970072e+06" 678 | ] 679 | }, 680 | "execution_count": 8, 681 | "metadata": {}, 682 | "output_type": "execute_result" 683 | } 684 | ], 685 | "source": [ 686 | "import pandas as pd\n", 687 | "import numpy as np\n", 688 | "\n", 689 | "def wgs84_to_web_mercator(df, lon=\"lon\", lat=\"lat\"):\n", 690 | " \"\"\"Converts decimal longitude/latitude to Web Mercator format\"\"\"\n", 691 | " k = 6378137\n", 692 | " df[\"x\"] = df[lon] * (k * np.pi/180.0)\n", 693 | " df[\"y\"] = np.log(np.tan((90 + df[lat]) * np.pi/360.0)) * k\n", 694 | " return df\n", 695 | "\n", 696 | "df = pd.DataFrame(dict(name=[\"Austin\", \"NYC\"], lon=[-97.7431,-74.0059], lat=[30.2672,40.7128]))\n", 697 | "wgs84_to_web_mercator(df)" 698 | ] 699 | }, 700 | { 701 | "cell_type": "code", 702 | "execution_count": 9, 703 | "metadata": {}, 704 | "outputs": [ 705 | { 706 | "data": { 707 | "text/html": [ 708 | "\n", 709 | "\n", 710 | "\n", 711 | "\n", 712 | "\n", 713 | "\n", 714 | "
\n" 715 | ] 716 | }, 717 | "metadata": {}, 718 | "output_type": "display_data" 719 | }, 720 | { 721 | "data": { 722 | "application/javascript": [ 723 | "(function(root) {\n", 724 | " function embed_document(root) {\n", 725 | " \n", 726 | " var docs_json = {\"049a9c4c-9990-4efa-8251-cdfe32b7509d\":{\"roots\":{\"references\":[{\"attributes\":{\"below\":[{\"id\":\"1170\"}],\"center\":[{\"id\":\"1177\"},{\"id\":\"1185\"}],\"left\":[{\"id\":\"1178\"}],\"renderers\":[{\"id\":\"1192\"},{\"id\":\"1197\"}],\"title\":{\"id\":\"1210\"},\"toolbar\":{\"id\":\"1188\"},\"x_range\":{\"id\":\"1162\"},\"x_scale\":{\"id\":\"1166\"},\"y_range\":{\"id\":\"1164\"},\"y_scale\":{\"id\":\"1168\"}},\"id\":\"1161\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1217\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"axis\":{\"id\":\"1178\"},\"dimension\":1,\"ticker\":null},\"id\":\"1185\",\"type\":\"Grid\"},{\"attributes\":{\"text\":\"\"},\"id\":\"1210\",\"type\":\"Title\"},{\"attributes\":{\"formatter\":{\"id\":\"1173\"},\"ticker\":{\"id\":\"1171\"}},\"id\":\"1170\",\"type\":\"MercatorAxis\"},{\"attributes\":{},\"id\":\"1166\",\"type\":\"LinearScale\"},{\"attributes\":{\"attribution\":\"Tiles by Carto, under CC BY 3.0. Data by OSM, under ODbL\",\"url\":\"http://a.basemaps.cartocdn.com/rastertiles/voyager/{Z}/{X}/{Y}.png\"},\"id\":\"1191\",\"type\":\"WMTSTileSource\"},{\"attributes\":{\"data_source\":{\"id\":\"1194\"},\"glyph\":{\"id\":\"1195\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"1196\"},\"selection_glyph\":null,\"view\":{\"id\":\"1198\"}},\"id\":\"1197\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"end\":-7453304,\"start\":-13884029},\"id\":\"1162\",\"type\":\"Range1d\"},{\"attributes\":{\"dimension\":\"lat\"},\"id\":\"1179\",\"type\":\"MercatorTicker\"},{\"attributes\":{\"dimension\":\"lon\"},\"id\":\"1173\",\"type\":\"MercatorTickFormatter\"},{\"attributes\":{\"fill_color\":{\"value\":\"orange\"},\"line_color\":{\"value\":\"#1f77b4\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1195\",\"type\":\"Circle\"},{\"attributes\":{\"dimension\":\"lon\"},\"id\":\"1171\",\"type\":\"MercatorTicker\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"orange\"},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"#1f77b4\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1196\",\"type\":\"Circle\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1186\"},{\"id\":\"1187\"}]},\"id\":\"1188\",\"type\":\"Toolbar\"},{\"attributes\":{\"data\":{\"x\":{\"__ndarray__\":\"TJjbA9nAZMGf/KLGNm1fwQ==\",\"dtype\":\"float64\",\"shape\":[2]},\"y\":{\"__ndarray__\":\"a6XdLQv+SkFnqxDllfVSQQ==\",\"dtype\":\"float64\",\"shape\":[2]}},\"selected\":{\"id\":\"1216\"},\"selection_policy\":{\"id\":\"1217\"}},\"id\":\"1194\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"axis\":{\"id\":\"1170\"},\"ticker\":null},\"id\":\"1177\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1168\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1187\",\"type\":\"WheelZoomTool\"},{\"attributes\":{},\"id\":\"1186\",\"type\":\"PanTool\"},{\"attributes\":{\"end\":6455972,\"start\":2698291},\"id\":\"1164\",\"type\":\"Range1d\"},{\"attributes\":{\"formatter\":{\"id\":\"1181\"},\"ticker\":{\"id\":\"1179\"}},\"id\":\"1178\",\"type\":\"MercatorAxis\"},{\"attributes\":{\"dimension\":\"lat\"},\"id\":\"1181\",\"type\":\"MercatorTickFormatter\"},{\"attributes\":{\"tile_source\":{\"id\":\"1191\"}},\"id\":\"1192\",\"type\":\"TileRenderer\"},{\"attributes\":{},\"id\":\"1216\",\"type\":\"Selection\"},{\"attributes\":{\"source\":{\"id\":\"1194\"}},\"id\":\"1198\",\"type\":\"CDSView\"}],\"root_ids\":[\"1161\"]},\"title\":\"Bokeh Application\",\"version\":\"2.0.1\"}};\n", 727 | " var render_items = [{\"docid\":\"049a9c4c-9990-4efa-8251-cdfe32b7509d\",\"root_ids\":[\"1161\"],\"roots\":{\"1161\":\"f1e96d90-01cf-43aa-9116-3940edff99ef\"}}];\n", 728 | " root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n", 729 | "\n", 730 | " }\n", 731 | " if (root.Bokeh !== undefined) {\n", 732 | " embed_document(root);\n", 733 | " } else {\n", 734 | " var attempts = 0;\n", 735 | " var timer = setInterval(function(root) {\n", 736 | " if (root.Bokeh !== undefined) {\n", 737 | " clearInterval(timer);\n", 738 | " embed_document(root);\n", 739 | " } else {\n", 740 | " attempts++;\n", 741 | " if (attempts > 100) {\n", 742 | " clearInterval(timer);\n", 743 | " console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n", 744 | " }\n", 745 | " }\n", 746 | " }, 10, root)\n", 747 | " }\n", 748 | "})(window);" 749 | ], 750 | "application/vnd.bokehjs_exec.v0+json": "" 751 | }, 752 | "metadata": { 753 | "application/vnd.bokehjs_exec.v0+json": { 754 | "id": "1161" 755 | } 756 | }, 757 | "output_type": "display_data" 758 | } 759 | ], 760 | "source": [ 761 | "p = figure(tools='pan, wheel_zoom', x_range=x_range, y_range=y_range, \n", 762 | " x_axis_type=\"mercator\", y_axis_type=\"mercator\")\n", 763 | "\n", 764 | "p.add_tile(WMTSTileSource(url=url, attribution=attribution))\n", 765 | "\n", 766 | "p.circle(x=df['x'], y=df['y'], fill_color='orange', size=10)\n", 767 | "show(p)" 768 | ] 769 | }, 770 | { 771 | "cell_type": "code", 772 | "execution_count": 10, 773 | "metadata": {}, 774 | "outputs": [], 775 | "source": [ 776 | "# EXERCISE: find some data in lat, lon (e.g. at http://data.gov), \n", 777 | "# import it into a dataframe or data source, and add it on the map above.\n" 778 | ] 779 | }, 780 | { 781 | "cell_type": "markdown", 782 | "metadata": {}, 783 | "source": [ 784 | "# Next Section" 785 | ] 786 | }, 787 | { 788 | "cell_type": "markdown", 789 | "metadata": {}, 790 | "source": [ 791 | "Click on this link to go to the next notebook: [10 - Exporting and Embedding](10%20-%20Exporting%20and%20Embedding.ipynb).\n", 792 | "\n", 793 | "To go back to the overview, click [here](00%20-%20Introduction%20and%20Setup.ipynb)." 794 | ] 795 | }, 796 | { 797 | "cell_type": "code", 798 | "execution_count": null, 799 | "metadata": {}, 800 | "outputs": [], 801 | "source": [] 802 | } 803 | ], 804 | "metadata": { 805 | "anaconda-cloud": {}, 806 | "kernelspec": { 807 | "display_name": "Python 3", 808 | "language": "python", 809 | "name": "python3" 810 | }, 811 | "language_info": { 812 | "codemirror_mode": { 813 | "name": "ipython", 814 | "version": 3 815 | }, 816 | "file_extension": ".py", 817 | "mimetype": "text/x-python", 818 | "name": "python", 819 | "nbconvert_exporter": "python", 820 | "pygments_lexer": "ipython3", 821 | "version": "3.8.2" 822 | } 823 | }, 824 | "nbformat": 4, 825 | "nbformat_minor": 4 826 | } 827 | -------------------------------------------------------------------------------- /tutorial/05 - Presentation Layouts.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\n", 8 | " \n", 9 | " \n", 17 | " \n", 20 | " \n", 21 | "
\n", 10 | " \n", 11 | " \n", 15 | " \n", 16 | " \n", 18 | "

Bokeh Tutorial

\n", 19 | "
\n", 22 | "\n", 23 | "

05. Presentation and Layout

" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 1, 29 | "metadata": {}, 30 | "outputs": [ 31 | { 32 | "data": { 33 | "text/html": [ 34 | "\n", 35 | "
\n", 36 | " \n", 37 | " Loading BokehJS ...\n", 38 | "
" 39 | ] 40 | }, 41 | "metadata": {}, 42 | "output_type": "display_data" 43 | }, 44 | { 45 | "data": { 46 | "application/javascript": [ 47 | "\n", 48 | "(function(root) {\n", 49 | " function now() {\n", 50 | " return new Date();\n", 51 | " }\n", 52 | "\n", 53 | " var force = true;\n", 54 | "\n", 55 | " if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n", 56 | " root._bokeh_onload_callbacks = [];\n", 57 | " root._bokeh_is_loading = undefined;\n", 58 | " }\n", 59 | "\n", 60 | " var JS_MIME_TYPE = 'application/javascript';\n", 61 | " var HTML_MIME_TYPE = 'text/html';\n", 62 | " var EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n", 63 | " var CLASS_NAME = 'output_bokeh rendered_html';\n", 64 | "\n", 65 | " /**\n", 66 | " * Render data to the DOM node\n", 67 | " */\n", 68 | " function render(props, node) {\n", 69 | " var script = document.createElement(\"script\");\n", 70 | " node.appendChild(script);\n", 71 | " }\n", 72 | "\n", 73 | " /**\n", 74 | " * Handle when an output is cleared or removed\n", 75 | " */\n", 76 | " function handleClearOutput(event, handle) {\n", 77 | " var cell = handle.cell;\n", 78 | "\n", 79 | " var id = cell.output_area._bokeh_element_id;\n", 80 | " var server_id = cell.output_area._bokeh_server_id;\n", 81 | " // Clean up Bokeh references\n", 82 | " if (id != null && id in Bokeh.index) {\n", 83 | " Bokeh.index[id].model.document.clear();\n", 84 | " delete Bokeh.index[id];\n", 85 | " }\n", 86 | "\n", 87 | " if (server_id !== undefined) {\n", 88 | " // Clean up Bokeh references\n", 89 | " var cmd = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n", 90 | " cell.notebook.kernel.execute(cmd, {\n", 91 | " iopub: {\n", 92 | " output: function(msg) {\n", 93 | " var id = msg.content.text.trim();\n", 94 | " if (id in Bokeh.index) {\n", 95 | " Bokeh.index[id].model.document.clear();\n", 96 | " delete Bokeh.index[id];\n", 97 | " }\n", 98 | " }\n", 99 | " }\n", 100 | " });\n", 101 | " // Destroy server and session\n", 102 | " var cmd = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n", 103 | " cell.notebook.kernel.execute(cmd);\n", 104 | " }\n", 105 | " }\n", 106 | "\n", 107 | " /**\n", 108 | " * Handle when a new output is added\n", 109 | " */\n", 110 | " function handleAddOutput(event, handle) {\n", 111 | " var output_area = handle.output_area;\n", 112 | " var output = handle.output;\n", 113 | "\n", 114 | " // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n", 115 | " if ((output.output_type != \"display_data\") || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", 116 | " return\n", 117 | " }\n", 118 | "\n", 119 | " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", 120 | "\n", 121 | " if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n", 122 | " toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n", 123 | " // store reference to embed id on output_area\n", 124 | " output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", 125 | " }\n", 126 | " if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", 127 | " var bk_div = document.createElement(\"div\");\n", 128 | " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", 129 | " var script_attrs = bk_div.children[0].attributes;\n", 130 | " for (var i = 0; i < script_attrs.length; i++) {\n", 131 | " toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n", 132 | " toinsert[toinsert.length - 1].firstChild.textContent = bk_div.children[0].textContent\n", 133 | " }\n", 134 | " // store reference to server id on output_area\n", 135 | " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", 136 | " }\n", 137 | " }\n", 138 | "\n", 139 | " function register_renderer(events, OutputArea) {\n", 140 | "\n", 141 | " function append_mime(data, metadata, element) {\n", 142 | " // create a DOM node to render to\n", 143 | " var toinsert = this.create_output_subarea(\n", 144 | " metadata,\n", 145 | " CLASS_NAME,\n", 146 | " EXEC_MIME_TYPE\n", 147 | " );\n", 148 | " this.keyboard_manager.register_events(toinsert);\n", 149 | " // Render to node\n", 150 | " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", 151 | " render(props, toinsert[toinsert.length - 1]);\n", 152 | " element.append(toinsert);\n", 153 | " return toinsert\n", 154 | " }\n", 155 | "\n", 156 | " /* Handle when an output is cleared or removed */\n", 157 | " events.on('clear_output.CodeCell', handleClearOutput);\n", 158 | " events.on('delete.Cell', handleClearOutput);\n", 159 | "\n", 160 | " /* Handle when a new output is added */\n", 161 | " events.on('output_added.OutputArea', handleAddOutput);\n", 162 | "\n", 163 | " /**\n", 164 | " * Register the mime type and append_mime function with output_area\n", 165 | " */\n", 166 | " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", 167 | " /* Is output safe? */\n", 168 | " safe: true,\n", 169 | " /* Index of renderer in `output_area.display_order` */\n", 170 | " index: 0\n", 171 | " });\n", 172 | " }\n", 173 | "\n", 174 | " // register the mime type if in Jupyter Notebook environment and previously unregistered\n", 175 | " if (root.Jupyter !== undefined) {\n", 176 | " var events = require('base/js/events');\n", 177 | " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", 178 | "\n", 179 | " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", 180 | " register_renderer(events, OutputArea);\n", 181 | " }\n", 182 | " }\n", 183 | "\n", 184 | " \n", 185 | " if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n", 186 | " root._bokeh_timeout = Date.now() + 5000;\n", 187 | " root._bokeh_failed_load = false;\n", 188 | " }\n", 189 | "\n", 190 | " var NB_LOAD_WARNING = {'data': {'text/html':\n", 191 | " \"
\\n\"+\n", 192 | " \"

\\n\"+\n", 193 | " \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n", 194 | " \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n", 195 | " \"

\\n\"+\n", 196 | " \"\\n\"+\n", 200 | " \"\\n\"+\n", 201 | " \"from bokeh.resources import INLINE\\n\"+\n", 202 | " \"output_notebook(resources=INLINE)\\n\"+\n", 203 | " \"\\n\"+\n", 204 | " \"
\"}};\n", 205 | "\n", 206 | " function display_loaded() {\n", 207 | " var el = document.getElementById(\"1001\");\n", 208 | " if (el != null) {\n", 209 | " el.textContent = \"BokehJS is loading...\";\n", 210 | " }\n", 211 | " if (root.Bokeh !== undefined) {\n", 212 | " if (el != null) {\n", 213 | " el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n", 214 | " }\n", 215 | " } else if (Date.now() < root._bokeh_timeout) {\n", 216 | " setTimeout(display_loaded, 100)\n", 217 | " }\n", 218 | " }\n", 219 | "\n", 220 | "\n", 221 | " function run_callbacks() {\n", 222 | " try {\n", 223 | " root._bokeh_onload_callbacks.forEach(function(callback) {\n", 224 | " if (callback != null)\n", 225 | " callback();\n", 226 | " });\n", 227 | " } finally {\n", 228 | " delete root._bokeh_onload_callbacks\n", 229 | " }\n", 230 | " console.debug(\"Bokeh: all callbacks have finished\");\n", 231 | " }\n", 232 | "\n", 233 | " function load_libs(css_urls, js_urls, callback) {\n", 234 | " if (css_urls == null) css_urls = [];\n", 235 | " if (js_urls == null) js_urls = [];\n", 236 | "\n", 237 | " root._bokeh_onload_callbacks.push(callback);\n", 238 | " if (root._bokeh_is_loading > 0) {\n", 239 | " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", 240 | " return null;\n", 241 | " }\n", 242 | " if (js_urls == null || js_urls.length === 0) {\n", 243 | " run_callbacks();\n", 244 | " return null;\n", 245 | " }\n", 246 | " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", 247 | " root._bokeh_is_loading = css_urls.length + js_urls.length;\n", 248 | "\n", 249 | " function on_load() {\n", 250 | " root._bokeh_is_loading--;\n", 251 | " if (root._bokeh_is_loading === 0) {\n", 252 | " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", 253 | " run_callbacks()\n", 254 | " }\n", 255 | " }\n", 256 | "\n", 257 | " function on_error() {\n", 258 | " console.error(\"failed to load \" + url);\n", 259 | " }\n", 260 | "\n", 261 | " for (var i = 0; i < css_urls.length; i++) {\n", 262 | " var url = css_urls[i];\n", 263 | " const element = document.createElement(\"link\");\n", 264 | " element.onload = on_load;\n", 265 | " element.onerror = on_error;\n", 266 | " element.rel = \"stylesheet\";\n", 267 | " element.type = \"text/css\";\n", 268 | " element.href = url;\n", 269 | " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", 270 | " document.body.appendChild(element);\n", 271 | " }\n", 272 | "\n", 273 | " const hashes = {\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\": \"JpP8FXbgAZLkfur7LiK3j9AGBhHNIvF742meBJrjO2ShJDhCG2I1uVvW+0DUtrmc\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\": \"xZlADit0Q04ISQEdKg2k3L4W9AwQBAuDs9nJL9fM/WwzL1tEU9VPNezOFX0nLEAz\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\": \"4BuPRZkdMKSnj3zoxiNrQ86XgNw0rYmBOxe7nshquXwwcauupgBF2DHLVG1WuZlV\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\": \"Dv1SQ87hmDqK6S5OhBf0bCuwAEvL5QYL0PuR/F1SPVhCS/r/abjkbpKDYL2zeM19\"};\n", 274 | "\n", 275 | " for (var i = 0; i < js_urls.length; i++) {\n", 276 | " var url = js_urls[i];\n", 277 | " var element = document.createElement('script');\n", 278 | " element.onload = on_load;\n", 279 | " element.onerror = on_error;\n", 280 | " element.async = false;\n", 281 | " element.src = url;\n", 282 | " if (url in hashes) {\n", 283 | " element.crossOrigin = \"anonymous\";\n", 284 | " element.integrity = \"sha384-\" + hashes[url];\n", 285 | " }\n", 286 | " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", 287 | " document.head.appendChild(element);\n", 288 | " }\n", 289 | " };var element = document.getElementById(\"1001\");\n", 290 | " if (element == null) {\n", 291 | " console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1001' but no matching script tag was found. \")\n", 292 | " return false;\n", 293 | " }\n", 294 | "\n", 295 | " function inject_raw_css(css) {\n", 296 | " const element = document.createElement(\"style\");\n", 297 | " element.appendChild(document.createTextNode(css));\n", 298 | " document.body.appendChild(element);\n", 299 | " }\n", 300 | "\n", 301 | " \n", 302 | " var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\"];\n", 303 | " var css_urls = [];\n", 304 | " \n", 305 | "\n", 306 | " var inline_js = [\n", 307 | " function(Bokeh) {\n", 308 | " Bokeh.set_log_level(\"info\");\n", 309 | " },\n", 310 | " function(Bokeh) {\n", 311 | " \n", 312 | " \n", 313 | " }\n", 314 | " ];\n", 315 | "\n", 316 | " function run_inline_js() {\n", 317 | " \n", 318 | " if (root.Bokeh !== undefined || force === true) {\n", 319 | " \n", 320 | " for (var i = 0; i < inline_js.length; i++) {\n", 321 | " inline_js[i].call(root, root.Bokeh);\n", 322 | " }\n", 323 | " if (force === true) {\n", 324 | " display_loaded();\n", 325 | " }} else if (Date.now() < root._bokeh_timeout) {\n", 326 | " setTimeout(run_inline_js, 100);\n", 327 | " } else if (!root._bokeh_failed_load) {\n", 328 | " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", 329 | " root._bokeh_failed_load = true;\n", 330 | " } else if (force !== true) {\n", 331 | " var cell = $(document.getElementById(\"1001\")).parents('.cell').data().cell;\n", 332 | " cell.output_area.append_execute_result(NB_LOAD_WARNING)\n", 333 | " }\n", 334 | "\n", 335 | " }\n", 336 | "\n", 337 | " if (root._bokeh_is_loading === 0) {\n", 338 | " console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n", 339 | " run_inline_js();\n", 340 | " } else {\n", 341 | " load_libs(css_urls, js_urls, function() {\n", 342 | " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", 343 | " run_inline_js();\n", 344 | " });\n", 345 | " }\n", 346 | "}(window));" 347 | ], 348 | "application/vnd.bokehjs_load.v0+json": "\n(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n\n if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n root._bokeh_onload_callbacks = [];\n root._bokeh_is_loading = undefined;\n }\n\n \n\n \n if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n var NB_LOAD_WARNING = {'data': {'text/html':\n \"
\\n\"+\n \"

\\n\"+\n \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n \"

\\n\"+\n \"\\n\"+\n \"\\n\"+\n \"from bokeh.resources import INLINE\\n\"+\n \"output_notebook(resources=INLINE)\\n\"+\n \"\\n\"+\n \"
\"}};\n\n function display_loaded() {\n var el = document.getElementById(\"1001\");\n if (el != null) {\n el.textContent = \"BokehJS is loading...\";\n }\n if (root.Bokeh !== undefined) {\n if (el != null) {\n el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(display_loaded, 100)\n }\n }\n\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n\n root._bokeh_onload_callbacks.push(callback);\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls == null || js_urls.length === 0) {\n run_callbacks();\n return null;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n root._bokeh_is_loading = css_urls.length + js_urls.length;\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n\n function on_error() {\n console.error(\"failed to load \" + url);\n }\n\n for (var i = 0; i < css_urls.length; i++) {\n var url = css_urls[i];\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n }\n\n const hashes = {\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\": \"JpP8FXbgAZLkfur7LiK3j9AGBhHNIvF742meBJrjO2ShJDhCG2I1uVvW+0DUtrmc\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\": \"xZlADit0Q04ISQEdKg2k3L4W9AwQBAuDs9nJL9fM/WwzL1tEU9VPNezOFX0nLEAz\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\": \"4BuPRZkdMKSnj3zoxiNrQ86XgNw0rYmBOxe7nshquXwwcauupgBF2DHLVG1WuZlV\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\": \"Dv1SQ87hmDqK6S5OhBf0bCuwAEvL5QYL0PuR/F1SPVhCS/r/abjkbpKDYL2zeM19\"};\n\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n if (url in hashes) {\n element.crossOrigin = \"anonymous\";\n element.integrity = \"sha384-\" + hashes[url];\n }\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n };var element = document.getElementById(\"1001\");\n if (element == null) {\n console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1001' but no matching script tag was found. \")\n return false;\n }\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n \n var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.0.1.min.js\"];\n var css_urls = [];\n \n\n var inline_js = [\n function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\n function(Bokeh) {\n \n \n }\n ];\n\n function run_inline_js() {\n \n if (root.Bokeh !== undefined || force === true) {\n \n for (var i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }\n if (force === true) {\n display_loaded();\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n } else if (force !== true) {\n var cell = $(document.getElementById(\"1001\")).parents('.cell').data().cell;\n cell.output_area.append_execute_result(NB_LOAD_WARNING)\n }\n\n }\n\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n run_inline_js();\n } else {\n load_libs(css_urls, js_urls, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n}(window));" 349 | }, 350 | "metadata": {}, 351 | "output_type": "display_data" 352 | } 353 | ], 354 | "source": [ 355 | "from bokeh.io import output_notebook, show\n", 356 | "from bokeh.plotting import figure\n", 357 | "\n", 358 | "output_notebook()" 359 | ] 360 | }, 361 | { 362 | "cell_type": "markdown", 363 | "metadata": {}, 364 | "source": [ 365 | "In the previous chapters we started to learn how to create single plots using differnet kinds of data. But we often want to plot more than one thing. Bokeh plots can be individually embedded in HTML documents, but it's often easier to\n", 366 | "combine multiple plots in one of Bokeh's built-in layouts. We will learn how to do that in this chapter\n", 367 | "\n", 368 | "The cell below defines a few data variables we will use in examples." 369 | ] 370 | }, 371 | { 372 | "cell_type": "code", 373 | "execution_count": 2, 374 | "metadata": {}, 375 | "outputs": [], 376 | "source": [ 377 | "x = list(range(11))\n", 378 | "y0, y1, y2 = x, [10-i for i in x], [abs(i-5) for i in x]" 379 | ] 380 | }, 381 | { 382 | "cell_type": "markdown", 383 | "metadata": {}, 384 | "source": [ 385 | "# Rows and Columns\n", 386 | "The `bokeh.layouts` modules provides the ``row`` and ``column`` functions to arrange plot objects in vertical or horizontal layouts. Below is an example of three plots arranged in a row." 387 | ] 388 | }, 389 | { 390 | "cell_type": "code", 391 | "execution_count": 3, 392 | "metadata": {}, 393 | "outputs": [ 394 | { 395 | "data": { 396 | "text/html": [ 397 | "\n", 398 | "\n", 399 | "\n", 400 | "\n", 401 | "\n", 402 | "\n", 403 | "
\n" 404 | ] 405 | }, 406 | "metadata": {}, 407 | "output_type": "display_data" 408 | }, 409 | { 410 | "data": { 411 | "application/javascript": [ 412 | "(function(root) {\n", 413 | " function embed_document(root) {\n", 414 | " \n", 415 | " var docs_json = {\"93fd67c1-e934-4920-b75e-ec6290893a00\":{\"roots\":{\"references\":[{\"attributes\":{\"children\":[{\"id\":\"1002\"},{\"id\":\"1038\"},{\"id\":\"1074\"}]},\"id\":\"1110\",\"type\":\"Row\"},{\"attributes\":{\"formatter\":{\"id\":\"1117\"},\"ticker\":{\"id\":\"1012\"}},\"id\":\"1011\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1136\",\"type\":\"UnionRenderers\"},{\"attributes\":{},\"id\":\"1009\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1137\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1012\",\"type\":\"BasicTicker\"},{\"attributes\":{\"axis\":{\"id\":\"1011\"},\"ticker\":null},\"id\":\"1014\",\"type\":\"Grid\"},{\"attributes\":{\"formatter\":{\"id\":\"1119\"},\"ticker\":{\"id\":\"1016\"}},\"id\":\"1015\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1016\",\"type\":\"BasicTicker\"},{\"attributes\":{\"axis\":{\"id\":\"1015\"},\"dimension\":1,\"ticker\":null},\"id\":\"1018\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1121\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"overlay\":{\"id\":\"1025\"}},\"id\":\"1021\",\"type\":\"BoxZoomTool\"},{\"attributes\":{},\"id\":\"1024\",\"type\":\"HelpTool\"},{\"attributes\":{},\"id\":\"1123\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1019\",\"type\":\"PanTool\"},{\"attributes\":{},\"id\":\"1020\",\"type\":\"WheelZoomTool\"},{\"attributes\":{},\"id\":\"1022\",\"type\":\"SaveTool\"},{\"attributes\":{},\"id\":\"1023\",\"type\":\"ResetTool\"},{\"attributes\":{\"bottom_units\":\"screen\",\"fill_alpha\":0.5,\"fill_color\":\"lightgrey\",\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":1.0,\"line_color\":\"black\",\"line_dash\":[4,4],\"line_width\":2,\"render_mode\":\"css\",\"right_units\":\"screen\",\"top_units\":\"screen\"},\"id\":\"1025\",\"type\":\"BoxAnnotation\"},{\"attributes\":{\"formatter\":{\"id\":\"1123\"},\"ticker\":{\"id\":\"1052\"}},\"id\":\"1051\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1125\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1127\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"source\":{\"id\":\"1033\"}},\"id\":\"1037\",\"type\":\"CDSView\"},{\"attributes\":{},\"id\":\"1052\",\"type\":\"BasicTicker\"},{\"attributes\":{},\"id\":\"1039\",\"type\":\"DataRange1d\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1019\"},{\"id\":\"1020\"},{\"id\":\"1021\"},{\"id\":\"1022\"},{\"id\":\"1023\"},{\"id\":\"1024\"}]},\"id\":\"1026\",\"type\":\"Toolbar\"},{\"attributes\":{},\"id\":\"1130\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"data_source\":{\"id\":\"1105\"},\"glyph\":{\"id\":\"1106\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"1107\"},\"selection_glyph\":null,\"view\":{\"id\":\"1109\"}},\"id\":\"1108\",\"type\":\"GlyphRenderer\"},{\"attributes\":{},\"id\":\"1131\",\"type\":\"Selection\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"navy\"},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"navy\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1035\",\"type\":\"Circle\"},{\"attributes\":{\"data\":{\"x\":[0,1,2,3,4,5,6,7,8,9,10],\"y\":[10,9,8,7,6,5,4,3,2,1,0]},\"selected\":{\"id\":\"1134\"},\"selection_policy\":{\"id\":\"1133\"}},\"id\":\"1069\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"value\":\"firebrick\"},\"line_alpha\":{\"value\":0.5},\"line_color\":{\"value\":\"firebrick\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1070\",\"type\":\"Triangle\"},{\"attributes\":{\"data_source\":{\"id\":\"1033\"},\"glyph\":{\"id\":\"1034\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"1035\"},\"selection_glyph\":null,\"view\":{\"id\":\"1037\"}},\"id\":\"1036\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"formatter\":{\"id\":\"1121\"},\"ticker\":{\"id\":\"1048\"}},\"id\":\"1047\",\"type\":\"LinearAxis\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"olive\"},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"olive\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1107\",\"type\":\"Square\"},{\"attributes\":{},\"id\":\"1043\",\"type\":\"LinearScale\"},{\"attributes\":{\"below\":[{\"id\":\"1011\"}],\"center\":[{\"id\":\"1014\"},{\"id\":\"1018\"}],\"left\":[{\"id\":\"1015\"}],\"plot_height\":250,\"plot_width\":250,\"renderers\":[{\"id\":\"1036\"}],\"title\":{\"id\":\"1112\"},\"toolbar\":{\"id\":\"1026\"},\"x_range\":{\"id\":\"1003\"},\"x_scale\":{\"id\":\"1007\"},\"y_range\":{\"id\":\"1005\"},\"y_scale\":{\"id\":\"1009\"}},\"id\":\"1002\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{\"below\":[{\"id\":\"1047\"}],\"center\":[{\"id\":\"1050\"},{\"id\":\"1054\"}],\"left\":[{\"id\":\"1051\"}],\"plot_height\":250,\"plot_width\":250,\"renderers\":[{\"id\":\"1072\"}],\"title\":{\"id\":\"1114\"},\"toolbar\":{\"id\":\"1062\"},\"x_range\":{\"id\":\"1039\"},\"x_scale\":{\"id\":\"1043\"},\"y_range\":{\"id\":\"1041\"},\"y_scale\":{\"id\":\"1045\"}},\"id\":\"1038\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1041\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"1045\",\"type\":\"LinearScale\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"value\":\"navy\"},\"line_alpha\":{\"value\":0.5},\"line_color\":{\"value\":\"navy\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1034\",\"type\":\"Circle\"},{\"attributes\":{\"axis\":{\"id\":\"1051\"},\"dimension\":1,\"ticker\":null},\"id\":\"1054\",\"type\":\"Grid\"},{\"attributes\":{\"data\":{\"x\":[0,1,2,3,4,5,6,7,8,9,10],\"y\":[0,1,2,3,4,5,6,7,8,9,10]},\"selected\":{\"id\":\"1131\"},\"selection_policy\":{\"id\":\"1130\"}},\"id\":\"1033\",\"type\":\"ColumnDataSource\"},{\"attributes\":{},\"id\":\"1005\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"1048\",\"type\":\"BasicTicker\"},{\"attributes\":{},\"id\":\"1003\",\"type\":\"DataRange1d\"},{\"attributes\":{\"axis\":{\"id\":\"1047\"},\"ticker\":null},\"id\":\"1050\",\"type\":\"Grid\"},{\"attributes\":{\"below\":[{\"id\":\"1083\"}],\"center\":[{\"id\":\"1086\"},{\"id\":\"1090\"}],\"left\":[{\"id\":\"1087\"}],\"plot_height\":250,\"plot_width\":250,\"renderers\":[{\"id\":\"1108\"}],\"title\":{\"id\":\"1116\"},\"toolbar\":{\"id\":\"1098\"},\"x_range\":{\"id\":\"1075\"},\"x_scale\":{\"id\":\"1079\"},\"y_range\":{\"id\":\"1077\"},\"y_scale\":{\"id\":\"1081\"}},\"id\":\"1074\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1007\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1055\",\"type\":\"PanTool\"},{\"attributes\":{},\"id\":\"1056\",\"type\":\"WheelZoomTool\"},{\"attributes\":{},\"id\":\"1060\",\"type\":\"HelpTool\"},{\"attributes\":{\"overlay\":{\"id\":\"1061\"}},\"id\":\"1057\",\"type\":\"BoxZoomTool\"},{\"attributes\":{},\"id\":\"1058\",\"type\":\"SaveTool\"},{\"attributes\":{},\"id\":\"1059\",\"type\":\"ResetTool\"},{\"attributes\":{},\"id\":\"1134\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1133\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"overlay\":{\"id\":\"1097\"}},\"id\":\"1093\",\"type\":\"BoxZoomTool\"},{\"attributes\":{},\"id\":\"1094\",\"type\":\"SaveTool\"},{\"attributes\":{},\"id\":\"1095\",\"type\":\"ResetTool\"},{\"attributes\":{\"bottom_units\":\"screen\",\"fill_alpha\":0.5,\"fill_color\":\"lightgrey\",\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":1.0,\"line_color\":\"black\",\"line_dash\":[4,4],\"line_width\":2,\"render_mode\":\"css\",\"right_units\":\"screen\",\"top_units\":\"screen\"},\"id\":\"1061\",\"type\":\"BoxAnnotation\"},{\"attributes\":{\"source\":{\"id\":\"1105\"}},\"id\":\"1109\",\"type\":\"CDSView\"},{\"attributes\":{\"text\":\"\"},\"id\":\"1112\",\"type\":\"Title\"},{\"attributes\":{},\"id\":\"1075\",\"type\":\"DataRange1d\"},{\"attributes\":{\"text\":\"\"},\"id\":\"1114\",\"type\":\"Title\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1055\"},{\"id\":\"1056\"},{\"id\":\"1057\"},{\"id\":\"1058\"},{\"id\":\"1059\"},{\"id\":\"1060\"}]},\"id\":\"1062\",\"type\":\"Toolbar\"},{\"attributes\":{\"text\":\"\"},\"id\":\"1116\",\"type\":\"Title\"},{\"attributes\":{\"source\":{\"id\":\"1069\"}},\"id\":\"1073\",\"type\":\"CDSView\"},{\"attributes\":{\"data_source\":{\"id\":\"1069\"},\"glyph\":{\"id\":\"1070\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"1071\"},\"selection_glyph\":null,\"view\":{\"id\":\"1073\"}},\"id\":\"1072\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"firebrick\"},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"firebrick\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1071\",\"type\":\"Triangle\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"value\":\"olive\"},\"line_alpha\":{\"value\":0.5},\"line_color\":{\"value\":\"olive\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1106\",\"type\":\"Square\"},{\"attributes\":{\"data\":{\"x\":[0,1,2,3,4,5,6,7,8,9,10],\"y\":[5,4,3,2,1,0,1,2,3,4,5]},\"selected\":{\"id\":\"1137\"},\"selection_policy\":{\"id\":\"1136\"}},\"id\":\"1105\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"formatter\":{\"id\":\"1125\"},\"ticker\":{\"id\":\"1084\"}},\"id\":\"1083\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1079\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1077\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"1117\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1081\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1091\",\"type\":\"PanTool\"},{\"attributes\":{},\"id\":\"1084\",\"type\":\"BasicTicker\"},{\"attributes\":{\"axis\":{\"id\":\"1083\"},\"ticker\":null},\"id\":\"1086\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1119\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"formatter\":{\"id\":\"1127\"},\"ticker\":{\"id\":\"1088\"}},\"id\":\"1087\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1088\",\"type\":\"BasicTicker\"},{\"attributes\":{\"axis\":{\"id\":\"1087\"},\"dimension\":1,\"ticker\":null},\"id\":\"1090\",\"type\":\"Grid\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1091\"},{\"id\":\"1092\"},{\"id\":\"1093\"},{\"id\":\"1094\"},{\"id\":\"1095\"},{\"id\":\"1096\"}]},\"id\":\"1098\",\"type\":\"Toolbar\"},{\"attributes\":{},\"id\":\"1092\",\"type\":\"WheelZoomTool\"},{\"attributes\":{},\"id\":\"1096\",\"type\":\"HelpTool\"},{\"attributes\":{\"bottom_units\":\"screen\",\"fill_alpha\":0.5,\"fill_color\":\"lightgrey\",\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":1.0,\"line_color\":\"black\",\"line_dash\":[4,4],\"line_width\":2,\"render_mode\":\"css\",\"right_units\":\"screen\",\"top_units\":\"screen\"},\"id\":\"1097\",\"type\":\"BoxAnnotation\"}],\"root_ids\":[\"1110\"]},\"title\":\"Bokeh Application\",\"version\":\"2.0.1\"}};\n", 416 | " var render_items = [{\"docid\":\"93fd67c1-e934-4920-b75e-ec6290893a00\",\"root_ids\":[\"1110\"],\"roots\":{\"1110\":\"eeaed001-55c8-44ba-bd5e-4887c7578d24\"}}];\n", 417 | " root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n", 418 | "\n", 419 | " }\n", 420 | " if (root.Bokeh !== undefined) {\n", 421 | " embed_document(root);\n", 422 | " } else {\n", 423 | " var attempts = 0;\n", 424 | " var timer = setInterval(function(root) {\n", 425 | " if (root.Bokeh !== undefined) {\n", 426 | " clearInterval(timer);\n", 427 | " embed_document(root);\n", 428 | " } else {\n", 429 | " attempts++;\n", 430 | " if (attempts > 100) {\n", 431 | " clearInterval(timer);\n", 432 | " console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n", 433 | " }\n", 434 | " }\n", 435 | " }, 10, root)\n", 436 | " }\n", 437 | "})(window);" 438 | ], 439 | "application/vnd.bokehjs_exec.v0+json": "" 440 | }, 441 | "metadata": { 442 | "application/vnd.bokehjs_exec.v0+json": { 443 | "id": "1110" 444 | } 445 | }, 446 | "output_type": "display_data" 447 | } 448 | ], 449 | "source": [ 450 | "from bokeh.layouts import row\n", 451 | "\n", 452 | "# create a new plot\n", 453 | "s1 = figure(width=250, plot_height=250)\n", 454 | "s1.circle(x, y0, size=10, color=\"navy\", alpha=0.5)\n", 455 | "\n", 456 | "# create another one\n", 457 | "s2 = figure(width=250, height=250)\n", 458 | "s2.triangle(x, y1, size=10, color=\"firebrick\", alpha=0.5)\n", 459 | "\n", 460 | "# create and another\n", 461 | "s3 = figure(width=250, height=250)\n", 462 | "s3.square(x, y2, size=10, color=\"olive\", alpha=0.5)\n", 463 | "\n", 464 | "# show the results in a row\n", 465 | "show(row(s1, s2, s3))" 466 | ] 467 | }, 468 | { 469 | "cell_type": "code", 470 | "execution_count": 4, 471 | "metadata": {}, 472 | "outputs": [], 473 | "source": [ 474 | "# EXERCISE: use column to arrange a few plots vertically (don't forget to import column)\n" 475 | ] 476 | }, 477 | { 478 | "cell_type": "markdown", 479 | "metadata": {}, 480 | "source": [ 481 | "# Grid plots\n", 482 | "\n", 483 | "Bokeh also provides a `gridplot` layout in `bokeh.layouts` for arranging plots in a grid, as show in the example below." 484 | ] 485 | }, 486 | { 487 | "cell_type": "code", 488 | "execution_count": 5, 489 | "metadata": {}, 490 | "outputs": [ 491 | { 492 | "data": { 493 | "text/html": [ 494 | "\n", 495 | "\n", 496 | "\n", 497 | "\n", 498 | "\n", 499 | "\n", 500 | "
\n" 501 | ] 502 | }, 503 | "metadata": {}, 504 | "output_type": "display_data" 505 | }, 506 | { 507 | "data": { 508 | "application/javascript": [ 509 | "(function(root) {\n", 510 | " function embed_document(root) {\n", 511 | " \n", 512 | " var docs_json = {\"2107de56-148c-497e-909d-34f46d8ba2a4\":{\"roots\":{\"references\":[{\"attributes\":{\"children\":[[{\"id\":\"1300\"},0,0],[{\"id\":\"1336\"},0,1],[{\"id\":\"1372\"},1,0]]},\"id\":\"1438\",\"type\":\"GridBox\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1317\"},{\"id\":\"1318\"},{\"id\":\"1319\"},{\"id\":\"1320\"},{\"id\":\"1321\"},{\"id\":\"1322\"}]},\"id\":\"1324\",\"type\":\"Toolbar\"},{\"attributes\":{\"below\":[{\"id\":\"1345\"}],\"center\":[{\"id\":\"1348\"},{\"id\":\"1352\"}],\"left\":[{\"id\":\"1349\"}],\"plot_height\":250,\"plot_width\":250,\"renderers\":[{\"id\":\"1370\"}],\"title\":{\"id\":\"1419\"},\"toolbar\":{\"id\":\"1360\"},\"toolbar_location\":null,\"x_range\":{\"id\":\"1337\"},\"x_scale\":{\"id\":\"1341\"},\"y_range\":{\"id\":\"1339\"},\"y_scale\":{\"id\":\"1343\"}},\"id\":\"1336\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1320\",\"type\":\"SaveTool\"},{\"attributes\":{},\"id\":\"1317\",\"type\":\"PanTool\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"value\":\"navy\"},\"line_alpha\":{\"value\":0.5},\"line_color\":{\"value\":\"navy\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1332\",\"type\":\"Circle\"},{\"attributes\":{\"data\":{\"x\":[0,1,2,3,4,5,6,7,8,9,10],\"y\":[0,1,2,3,4,5,6,7,8,9,10]},\"selected\":{\"id\":\"1416\"},\"selection_policy\":{\"id\":\"1415\"}},\"id\":\"1331\",\"type\":\"ColumnDataSource\"},{\"attributes\":{},\"id\":\"1301\",\"type\":\"DataRange1d\"},{\"attributes\":{\"below\":[{\"id\":\"1309\"}],\"center\":[{\"id\":\"1312\"},{\"id\":\"1316\"}],\"left\":[{\"id\":\"1313\"}],\"plot_height\":250,\"plot_width\":250,\"renderers\":[{\"id\":\"1334\"}],\"title\":{\"id\":\"1409\"},\"toolbar\":{\"id\":\"1324\"},\"toolbar_location\":null,\"x_range\":{\"id\":\"1301\"},\"x_scale\":{\"id\":\"1305\"},\"y_range\":{\"id\":\"1303\"},\"y_scale\":{\"id\":\"1307\"}},\"id\":\"1300\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{\"below\":[{\"id\":\"1381\"}],\"center\":[{\"id\":\"1384\"},{\"id\":\"1388\"}],\"left\":[{\"id\":\"1385\"}],\"plot_height\":250,\"plot_width\":250,\"renderers\":[{\"id\":\"1406\"}],\"title\":{\"id\":\"1429\"},\"toolbar\":{\"id\":\"1396\"},\"toolbar_location\":null,\"x_range\":{\"id\":\"1373\"},\"x_scale\":{\"id\":\"1377\"},\"y_range\":{\"id\":\"1375\"},\"y_scale\":{\"id\":\"1379\"}},\"id\":\"1372\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{\"formatter\":{\"id\":\"1410\"},\"ticker\":{\"id\":\"1310\"}},\"id\":\"1309\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1318\",\"type\":\"WheelZoomTool\"},{\"attributes\":{},\"id\":\"1305\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1303\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"1307\",\"type\":\"LinearScale\"},{\"attributes\":{\"overlay\":{\"id\":\"1323\"}},\"id\":\"1319\",\"type\":\"BoxZoomTool\"},{\"attributes\":{},\"id\":\"1310\",\"type\":\"BasicTicker\"},{\"attributes\":{\"axis\":{\"id\":\"1309\"},\"ticker\":null},\"id\":\"1312\",\"type\":\"Grid\"},{\"attributes\":{\"formatter\":{\"id\":\"1412\"},\"ticker\":{\"id\":\"1314\"}},\"id\":\"1313\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1314\",\"type\":\"BasicTicker\"},{\"attributes\":{\"axis\":{\"id\":\"1313\"},\"dimension\":1,\"ticker\":null},\"id\":\"1316\",\"type\":\"Grid\"},{\"attributes\":{\"data_source\":{\"id\":\"1403\"},\"glyph\":{\"id\":\"1404\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"1405\"},\"selection_glyph\":null,\"view\":{\"id\":\"1407\"}},\"id\":\"1406\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"text\":\"\"},\"id\":\"1409\",\"type\":\"Title\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"olive\"},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"olive\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1405\",\"type\":\"Square\"},{\"attributes\":{\"bottom_units\":\"screen\",\"fill_alpha\":0.5,\"fill_color\":\"lightgrey\",\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":1.0,\"line_color\":\"black\",\"line_dash\":[4,4],\"line_width\":2,\"render_mode\":\"css\",\"right_units\":\"screen\",\"top_units\":\"screen\"},\"id\":\"1359\",\"type\":\"BoxAnnotation\"},{\"attributes\":{\"source\":{\"id\":\"1403\"}},\"id\":\"1407\",\"type\":\"CDSView\"},{\"attributes\":{\"bottom_units\":\"screen\",\"fill_alpha\":0.5,\"fill_color\":\"lightgrey\",\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":1.0,\"line_color\":\"black\",\"line_dash\":[4,4],\"line_width\":2,\"render_mode\":\"css\",\"right_units\":\"screen\",\"top_units\":\"screen\"},\"id\":\"1323\",\"type\":\"BoxAnnotation\"},{\"attributes\":{},\"id\":\"1322\",\"type\":\"HelpTool\"},{\"attributes\":{},\"id\":\"1321\",\"type\":\"ResetTool\"},{\"attributes\":{},\"id\":\"1426\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1425\",\"type\":\"UnionRenderers\"},{\"attributes\":{},\"id\":\"1410\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1412\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"source\":{\"id\":\"1367\"}},\"id\":\"1371\",\"type\":\"CDSView\"},{\"attributes\":{\"data_source\":{\"id\":\"1367\"},\"glyph\":{\"id\":\"1368\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"1369\"},\"selection_glyph\":null,\"view\":{\"id\":\"1371\"}},\"id\":\"1370\",\"type\":\"GlyphRenderer\"},{\"attributes\":{},\"id\":\"1339\",\"type\":\"DataRange1d\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"firebrick\"},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"firebrick\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1369\",\"type\":\"Triangle\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"value\":\"olive\"},\"line_alpha\":{\"value\":0.5},\"line_color\":{\"value\":\"olive\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1404\",\"type\":\"Square\"},{\"attributes\":{\"data\":{\"x\":[0,1,2,3,4,5,6,7,8,9,10],\"y\":[5,4,3,2,1,0,1,2,3,4,5]},\"selected\":{\"id\":\"1436\"},\"selection_policy\":{\"id\":\"1435\"}},\"id\":\"1403\",\"type\":\"ColumnDataSource\"},{\"attributes\":{},\"id\":\"1373\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"1379\",\"type\":\"LinearScale\"},{\"attributes\":{\"text\":\"\"},\"id\":\"1429\",\"type\":\"Title\"},{\"attributes\":{},\"id\":\"1377\",\"type\":\"LinearScale\"},{\"attributes\":{\"source\":{\"id\":\"1331\"}},\"id\":\"1335\",\"type\":\"CDSView\"},{\"attributes\":{\"axis\":{\"id\":\"1381\"},\"ticker\":null},\"id\":\"1384\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1415\",\"type\":\"UnionRenderers\"},{\"attributes\":{},\"id\":\"1375\",\"type\":\"DataRange1d\"},{\"attributes\":{\"data_source\":{\"id\":\"1331\"},\"glyph\":{\"id\":\"1332\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"1333\"},\"selection_glyph\":null,\"view\":{\"id\":\"1335\"}},\"id\":\"1334\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"navy\"},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"navy\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1333\",\"type\":\"Circle\"},{\"attributes\":{},\"id\":\"1416\",\"type\":\"Selection\"},{\"attributes\":{\"formatter\":{\"id\":\"1430\"},\"ticker\":{\"id\":\"1382\"}},\"id\":\"1381\",\"type\":\"LinearAxis\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"value\":\"firebrick\"},\"line_alpha\":{\"value\":0.5},\"line_color\":{\"value\":\"firebrick\"},\"size\":{\"units\":\"screen\",\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1368\",\"type\":\"Triangle\"},{\"attributes\":{\"data\":{\"x\":[0,1,2,3,4,5,6,7,8,9,10],\"y\":[10,9,8,7,6,5,4,3,2,1,0]},\"selected\":{\"id\":\"1426\"},\"selection_policy\":{\"id\":\"1425\"}},\"id\":\"1367\",\"type\":\"ColumnDataSource\"},{\"attributes\":{},\"id\":\"1389\",\"type\":\"PanTool\"},{\"attributes\":{},\"id\":\"1337\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"1382\",\"type\":\"BasicTicker\"},{\"attributes\":{\"formatter\":{\"id\":\"1432\"},\"ticker\":{\"id\":\"1386\"}},\"id\":\"1385\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1341\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1386\",\"type\":\"BasicTicker\"},{\"attributes\":{\"formatter\":{\"id\":\"1420\"},\"ticker\":{\"id\":\"1346\"}},\"id\":\"1345\",\"type\":\"LinearAxis\"},{\"attributes\":{\"axis\":{\"id\":\"1385\"},\"dimension\":1,\"ticker\":null},\"id\":\"1388\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1343\",\"type\":\"LinearScale\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1389\"},{\"id\":\"1390\"},{\"id\":\"1391\"},{\"id\":\"1392\"},{\"id\":\"1393\"},{\"id\":\"1394\"}]},\"id\":\"1396\",\"type\":\"Toolbar\"},{\"attributes\":{},\"id\":\"1390\",\"type\":\"WheelZoomTool\"},{\"attributes\":{},\"id\":\"1430\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1394\",\"type\":\"HelpTool\"},{\"attributes\":{},\"id\":\"1346\",\"type\":\"BasicTicker\"},{\"attributes\":{\"overlay\":{\"id\":\"1395\"}},\"id\":\"1391\",\"type\":\"BoxZoomTool\"},{\"attributes\":{},\"id\":\"1392\",\"type\":\"SaveTool\"},{\"attributes\":{},\"id\":\"1353\",\"type\":\"PanTool\"},{\"attributes\":{},\"id\":\"1393\",\"type\":\"ResetTool\"},{\"attributes\":{\"axis\":{\"id\":\"1345\"},\"ticker\":null},\"id\":\"1348\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1432\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"formatter\":{\"id\":\"1422\"},\"ticker\":{\"id\":\"1350\"}},\"id\":\"1349\",\"type\":\"LinearAxis\"},{\"attributes\":{\"text\":\"\"},\"id\":\"1419\",\"type\":\"Title\"},{\"attributes\":{},\"id\":\"1350\",\"type\":\"BasicTicker\"},{\"attributes\":{\"axis\":{\"id\":\"1349\"},\"dimension\":1,\"ticker\":null},\"id\":\"1352\",\"type\":\"Grid\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1353\"},{\"id\":\"1354\"},{\"id\":\"1355\"},{\"id\":\"1356\"},{\"id\":\"1357\"},{\"id\":\"1358\"}]},\"id\":\"1360\",\"type\":\"Toolbar\"},{\"attributes\":{},\"id\":\"1354\",\"type\":\"WheelZoomTool\"},{\"attributes\":{},\"id\":\"1358\",\"type\":\"HelpTool\"},{\"attributes\":{\"overlay\":{\"id\":\"1359\"}},\"id\":\"1355\",\"type\":\"BoxZoomTool\"},{\"attributes\":{},\"id\":\"1356\",\"type\":\"SaveTool\"},{\"attributes\":{},\"id\":\"1357\",\"type\":\"ResetTool\"},{\"attributes\":{},\"id\":\"1420\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1435\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"bottom_units\":\"screen\",\"fill_alpha\":0.5,\"fill_color\":\"lightgrey\",\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":1.0,\"line_color\":\"black\",\"line_dash\":[4,4],\"line_width\":2,\"render_mode\":\"css\",\"right_units\":\"screen\",\"top_units\":\"screen\"},\"id\":\"1395\",\"type\":\"BoxAnnotation\"},{\"attributes\":{},\"id\":\"1436\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1422\",\"type\":\"BasicTickFormatter\"}],\"root_ids\":[\"1438\"]},\"title\":\"Bokeh Application\",\"version\":\"2.0.1\"}};\n", 513 | " var render_items = [{\"docid\":\"2107de56-148c-497e-909d-34f46d8ba2a4\",\"root_ids\":[\"1438\"],\"roots\":{\"1438\":\"641491c6-6ab8-4a61-ad77-c6bedea5731d\"}}];\n", 514 | " root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n", 515 | "\n", 516 | " }\n", 517 | " if (root.Bokeh !== undefined) {\n", 518 | " embed_document(root);\n", 519 | " } else {\n", 520 | " var attempts = 0;\n", 521 | " var timer = setInterval(function(root) {\n", 522 | " if (root.Bokeh !== undefined) {\n", 523 | " clearInterval(timer);\n", 524 | " embed_document(root);\n", 525 | " } else {\n", 526 | " attempts++;\n", 527 | " if (attempts > 100) {\n", 528 | " clearInterval(timer);\n", 529 | " console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n", 530 | " }\n", 531 | " }\n", 532 | " }, 10, root)\n", 533 | " }\n", 534 | "})(window);" 535 | ], 536 | "application/vnd.bokehjs_exec.v0+json": "" 537 | }, 538 | "metadata": { 539 | "application/vnd.bokehjs_exec.v0+json": { 540 | "id": "1438" 541 | } 542 | }, 543 | "output_type": "display_data" 544 | } 545 | ], 546 | "source": [ 547 | "from bokeh.layouts import gridplot\n", 548 | "\n", 549 | "# create a new plot\n", 550 | "s1 = figure(width=250, plot_height=250)\n", 551 | "s1.circle(x, y0, size=10, color=\"navy\", alpha=0.5)\n", 552 | "\n", 553 | "# create another one\n", 554 | "s2 = figure(width=250, height=250)\n", 555 | "s2.triangle(x, y1, size=10, color=\"firebrick\", alpha=0.5)\n", 556 | "\n", 557 | "# create and another\n", 558 | "s3 = figure(width=250, height=250)\n", 559 | "s3.square(x, y2, size=10, color=\"olive\", alpha=0.5)\n", 560 | "\n", 561 | "# put all the plots in a gridplot\n", 562 | "p = gridplot([[s1, s2], [s3, None]], toolbar_location=None)\n", 563 | "\n", 564 | "# show the results\n", 565 | "show(p)" 566 | ] 567 | }, 568 | { 569 | "cell_type": "code", 570 | "execution_count": 6, 571 | "metadata": {}, 572 | "outputs": [], 573 | "source": [ 574 | "# EXERCISE: create a gridplot of your own\n" 575 | ] 576 | }, 577 | { 578 | "cell_type": "markdown", 579 | "metadata": {}, 580 | "source": [ 581 | "# Next Section" 582 | ] 583 | }, 584 | { 585 | "cell_type": "markdown", 586 | "metadata": {}, 587 | "source": [ 588 | "Click on this link to go to the next notebook: [06 - Linking and Interactions](06%20-%20Linking%20and%20Interactions.ipynb).\n", 589 | "\n", 590 | "To go back to the overview, click [here](00%20-%20Introduction%20and%20Setup.ipynb)." 591 | ] 592 | }, 593 | { 594 | "cell_type": "code", 595 | "execution_count": null, 596 | "metadata": {}, 597 | "outputs": [], 598 | "source": [] 599 | } 600 | ], 601 | "metadata": { 602 | "kernelspec": { 603 | "display_name": "Python 3", 604 | "language": "python", 605 | "name": "python3" 606 | }, 607 | "language_info": { 608 | "codemirror_mode": { 609 | "name": "ipython", 610 | "version": 3 611 | }, 612 | "file_extension": ".py", 613 | "mimetype": "text/x-python", 614 | "name": "python", 615 | "nbconvert_exporter": "python", 616 | "pygments_lexer": "ipython3", 617 | "version": "3.8.2" 618 | } 619 | }, 620 | "nbformat": 4, 621 | "nbformat_minor": 4 622 | } 623 | --------------------------------------------------------------------------------