Plotting with basic glyphs#

Creating figures#

Bokeh plots you create with the bokeh.plotting interface come with a default set of tools and visual styles. For information on how to customize the visual style of plots, see Styling visual attributes. For information about changing or specifying tools, see Configuring plot tools.

Scatter markers#

Bokeh includes a large variety of markers for creating scatter plots. For example, to render circle scatter markers on a plot, use the circle() method of figure():

from bokeh.plotting import figure, output_file, show

# output to static HTML file
output_file("line.html")

p = figure(width=400, height=400)

# add a circle renderer with a size, color, and alpha
p.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=20, color="navy", alpha=0.5)

# show the results
show(p)

Similarly, use the square() method of figure() to scatter square markers on a plot:

from bokeh.plotting import figure, output_file, show

# output to static HTML file
output_file("square.html")

p = figure(width=400, height=400)

# add a square renderer with a size, color, and alpha
p.square([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=20, color="olive", alpha=0.5)

# show the results
show(p)

Bokeh’s built-in scatter markers consist of a set of base markers, most of which can be combined with different kinds of additional visual features. This is an overview of all available scatter markers:

To see details and example plots for any of the available scatter markers, click on the corresponding glyph method in the following list:

All the markers have the same set of properties: x, y, size (in screen units), and angle (in radians by default). The circle() marker is an exception: this method accepts an additional radius property that you can use with data units.

Line glyphs#

Single lines#

The example below shows how to generate a single line glyph from one-dimensional sequences of x and y points using the line() glyph method:

from bokeh.plotting import figure, output_file, show

output_file("line.html")

p = figure(width=400, height=400)

# add a line renderer
p.line([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], line_width=2)

show(p)

Step lines#

For some kinds of data, discrete steps between data points may work better than linear segments. To produce this type of data representation, use the step() glyph method.

from bokeh.plotting import figure, output_file, show

output_file("line.html")

p = figure(width=400, height=400)

# add a steps renderer
p.step([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], line_width=2, mode="center")

show(p)

Adjust the mode parameter to draw step levels with the x-coordinates before, after, or in the middle of each step.

Multiple lines#

If you want to draw multiple lines in one go, use the multi_line() glyph method as follows:

from bokeh.plotting import figure, output_file, show

output_file("patch.html")

p = figure(width=400, height=400)

p.multi_line([[1, 3, 2], [3, 4, 6, 6]], [[2, 1, 4], [4, 7, 8, 5]],
             color=["firebrick", "navy"], alpha=[0.8, 0.3], line_width=4)

show(p)

Note

Unlike many other glyph methods, multi_line() accepts a list of lists of x and y positions for each line. The multi_line() method also expects a scalar value or a list of scalars for each line for parameters such as color, alpha, and line width. You can similarly use a ColumnDataSource consisting of a list of lists of point coordinates and a list of scalar values of matching length.

Missing points#

You can pass NaN values to line() and multi_line() glyphs. This produces disjointed lines with gaps for NaN values.

from bokeh.plotting import figure, output_file, show

output_file("line.html")

p = figure(width=400, height=400)

# add a line renderer with a NaN
nan = float('nan')
p.line([1, 2, 3, nan, 4, 5], [6, 7, 2, 4, 4, 5], line_width=2)

show(p)

Stacked lines#

You may wish to stack lines with a common index when working with time series of percentages and other similar data. To do so, you can use the vline_stack() and hline_stack() convenience methods.

from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, output_file, show

output_file("vline_stack.html")

source = ColumnDataSource(data=dict(
    x=[1, 2, 3, 4, 5],
    y1=[1, 2, 4, 3, 4],
    y2=[1, 4, 2, 2, 3],
))
p = figure(width=400, height=400)

p.vline_stack(['y1', 'y2'], x='x', source=source)

show(p)

Note

This and other examples in this chapter rely on `ColumnDataSource for data structuring. For information on how to work with this data structure, see Providing data.

Bars and rectangles#

Bars#

To make drawing rectangular bars more convenient, Bokeh provides hbar() and vbar() glyph functions that combine the coordinate systems above.

To draw vertical bars by specifying a center x-coordinate, width, and top and bottom endpoints, use the vbar() glyph function:

from bokeh.plotting import figure, output_file, show

output_file('vbar.html')

p = figure(width=400, height=400)
p.vbar(x=[1, 2, 3], width=0.5, bottom=0,
       top=[1.2, 2.5, 3.7], color="firebrick")

show(p)

To draw horizontal bars by specifying a center y-coordinate, height, and left and right endpoints, use the hbar() glyph function:

from bokeh.plotting import figure, output_file, show

output_file('hbar.html')

p = figure(width=400, height=400)
p.hbar(y=[1, 2, 3], height=0.5, left=0,
       right=[1.2, 2.5, 3.7], color="navy")

show(p)

Stacked bars#

To stack the bars, you can use the vbar_stack() and hbar_stack() convenience methods.

from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, output_file, show

output_file("hbar_stack.html")

source = ColumnDataSource(data=dict(
    y=[1, 2, 3, 4, 5],
    x1=[1, 2, 4, 3, 4],
    x2=[1, 4, 2, 2, 3],
))
p = figure(width=400, height=400)

p.hbar_stack(['x1', 'x2'], y='y', height=0.8, color=("grey", "lightgrey"), source=source)

show(p)

For more examples of stacked bars, see Handling categorical data.

Rectangles#

To draw axis aligned rectangles by specifying the left, right, top, and bottom positions, use the quad() glyph function:

from bokeh.plotting import figure, show

p = figure(width=400, height=400)
p.quad(top=[2, 3, 4], bottom=[1, 2, 3], left=[1, 2, 3],
       right=[1.2, 2.5, 3.7], color="#B3DE69")

show(p)

To draw axis aligned rectangles by specifying the x and y coordinates for a corner, and a width and height, use the block() glyph function:

from bokeh.plotting import figure, show

p = figure(width=400, height=400)
p.block(x=[1, 2, 3], y=[1, 2, 3], width=[0.2, 0.5, 0.1], height=1.5)

show(p)

To draw arbitrary rectangles by specifying center coordinates, width, height, and angle, use the rect() glyph function:

from math import pi

from bokeh.plotting import figure, show

p = figure(width=400, height=400)
p.rect(x=[1, 2, 3], y=[1, 2, 3], width=0.2, height=40, color="#CAB2D6",
       angle=pi/3, height_units="screen")

show(p)

Hex tiles#

Bokeh can plot hexagonal tiles, which you can use to show binned aggregations and more. The hex_tile() method takes a size parameter to define the size of the hex grid and axial coordinates to specify the tiles.

import numpy as np

from bokeh.io import output_file, show
from bokeh.plotting import figure
from bokeh.util.hex import axial_to_cartesian

output_file("hex_coords.html")

q = np.array([0,  0, 0, -1, -1,  1, 1])
r = np.array([0, -1, 1,  0,  1, -1, 0])

p = figure(width=400, height=400, toolbar_location=None)
p.grid.visible = False

p.hex_tile(q, r, size=1, fill_color=["firebrick"]*3 + ["navy"]*4,
           line_color="white", alpha=0.5)

x, y = axial_to_cartesian(q, r, 1, "pointytop")

p.text(x, y, text=["(%d, %d)" % (q,r) for (q, r) in zip(q, r)],
       text_baseline="middle", text_align="center")

show(p)

A more practical example below computes counts per bin using the hexbin() function and plots the color mapped counts.

import numpy as np

from bokeh.io import output_file, show
from bokeh.plotting import figure
from bokeh.transform import linear_cmap
from bokeh.util.hex import hexbin

n = 50000
x = np.random.standard_normal(n)
y = np.random.standard_normal(n)

bins = hexbin(x, y, 0.1)

p = figure(tools="wheel_zoom,reset", match_aspect=True, background_fill_color='#440154')
p.grid.visible = False

p.hex_tile(q="q", r="r", size=0.1, line_color=None, source=bins,
           fill_color=linear_cmap('counts', 'Viridis256', 0, max(bins.counts)))

output_file("hex_tile.html")

show(p)

You can simplify this code by calling the hexbin() method of figure().

Directed areas#

Directed areas are filled regions between two series that share a common index. For instance, a vertical directed area has one x coordinate array and two y coordinate arrays, y1 and y2, defining the space for Bokeh to fill.

Single areas#

To fill an area in vertical direction, use the varea() method. You can do the same in horizontal direction with harea().

from bokeh.plotting import figure, output_file, show

output_file("varea.html")

p = figure(width=400, height=400)

p.varea(x=[1, 2, 3, 4, 5],
        y1=[2, 6, 4, 3, 5],
        y2=[1, 4, 2, 2, 3])

show(p)

Stacked areas#

To stack directed areas, use the varea_stack() and harea_stack() convenience methods.

from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, output_file, show

output_file("varea_stack.html")

source = ColumnDataSource(data=dict(
    x=[1, 2, 3, 4, 5],
    y1=[1, 2, 4, 3, 4],
    y2=[1, 4, 2, 2, 3],
))
p = figure(width=400, height=400)

p.varea_stack(['y1', 'y2'], x='x', color=("grey", "lightgrey"), source=source)

show(p)

Patches and polygons#

Single patches#

The following example generates a single polygonal patch from one-dimensional sequences of x and y points using the patch() glyph method:

from bokeh.plotting import figure, output_file, show

output_file("patch.html")

p = figure(width=400, height=400)

# add a patch renderer with an alpha and line width
p.patch([1, 2, 3, 4, 5], [6, 7, 8, 7, 3], alpha=0.5, line_width=2)

show(p)

Multiple patches#

To plot several polygonal patches, use the patches() glyph method:

from bokeh.plotting import figure, output_file, show

output_file("patch.html")

p = figure(width=400, height=400)

p.patches([[1, 3, 2], [3, 4, 6, 6]], [[2, 1, 4], [4, 7, 8, 5]],
          color=["firebrick", "navy"], alpha=[0.8, 0.3], line_width=2)

show(p)

Note

Unlike many other glyph methods, patches() accepts a list of lists of x and y positions for each line. The patches() method also expects a scalar value or a list of scalars for each patch for parameters such as color, alpha, and line width. You can similarly use a ColumnDataSource consisting of a list of lists of point coordinates and a list of scalar values of matching length.

Missing points#

Just as with the line() and multi_line() methods, you can pass NaN values to patch() and patches() glyphs. This produces disjointed patches with gaps for NaN values.

from bokeh.plotting import figure, output_file, show

output_file("patch.html")

p = figure(width=400, height=400)

# add a patch renderer with a NaN value
nan = float('nan')
p.patch([1, 2, 3, nan, 4, 5, 6], [6, 7, 5, nan, 7, 3, 6], alpha=0.5, line_width=2)

show(p)

Warning

Bokeh doesn’t currently support hit testing on patch objects with NaN values.

Polygons#

The multi_polygons() glyph uses nesting to accept a variety of information relevant to polygons. The method duplicates the functionality of patches() but you can also use it to render holes inside polygons.

Note

Unlike many other glyph methods, multi_polygons() accepts a triple-nested lists of x and y positions for the exterior and holes composing each polygon. The multi_polygons() method also expects a scalar value or a list of scalars for each item for parameters such as color, alpha, and line width. You can similarly use a ColumnDataSource consisting of a triple- nested list of point coordinates and a list of scalars, with the top-level list of point coordinates being of equal length with the list of scalars.

Simple polygon#

The following example generates a single polygon from a triple-nested list of one-dimensional sequences of x and y points using the multi_polygons() glyph method.

from bokeh.plotting import figure, output_file, show

output_file('multipolygon_simple.html')

p = figure(width=400, height=400)
p.multi_polygons(xs=[[[[1, 1, 2, 2]]]],
                 ys=[[[[3, 4, 4, 3]]]])

show(p)

Polygon with holes#

The following example generates a single polygon with holes from three sequences of x and y points. The first sequence represents the exterior of the polygon and the following sequences represent the holes.

from bokeh.plotting import figure, output_file, show

output_file('multipolygon_with_holes.html')

p = figure(width=400, height=400)
p.multi_polygons(xs=[[[ [1, 2, 2, 1], [1.2, 1.6, 1.6], [1.8, 1.8, 1.6] ]]],
                 ys=[[[ [3, 3, 4, 4], [3.2, 3.6, 3.2], [3.4, 3.8, 3.8] ]]])

show(p)

Multi-polygon with separate parts#

A single polygon concept can comprise multiple polygon geometries. The following example generates a multi-polygon glyph from several sequences of x and y points. Each item in the sequence represents a part of the glyph.

from bokeh.plotting import figure, output_file, show

output_file('multipolygon_with_separate_parts.html')

p = figure(width=400, height=400)
p.multi_polygons(xs=[[[ [1, 1, 2, 2], [1.2, 1.6, 1.6], [1.8, 1.8, 1.6] ], [ [3, 4, 3] ]]],
                 ys=[[[ [4, 3, 3, 4], [3.2, 3.2, 3.6], [3.4, 3.8, 3.8] ], [ [1, 1, 3] ]]])

show(p)

Multiple multi-polygons#

The top-level of nesting separates each multi-polygon from the rest. You can think of each multi-polygon as a row in the data source, potentially with a corresponding label or color.

from bokeh.plotting import figure, output_file, show

output_file('multipolygons.html')

p = figure(width=400, height=400)
p.multi_polygons(
    xs=[
        [[ [1, 1, 2, 2], [1.2, 1.6, 1.6], [1.8, 1.8, 1.6] ], [ [3, 3, 4] ]],
        [[ [1, 2, 2, 1], [1.3, 1.3, 1.7, 1.7] ]]],
    ys=[
        [[ [4, 3, 3, 4], [3.2, 3.2, 3.6], [3.4, 3.8, 3.8] ], [ [1, 3, 1] ]],
        [[ [1, 1, 2, 2], [1.3, 1.7, 1.7, 1.3] ]]],
    color=['blue', 'red'])

show(p)

Ellipses#

The ellipse() glyph method accepts the same properties as rect(), but renders ellipse shapes.

from math import pi

from bokeh.plotting import figure, output_file, show

output_file('ellipses.html')

p = figure(width=400, height=400)
p.ellipse(x=[1, 2, 3], y=[1, 2, 3], width=[0.2, 0.3, 0.1], height=0.3,
          angle=pi/3, color="#CAB2D6")

show(p)

Images#

You can display images on Bokeh plots using the image(), image_rgba(), and image_url() glyph methods. You can use hovering tooltips with image glyphs to let the user see the values of each pixel. For more information on how to enable hovering tooltips for images, see Image hover.

Raw RGBA data#

The following example shows how to display images using raw RGBA data with the image_rgba() method.

import numpy as np

from bokeh.plotting import figure, output_file, show

# create an array of RGBA data
N = 20
img = np.empty((N, N), dtype=np.uint32)
view = img.view(dtype=np.uint8).reshape((N, N, 4))
for i in range(N):
    for j in range(N):
        view[i, j, 0] = int(255 * i / N)
        view[i, j, 1] = 158
        view[i, j, 2] = int(255 * j / N)
        view[i, j, 3] = 255

output_file("image_rgba.html")

p = figure(width=400, height=400, x_range=(0, 10), y_range=(0, 10))

p.image_rgba(image=[img], x=[0], y=[0], dw=[10], dh=[10])

show(p)

Color mapped images#

The following example shows how to supply an array of scalar values and have Bokeh automatically color map the data in the browser with the image() glyph method.

import numpy as np

from bokeh.plotting import figure, output_file, show

output_file("image.html", title="image.py example")

x = np.linspace(0, 10, 250)
y = np.linspace(0, 10, 250)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)

p = figure(width=400, height=400)
p.x_range.range_padding = p.y_range.range_padding = 0

p.image(image=[d], x=0, y=0, dw=10, dh=10, palette="Sunset11", level="image")
p.grid.grid_line_width = 0.5

show(p)

Note that this example sets the render level to "image". Normally, Bokeh draws all glyphs above grid lines, but with this render level they appear below the grid lines.

Origin and Anchor#

The image() and image_rgba() glyphs provide origin and anchor properties for controlling the relative position and orientation of the image.

When drawn, the image will cover a rectangular drawing region of size dw by dh.

The anchor property specifies where that rectangular drawing region is located, relative to the glyph coordinates x and y. It can be used to shift the image vertically or horizontally from x and y.

The origin property specifies which corner of the rectangular drawing region corresponds to the [0, 0] pixel of the image array. It can be used to flip the image vertically or horizontally within its drawing region.

The example below lets you explore all the different combinations of anchor an origin for a simple 2x2 image.

Segments and rays#

To draw multiple individual line segments use the segment() and ray() glyph methods.

The segment() method accepts the starting points x0 and y0 and end points x1 and y1. It renders segments between those points.

from bokeh.plotting import figure, show

p = figure(width=400, height=400)
p.segment(x0=[1, 2, 3], y0=[1, 2, 3], x1=[1.2, 2.4, 3.1],
          y1=[1.2, 2.5, 3.7], color="#F4A582", line_width=3)

show(p)

The ray() method accepts the starting points x and y with a length (in screen units) and an angle. The angle_units parameter defaults to "rad" but you can also set it to "deg" to have the angle measured in degrees instead of radians. To have an “infinite” ray that always extends to the edge of the plot, set length to 0.

from bokeh.plotting import figure, show

p = figure(width=400, height=400)
p.ray(x=[1, 2, 3], y=[1, 2, 3], length=45, angle=[30, 45, 60],
      angle_units="deg", color="#FB8072", line_width=2)

show(p)

Wedges and arcs#

To draw a simple line arc, use the arc() glyph method, which accepts radius, start_angle, and end_angle to determine position. Additionally, the direction property determines whether to render clockwise ("clock") or anti-clockwise ("anticlock") between the start and end angles.

from bokeh.plotting import figure, show

p = figure(width=400, height=400)
p.arc(x=[1, 2, 3], y=[1, 2, 3], radius=0.1, start_angle=0.4, end_angle=4.8, color="navy")

show(p)

The wedge() glyph method accepts the same properties as arc() but renders a filled wedge instead:

from bokeh.plotting import figure, show

p = figure(width=400, height=400)
p.wedge(x=[1, 2, 3], y=[1, 2, 3], radius=0.2, start_angle=0.4, end_angle=4.8,
        color="firebrick", alpha=0.6, direction="clock")

show(p)

The annular_wedge() glyph method is similar to wedge() but leaves an inner portion of the wedge hollow. It accepts an inner_radius and outer_radius instead of just radius.

from bokeh.plotting import figure, show

p = figure(width=400, height=400)
p.annular_wedge(x=[1, 2, 3], y=[1, 2, 3], inner_radius=0.1, outer_radius=0.25,
                start_angle=0.4, end_angle=4.8, color="green", alpha=0.6)

show(p)

Finally, the annulus() glyph method also accepts inner_radius and outer_radius to produce hollow circles.

from bokeh.plotting import figure, show

p = figure(width=400, height=400)
p.annulus(x=[1, 2, 3], y=[1, 2, 3], inner_radius=0.1, outer_radius=0.25,
          color="orange", alpha=0.6)

show(p)

Specialized curves#

To draw parameterized quadratic and cubic curves, use the quadratic() and bezier() glyph methods. For more detail on these curves, see reference documentation.

Combining multiple glyphs#

You can combine multiple glyphs on a single plot by calling their methods on a single figure().

from bokeh.plotting import figure, output_file, show

x = [1, 2, 3, 4, 5]
y = [6, 7, 8, 7, 3]

output_file("multiple.html")

p = figure(width=400, height=400)

# add both a line and circles on the same plot
p.line(x, y, line_width=2)
p.circle(x, y, fill_color="white", size=8)

show(p)

This principle applies to all bokeh.plotting glyph methods. You can add as many glyphs to a Bokeh plot as you want.

Setting ranges#

By default, Bokeh attempts to automatically set the data bounds of plots to fit snugly around the data. You may, however, need to set a plot’s range explicitly. To do so, set the x_range and/or y_range properties using a Range1d object that lets you set the start and end points of the range you want.

p.x_range = Range1d(0, 100)

For convenience, the figure() function can also accept (start, end) tuples as values for the x_range or y_range parameters. Here’s how you can use both methods to set a range:

from bokeh.models import Range1d
from bokeh.plotting import figure, output_file, show

output_file("title.html")

# create a new plot with a range set with a tuple
p = figure(width=400, height=400, x_range=(0, 20))

# set a range using a Range1d
p.y_range = Range1d(0, 15)

p.circle([1, 2, 3, 4, 5], [2, 5, 8, 2, 7], size=10)

show(p)

Ranges also have a bounds property that lets you specify the limits of the plot beyond which the user cannot pan or zoom.

# set a range using a Range1d
p.y_range = Range1d(0, 15, bounds=(0, None))

Specifying axis types#

All the examples above use the default linear axis. This axis is suitable for plots that need to show numerical data on a linear scale. However, you may have categorical data or need to display numerical data on a datetime or log scale. This section shows you how to specify the axis type when using the bokeh.plotting interface.

Categorical axes#

To create a categorical axis, specify a FactorRange for one of the plot’s ranges or a list of factors to be converted to one. Here’s an example:

from bokeh.plotting import figure, output_file, show

factors = ["a", "b", "c", "d", "e", "f", "g", "h"]
x = [50, 40, 65, 10, 25, 37, 80, 60]

output_file("categorical.html")

p = figure(y_range=factors)

p.circle(x, factors, size=15, fill_color="orange", line_color="green", line_width=3)

show(p)

For complete details, see Handling categorical data.

Datetime axes#

Note

The example in this section requires a network connection and depends on the open source Pandas library to present realistic time series data.

For time series, or any data that involves dates or time, you may want to use axes with labels suitable for different date and time scales.

The figure() function accepts x_axis_type and y_axis_type as arguments. To specify a datetime axis, pass "datetime" for the value of either of these parameters.

import pandas as pd

from bokeh.plotting import figure, output_file, show
from bokeh.sampledata.stocks import AAPL

df = pd.DataFrame(AAPL)
df['date'] = pd.to_datetime(df['date'])

output_file("datetime.html")

# create a new plot with a datetime axis type
p = figure(width=800, height=250, x_axis_type="datetime")

p.line(df['date'], df['close'], color='navy', alpha=0.5)

show(p)

Note

Future versions of Bokeh will attempt to auto-detect situations when datetime axes are appropriate and add them automatically.

Log scale axes#

Data that grows exponentially or covers many orders of magnitude often requires one axis to be on a log scale. For data that has a power law relationship, you may want to use log scales on both axes.

You can use the same figure() arguments, x_axis_type and y_axis_type, to set one or both of the axes to "log".

By default, Bokeh calculates log axis ranges to fit around positive value data. For information on how to set your own ranges, see Setting ranges.

from bokeh.plotting import figure, output_file, show

x = [0.1, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0]
y = [10**xx for xx in x]

output_file("log.html")

# create a new plot with a log axis type
p = figure(width=400, height=400, y_axis_type="log")

p.line(x, y, line_width=2)
p.circle(x, y, fill_color="white", size=8)

show(p)

Twin axes#

You can add multiple axes representing different ranges to a single plot. To do this, configure the plot with “extra” named ranges in the extra_x_range and extra_y_range properties. You can then refer to these named ranges when adding new glyph methods as well as when adding new axis objects with the add_layout method of the Plot. Here’s an example:

from numpy import arange, linspace, pi, sin

from bokeh.models import LinearAxis, Range1d
from bokeh.plotting import figure, output_file, show

x = arange(-2*pi, 2*pi, 0.1)
y = sin(x)
y2 = linspace(0, 100, len(y))

output_file("twin_axis.html")

p = figure(x_range=(-6.5, 6.5), y_range=(-1.1, 1.1))

p.circle(x, y, color="red")

p.extra_y_ranges = {"foo": Range1d(start=0, end=100)}
p.circle(x, y2, color="blue", y_range_name="foo")
p.add_layout(LinearAxis(y_range_name="foo"), 'left')

show(p)