{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "![title](header.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you've ever wondered how blogs and news websites create interactive data visualisations, chances are they either use d3.js (written in javascript), or Bokeh (written in Python). If you're looking to spend some time to make your visualisations stand out from the rest, Bokeh is a great way to do it. By the end of this guide you should have some idea how to create interactive visualisations and be able to start your journey to make world class data visualisations.\n", "\n", "## Getting Started With Bokeh\n", "\n", "Bokeh can output to HTML or directly into the notebook, but we need to tell it which one we want. For outputting to our notebook we just use the output_notebook() function:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n", " \n", " Loading BokehJS ...\n", "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": [ "\n", "(function(global) {\n", " function now() {\n", " return new Date();\n", " }\n", "\n", " var force = true;\n", "\n", " if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n", " window._bokeh_onload_callbacks = [];\n", " window._bokeh_is_loading = undefined;\n", " }\n", "\n", "\n", " \n", " if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n", " window._bokeh_timeout = Date.now() + 5000;\n", " window._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", " if (window.Bokeh !== undefined) {\n", " document.getElementById(\"a4d7cb63-af10-4fea-9179-48f0cbcd3ad5\").textContent = \"BokehJS successfully loaded.\";\n", " } else if (Date.now() < window._bokeh_timeout) {\n", " setTimeout(display_loaded, 100)\n", " }\n", " }\n", "\n", " function run_callbacks() {\n", " window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n", " delete window._bokeh_onload_callbacks\n", " console.info(\"Bokeh: all callbacks have finished\");\n", " }\n", "\n", " function load_libs(js_urls, callback) {\n", " window._bokeh_onload_callbacks.push(callback);\n", " if (window._bokeh_is_loading > 0) {\n", " console.log(\"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.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", " window._bokeh_is_loading = js_urls.length;\n", " for (var i = 0; i < js_urls.length; i++) {\n", " var url = js_urls[i];\n", " var s = document.createElement('script');\n", " s.src = url;\n", " s.async = false;\n", " s.onreadystatechange = s.onload = function() {\n", " window._bokeh_is_loading--;\n", " if (window._bokeh_is_loading === 0) {\n", " console.log(\"Bokeh: all BokehJS libraries loaded\");\n", " run_callbacks()\n", " }\n", " };\n", " s.onerror = function() {\n", " console.warn(\"failed to load library \" + url);\n", " };\n", " console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", " document.getElementsByTagName(\"head\")[0].appendChild(s);\n", " }\n", " };var element = document.getElementById(\"a4d7cb63-af10-4fea-9179-48f0cbcd3ad5\");\n", " if (element == null) {\n", " console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'a4d7cb63-af10-4fea-9179-48f0cbcd3ad5' but no matching script tag was found. \")\n", " return false;\n", " }\n", "\n", " var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.js\"];\n", "\n", " var inline_js = [\n", " function(Bokeh) {\n", " Bokeh.set_log_level(\"info\");\n", " },\n", " \n", " function(Bokeh) {\n", " \n", " document.getElementById(\"a4d7cb63-af10-4fea-9179-48f0cbcd3ad5\").textContent = \"BokehJS is loading...\";\n", " },\n", " function(Bokeh) {\n", " console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n", " Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n", " console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n", " Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n", " }\n", " ];\n", "\n", " function run_inline_js() {\n", " \n", " if ((window.Bokeh !== undefined) || (force === true)) {\n", " for (var i = 0; i < inline_js.length; i++) {\n", " inline_js[i](window.Bokeh);\n", " }if (force === true) {\n", " display_loaded();\n", " }} else if (Date.now() < window._bokeh_timeout) {\n", " setTimeout(run_inline_js, 100);\n", " } else if (!window._bokeh_failed_load) {\n", " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", " window._bokeh_failed_load = true;\n", " } else if (force !== true) {\n", " var cell = $(document.getElementById(\"a4d7cb63-af10-4fea-9179-48f0cbcd3ad5\")).parents('.cell').data().cell;\n", " cell.output_area.append_execute_result(NB_LOAD_WARNING)\n", " }\n", "\n", " }\n", "\n", " if (window._bokeh_is_loading === 0) {\n", " console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n", " run_inline_js();\n", " } else {\n", " load_libs(js_urls, function() {\n", " console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n", " run_inline_js();\n", " });\n", " }\n", "}(this));" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import numpy as np\n", "import pandas as pd\n", "\n", "import bokeh.plotting as bkp\n", "\n", "bkp.output_notebook()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's worth noting that although Bokeh code is written in Python, it works via Javascript behind the scenes, however we don't really need to worry about this.\n", "\n", "Plotting a scatter graph is similar to plotting using matplotlib - we take a list or array of x and y points in and pass them as a function. The difference here is that we create a figure then call a method on it to plot:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "x = np.linspace(-10,10,101)\n", "y = np.sin(x)\n", "\n", "fig = bkp.figure(width=500,height=200)\n", "fig.circle(x,y,size=7,color=\"red\", alpha=0.8)\n", "\n", "bkp.show(fig)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The are a number of shapes we can use to plot, more can be found in the documentation [here.](http://bokeh.pydata.org/en/latest/docs/user_guide/plotting.html)\n", "\n", "Let's also look at another helpful plot - the line plot. This works in exactly the same way to the scatter plot, but we use the line method instead of the circle method." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig2 = bkp.figure(width=500,height=200)\n", "fig2.line(x,y, line_width=2)\n", "\n", "bkp.show(fig2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plotting shapes on our axis is simple - we just use the method for the shape we want. For rectangles this is quad, and takes arguements for the top, bottom, left and right of each rectangle:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig3 = bkp.figure(width=500,height=500)\n", "fig3.quad(top=[5,3,5], bottom=[0,2,0], left=[0,1,3], right=[1,3,4], color=\"#ff9933\")\n", "\n", "bkp.show(fig3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We could use this method for bar graphs, but thankfully Bokeh gives us the vbar and hbar methods to speed things up:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig4 = bkp.figure(width=500,height=500)\n", "fig4.vbar(x=[1,2,3], width=0.3, bottom=0, top=[1,2,3])\n", "\n", "bkp.show(fig4)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are many, many more methods for plotting in Bokeh, but for the sake of space, we're not going to look at them here. Next up - annotating our plots.\n", "\n", "## Titles and Annotations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Bokeh gives us a number of ways to annotate our plots. The simplest being title, which we include in the figure function:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig = bkp.figure(title=\"My First Plot\", width=500,height=200)\n", "fig.circle(x,y,size=7,color=\"red\", alpha=0.8)\n", "\n", "bkp.show(fig)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Similarly to matplotlib, there is more than one way to edit the title of a plot. We can edit the attributes of a plot after initialising it by assigning values to methods on the plot:" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig5 = bkp.figure(width=500,height=200)\n", "fig5.circle([1,2],[1,2], size=100, alpha=0.6)\n", "\n", "fig5.title.text = \"I'm a new title!\"\n", "fig5.title.align = \"right\"\n", "fig5.title.text_font_size = \"30px\"\n", "\n", "bkp.show(fig5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we can see that the toolbar is unhelpfully placed on the right of the graph, meaning it hangs awkwardly off the side of our plot. We can change the location of the toolbar with the toolbar_location arguement or method:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig5.toolbar_location=\"above\"\n", "fig5.title.align = \"left\"\n", "\n", "bkp.show(fig5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, adding a legend can take some work, but is simple enough - we can either plot the two sets seperately or provide a list of labels corresponding with our data points." ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "x = np.linspace(-10,10,101)\n", "\n", "y1 = np.sin(x)\n", "y2 = np.cos(x)\n", "\n", "fig6 = bkp.figure(height=200)\n", "\n", "fig6.circle(x,y1,color='blue',legend='y1',alpha=0.5,size=6)\n", "fig6.circle(x,y2,color='red', legend='y2', alpha=0.5,size=6)\n", "\n", "bkp.show(fig6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Mini Project\n", "\n", "Your challenge is to create the HiPy logo as best you can using Bokeh shapes! If you're finding this hard - try starting with rectangles and then look up the patch plotting method that allows custom shapes. From here it should be pretty easy. Good Luck!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.0" } }, "nbformat": 4, "nbformat_minor": 2 }