I'm working on a Bokeh plot, and have got it almost the way I want it. However, I'm struggling with adding multi-line axis labels.
Here’s what I have so far:
import bokeh.io
from bokeh.plotting import gridplot, figure, output_file, show, output_notebook
from bokeh.models import ColumnDataSource, BoxAnnotation, CategoricalAxis, FactorRange
from bokeh.embed import components
x = ['label1','label2','label3','label4','label5','label6','label7', 'Trying for a multiline label']
y = [1.0, 2.7, 1.2, 4.5, 1.5, 2.2, 1.8, 4.6]
p = figure(x_range=[*x], y_range=(0, 5), plot_height=400, plot_width=550)
dots = p.circle(x=x, y=y, color='black',size=10)
line = p.line(x=x, y=y, color='black')
numbers = [str(x) for x in y]
p.extra_x_ranges = {"extra_numbers": FactorRange(factors=numbers)}
p.add_layout(CategoricalAxis(x_range_name="extra_numbers"), 'below')
p.title.text = 'Plot'
p.title.text_font_size = "25px"
p.title.text_color = "black"
p.title.align = "center"
low_box = BoxAnnotation(top=2, fill_alpha=0.1, fill_color='green')
mid_box = BoxAnnotation(bottom=2, top=3, fill_alpha=0.1, fill_color='orange')
high_box = BoxAnnotation(bottom=3, fill_alpha=0.1, fill_color='red')
p.add_layout(low_box)
p.add_layout(mid_box)
p.add_layout(high_box)
show(p)
The above code yields the following plot:
You can clearly see that the last label didn't wrap properly, and I have not managed to figure our how to make it wrap and create a multi-line axis label. Is there an easy way to do this? The best alternative solution I came up with was trying to use vertical labels, but as you can see, they don’t look very nice, and mess up the second axis below the text:
from bokeh.models import CategoricalAxis, FactorRange
from bokeh.plotting import figure, show
from math import pi
x = ['label1','label2','label3','label4','label5','label6','label7', 'Trying for a multiline label']
y = [1.0, 2.7, 1.2, 4.5, 1.5, 2.2, 1.8, 4.6]
p = figure(x_range=[*x], y_range=(0, 5), plot_height=900)
dots = p.circle(x=x, y=y, color='black',size=10)
line = p.line(x=x, y=y, color='black')
p.xaxis.major_label_orientation = pi/2
p.xaxis.major_label_text_font_size = "1.4em"
p.xaxis.axis_line_width = 2
p.yaxis.axis_line_width = 2
numbers = [str(x) for x in y]
p.extra_x_ranges = {"extra_numbers": FactorRange(factors=numbers)}
p.add_layout(CategoricalAxis(x_range_name="extra_numbers"), 'below')
show(p)
Now the graph looks like this:
An easy fix is, of course, switching the order of the axes so that the numbers are on top and the labels are below; however, that is still a haphazard solution, and isn't really what I'm going for. I would really like text wrapping in the label. For instance, the plot below is similar to what I want to accomplish (the code for this non-Bokeh graph below can be found here).
Any help or advice would be greatly appreciated!
Multiline labels are not possible with the stock Bokeh axes models. In order to achieve this, you'd have to create a custom Axis
subclass and manage all the wrapping yourself because canvas
context doesn't support it in any way.
As an alternative, you can try angled labels. A downside of such approach is having to figure out the right offsets of the plot to make sure that the labels are not cut off.