Search code examples
python-3.xbokeh

Bokeh Adjust Glyph's top padding


I have a multi bar plot made with Bokeh using the vbar glyph method. The plot is fine but when I try to add labels they are "pushed" outside of the figure and become unreadable. Is there a way to adjust the top padding between the glyph and the figure?

Here is the code I'm using, if you render it the problem is very obvious

categories = ['1', '2', '3', '4', '5']
sets = ['full', 'train', 'test']

data = {
    'x' : categories,
    'full' : full_dset_count,
    'train' : train_dset_count,
    'test' : test_dset_count,
    'empty':['']*5
}

x = [ (cat, set_) for cat in categories for set_ in sets ]
counts = sum( zip( data['full'], data['train'], data['test'] ), () )
colors = ['#3cba54', "#f4c20d", "#db3236"]*5

full_p = sum(zip( round(data['full'], 4), data['empty'], data['empty']), ())
train_p = sum(zip( data['empty'], round(data['train'], 4), data['empty']), ())
test_p = sum(zip( data['empty'], data['empty'], round(data['test'], 4)), ())


source = ColumnDataSource(data=dict(
    x=x, counts=counts, colors=colors,
    full_p=full_p, train_p=train_p, test_p=test_p

))

plt = figure(
    x_range=FactorRange(*x),
    plot_height=500,
    plot_width=900,
    title="Distribuzione dei valori delle recensioni per l'insieme totale, di training e di test",
    x_axis_label="Valore delle recensioni",
    y_axis_label="Percentuale delle osservazioni"
)

plt.vbar(
    x='x',
    top='counts',
    width=0.5,
    color='colors',
    source=source    
)

plt.y_range.start = 0
plt.x_range.range_padding = 0.1
plt.xaxis.major_label_orientation = 1
plt.xgrid.grid_line_color = None

full_labels = LabelSet(
    x='x', y='counts', text='full_p', level='glyph',
    x_offset=-30, y_offset=0, source=source, render_mode='canvas'
)
train_labels = LabelSet(
    x='x', y='counts', text='train_p', level='glyph',
    x_offset=-20, y_offset=15, source=source, render_mode='canvas'
)

test_labels = LabelSet(
    x='x', y='counts', text='test_p', level='glyph',
    x_offset=-10, y_offset=30, source=source, render_mode='canvas'
)

plt.add_layout(full_labels)
plt.add_layout(train_labels)
plt.add_layout(test_labels)

show(plt)

The values for full_dset_count, train_dset_count and test_dset_count are almost identical. In order to reproduce the problem the following values can be used : [ 7.23934138, 9.58753275, 18.32419776, 35.79029432, 29.05863379]


Solution

  • You can adjust it, but only in the data units - you're already using it for the X range with plt.x_range.range_padding. One issue with this approach - it won't work for all label and plot sizes, since those sizes are in screen units and not data units. It's not a big deal if you can set these sizes in advance and make sure that the labels fit.

    Another approach is to use 2 label sets instead of each one. One set would be for labels outside of the bars, when they can fit there. The other set would be for labels within the bars - they're moved there for the bars that are too tall. Similar to how it's handled on this image from Matplotlib documentation:enter image description here