{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", " \n", " \n", " \n", " \n", "
\n", " \n", " \n", " \n", " \n", "

Bokeh Tutorial

\n", "
\n", "\n", "

04. Styling Visual Attributes

" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from bokeh.io import output_notebook, show\n", "from bokeh.plotting import figure" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "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 = \"1\";\n", "\n", " if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force !== \"\") {\n", " window._bokeh_onload_callbacks = [];\n", " window._bokeh_is_loading = undefined;\n", " }\n", "\n", "\n", " \n", " if (typeof (window._bokeh_timeout) === \"undefined\" || force !== \"\") {\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", " Bokeh.$(\"#3907c51d-236a-489f-9d92-e5e0d105adf4\").text(\"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(\"3907c51d-236a-489f-9d92-e5e0d105adf4\");\n", " if (element == null) {\n", " console.log(\"Bokeh: ERROR: autoload.js configured with elementid '3907c51d-236a-489f-9d92-e5e0d105adf4' but no matching script tag was found. \")\n", " return false;\n", " }\n", "\n", " var js_urls = ['https://cdn.pydata.org/bokeh/dev/bokeh-0.12.2rc3.min.js', 'https://cdn.pydata.org/bokeh/dev/bokeh-widgets-0.12.2rc3.min.js', 'https://cdn.pydata.org/bokeh/dev/bokeh-compiler-0.12.2rc3.min.js'];\n", "\n", " var inline_js = [\n", " function(Bokeh) {\n", " Bokeh.set_log_level(\"info\");\n", " },\n", " \n", " function(Bokeh) {\n", " \n", " Bokeh.$(\"#3907c51d-236a-489f-9d92-e5e0d105adf4\").text(\"BokehJS is loading...\");\n", " },\n", " function(Bokeh) {\n", " console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/dev/bokeh-0.12.2rc3.min.css\");\n", " Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/dev/bokeh-0.12.2rc3.min.css\");\n", " console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/dev/bokeh-widgets-0.12.2rc3.min.css\");\n", " Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/dev/bokeh-widgets-0.12.2rc3.min.css\");\n", " }\n", " ];\n", "\n", " function run_inline_js() {\n", " \n", " if ((window.Bokeh !== undefined) || (force === \"1\")) {\n", " for (var i = 0; i < inline_js.length; i++) {\n", " inline_js[i](window.Bokeh);\n", " }if (force === \"1\") {\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) {\n", " var cell = $(\"#3907c51d-236a-489f-9d92-e5e0d105adf4\").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": [ "output_notebook()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Colors and Properties" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Colors\n", "\n", "There are many places where you may need to specify colors. Bokeh can accept colors in a variety of different ways:\n", "\n", "* any of the [147 named CSS colors](http://www.w3schools.com/cssref/css_colornames.asp), e.g ``'green'``, ``'indigo'``\n", "* an RGB(A) hex value, e.g., ``'#FF0000'``, ``'#44444444'``\n", "* a 3-tuple of integers *(r,g,b)* between 0 and 255\n", "* a 4-tuple of *(r,g,b,a)* where *r*, *g*, *b* are integers between 0 and 255 and *a* is a floating point value between 0 and 1\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## Properties\n", "\n", "Regardless of what API (``models``, ``plotting``, or ``charts``) is used to create a Bokeh plot, styling the visual aspects of the plot can always be accomplished by setting attributes on the Bokeh model objects that comprise the resulting plot. Visual properties come in three kinds: line, fill, and text properties. For full information with code and examples see the [Styling Visual Properties](http://bokeh.pydata.org/en/latest/docs/user_guide/styling.html) section of the user guide. \n", "\n", "----\n", "\n", "### Line Properties\n", "\n", "Set the visual appearance of lines. The most common are ``line_color``, ``line_alpha``, ``line_width`` and ``line_dash``.\n", "\n", "### Fill Properties\n", "\n", "Set the visual appearance of filled areas: ``fill_color`` and ``fill_alpha``.\n", "\n", "### Text Properties\n", "\n", "Set the visual appearance of lines of text. The most common are ``text_font``, ``text_font_size``, ``text_color``, and ``text_alpha``.\n", "\n", "----\n", "\n", "Sometimes a prefix is used with property names, e.g. to distinguish between different line properties on the same object, or to give a more meaningful name. For example, to set the line width of the plot outline, you would say ``myplot.outline_line_width = 2``. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Plots\n", "\n", "Many top-level attributes of plots (outline, border, etc.) can be configured. See the [Plots](http://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#plots) section of the styling guide for full information. \n", "\n", "Here is an example that tweaks the plot outline:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# create a new plot with a title\n", "p = figure(plot_width=400, plot_height=400)\n", "p.outline_line_width = 7\n", "p.outline_line_alpha = 0.3\n", "p.outline_line_color = \"navy\"\n", "\n", "p.circle([1,2,3,4,5], [2,5,8,2,7], size=10)\n", "\n", "show(p)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# EXERCISE Create a plot of your own and customize several plot-level properties\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Glyphs\n", "\n", "It's also possible to style the visual properties of glyphs. When using `bokeh.plotting` this is often done when calling the glyph methods:\n", "```python\n", "p.circle(line_color=\"red\", fill_alpha=0.2, ...)\n", "```\n", "But it is also possible to set these properties directly on glyph objects. Glyph objects are found on `GlyphRenderer` objects, which are returned by the `Plot.add_glyph` and `bokeh.plotting` glyph methods like `circle`, `rect`, etc. Let's look at an example:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "p = figure(plot_width=400, plot_height=400)\n", "\n", "# keep a reference to the returned GlyphRenderer\n", "r = p.circle([1,2,3,4,5], [2,5,8,2,7])\n", "\n", "r.glyph.size = 50\n", "r.glyph.fill_alpha = 0.2\n", "r.glyph.line_color = \"firebrick\"\n", "r.glyph.line_dash = [5, 1]\n", "r.glyph.line_width = 2\n", "\n", "show(p)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Selection and non-selection visuals\n", "\n", "You can also control how glyphs look when there are selections involved. The set of \"selected\" points is displayed according to the optional `.selection_glyph` property of a `GlyphRenderer`:\n", "```python\n", "r.selection_glyph = Circle(fill_alpha=1, fill_color=\"firebrick\", line_color=None) \n", "```\n", "When there is a non-empty selection, the set of \"unselected: points is displayed according to the optional `.nonselection_glyph` property of a `GlyphRenderer`:\n", "```python\n", "r.nonselection_glyph = Circle(fill_alpha=0.2, fill_color=\"grey\", line_color=None) \n", "```\n", "\n", "When using the `bokeh.plotting` interface, it is easier to pass these visual properties to the glyph methods as shown below. The glyph method will create the selection or nonselection glyphs and attach them to the renderer for you. " ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "p = figure(plot_width=400, plot_height=400, tools=\"tap\", title=\"Select a circle\")\n", "renderer = p.circle([1, 2, 3, 4, 5], [2, 5, 8, 2, 7], size=50,\n", "\n", " # set visual properties for selected glyphs\n", " selection_color=\"firebrick\",\n", "\n", " # set visual properties for non-selected glyphs\n", " nonselection_fill_alpha=0.2,\n", " nonselection_fill_color=\"grey\",\n", " nonselection_line_color=\"firebrick\",\n", " nonselection_line_alpha=1.0)\n", "\n", "show(p)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is also possible to specify the visual appearance of glyphs when they are \"inspected\", e.g. by a hover tool. This is accomplished by setting an optional `hover_glyph` on the glyph renderer:\n", "```python\n", "r.hover_glyph = Circle(fill_alpha=1, fill_color=\"firebrick\", line_color=None) \n", "```\n", "Or if using `bokeh.plotting` glyph methods, by passing `hover_fill_alpha`, etc. to the glyph method. Lets look at an example that works together with a `HoverTool` configured for \"hline\" hit-testing." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from bokeh.models.tools import HoverTool\n", "from bokeh.sampledata.glucose import data\n", "\n", "subset = data.ix['2010-10-06']\n", "\n", "x, y = subset.index.to_series(), subset['glucose']\n", "\n", "# Basic plot setup\n", "p = figure(width=600, height=300, x_axis_type=\"datetime\", tools=\"\",\n", " toolbar_location=None, title='Hover over points')\n", "\n", "p.line(x, y, line_dash=\"4 4\", line_width=1, color='gray')\n", "\n", "cr = p.circle(x, y, size=20,\n", " fill_color=\"grey\", hover_fill_color=\"firebrick\",\n", " fill_alpha=0.05, hover_alpha=0.3,\n", " line_color=None, hover_line_color=\"white\")\n", "\n", "p.add_tools(HoverTool(tooltips=None, renderers=[cr], mode='hline'))\n", "\n", "show(p)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# EXERCISE: experiment with standard, selected, hover glyph visual properties\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Axes\n", "\n", "[Axes](http://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#axes)\n", "\n", "To style axes, you first must get ahold of `Axis` objects. The simplest way is to use some convenience methods on `Plot`: [axis](http://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.Figure.axis), [xaxis](http://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.Figure.xaxis), and [yaxis](http://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.Figure.yaxis). These methods return lists of axis objects:\n", "\n", "```\n", ">>> p.xaxis\n", "[]\n", "```\n", "However, you can set properties on all the elements of the list as if it was a single object:\n", "```\n", "p.xaxis.axis_label = \"Temperature\"\n", "p.axis.major_label_text_color = \"orange\"\n", "```\n", "These are referred to as \"splattable\" lists, and tab completion works on them as well. \n" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# EXERCISE Try out tab completion. Type p.xaxis. to see a list of attributes that can be set.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* **axis** \n", " - [line properties](http://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#line-properties)\n", "* **axis_label** \n", " - [text properties](http://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#text-properties)\n", " - ``axis_label_standoff``\n", "* **major_label** \n", " - [text properties](http://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#text-properties)\n", " - ``orientation``\n", "* **major_tick** \n", " - [line_properties](http://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#line-properties)\n", " - ``major_tick_in`` \n", " - ``major_tick_out``\n", "* **minor_tick** \n", " - [line properties](http://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#line-properties)\n", " - ``minor_tick_in``\n", " - ``minor_tick_out``\n", "\n", "As a simple first example, let's change the orientation of the major tick labels on both axes of a plot:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from math import pi\n", "\n", "p = figure(plot_width=400, plot_height=400)\n", "p.x([1,2,3,4,5], [2,5,8,2,7], size=10, line_width=2)\n", "\n", "p.xaxis.major_label_orientation = pi/4\n", "p.yaxis.major_label_orientation = \"vertical\"\n", "\n", "show(p)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The next example shows customizations on several of the different Axis properties at once:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "p = figure(plot_width=400, plot_height=400)\n", "p.asterisk([1,2,3,4,5], [2,5,8,2,7], size=12, color=\"olive\")\n", "\n", "# change just some things about the x-axes\n", "p.xaxis.axis_label = \"Temp\"\n", "p.xaxis.axis_line_width = 3\n", "p.xaxis.axis_line_color = \"red\"\n", "\n", "# change just some things about the y-axes\n", "p.yaxis.axis_label = \"Pressure\"\n", "p.yaxis.major_label_text_color = \"orange\"\n", "p.yaxis.major_label_orientation = \"vertical\"\n", "\n", "# change things on all axes\n", "p.axis.minor_tick_in = -3\n", "p.axis.minor_tick_out = 6\n", "\n", "show(p)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# EXERCISE Create a plot of your own and customize several axis properties\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are further customizations possible. See the [User Guide](http://bokeh.pydata.org/en/latest/docs/user_guide.html) for more information on topics such as [tick label formatting](http://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#tick-label-formats) or [limiting axis bounds](http://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#bounds)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Grids\n", "\n", "[Grids](http://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#grids)\n", "\n", "* **grid** [line properties](http://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#line-properties)\n", "* **band** [fill properties]()" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "p = figure(plot_width=400, plot_height=400)\n", "p.circle([1,2,3,4,5], [2,5,8,2,7], size=10)\n", "\n", "# change just some things about the x-grid\n", "p.xgrid.grid_line_color = None\n", "\n", "# change just some things about the y-grid\n", "p.ygrid.grid_line_alpha = 0.5\n", "p.ygrid.grid_line_dash = [6, 4]\n", "\n", "show(p)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "p = figure(plot_width=400, plot_height=400)\n", "p.circle([1,2,3,4,5], [2,5,8,2,7], size=10)\n", "\n", "# change just some things about the x-grid\n", "p.xgrid.grid_line_color = None\n", "\n", "# change just some things about the y-grid\n", "p.ygrid.band_fill_alpha = 0.1\n", "p.ygrid.band_fill_color = \"navy\"\n", "\n", "show(p)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# EXERCISE Create a plot of your own and customize several grid properties\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Legends\n", "\n", "[Legends](http://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#legends)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import numpy as np\n", "\n", "x = np.linspace(0, 4*np.pi, 100)\n", "y = np.sin(x)\n", "\n", "p = figure()\n", "\n", "p.circle(x, y, legend=\"sin(x)\")\n", "p.line(x, y, legend=\"sin(x)\")\n", "\n", "p.line(x, 2*y, legend=\"2*sin(x)\", line_dash=[4, 4], line_color=\"orange\", line_width=2)\n", "\n", "p.line(x, 3*y, legend=\"3*sin(x)\", line_color=\"green\")\n", "p.square(x, 3*y, legend=\"3*sin(x)\", fill_color=\"white\", line_color=\"green\")\n", "\n", "p.legend.location = \"bottom_left\"\n", "\n", "show(p)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# EXERCISE Create a plot of your own and add a legend\n", "\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python [default]", "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.5.2" } }, "nbformat": 4, "nbformat_minor": 0 }