$ pip install bokehInstall (v3.9+). Needs only a browser to view.from bokeh.plotting import figure, show★The primary interface — nearly every script starts here.from bokeh.io import output_file, saveSend results to a standalone HTML file.from bokeh.models import ColumnDataSourceLow-level objects for data, tools, annotations.output_notebook()Render inline in a Jupyter notebook.from bokeh.sampledata import ...Bundled demo datasets for practice.
p = figure()★New plot with axes, grid & default toolbar.figure(width=600, height=400)Size in pixels. Notplot_width— see gotchas.figure(title="T", x_axis_label="x")★Title and axis labels up front.figure(x_range=(0, 10))Fix an axis range (tuple = numeric bounds).figure(x_range=["a","b","c"])A list makes a categorical axis.figure(x_axis_type="datetime")★Time axis; also"log","mercator".figure(tools="pan,wheel_zoom,reset")Choose the toolbar (see Tools card).figure(sizing_mode="stretch_width")Responsive width; alsoscale_both.figure(plot_width=600)removedPre-3.0 name — usewidth/heightnow.
p.scatter(x, y, size=10)★The recommended marker method (default = circle).p.scatter(x, y, marker="square")Pick any marker by name.p.scatter(x, y, size=12, color="navy", alpha=.5)★Size in screen px; colour + transparency.p.scatter("mpg", "hp", source=src)Reference columns by name from a data source.p.circle(x, y, radius=0.5)radiusis in data units — scales on zoom.markers: circle square triangle diamond hex…star plus x asterisk dot cross inverted_trianglemarker="circle_x"Combo markers:*_cross,*_dot,*_x.
p.line(x, y, line_width=2)★A single connected line.p.line(x, y, line_dash="dashed", color="red")Dashed / dotted styled line.p.step(x, y, mode="center")Step chart;before/after/center.p.multi_line(xs, ys)Many independent lines in one call.p.vline_stack(["a","b"], x="i", source=src)Stack columns into cumulative lines.p.line("x", "y", source=src)Drive a line from a ColumnDataSource.
p.vbar(x=cats, top=vals, width=0.8)★Vertical bars — the usual bar chart.p.hbar(y=cats, right=vals, height=0.8)Horizontal bars.p.vbar_stack(stacks, x="cat", source=src)Stacked bars from several columns.p.quad(left, right, top, bottom)Axis-aligned rectangles — histogram bins.p.varea(x, y1, y2)Filled band between two curves (hareatoo).p.patch(x, y) · p.patches(xs, ys)One / many filled polygons.p.wedge(x, y, radius, start_angle, end_angle)Pie / donut slices (pair withcumsum).
src = ColumnDataSource(data=dict(x=[..], y=[..]))★The shared table behind glyphs, hover & selection.ColumnDataSource(df)★Straight from a pandas DataFrame.p.scatter("x", "y", source=src)★Refer to columns by name — one source, many glyphs.src.data = new_dictReplace all data (columns must stay same length).src.stream(new, rollover=200)Append rows for live / streaming data.view = CDSView(filter=GroupFilter(..))A filtered slice of one source.p.scatter(..., source=src, view=view)Render only the rows the view keeps.
from bokeh.transform import factor_cmap, linear_cmap★Map a column to colour, computed in the browser.factor_cmap("cat", Category10[3], factors)★Categorical → discrete palette.linear_cmap("v", "Viridis256", low, high)Continuous → colour gradient (log_cmaptoo).from bokeh.palettes import Spectral6, Viridis256Named palettes;Category10,Turbo256, …from bokeh.transform import cumsumRunning sum — turns counts into pie angles.from bokeh.transform import jitter, dodgeSpread / offset points within categories.
p.line(..., line_color="red", line_alpha=.6)Line visual properties.p.scatter(..., fill_color="navy", fill_alpha=.3)Fill properties (glyphs have fill + line + text).p.xaxis.axis_label = "speed"★Plural selectors hit every matching element.p.xgrid.grid_line_color = NoneHide the vertical gridlines.p.background_fill_color = "#fafafa"Plot background; alsoborder_fill_color.p.title.text_color = "olive"Style the title text.curdoc().theme = "dark_minimal"Global theme:caliber,night_sky,contrast.
from bokeh.models import Span, BoxAnnotation, LabelReference lines, shaded regions, text.p.add_layout(Span(location=5, dimension="height"))A vertical/horizontal reference line.BoxAnnotation(bottom=2, top=6, fill_alpha=.1)Shade a band of interest.Label(x=2, y=5, text="peak")Anchored text;LabelSetfor many.p.add_layout(ColorBar(color_mapper=m), "right")★Colour bar for a mapped column.Arrow(x_start, y_start, x_end, y_end)Annotate with a directional arrow.
p.line(..., legend_label="Temp.")★One fixed label for this renderer.p.scatter(..., legend_field="origin")One entry per factor, resolved in the browser.p.scatter(..., legend_group="origin")Same, but grouped in Python at render time.p.legend.location = "top_left"★Position inside the frame.p.legend.orientation = "horizontal"Lay entries in a row.p.legend.click_policy = "hide"★Click entries tohideormuteseries.p.add_layout(p.legend[0], "right")Move the legend outside the plot frame.
figure(tools="pan,box_zoom,wheel_zoom,reset,save")★Comma-string of tool names on creation.p.add_tools(HoverTool(), BoxSelectTool())Add configured tool objects later.figure(toolbar_location="above")above/below/left/right/None.figure(active_scroll="wheel_zoom")Make a tool active by default.navigate: pan box_zoom wheel_zoom reset saveThe everyday toolbar.select: box_select lasso_select poly_select tapSelection tools feedsource.selected.inspect: hover crosshairPassive — no click needed.
HoverTool(tooltips=[("label", "@field")])★@field= a column value at the point.("(x,y)", "($x, $y)")$x $y= cursor position in data space.("value", "@y{0.00}")Number formatting inside braces.formatters={"@d": "datetime"}Format a date column:"@d{%F}".HoverTool(mode="vline")Trigger along a whole vertical line.tooltips="@x has value @y"Plain-string form for quick tooltips.special: $x $y $sx $sy $name $indexCursor · screen px · renderer · row index.
p2.x_range = p1.x_range★Share a range → linked pan & zoom.p2.y_range = p1.y_rangeLink the other axis too.p1, p2 share one source★Selecting in one highlights in both (brushing).CrosshairTool(...) sharedA crosshair that tracks across linked plots.gridplot([[p1, p2]])Grid layouts keep one merged toolbar.
from bokeh.layouts import row, column, gridplotCompose plots & widgets.row(p1, p2, p3)★Side by side.column(p1, p2)★Stacked vertically.gridplot([[p1, p2], [p3, None]])★Grid with a shared toolbar;None= gap.from bokeh.models import TabPanel, TabsRenamed: it wasPanelbefore 3.0.Tabs(tabs=[TabPanel(child=p1, title="t1")])★Tabbed layout.from bokeh.models.widgets import PanelremovedGone since 3.0 — importTabPanelinstead.
from bokeh.models import Slider, Select, ButtonUI elements outside the plot.Slider(start=0, end=10, value=1, step=.1)★A numeric slider with a title.Select(options=["a","b"], value="a")Dropdown menu.Button(label="Go", button_type="success")Clickable button.CheckboxGroup · RadioGroup · TextInputMulti-select, single-select, free text.DateRangeSlider · Spinner · MultiChoice…and many more input widgets.column(slider, p)Drop widgets into any layout beside plots.
from bokeh.models import CustomJS★Runs in the browser — no server needed.slider.js_on_change("value", CustomJS(..))★React to a widget value in JS.CustomJS(args=dict(src=src), code="...")argsexposes Python objects to the JScode.btn.js_on_event("button_click", cb)React to UI events, not just properties.src.selected.js_on_change("indices", cb)Respond to a selection.slider.on_change("value", callback)Python callback — needs the Bokeh server.
output_file("plot.html", title="My plot")★Set an HTML file as the target.show(p)★Write the file and open it in a browser.save(p)Write the file without opening it.output_file("p.html", mode="inline")Embed BokehJS so the file works offline.output_notebook() ; show(p)Render inline in Jupyter.reset_output()Clear output state between plots.
from bokeh.embed import file_html, componentsTurn a plot into HTML you control.file_html(p, CDN, "title")★A complete standalone HTML string.script, div = components(p)★Snippets to drop into your own template.json_item(p, "myplot")JSON forBokeh.embed.embed_itemin JS.from bokeh.io import export_png, export_svgStatic image export.export_png(p, filename="p.png")Needs Selenium + a headless browser driver.
from bokeh.io import curdocThe current session's document.curdoc().add_root(layout)Publish your layout to the app.slider.on_change("value", update)Python callbacks run on the server.$ bokeh serve --show app.py★Run the app & open it.curdoc().add_periodic_callback(fn, 1000)Push updates every second (live data).use it for: Python logic · streaming · big dataOtherwise preferCustomJSstandalone files.