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

Bokeh Tutorial

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

01. Basic Plotting

" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "This section of the tutorial covers the [`bokeh.plotting`](https://bokeh.pydata.org/en/latest/docs/user_guide/plotting.html) \n", "interface. This interface is a \"mid-level\" interface, and the main idea can be described by the statement:\n", "\n", "**Starting from simple default figures (with sensible default tools, grids and axes), add markers and other shapes whose visual attributes are tied to directly data.**\n", "\n", "We will see that it is possible to customize and change all of the defaults, but having them means that it is possible to get up and running very quickly. " ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "# Imports and Setup\n", "\n", "When using the [`bokeh.plotting`](https://bokeh.pydata.org/en/latest/docs/user_guide/plotting.html) interface, there are a few common imports:\n", "* Use the [`figure`](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure) function to create new plot objects to work with. \n", "* Call the functions [`output_file`](https://bokeh.pydata.org/en/latest/docs/reference/resources_embedding.html#bokeh.io.output_file) or [`output_notebook`](https://bokeh.pydata.org/en/latest/docs/reference/resources_embedding.html#bokeh.io.output_notebook) (possibly in combination) to tell Bokeh how to display or save output. \n", "* Execute [`show`](https://bokeh.pydata.org/en/latest/docs/reference/resources_embedding.html#bokeh.io.show) and [`save`](https://bokeh.pydata.org/en/latest/docs/reference/resources_embedding.html#bokeh.io.save) to display or save plots and layouts." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "import numpy as np # we will use this later, so import it now\n", "\n", "from bokeh.io import output_notebook, show\n", "from bokeh.plotting import figure" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "In this case, we are in the Jupyter notebook, so we will call `output_notebook()` below. We only need to call this once, and all subsequent calls to `show()` will display inline in the notebook." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "output_notebook()" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "If everything is working, you should see a Bokeh logo and a message like *\\\"BokehJS 1.4.0 successfully loaded.\"* as the output. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook uses Bokeh sample data. If you haven't downloaded it already, this can be downloaded by running the following:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import bokeh.sampledata\n", "bokeh.sampledata.download()" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "# Scatter Plots\n", "\n", "Bokeh can draw many types of visual shapes (called *glyphs*), including lines, bars, patches, hex tiles and more. One of the most common visualization tasks is to draw a scatter plot of data using small *marker* glyphs to represent each point. \n", "\n", "In this section you will see how to use Bokeh's various marker glyphs to create simple scatter plots. \n", "\n", "The basic outline is:\n", "* create a blank figure: `p = figure(...)`\n", "* call a glyph method such as `p.circle` on the figure\n", "* `show` the figure\n", "\n", "Execute the cell below to create a small scatter plot with circle glyphs:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# create a new plot with default tools, using figure\n", "p = figure(width=400, height=400)\n", "\n", "# add a circle renderer with x and y coordinates, size, color, and alpha\n", "p.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=15, line_color=\"navy\", fill_color=\"orange\", fill_alpha=0.5)\n", "\n", "show(p) # show the results" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "In the output above, you can see the effect of the different options for `line_color`, `fill_alpha`, etc. Try changing some of these values and re-executing the cell to update the plot." ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "All Bokeh scatter markers accept `size` (measured in screen space units) as a property. Circles in particular also have `radius` (measured in \"data\" space units). " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# EXERCISE: Try changing the example above to set a `radius` value instead of `size`\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "To scatter square markers instead of circles, you can use the `square` method on figures. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# create a new plot using figure\n", "p = figure(width=400, height=400)\n", "\n", "# add a square renderer with a size, color, alpha, and sizes\n", "p.square([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=[10, 15, 20, 25, 30], color=\"firebrick\", alpha=0.6)\n", "\n", "show(p) # show the results" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "Note that in the example above, we are also specifying different sizes for each individual marker. **In general, all of a glyph's properties can be \"vectorized\" in this fashion.** Also note that we have passed ``color`` as a shorthand to set both the line and fill colors easily at the same time. This is a convenience specific to ``bokeh.plotting``.\n", "\n", "There are many marker types available in Bokeh, you can see details and\n", "example plots for all of them in the reference guide by clicking on entries in the list below:\n", "\n", "* [asterisk()](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure.asterisk)\n", "* [circle()](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure.circle)\n", "* [circle_cross()](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure.circle_cross)\n", "* [circle_x()](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure.circle_x)\n", "* [cross()](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure.cross)\n", "* [diamond()](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure.diamond)\n", "* [diamond_cross()](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure.diamond_cross)\n", "* [hex()](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure.hex)\n", "* [inverted_triangle()](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure.inverted_triangle)\n", "* [square()](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure.square)\n", "* [square_cross()](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure.square_cross)\n", "* [square_x()](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure.square_x)\n", "* [triangle()](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure.triangle)\n", "* [x()](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure.x)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# EXERCISE: Make a scatter plot using the \"autompg\" dataset\n", "\n", "from bokeh.sampledata.autompg import autompg as df # run df.head() to inspect \n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "# Line Plots\n", "\n", "Another common visualization task is the drawing of line plots. This can be accomplished in Bokeh by calling the `p.line(...)` glyph method as shown below." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# create a new plot (with a title) using figure\n", "p = figure(width=400, height=400, title=\"My Line Plot\")\n", "\n", "# add a line renderer\n", "p.line([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], line_width=2)\n", "\n", "show(p) # show the results" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "In addition to `line_width`, there are other options such as `line_color` or `line_dash` that can be set. Try setting some of the [other properties of line](https://bokeh.pydata.org/en/latest/docs/reference/plotting.html#bokeh.plotting.figure.Figure.line) and re-running the cell above." ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "### Datetime axes\n", "\n", "It's often the case that timeseries data is represented by drawing lines. Let's look at an example using the \"glucose\" data set, which is available in a Pandas dataframe:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "from bokeh.sampledata.glucose import data\n", "data.head()" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "We'd like to plot a subset of this data, and have a nice datetime axis as well. We can ask Bokeh for a datetime axis by passing `x_axis_type=\"datetime\"` to the call to `figure`. This is shown below, as well as configuration of a some other options such as plot dimensions, axis titles, and grid line properies. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# reduce data size to one week\n", "week = data.loc['2010-10-01':'2010-10-08']\n", "\n", "p = figure(x_axis_type=\"datetime\", title=\"Glocose Range\", height=350, width=800)\n", "p.xgrid.grid_line_color=None\n", "p.ygrid.grid_line_alpha=0.5\n", "p.xaxis.axis_label = 'Time'\n", "p.yaxis.axis_label = 'Value'\n", "\n", "p.line(week.index, week.glucose)\n", "\n", "show(p)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# EXERCISE: Look at the AAPL data from bokeh.sampledata.stocks and create a line plot using it\n", "from bokeh.sampledata.stocks import AAPL\n", "\n", "# AAPL.keys()\n", "# dict_keys(['date', 'open', 'high', 'low', 'close', 'volume', 'adj_close'])\n", "\n", "dates = np.array(AAPL['date'], dtype=np.datetime64) # convert date strings to real datetimes\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "# Hex Tiling\n", "\n", "Bokeh supports drawing low level hex tilings using [axial coordinates](https://www.redblobgames.com/grids/hexagons/#coordinates-axial) and the `hex_tile` method, as described in the [Hex Tiles](https://docs.bokeh.org/en/latest/docs/user_guide/plotting.html#hex-tiles) section of the User's Guide. However, one of the most common uses of hex tilings is to visualize binning. Bokeh encapsulates this common operation in the `hexbin` function, whose output can be passed directly to `hex_tile` as seen below." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "from bokeh.palettes import Viridis256\n", "from bokeh.util.hex import hexbin\n", "\n", "n = 50000\n", "x = np.random.standard_normal(n)\n", "y = np.random.standard_normal(n)\n", "\n", "bins = hexbin(x, y, 0.1)\n", "\n", "# color map the bins by hand, will see how to use linear_cmap later\n", "color = [Viridis256[int(i)] for i in bins.counts/max(bins.counts)*255]\n", "\n", "# match_aspect ensures neither dimension is squished, regardless of the plot size\n", "p = figure(tools=\"wheel_zoom,reset\", match_aspect=True, background_fill_color='#440154')\n", "p.grid.visible = False\n", "\n", "p.hex_tile(bins.q, bins.r, size=0.1, line_color=None, fill_color=color)\n", "\n", "show(p)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# Exercise: Experiment with the size parameter to hexbin, and using different data as input\n" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "# Images\n", "\n", "Another common task is to display images, which might represent heat maps, or sensor data of some sort. Bokeh provides two glyph methods for displaying images:\n", "\n", "* `image` which can be used, together with a palette, to show colormapped 2d data in a plot\n", "* `image_rgba` which can be used to display raw RGBA pixel data in a plot. \n", "\n", "The first example below shows how to call `image` with a 2d array and a palette" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "N = 500\n", "x = np.linspace(0, 10, N)\n", "y = np.linspace(0, 10, N)\n", "xx, yy = np.meshgrid(x, y)\n", "\n", "img = np.sin(xx)*np.cos(yy)\n", "\n", "p = figure(x_range=(0, 10), y_range=(0, 10))\n", "\n", "# must give a vector of image data for image parameter\n", "p.image(image=[img], x=0, y=0, dw=10, dh=10, palette=\"Spectral11\")\n", "\n", "show(p) " ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "A palette can be any list of colors, or one of the named built-in palettes, which can be seen in the [bokeh.palettes reference guide](https://bokeh.pydata.org/en/latest/docs/reference/palettes.html). Try changing the palette, or the array data and re-running the cell above.\n", "\n", "The next example shows how to use the `image_rgba` method to display raw RGBA data (created with help from NumPy). " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "from __future__ import division\n", "import numpy as np\n", " \n", "N = 20\n", "img = np.empty((N,N), dtype=np.uint32) \n", "\n", "# use an array view to set each RGBA channel individiually\n", "view = img.view(dtype=np.uint8).reshape((N, N, 4))\n", "for i in range(N):\n", " for j in range(N):\n", " view[i, j, 0] = int(i/N*255) # red\n", " view[i, j, 1] = 158 # green\n", " view[i, j, 2] = int(j/N*255) # blue\n", " view[i, j, 3] = 255 # alpha\n", " \n", "# create a new plot (with a fixed range) using figure\n", "p = figure(x_range=[0,10], y_range=[0,10])\n", "\n", "# add an RGBA image renderer\n", "p.image_rgba(image=[img], x=[0], y=[0], dw=[10], dh=[10])\n", "\n", "show(p) " ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "Try changing the RGBA data and re-running the cell above." ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "# Other Kinds of Glyphs\n", "\n", "Bokeh supports many other kinds of glyphs. You can click on the User Guide links below to see how to create plots with these glyphs using the [`bokeh.plotting`](https://bokeh.pydata.org/en/latest/docs/user_guide/plotting.html) interface.\n", "\n", "* [Ovals and Ellipses](https://docs.bokeh.org/en/latest/docs/user_guide/plotting.html#ovals-and-ellipses)\n", "* [Segments and Rays](https://docs.bokeh.org/en/latest/docs/user_guide/plotting.html#segments-and-rays)\n", "* [Wedges and Arcs](https://bokeh.pydata.org/en/latest/docs/user_guide/plotting.html#wedges-and-arcs)\n", "* [Specialized Curves](https://bokeh.pydata.org/en/latest/docs/user_guide/plotting.html#specialized-curves)\n", "\n", "We will cover various kinds of Bar plots (e.g. with stacking and grouping) using [Bars and Rectangles](https://docs.bokeh.org/en/latest/docs/user_guide/plotting.html#bars-and-rectangles) much more extensively in the [Bar and Categorical Data Plots](07%20-%20Bar%20and%20Categorical%20Data%20Plots.ipynb) chapter of this tutorial." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# EXERCISE: Plot some of the other glyph types, following the examples in the User Guide. \n", "\n" ] }, { "cell_type": "markdown", "metadata": { "Collapsed": "false" }, "source": [ "# Plots with Multiple Glyphs \n", "\n", "Finally, it should be noted that is possible to combine more than one glyph on a single figure. When multiple calls to glyph methods happen on a single figure, the glyphs are draw in the order called, as shown below." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# set up some data\n", "x = [1, 2, 3, 4, 5]\n", "y = [6, 7, 8, 7, 3]\n", "\n", "# create a new plot with figure\n", "p = figure(width=400, height=400)\n", "\n", "# add both a line and circles on the same plot\n", "p.line(x, y, line_width=2)\n", "p.circle(x, y, fill_color=\"white\", size=8)\n", "\n", "show(p) # show the results" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# EXERCISE: create your own plot combining multiple glyphs together\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Next Section" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Click on this link to go to the next notebook: [02 - Styling and Theming](02%20-%20Styling%20and%20Theming.ipynb).\n", "\n", "To go back to the overview, click [here](00%20-%20Introduction%20and%20Setup.ipynb)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.9.13" } }, "nbformat": 4, "nbformat_minor": 4 }