Code examples showing how to make Bokeh bar charts, stacked and grouped bars, histograms, line, step and scatter plots, heatmaps, pie charts, candlestick charts, Gantt charts, hexbin plots and gridplot layouts accessible with py-maidr.
Bokeh Examples
maidr also supports Bokeh figures and layouts. Pass a figure, a gridplot, or a row/column of figures to maidr.show(), maidr.render() or maidr.save_html(), and maidr produces a page where the interactive Bokeh plot keeps its pan, zoom and hover tools and can also be read from the keyboard, as sound, as text and in braille. For the other libraries, see the Matplotlib / Seaborn, Plotly and Altair pages.
WarningExperimental
Bokeh support is experimental. What it reads, and how, may change without a deprecation period, whichever plot type is drawn: see Plot Type Stability. Install it with pip install "maidr[bokeh]".
maidr reads the figure’s own renderers and data sources, not the drawn canvas, so what you hear is the data you plotted. As you move through a chart the mark being read is highlighted in the Bokeh plot itself: a bar, bin, cell, slice, candle or point is selected, dimming the rest, and a line, step or area shows a ring on the current point. A bar, bin, cell or point whose data source another mark also draws from, such as markers on a line built from one ColumnDataSource, gets the ring too, so the other mark is never faded; so do the bars of a dodged chart, or the segments of a stack, drawn from one ColumnDataSource, since selecting their row would light up every bar at that category.
Bokeh
read as
vbar, hbar
bar
vbar_stack, hbar_stack
stacked bar
vbar with dodge() or an offset such as ("a", -0.2), or on a nested FactorRange
dodged bar
quad
histogram
line, multi_line
line, one series per renderer or path
step
step
scatter, circle
point
rect coloured through a continuous colour mapper (linear_cmap, log_cmap)
heatmap
image of up to 10,000 cells
heatmap
wedge, annular_wedge
pie, one slice per row
segment wicks with vbar bodies on a datetime axis (Bokeh’s OHLC recipe)
candlestick
varea, varea_stack, harea, harea_stack
area [experimental], stacked area [experimental]
hbar from one date to another (left=start, right=end on a datetime axis)
gantt [experimental]
hex_tile coloured through a colour mapper, as p.hexbin draws it
hexbin [experimental]
Any other glyph is left out with a warning naming it, and a figure with nothing maidr can read is still drawn. A Tabs layout is read one panel at a time: the active one.
Bar Plot
Show the code
import seaborn as snsfrom bokeh.plotting import figureimport maidr penguins = sns.load_dataset("penguins")avg_mass = penguins.groupby("species")["body_mass_g"].mean()species = avg_mass.index.tolist()p = figure( x_range=species, title="Average Body Mass of Penguins by Species", x_axis_label="Species", y_axis_label="Body Mass (g)", height=350,)p.vbar(x=species, top=avg_mass.round(1).tolist(), width=0.8, color="#4c72b0")maidr.show(p)
Horizontal Bar Plot
Show the code
import seaborn as snsfrom bokeh.plotting import figureimport maidr penguins = sns.load_dataset("penguins")counts = penguins["island"].value_counts().sort_values()p = figure( y_range=counts.index.tolist(), title="Penguins Counted on Each Island", x_axis_label="Penguins", y_axis_label="Island", height=300,)p.hbar(y=counts.index.tolist(), right=counts.tolist(), height=0.7, color="#55a868")maidr.show(p)
Stacked Bar Plot
Show the code
import seaborn as snsfrom bokeh.plotting import figureimport maidr tips = sns.load_dataset("tips")totals = tips.pivot_table( index="day", columns="sex", values="total_bill", aggfunc="sum", observed=True)days = [str(day) for day in totals.index]source = {"day": days, "Male": totals["Male"].round(2), "Female": totals["Female"].round(2)}p = figure( x_range=days, title="Total Bill by Day and Sex", x_axis_label="Day", y_axis_label="Total Bill ($)", height=350,)p.vbar_stack( ["Male", "Female"], x="day", width=0.8, source=source, color=["#4c72b0", "#dd8452"], legend_label=["Male", "Female"],)p.legend.title ="Sex"maidr.show(p)
Dodged (Grouped) Bar Plot
Show the code
import seaborn as snsfrom bokeh.models import ColumnDataSourcefrom bokeh.plotting import figurefrom bokeh.transform import dodgeimport maidr penguins = sns.load_dataset("penguins").dropna()by_sex = penguins.pivot_table( index="species", columns="sex", values="body_mass_g", aggfunc="mean").round(1)species = by_sex.index.tolist()source = ColumnDataSource( {"species": species, "Male": by_sex["Male"], "Female": by_sex["Female"]})p = figure( x_range=species, title="Average Body Mass by Species and Sex", x_axis_label="Species", y_axis_label="Body Mass (g)", height=350,)p.vbar( x=dodge("species", -0.2, range=p.x_range), top="Male", width=0.35, source=source, color="#4c72b0", legend_label="Male",)p.vbar( x=dodge("species", 0.2, range=p.x_range), top="Female", width=0.35, source=source, color="#dd8452", legend_label="Female",)p.legend.title ="Sex"maidr.show(p)
A vbar on a nested FactorRange – x=[("Adelie", "Male"), ("Adelie", "Female"), ...] – is read the same way: the inner factor names the group, and the outer one the category.
An image is read as a heatmap whose cells are the array’s, bottom row first as Bokeh draws it, each row and column named by the centre of its cells. An image of more than 10,000 cells is left out with a warning, since no one walks a photograph cell by cell.
Each slice is announced with the value it was computed from – here the penguins column, which the angles are proportional to – and named by the legend. The right arrow walks the pie clockwise, as for every pie in maidr, so on a pie Bokeh drew counterclockwise from 3 o’clock it starts on the slice drawn last.
This is Bokeh’s own candlestick recipe: a segment from each day’s high to its low and a vbar between its open and close, one renderer for rising days and one for falling. maidr reads them together as one candlestick, a candle per date. A body is drawn between two prices whichever is larger, so maidr needs to be told which one is the open: here the prices are passed straight through (top=open, bottom=close), which draws a rising day’s body upside down and says so; a source with open and close columns says so too. Bodies drawn the right way up with neither are not guessed at, and are read as bars. A day that closes exactly where it opened gets no body from this recipe, so its open and close are not in the figure: maidr leaves that day out, with a warning.
Each figure of a gridplot, row or column is a subplot. Press Enter to go into one, Escape to come back out, and the arrow keys between them, in the directions they are laid out on the page. A figure with nothing maidr can read is left out of the grid.
An hbar running from one date to another is read as a Gantt chart: each task is a lane, the up and down arrows move between lanes as they are drawn, and every bar is announced with its start and end dates and its length in days.
Hexbin Plot [experimental]
Show the code
import seaborn as snsfrom bokeh.plotting import figureimport maidr penguins = sns.load_dataset("penguins").dropna()flipper = (penguins["flipper_length_mm"] -200) /10mass = (penguins["body_mass_g"] -4200) /800p = figure( title="Flipper Length vs Body Mass (standardised)", x_axis_label="Flipper Length (x10 mm from 200)", y_axis_label="Body Mass (x800 g from 4200)", match_aspect=True, height=400,)tiles, bins = p.hexbin(flipper.to_numpy(), mass.to_numpy(), size=0.3)p.add_layout(tiles.construct_color_bar(title="Penguins"), "right")maidr.show(p)
Each hexagon is a bin announced by its centre and how many penguins fell in it; the up and down arrows move to the nearest bin of the staggered row above or below.
A graph from_networkx made from a directed graph (nx.DiGraph) is walked node by node in topological order: each node announces what feeds it, what it feeds, and whether it is a branch or merge point, and is ringed in the plot. An undirected graph is left unread, as before, since an undirected edge has no direction to announce. Needs networkx (pip install maidr[networkx]).
Saving and embedding
maidr.save_html(p, "chart.html") writes the same page to a file. use_cdn decides where maidr.js comes from, exactly as for matplotlib and Plotly, and where BokehJS comes from too:
use_cdn=False inlines BokehJS from your installed bokeh package and ships the bundled maidr.js, so the saved page works with no network connection. BokehJS is over a megabyte, so the page is that much larger.
use_cdn=True and the default "auto" load BokehJS from Bokeh’s CDN, at the version you have installed, so opening the page needs a network connection. "auto" falls back to the bundled copy for maidr.js only; a local fallback for BokehJS would have to carry the whole of it in every page, which is what False is for.