Wheel Zoom Tool and Categorical Axis Range

Hello! I am trying to add a wheel zoom tool to my bokeh boxplot, but for some reason I can’t get the range to be constrained. I’m using the bokeh documentation with the cars as an example:

p.x_range = FactorRange(factors = kinds, bounds = 'auto') 
wheel_zoom = WheelZoomTool(maintain_focus=False)
pan = PanTool()
p.add_tools(wheel_zoom, pan)
p.toolbar.active_scroll = wheel_zoom
p.toolbar.active_drag = pan
p.add_tools(ResetTool())

Even though I’ve set the bounds to be automatically assigned according to the labels, I am still able to zoom out beyond the range of the categorical axis. Is there something I can do to constrain the range of the x-axis between the first label and the last label when using Wheel Zoom? Thanks!

Here is the complete code for reproducibility

import pandas as pd

from bokeh.models import ColumnDataSource, Whisker, WheelZoomTool, ResetTool, PanTool, FactorRange
from bokeh.plotting import figure, show
from bokeh.sampledata.autompg2 import autompg2
from bokeh.transform import factor_cmap

df = autompg2[["class", "hwy"]].rename(columns={"class": "kind"})

kinds = df.kind.unique()

# compute quantiles
grouper = df.groupby("kind")
qs = grouper.hwy.quantile([0.25, 0.5, 0.75]).unstack().reset_index()
qs.columns = ["kind", "q1", "q2", "q3"]

# compute IQR outlier bounds
iqr = qs.q3 - qs.q1
qs["upper"] = qs.q3 + 1.5*iqr
qs["lower"] = qs.q1 - 1.5*iqr

# update the whiskers to actual data points
for kind, group in grouper:
    qs_idx = qs.query(f"kind=={kind!r}").index[0]
    data = group["hwy"]

    # the upper whisker is the maximum between p3 and upper
    q3 = qs.loc[qs_idx, "q3"]
    upper = qs.loc[qs_idx, "upper"]
    wiskhi = group[(q3 <= data) & (data <= upper)]["hwy"]
    qs.loc[qs_idx, "upper"] = q3 if len(wiskhi) == 0 else wiskhi.max()

    # the lower whisker is the minimum between q1 and lower
    q1 = qs.loc[qs_idx, "q1"]
    lower = qs.loc[qs_idx, "lower"]
    wisklo = group[(lower <= data) & (data<= q1)]["hwy"]
    qs.loc[qs_idx, "lower"] = q1 if len(wisklo) == 0 else wisklo.min()

df = pd.merge(df, qs, on="kind", how="left")

source = ColumnDataSource(qs)

p = figure(x_range=kinds, tools="", toolbar_location=None,
           title="Highway MPG distribution by vehicle class",
           background_fill_color="#eaefef", y_axis_label="MPG")

# outlier range
whisker = Whisker(base="kind", upper="upper", lower="lower", source=source)
whisker.upper_head.size = whisker.lower_head.size = 20
p.add_layout(whisker)

# quantile boxes
cmap = factor_cmap("kind", "TolRainbow7", kinds)
p.vbar("kind", 0.7, "q2", "q3", source=source, color=cmap, line_color="black")
p.vbar("kind", 0.7, "q1", "q2", source=source, color=cmap, line_color="black")

# outliers
outliers = df[~df.hwy.between(df.lower, df.upper)]
p.scatter("kind", "hwy", source=outliers, size=6, color="black", alpha=0.3)

p.xgrid.grid_line_color = None
p.axis.major_label_text_font_size="14px"
p.axis.axis_label_text_font_size="12px"

p.toolbar_location = None

p.x_range = FactorRange(factors = kinds, bounds = 'auto') 
wheel_zoom = WheelZoomTool(maintain_focus=False)
pan = PanTool()
p.add_tools(wheel_zoom, pan)
p.toolbar.active_scroll = wheel_zoom
p.toolbar.active_drag = pan
p.add_tools(ResetTool())

show(p)

@gzang This just seems like a bug wrt the combination of wheel zoom and factor ranges, specifically. Please open a GitHub Issue with full details. I don’t have any immediate workarounds to suggest.

1 Like

Got it, I’ll open an issue now!

Hi there, just wanted to update that I ended up making an issue on Github, but the response wasn’t directly addressing the bug. Was wondering if I should label the issue on Github in any way to get this escalated? Thank you! The github issue is linked below.

Wheel Zoom Tool and Categorical Axis Range #14568

I’ve gone ahead and triaged the issue. But I should be clear that Bokeh is a volunteer OSS project, and outside occasional officially funded and scoped grant work, any “escalation” is purely up to individual contributors’ personal available bandwidth and interest.

1 Like

Of course! Thank you for the response, and look forward to Bokeh’s continuous development :slight_smile:

1 Like