Note that Bokeh plots created using the bokeh.plotting interface come with a default set of tools and default visual styles. See Styling Visual Attributes for information about how to customize the visual style of plots, and Configuring Plot Tools for information about changing or specifying tools.
To scatter circle markers on a plot, use the circle() method of Figure:
circle()
Figure
from bokeh.plotting import figure, output_file, show # output to static HTML file output_file("line.html") p = figure(plot_width=400, plot_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, to scatter square markers, use the square() method of Figure:
square()
from bokeh.plotting import figure, output_file, show # output to static HTML file output_file("square.html") p = figure(plot_width=400, plot_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)
There are lots of marker types available in Bokeh, you can see details and example plots for all of them by clicking on entries in the list below:
asterisk()
circle_cross()
circle_dot()
circle_x()
circle_y()
cross()
dash()
dot()
diamond()
diamond_cross()
diamond_dot()
hex()
hex_dot()
inverted_triangle()
plus()
square_cross()
square_dot()
square_pin()
square_x()
triangle()
triangle_dot()
triangle_pin()
x()
y()
All the markers have the same set of properties: x, y, size (in screen units), and angle (radians by default). Additionally, circle() has a radius property that can be used to specify data-space units.
x
y
size
angle
radius
Below is an example that shows how to generate a single line glyph from one-dimensional sequences of x and y points using the line() glyph method:
line()
from bokeh.plotting import figure, output_file, show output_file("line.html") p = figure(plot_width=400, plot_height=400) # add a line renderer p.line([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], line_width=2) show(p)
For some kinds of data, it may be more appropriate to draw discrete steps between data points, instead of connecting points with linear segments. The step() glyph method can be used to accomplish this:
step()
from bokeh.plotting import figure, output_file, show output_file("line.html") p = figure(plot_width=400, plot_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)
Step levels can be drawn before, after, or centered on the x-coordinates, as configured by the mode parameter.
mode
Sometimes it is useful to plot multiple lines all at once. This can be accomplished with the multi_line() glyph method:
multi_line()
from bokeh.plotting import figure, output_file, show output_file("patch.html") p = figure(plot_width=400, plot_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
This glyph is unlike most other glyphs. Instead of accepting a one-dimensional list or array of scalar values, it accepts a “list of lists” for x and y positions of each line, parameters xs and ys. multi_line also expects a scalar value or a list of scalers per each line for parameters such as color, alpha, linewidth, etc. Similarly, a ColumnDataSource may be used consisting of a “list of lists” and a list of scalars where the length of the list of scalars and length of lists must match.
NaN values can be passed to line() and multi_line() glyphs. In this case, you end up with single logical line objects, that have multiple disjoint components when rendered:
NaN
from bokeh.plotting import figure, output_file, show output_file("line.html") p = figure(plot_width=400, plot_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)
In some instances, it is desirable to stack lines that are aligned on a common index (e.g. time series of percentages). The vline_stack() and hline_stack() convenience methods can be used to accomplish this. Note that these methods stack columns from an explicitly supplied ColumnDataSource (see the section Providing Data for more information.
vline_stack()
hline_stack()
ColumnDataSource
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(plot_width=400, plot_height=400) p.vline_stack(['y1', 'y2'], x='x', source=source) show(p)
When drawing rectangular bars (often representing intervals), it is often more convenient to have coordinates that are a hybrid of the two systems above. Bokeh provides the hbar() and vbar() glyphs function for this purpose.
hbar()
vbar()
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(plot_width=400, plot_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(plot_width=400, plot_height=400) p.hbar(y=[1, 2, 3], height=0.5, left=0, right=[1.2, 2.5, 3.7], color="navy") show(p)
It is often desirable to stack bars. This can be accomplished with the vbar_stack() and hbar_stack() convenience methods. Note that these methods stack columns from an explicitly supplied ColumnDataSource (see the section Providing Data for more information).
vbar_stack()
hbar_stack()
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(plot_width=400, plot_height=400) p.hbar_stack(['x1', 'x2'], y='y', height=0.8, color=("grey", "lightgrey"), source=source) show(p)
More examples of stacked bars can be found in the section Handling Categorical Data.
To draw axis aligned rectangles (“quads”) by specifying the left, right, top, and bottom positions, use the quad() glyph function:
left
right
top
bottom
quad()
from bokeh.plotting import figure, output_file, show output_file('rectangles.html') p = figure(plot_width=400, plot_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 arbitrary rectangles by specifying a center point, width, height, and angle, use the rect() glyph function:
rect()
from math import pi from bokeh.plotting import figure, output_file, show output_file('rectangles_rotated.html') p = figure(plot_width=400, plot_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)
Bokeh can plot hexagonal tiles, which are often used for showing binned aggregations. The hex_tile() method takes a size parameter to define the size of the hex grid, and axial coordinates to specify which tiles are present.
hex_tile()
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(plot_width=400, plot_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 realistic example below computes counts per bin using the hexbin() function and plots the colormapped counts:
hexbin()
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)
The above code can be made even simpler by calling the hexbin() method of Figure.
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, which will be filled between.
A single directed area between two aligned series can be created in the vertical direction with varea() or in the horizontal direction with harea().
varea()
harea()
from bokeh.plotting import figure, output_file, show output_file("varea.html") p = figure(plot_width=400, plot_height=400) p.varea(x=[1, 2, 3, 4, 5], y1=[2, 6, 4, 3, 5], y2=[1, 4, 2, 2, 3]) show(p)
It is often desirable to stack directed areas. This can be accomplished with the varea_stack() and harea_stack() convenience methods. Note that these methods stack columns from an explicitly supplied ColumnDataSource (see the section Providing Data for more information).
varea_stack()
harea_stack()
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(plot_width=400, plot_height=400) p.varea_stack(['y1', 'y2'], x='x', color=("grey", "lightgrey"), source=source) show(p)
Below is an example that shows how to generate a single polygonal patch glyph from one-dimensional sequences of x and y points using the patch() glyph method:
patch()
from bokeh.plotting import figure, output_file, show output_file("patch.html") p = figure(plot_width=400, plot_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)
Sometimes it is useful to plot multiple polygonal patches all at once. This can be accomplished with the patches() glyph method:
patches()
from bokeh.plotting import figure, output_file, show output_file("patch.html") p = figure(plot_width=400, plot_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)
This glyph is unlike most other glyphs. Instead of accepting a one-dimensional list or array of scalar values, it accepts a “list of lists” for x and y positions of each patch, parameters xs and ys. patches also expects a scalar value or a list of scalers per each patch for parameters such as color, alpha, linewidth, etc. Similarly, a ColumnDataSource may be used consisting of a “list of lists” and a list of scalars where the length of the list of scalars and length of lists must match.
Just as with line() and multi_line(), NaN values can be passed to patch() and patches() glyphs. In this case, you end up with single logical patch objects, that have multiple disjoint components when rendered:
from bokeh.plotting import figure, output_file, show output_file("patch.html") p = figure(plot_width=400, plot_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
Hit testing on patch objects with NaN values is not currently supported.
The multi_polygons() glyph uses nesting to accept a variety of information relevant to polygons. Anything that can be rendered as a patches() can also be rendered as multi_polygons(), but additionally, multi_polygons() can render holes inside each polygon.
multi_polygons()
This glyph is unlike most other glyphs. Instead of accepting a one-dimensional list or array of scalar values, it accepts a 3 times nested list of x and y positions for the exterior and holes composing each polygon. MultiPolygons also expects a scalar value or a list of scalers per each item for parameters such as color, alpha, linewidth, etc. Similarly, one can use a ColumnDataSource consisting of a 3 times nested list and a list of scalars where the length of the list of scalars and length of the top level list must match.
Below is an example that shows how to generate a single polygon glyph from 3 times nested 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(plot_width=400, plot_height=400) p.multi_polygons(xs=[[[[1, 1, 2, 2]]]], ys=[[[[3, 4, 4, 3]]]]) show(p)
Below is an example that shows how to generate 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(plot_width=400, plot_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)
Sometimes one conceptual polygon is composed of multiple polygon geometries. Below is an example that shows how to generate a MultiPolygon glyph from several sequences of x and y points. Each item in the sequence represents a part of the MultiPolygon:
from bokeh.plotting import figure, output_file, show output_file('multipolygon_with_separate_parts.html') p = figure(plot_width=400, plot_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)
The top-level of nesting is used to separate each MultiPolygon from the others. Each MultiPolygon can be thought of 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(plot_width=400, plot_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)
The ellipse() glyph method accepts the same properties as rect(), but renders ellipse shapes:
ellipse()
from math import pi from bokeh.plotting import figure, output_file, show output_file('ellipses.html') p = figure(plot_width=400, plot_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)
You can display images on Bokeh plots using the image(), image_rgba(), and image_url() glyph methods. It is possible to use a hover tool with image glyphs to allow for interactive inspection of the values of any pixel. For more information on how to enable hover with images, please consult the Image Hover section of the User’s Guide.
image()
image_rgba()
image_url()
The first example here shows how to display images in Bokeh plots from raw RGBA data using image_rgba():
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(plot_width=400, plot_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)
It is also possible to provide an array of scalar values, and have Bokeh automatically colormap the data in the browser by using the image() glyph method. The next example shows how to do this:
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(plot_width=400, plot_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="Spectral11", level="image") p.grid.grid_line_width = 0.5 show(p)
Also note that in the above example we have set the render level to "image". Normally, all glyphs are drawn above grid lines, but setting the "image" render level can be used to draw underneath the grid lines.
"image"
Sometimes it is useful to be able to draw many individual line segments at once. Bokeh provides the segment() and ray() glyph methods to render these.
segment()
ray()
The segment() function accepts start points x0, y0 and end points x1 and y1 and renders segments between these:
x0
y0
x1
y1
from bokeh.plotting import figure, show p = figure(plot_width=400, plot_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() function accepts start points x, y with a length (in screen units) and an angle. The default angle_units are "rad" but can also be changed to "deg". To have an “infinite” ray, that always extends to the edge of the plot, specify 0 for the length:
length
angle_units
"rad"
"deg"
0
from bokeh.plotting import figure, show p = figure(plot_width=400, plot_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)
To draw a simple line arc, Bokeh provides 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.
arc()
start_angle
end_angle
direction
"clock"
"anticlock"
from bokeh.plotting import figure, show p = figure(plot_width=400, plot_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:
wedge()
from bokeh.plotting import figure, show p = figure(plot_width=400, plot_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 arc(), but draws a filled area. It accepts an inner_radius and outer_radius instead of just radius:
annular_wedge()
inner_radius
outer_radius
from bokeh.plotting import figure, show p = figure(plot_width=400, plot_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 methods, which accepts inner_radius and outer_radius, can be used to draw filled rings:
annulus()
from bokeh.plotting import figure, show p = figure(plot_width=400, plot_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)
Bokeh also provides quadratic() and bezier() glyph methods for drawing parameterized quadratic and cubic curves. These are somewhat uncommon; please refer to the reference documentation for details.
quadratic()
bezier()
Combining multiple glyphs on a single plot is a matter of calling more than one glyph method 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(plot_width=400, plot_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 holds in general for all the glyph methods in bokeh.plotting. Any number of glyphs may be added to a Bokeh plot.
By default, Bokeh will attempt to automatically set the data bounds of plots to fit snugly around the data. Sometimes, you may need to set a plot’s range explicitly. This can be accomplished by setting the x_range or y_range properties using a Range1d object that gives the start and end points of the range you want:
x_range
y_range
Range1d
p.x_range = Range1d(0, 100)
As a convenience, the figure() function can also accept tuples of (start, end) as values for the x_range or y_range parameters. Below is an example that shows both methods of setting the range:
figure()
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(plot_width=400, plot_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 allows you to specify limits of the plot that you do not want the user to be able to pan/zoom beyond.
bounds
# set a range using a Range1d p.y_range = Range1d(0, 15, bounds=(0, None))
All the examples above use the default linear axis. This axis is suitable for many plots that need to show numerical data on a linear scale. In other cases you may have categorical data, or need to display numerical data on a datetime or log scale. This section shows how to specify the axis type when using bokeh.plotting interface.
Categorical axes are created by specifying a FactorRange for one of the plot ranges (or a list of factors to be converted to one). Below is a simple example, for complete details see Handling Categorical Data.
FactorRange
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)
When dealing with timeseries data, or any data that involves dates or times, it is desirable to have an axis that can display labels that are appropriate to different date and time scales.
This example requires a network connection and depends on the open source Pandas library in order to more easily present realistic timeseries data.
We have seen how to use the figure() function to create plots using the bokeh.plotting interface. This 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.
x_axis_type
y_axis_type
"datetime"
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(plot_width=800, plot_height=250, x_axis_type="datetime") p.line(df['date'], df['close'], color='navy', alpha=0.5) show(p)
Future versions of Bokeh will attempt to auto-detect situations when datetime axes are appropriate, and add them automatically by default.
When dealing with data that grows exponentially or is of many orders of magnitude, it is often necessary to have one axis on a log scale. Another scenario involves plotting data that has a power law relationship, when it is desirable to use log scales on both axes.
As we saw above, the figure() function accepts x_axis_type and y_axis_type as arguments. To specify a log axis, pass "log" for the value of either of these parameters.
"log"
By default, log axis ranges are calculated to fit around positive valued data. To set your own ranges, see the section on 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(plot_width=400, plot_height=400, y_axis_type="log") p.line(x, y, line_width=2) p.circle(x, y, fill_color="white", size=8) show(p)
It is possible to 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. Then these named ranges can be referred to when adding new glyph methods, and also to add new axes objects using the add_layout method on Plot. An example is given below:
extra_x_range
extra_y_range
add_layout
Plot
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)