How to add data labels to a bar chart in Bokeh?

NOTE FROM BOKEH MAINTAINERS The portions of the answer below that refer to the bokeh.charts are of historical interest only. The bokeh.charts API was deprecated and subsequently removed from Bokeh. See the answers here and above for information on the stable bokeh.plotting API


Yes, you can add labels to each bar of the chart. There are a few ways to do this. By default, your labels are tied to your data. But you can change what is displayed. Here are a few ways to do that using your example:

from bokeh.charts import Bar, output_file, show
from bokeh.sampledata.autompg import autompg as df
from bokeh.layouts import gridplot

from pandas import DataFrame
from bokeh.plotting import figure, ColumnDataSource
from bokeh.models import Range1d, HoverTool

# output_file("bar.html")

""" Adding some sample labels a few different ways.
    Play with the sample data and code to get an idea what does what.
    See below for output.
"""

Sample data (new labels):

I used some logic to determine the new dataframe column. Of course you could use another column already in df (it all depends on what data you're working). All you really need here is to supply a new column to the dataframe.

# One method
labels = []
for number in df['cyl']:
    if number == 3:
        labels.append("three")
    if number == 4:
        labels.append("four")
    if number == 5:
        labels.append("five")
    if number == 6:
        labels.append("six")
    if number == 8:
        labels.append("eight")

df['labels'] = labels

Another way to get a new dataframe column. Again, we just need to supply df a new column to use on our bar plot.

# Another method
def new_labels(x):
    if x % 2 != 0 or x == 6:
        y = "Inline"
    elif x % 2 == 0:
        y = "V"
    else:
        y = "nan"
    return y

df["more_labels"] = df["cyl"].map(new_labels)

Now the bar chart:

I've done it two ways. p1 just specifies the new labels. Note that because I used strings it put them in alphabetical order on the chart. p2 uses the original labels, plus adds my new labels on the same bar.

# Specifying your labels
p1 = Bar(df, label='labels', values='mpg',
         title="Total MPG by CYL, remapped labels, p1",
         width=400, height=400, legend="top_right")
p2 = Bar(df, label=['cyl', 'more_labels'], values='mpg',
         title="Total MPG by CYL, multiple labels, p2", width=400, height=400,
         legend="top_right")

Another way:

Bokeh has three main "interface levels". High level charts provides quick easy access but limited functionality; plotting which gives more options; models gives even more options.

Here I'm using the plotting interface and the Figure class that contains a rect method. This gives you more detailed control of your chart.

# Plot with "intermediate-level" bokeh.plotting interface
new_df = DataFrame(df.groupby(['cyl'])['mpg'].sum())
factors = ["three", "four", "five", "six", "eight"]
ordinate = new_df['mpg'].tolist()
mpg = [x * 0.5 for x in ordinate]

p3 = figure(x_range=factors, width=400, height=400,
            title="Total MPG by CYL, using 'rect' instead of 'bar', p3")
p3.rect(factors, y=mpg, width=0.75, height=ordinate)
p3.y_range = Range1d(0, 6000)
p3.xaxis.axis_label = "x axis name"
p3.yaxis.axis_label = "Sum(Mpg)"

A fourth way to add specific labels:

Here I'm using the hover plot tool. Hover over each bar to display your specified label.

# With HoverTool, using 'quad' instead of 'rect'
top = [int(x) for x in ordinate]
bottom = [0] * len(top)
left = []
[left.append(x-0.2) for x in range(1, len(top)+1)]
right = []
[right.append(x+0.2) for x in range(1, len(top)+1)]
cyl = ["three", "four", "five", "six", "eight"]
source = ColumnDataSource(
    data=dict(
        top=[int(x) for x in ordinate],
        bottom=[0] * len(top),
        left=left,
        right=right,
        cyl=["three", "four", "five", "six", "eight"],
    )
)

hover = HoverTool(
    tooltips=[
        ("cyl", "@cyl"),
        ("sum", "@top")
    ]
)

p4 = figure(width=400, height=400,
            title="Total MPG by CYL, with HoverTool and 'quad', p4")
p4.add_tools(hover)
p4.quad(top=[int(x) for x in ordinate], bottom=[0] * len(top),
        left=left, right=right, color="green", source=source)
p4.xaxis.axis_label = "x axis name"

Show all four charts in a grid:

grid = gridplot([[p1, p2], [p3, p4]])
show(grid)

These are the ways I am aware of. There may be others. Change whatever you like to fit your needs. Here is what running all of this will output (you'll have to run it or serve it to get the hovertool):

bokeh bar specify labels bokeh bar specify labels


Use Labelset

Use Labelset to create a label over each individual bar

In my example I'm using vbar with the plotting interface, it is a little bit more low level then the Charts interface, but there might be a way to add it into the Bar chart.

from bokeh.palettes import PuBu
from bokeh.io import show, output_notebook
from bokeh.models import ColumnDataSource, ranges, LabelSet
from bokeh.plotting import figure
output_notebook()

source = ColumnDataSource(dict(x=['Áætlaðir','Unnir'],y=[576,608]))

x_label = ""
y_label = "Tímar (klst)"
title = "Tímar; núllti til þriðji sprettur."
plot = figure(plot_width=600, plot_height=300, tools="save",
        x_axis_label = x_label,
        y_axis_label = y_label,
        title=title,
        x_minor_ticks=2,
        x_range = source.data["x"],
        y_range= ranges.Range1d(start=0,end=700))


labels = LabelSet(x='x', y='y', text='y', level='glyph',
        x_offset=-13.5, y_offset=0, source=source, render_mode='canvas')

plot.vbar(source=source,x='x',top='y',bottom=0,width=0.3,color=PuBu[7][2])

plot.add_layout(labels)
show(plot)

Bar chart with labels on top of each bar

You can find more about labelset here: Bokeh annotations

Tags:

Python

Bokeh