{ "metadata": { "name": "", "signature": "sha256:cb1b97254da234e4df4ba840297cd29cadf844410f4a0257328e386c16c67ab7" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "code", "collapsed": false, "input": [ "%autosave 10" ], "language": "python", "metadata": {}, "outputs": [ { "javascript": [ "IPython.notebook.set_autosave_interval(10000)" ], "metadata": {}, "output_type": "display_data" }, { "output_type": "stream", "stream": "stdout", "text": [ "Autosaving every 10 seconds\n" ] } ], "prompt_number": 3 }, { "cell_type": "raw", "metadata": {}, "source": [ "tarball is missing the data folder, so hit it later today\n", "\n", " wget http://cdn.pydata.org/BokehTutorial.tgz" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Main tutorial startpoint: `BokehTutorial/html/index.html`\n", "- This is an interactive class, not many slides.\n", "- Extremely large number of exercises. Need to work on most of them at home.\n", " - This will take a long time!\n", "- Very easy to shove into IPython Notebook. Just use `output_notebook()` then restart your kernel (Kernel -> Restart).\n", "\n", "## Idea\n", "\n", "- Interactive web visualisation without JavaScript\n", "- Many bindings, not just Python\n", "\n", "##\u00a0Bokeh\n", "\n", "- Built on HTML5 canvas. Not d3.js. Faster than SVG.\n", "- Novel and custom visualisations, again without with JavaScript, directly in the binding (i.e. Python)\n", "- Takes a lot of inspiration from the Grammar of Graphics (the basis of ggplot)\n", "- Two parts\n", " - Reactive JavaScript portion, `bokehjs`, accepts JSON input of layout and data. Standalone, pure JavaScript, anything can provide JSON.\n", " - See: [http://jsfiddle.net/user/bokeh/fiddles/](http://jsfiddle.net/user/bokeh/fiddles/)\n", " - Language bindings to a server.\n", "\n", "##\u00a0Concepts\n", "\n", "- Plots are based on glyphs\n", "- Visual elements of glpyhs connect to vectors" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "!!AI these plots are mind blowing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Coming in future releases\n", "\n", "(0.5 coming end of April 2014; a lot of this coming then)\n", "\n", "- **Abstract rendering** - dynamic downsampling for millions of points\n", "- **matplotlib compatibility** - use Bokeh from pandas, ggplot.py, Seaborn\n", "- **Language bindings** - Scala is coming\n", "- **Constraints-based layout system** - legends, annotations, grid plots\n", "- **Usability** - error messages, discoverable parameters, more live gallery examples" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import numpy as np\n", "from bokeh.plotting import *\n", "from bokeh.objects import Range1d" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 4 }, { "cell_type": "code", "collapsed": false, "input": [ "# scripts/lines.py\n", "\n", "# Skip the first point because it can be troublesome\n", "theta = np.linspace(0, 8*np.pi, 10000)[1:]\n", "\n", "# Compute the radial coordinates for some different spirals\n", "lituus = theta**(-1/2) # lituus\n", "golden = np.exp(0.306349*theta) # golden\n", "arch = theta # Archimedean\n", "fermat = theta**(1/2) # Fermat's\n", "\n", "# Now compute the X and Y coordinates (polar mappers planned for Bokeh later)\n", "golden_x = golden*np.cos(theta)\n", "golden_y = golden*np.sin(theta)\n", "lituus_x = lituus*np.cos(theta)\n", "lituus_y = lituus*np.sin(theta)\n", "arch_x = arch*np.cos(theta)\n", "arch_y = arch*np.sin(theta)\n", "fermat_x = fermat*np.cos(theta)\n", "fermat_y = fermat*np.sin(theta)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 5 }, { "cell_type": "code", "collapsed": false, "input": [ "# Make sure we can output to IPython Notebook\n", "output_notebook()" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\n", "\n", " \n", " \n", " Bokeh Plot\n", " \n", " \n", " \n", " \n", " \n", "

Configuring embedded BokehJS mode.

\n", " \n", "" ], "metadata": {}, "output_type": "display_data" } ], "prompt_number": 6 }, { "cell_type": "code", "collapsed": false, "input": [ "# Plot the Archimedean spiral using the `line` renderer. Note how we set the\n", "# color, line thickness, title, and legend value.\n", "line(arch_x, arch_y, color=\"red\", line_width=2,\n", " title=\"Archimean\", legend=\"Archimedean\")\n", "show()" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\n", "\n", " \n", " \n", " Bokeh Plot\n", " \n", " \n", " \n", "
Plots
\n", " \n", "" ], "metadata": {}, "output_type": "display_data" } ], "prompt_number": 7 }, { "cell_type": "code", "collapsed": false, "input": [ "# EXERCISE: reproduce the above plot for one of the other spirals\n", "line(golden_x, golden_y, color=\"gold\", line_width=2,\n", " title=\"Golden\", legend=\"Golden\")\n", "show()" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\n", "\n", " \n", " \n", " Bokeh Plot\n", " \n", " \n", " \n", "
Plots
\n", " \n", "" ], "metadata": {}, "output_type": "display_data" } ], "prompt_number": 8 }, { "cell_type": "code", "collapsed": false, "input": [ "# We can plot more than one plot on the same figure.\n", "\n", "# Let's try to put all lines on one plot for comparison. First we need to\n", "# turn on `hold` so that each renderer does not create a brand new plot\n", "hold()\n", "\n", "# Next we need to actually create a new figure, so that the following\n", "# renderers work on a new plot, and not the last one.\n", "figure()\n", "\n", "line(arch_x, arch_y, color=\"red\", line_width=2,\n", " title=\"Archimean\", legend=\"Archimedean\")\n", "line(golden_x, golden_y, color=\"gold\", line_width=2,\n", " title=\"Golden\", legend=\"Golden\")\n", "show()" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\n", "\n", " \n", " \n", " Bokeh Plot\n", " \n", " \n", " \n", "
Plots
\n", " \n", "" ], "metadata": {}, "output_type": "display_data" } ], "prompt_number": 9 }, { "cell_type": "code", "collapsed": false, "input": [ "# OK, so that doesn't look so good because Bokeh tried to autoscale to\n", "# accomodate all the data. We can use the Range1d object to set the plot range\n", "# explicitly\n", "\n", "# EXERCISE: create a new figure\n", "hold()\n", "figure()\n", "\n", "# EXERCISE: add x_range and y_range parameters to the first `line`, to set the\n", "# range to [-10, 10]. NOTE: Range1d are created like: Range1d(start=0, end-10)\n", "line(arch_x, arch_y, color=\"red\", line_width=2,\n", " title=\"Archimean\", legend=\"Archimedean\",\n", " x_range=Range1d(start=0, end=10), y_range=Range1d(start=0, end=10))\n", "line(golden_x, golden_y, color=\"gold\", line_width=2,\n", " title=\"Golden\", legend=\"Golden\")\n", "show()" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\n", "\n", " \n", " \n", " Bokeh Plot\n", " \n", " \n", " \n", "
Plots
\n", " \n", "" ], "metadata": {}, "output_type": "display_data" } ], "prompt_number": 10 }, { "cell_type": "code", "collapsed": false, "input": [ "# scripts/scatter.py\n", "\n", "# Recreate the Fermat spiral from the last exercise, with some different scalings\n", "# and number of turnings\n", "theta1 = np.linspace(0, 256*np.pi, 500)[1:]\n", "f1 = theta1**(1/2)\n", "x1 = 0.5*f1*np.cos(theta1)\n", "y1 = 0.5*f1*np.sin(theta1)\n", "\n", "theta2 = np.linspace(0, 96*np.pi, 500)[1:]\n", "f2 = theta2**(1/2)\n", "x2 = f2*f2*np.cos(theta2)\n", "y2 = f2*f2*np.sin(theta2)\n", "\n", "theta3 = np.linspace(0, 32*np.pi, 500)[1:]\n", "f3 = 2*theta3**(1/2)\n", "x3 = 2*f3*np.cos(theta3)\n", "y3 = 2*f3*np.sin(theta3)\n", "\n", "TOOLS=\"pan,wheel_zoom,box_zoom,reset,previewsave\"\n", "output_notebook()" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\n", "\n", " \n", " \n", " Bokeh Plot\n", " \n", " \n", " \n", " \n", " \n", "

Configuring embedded BokehJS mode.

\n", " \n", "" ], "metadata": {}, "output_type": "display_data" } ], "prompt_number": 11 }, { "cell_type": "code", "collapsed": false, "input": [ "# EXERCISE: create two Range1d objects to reuse in the plots. Use the [-20, 20]\n", "# for the bounds.\n", "#\n", "# These plots are interconnected because we're sharing the same RangeId objects.\n", "# This is pretty cool; if you don't want this then create new instances for\n", "# each plot.\n", "xr = Range1d(start=-20, end=20)\n", "yr = Range1d(start=-20, end=20)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 12 }, { "cell_type": "code", "collapsed": false, "input": [ "# EXERCISE: Plot all the sets of points on different plots. Use the ranges above\n", "# for x_range and y_range. Set different colors as well. Try setting line_color\n", "# and fill_color instead of just color. You can also set alpha, line_alpha, and\n", "# fill_alpha if you like. Set tools to TOOLS on the first renderer. Change\n", "# the value of the 'marker' parameter, \"circle\", \"square\", \"triangle\", etc. One\n", "# example is given\n", "hold()\n", "figure()\n", "scatter(x1, y1, size=12, color=\"red\", alpha=0.5,\n", " x_range=xr, y_range=yr, tools=TOOLS)\n", "show()" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\n", "\n", " \n", " \n", " Bokeh Plot\n", " \n", " \n", " \n", "
Plots
\n", " \n", "" ], "metadata": {}, "output_type": "display_data" } ], "prompt_number": 13 }, { "cell_type": "code", "collapsed": false, "input": [ "hold()\n", "figure()\n", "scatter(x2, y2, size=12, color=\"red\", alpha=0.5,\n", " x_range=xr, y_range=yr, tools=TOOLS)\n", "show()" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\n", "\n", " \n", " \n", " Bokeh Plot\n", " \n", " \n", " \n", "
Plots
\n", " \n", "" ], "metadata": {}, "output_type": "display_data" } ], "prompt_number": 14 }, { "cell_type": "code", "collapsed": false, "input": [ "hold()\n", "figure()\n", "scatter(x3, y3, size=12, color=\"red\", alpha=0.5,\n", " x_range=xr, y_range=yr, tools=TOOLS)\n", "show()" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\n", "\n", " \n", " \n", " Bokeh Plot\n", " \n", " \n", " \n", "
Plots
\n", " \n", "" ], "metadata": {}, "output_type": "display_data" } ], "prompt_number": 15 }, { "cell_type": "code", "collapsed": false, "input": [ "# scripts/styles.py\n", "\n", "import numpy as np\n", "import pandas as pd\n", "from bokeh.plotting import *\n", "from bokeh.objects import Range1d\n", "\n", "# Define some categories\n", "categories = [\n", " 'ousia', 'poson', 'poion', 'pros ti', 'pou',\n", " 'pote', 'keisthai', 'echein', 'poiein', 'paschein',\n", "]\n", "\n", "# Create data\n", "N = 10\n", "data = { cat : np.random.randint(10, 100, size=N) for cat in categories }\n", "\n", "# Define a little function to stack series together to make polygons. Soon\n", "# this will be built into Bokeh.\n", "def stacked(data, categories):\n", " ys = []\n", " last = np.zeros(len(data.values()[0]))\n", " for cat in categories:\n", " next = last + data[cat]\n", " ys.append(np.hstack((last[::-1], next)))\n", " last = next\n", " return ys\n", "\n", "# Get the y coordinates of the stacked data\n", "ys = stacked(data, categories)\n", "\n", "# The x coordinates for each polygon are simply the series concatenated\n", "# with its reverse.\n", "xs = [np.hstack((categories[::-1], categories))] * len(ys)\n", "\n", "# Pick out a color palette\n", "colors = brewer[\"Spectral\"][len(ys)]" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 16 }, { "cell_type": "code", "collapsed": false, "input": [ "# EXERCISE: play around with parameters like:\n", "# - line_color\n", "# - line_alpha\n", "# - line_width\n", "# - line_dash (e.g., [2,4])\n", "# - fill_color\n", "# - fill_alpha\n", "# - background_fill\n", "hold()\n", "figure()\n", "patches(xs, ys, x_range=categories, y_range=Range1d(start=0, end=800),\n", " color=colors, alpha=0.8, line_color=None, background_fill=\"lightgrey\",\n", " title=\"Categories of Brewering\")" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 17, "text": [ "" ] } ], "prompt_number": 17 }, { "cell_type": "code", "collapsed": false, "input": [ "# EXERCISE: configure all of the following plot properties\n", "ygrid().grid_line_color = \"white\" # None to get rid of a line\n", "ygrid().grid_line_width = 2\n", "axis().major_label_text_font_size = \"12pt\"\n", "axis().major_label_text_font_style = \"bold\"\n", "axis().major_label_standoff = 10 # distance of tick labels from ticks\n", "axis().axis_line_color = None # color, or None, to suppress the line\n", "xaxis().major_label_orientation = np.pi/4 # radians, \"horizontal\", \"vertical\", \"normal\"\n", "xaxis().major_tick_in = 10 # distance ticks extends into the plot\n", "xaxis().major_tick_out = 0 # and distance they extend out\n", "xaxis().major_tick_line_color = \"white\"\n", "\n", "show()" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\n", "\n", " \n", " \n", " Bokeh Plot\n", " \n", " \n", " \n", "
Plots
\n", " \n", "" ], "metadata": {}, "output_type": "display_data" } ], "prompt_number": 18 } ], "metadata": {} } ] }