Ranges and axes#

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, show

# 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.scatter([1, 2, 3, 4, 5], [2, 5, 8, 2, 7], size=10)


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))

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, show

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

p = figure(y_range=factors)

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


Datetime axes#


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, show
from bokeh.sampledata.stocks import AAPL

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

# 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)



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, show

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

# 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.scatter(x, y, fill_color="white", size=8)


Mercator axes#

Mercator axes are useful for tile sources. You can use the same figure() arguments, x_axis_type and y_axis_type, to set one or both of the axes to "mercator".

from bokeh.plotting import figure, show

# range bounds supplied in web mercator coordinates
p = figure(x_range=(-2000000, 2000000), y_range=(1000000, 7000000),
           x_axis_type="mercator", y_axis_type="mercator")

p.add_tile("CartoDB Positron", retina=True)


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.layouts import column
from bokeh.models import (CustomJS, LinearAxis, Range1d, Select,
                          WheelZoomTool, ZoomInTool, ZoomOutTool)
from bokeh.palettes import Sunset6
from bokeh.plotting import figure, show

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

blue, red = Sunset6[2], Sunset6[5]

p = figure(x_range=(-2*pi, 2*pi), y_range=(-1, 1), tools="pan,box_zoom,save,reset")
p.background_fill_color = "#fafafa"

blue_circles = p.scatter(x, y, line_color="black", fill_color=blue, size=12)
p.axis.axis_label = "light blue circles"
p.axis.axis_label_text_color = blue

p.extra_x_ranges['foo'] = Range1d(-2*pi, 2*pi)
p.extra_y_ranges['foo'] = Range1d(0, 100)
red_circles = p.scatter(x, y2, color=red, size=8,

ax2 = LinearAxis(
    axis_label="red circles",
ax2.axis_label_text_color = red
p.add_layout(ax2, 'left')

ax3 = LinearAxis(
    axis_label="red circles",
ax3.axis_label_text_color = red
p.add_layout(ax3, 'below')

wheel_zoom = WheelZoomTool()

p.toolbar.active_scroll = wheel_zoom

zoom_in_blue = ZoomInTool(renderers=[blue_circles], description="Zoom in blue circles")
zoom_out_blue = ZoomOutTool(renderers=[blue_circles], description="Zoom out blue circles")
p.add_tools(zoom_in_blue, zoom_out_blue)

zoom_in_red = ZoomInTool(renderers=[red_circles], description="Zoom in red circles")
zoom_out_red = ZoomOutTool(renderers=[red_circles], description="Zoom out red circles")
p.add_tools(zoom_in_red, zoom_out_red)

select = Select(title="Zoom together:", options=["none", "cross", "all"], value=wheel_zoom.zoom_together)
select.js_on_change("value", CustomJS(
    args=dict(select=select, wheel_zoom=wheel_zoom),
export default ({select, wheel_zoom}) => {
  wheel_zoom.zoom_together = select.value
show(column(select, p))

Fixed location axis#

By defatult Bokeh places axes on the sides of plots, but it is possible to locate axes anywhere along a range by setting their fixed_location property:

import numpy as np

from bokeh.plotting import figure, show

x = np.linspace(-6, 6, 500)
y = 8*np.sin(x)*np.sinc(x)

p = figure(width=800, height=300, title="", tools="",
           toolbar_location=None, match_aspect=True)

p.line(x, y, color="navy", alpha=0.4, line_width=4)
p.background_fill_color = "#efefef"
p.xaxis.fixed_location = 0
p.yaxis.fixed_location = 0