It is not possible to use a CategoricalAxis with a FactorRange and set the x_range because start and end are readonly properties.
But there are workarounds, two are listed below.
using CustomJSTickFormatter
In the bokeh version 3.0.0 a CustomJSTickFormatter was added. With this it is possible to use a normal LinearRange and format the ticks. With this approach you can set x_range as you like it.
from bokeh.models import CustomJSTickFormatter, Range1d from bokeh.plotting import figure, show, output_notebook output_notebook() fruits = ["Apples", "Pears", "Nectarines", "Plums", "Grapes", "Strawberries"] x = list(range(len(fruits))) y = [5, 3, 4, 2, 4, 6] p = figure(height=250, title="Fruit Counts") p.x_range = Range1d(-0.5, 3.5, bounds=(-0.5, 4.5)) p.y_range = Range1d(0, 7) p.vbar(x=x, top=y, width=0.9) p.xgrid.grid_line_color = None p.xaxis.formatter = CustomJSTickFormatter( code=""" var new_tick= tick.toFixed(2) const fruits = ["Apples", "Pears", "Nectarines", "Plums", "Grapes", "Strawberries"] const range = [...Array(fruits.length).keys()] if( tick in range ) new_tick= fruits[tick] else new_tick= '' return new_tick """ ) p.xaxis.minor_tick_line_color = None p.xaxis.axis_label_text_color = None show(p)
using FixedTicker and major_label_overrides
With a FixedTicker you can set the ticks to you wanted positions and major_label_overrides offers you the opportunity to set the label text very precise. This is also available before the 3.0.0 release.
from bokeh.models import FixedTicker, Range1d from bokeh.plotting import figure, show, output_notebook output_notebook() fruits = ["Apples", "Pears", "Nectarines", "Plums", "Grapes", "Strawberries"] x = list(range(len(fruits))) y = [5, 3, 4, 2, 4, 6] p = figure(height=250, title="Fruit Counts") p.x_range = Range1d(-0.5, 3.5, bounds=(-0.5, 4.5)) p.y_range = Range1d(0, 7) p.vbar(x=x, top=y, width=0.9) p.xgrid.grid_line_color = None p.xaxis.ticker = FixedTicker( desired_num_ticks=len(fruits), num_minor_ticks = 0, ticks=x ) p.xaxis.major_label_overrides = {i: label for i, label in zip(x, fruits)} show(p)
The visual results should be the same and the inital view looks like below:
