Repository navigation
[BUG] Behaviour of automated padding for ranges is inconsistent #9724
Description
Exists in Bokeh from 0.13.0 till 1.4.0
Tested on Windows 10 and Ubunto 16.0, on Firefox and Chrome (most recent versions).
That's the weird bug, so explanation will be rather long.
Example 1:
from bokeh.plotting import figure, output_file, show
x = [0.1, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0]
y1 = [30 for xx in x]
legend_items = []
p = figure(plot_width=500, plot_height=400)
l1 = p.line(x, y1, line_width=2, color='red')
p.y_range.start = 0
li1 = LegendItem(label='Line 1', renderers=[l1])
legend_items.append(li1)
legend = Legend(items=legend_items, location=(0, -30))
legend.click_policy = "hide"
p.add_layout(legend, 'left')
show(p)
That produces pretty looking chart:

Automatic padding added a little bit of space between horizontal line and top end of the chart, so line is clearly visible.
Example 2:
from bokeh.plotting import figure, output_file, show
x = [0.1, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0]
y1 = [300 for xx in x]
legend_items = []
p = figure(plot_width=500, plot_height=400)
l1 = p.line(x, y1, line_width=2, color='red')
p.y_range.start = 0
li1 = LegendItem(label='Line 1', renderers=[l1])
legend_items.append(li1)
legend = Legend(items=legend_items, location=(0, -30))
legend.click_policy = "hide"
p.add_layout(legend, 'left')
show(p)
With line located at Y=300, there's no automatic padding added, so line located directly on the border of the chart.
One can argue, it can be easily fixed bu using customized y_range, but manually setting y_range bounds makes it hard to use new feature from 1.4.0 (#9144):
Example 3 (only works in Bokeh 1.4.0):
from bokeh.plotting import figure, output_file, show
x = [0.1, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0]
y1 = [30 for xx in x]
y2 = [60 for xx in x]
y3 = [90 for xx in x]
legend_items = []
# create a new plot with a log axis type
p = figure(plot_width=500, plot_height=400)
l1 = p.line(x, y1, line_width=2, color='red')
l2 = p.line(x, y2, line_width=2, color='blue')
l3 = p.line(x, y3, line_width=2, color='green')
p.y_range.start = 0
li1 = LegendItem(label='Line 1', renderers=[l1])
li2 = LegendItem(label='Line 2', renderers=[l2])
li3 = LegendItem(label='Line 3', renderers=[l3])
legend_items.append(li1)
legend_items.append(li2)
legend_items.append(li3)
p.y_range.only_visible = True
legend = Legend(items=legend_items, location=(0, -30))
legend.click_policy = "hide"
p.add_layout(legend, 'left')
show(p)
Now we are using automated Y-range scaling.
All lines are clearly visible no matter how many lines we have hidden:
or
But if we change Y-values for lines back to 300...
Example 4 (only works in Bokeh 1.4.0):
from bokeh.plotting import figure, output_file, show
x = [0.1, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0]
y1 = [300 for xx in x]
y2 = [600 for xx in x]
y3 = [900 for xx in x]
legend_items = []
# create a new plot with a log axis type
p = figure(plot_width=500, plot_height=400)
l1 = p.line(x, y1, line_width=2, color='red')
l2 = p.line(x, y2, line_width=2, color='blue')
l3 = p.line(x, y3, line_width=2, color='green')
p.y_range.start = 0
li1 = LegendItem(label='Line 1', renderers=[l1])
li2 = LegendItem(label='Line 2', renderers=[l2])
li3 = LegendItem(label='Line 3', renderers=[l3])
legend_items.append(li1)
legend_items.append(li2)
legend_items.append(li3)
p.y_range.only_visible = True
legend = Legend(items=legend_items, location=(0, -30))
legend.click_policy = "hide"
p.add_layout(legend, 'left')
show(p)
For 3 lines it works fine:
For 2 lines it works fine:
But with only 1 line visible automated padding not working again:
What I would expect?
I would expect to see similar gap between red line and top boundary of the chart, as I see for blue and green lines.
Why it's important?
Without callbacks there's no way to change y_range for the chart when certain lines became hidden and visible. As far as I understand, #9144 was supposed to provide simple way to do so, but since automated padding not always working the way you expect it to work, it's almost useless.






Activity
bryevdv commentedon Feb 24, 2020
@Tikonderoga can you please try and report back with 2.0.0rc1 (available on pypi or bokeh/dev channel on anaconda.org)
Please note the starting with 2.0 hiding glyphs in the legend will remove them from automatic ranging computations (often requested feature)
Also, AFAICT, a summary of the issue is this, please confirm:
Tikonderoga commentedon Feb 24, 2020
@bryevdv Sorry, as I mentioned, it's weird issue and my description was lacking.
Let me try it again, using your template.
Issue (exists for all versions of Bokeh?):
In Bokeh > 1.4.0 is manifests itself in a case when p.y_range.only_visible = True:
Y-range is small, all works fine:

Y-range is large, breaks when 1 line visible:

I think there's something wrong in a way how automatic padding is being calculated - it's looks like it's not taking size of range into consideration. And when you have only one line visible out of many, same code path is being used.
bryevdv commentedon Feb 26, 2020
@Tikonderoga I have a suspicion, but have not yet been able to look into it. You could check out the BokehJS for DataRange1d in the mean time, if you like. My suspicion is this:
If this is actually the case, I am not sure I could conclude that the observed behavior is not, in fact, the correct behavior. I would not be in favor of adding magic, implicit special cases. So what could be done? Some ideas:
bryevdv commentedon Feb 26, 2020
Looking quickly at the code, I do believe this is the case, and as I mentioned above this behavior not inconsisten, it is in fact completely consistent: the padding is always a percentage of the computed min/max range, even if that range is zero.
For now, changing
range_padding_unitsis an option. If you are interested to work up a PR to add a newmin_paddingproperty, then I would be happy to help out. Otherwise, I am inclined to close this withnoactionsince it is a pretty obscure corner case.Tikonderoga commentedon Feb 26, 2020
@bryevdv
First of all, I would still argue it's inconsistent:
Look at the image below. For all lines, at any Y-value, computed min/max range would be zero. Why there's padding added for lines A, B and C, but not for D?
Also, which part of Bokeh code should I look into?
Is it bokeh/bokehjs/src/lib/models/ranges/data_range1d.ts
?
bryevdv commentedon Feb 26, 2020
It's not really possible for me to speculate with just the images. Are there other invisible glyphs? What is the configuration of the data range? For that matter, are the "ABCD" labels or text glyphs, or were they added outside Bokeh after the fact completely? Code for what you have above would help clarify.
Yes that's the main file of interest. The range will ask any available renderers for their bounds (and then union them together), so there's also some possibility that the bounds computation in the
Lineglyph is also relevant.Tikonderoga commentedon Feb 26, 2020
@bryevdv
Sure, I'll provide code and a lot of test cases where padding (both manual and automatic) appears to be broken. There will be multiple comments
Case 1.
What's wrong?
Computed range for all lines is 0, yet for first few lines, where Y is relatively small, automated padding is added, but for line with Y=500 its too small.
bryevdv commentedon Feb 26, 2020
@Tikonderoga That's perfect I can run that and actually probe what the JS is doing.
FYI I think you could just pass e.g.
y1=2to the line call and not generate those lists.bryevdv commentedon Feb 26, 2020
Looking at those now it looks possibly like the padding is always 1, which simple shows up very tiny on the scale of the last plot.
Tikonderoga commentedon Feb 26, 2020
Case 2: Auto-padding works correctly!
Case 3 : Auto-padding broken with y_range.start = 0
That's an interesting one: all charts appears to be okay, even the last one, but one in the middle is not.
Tikonderoga commentedon Feb 26, 2020
Case 4: Playing with range_padding. Not working for straight lines!
If computed range for line is 0, manual padding not working at all.
Tikonderoga commentedon Feb 26, 2020
Case 5: manual padding with non-straight lines:
10 remaining items
Tikonderoga commentedon Feb 28, 2020
@mattpap @bryevdv
I'm trying to build a fix and verify it before creating pull request.
I've managed to build Bokeh locally, but when I'm trying to run tests in notebook, I'm seeing following error:
How would I fix that?
Should I go to Discourse with such questions?
mattpap commentedon Feb 28, 2020
My personal approach to this is as follows:
BOKEH_DEV=true bokeh staticBOKEH_RESOURCES=server-dev jupyter-labThis will make sure that you always have the most recently built bundles and debugging experience in jlab is the most efficient. Another personal preference is not to use jlab for bokeh development, as it's usually more trouble than it's worth.
bryevdv commentedon Feb 28, 2020
BOKEH_RESOURCES=inlineis another option, but also agree: if you are not working on something that is specifically jupyter-related, then working there adds quite a lot of (IMO) otherwise unnecessary complication.Tikonderoga commentedon Feb 28, 2020
Thanks,
does the trick.
Tikonderoga commentedon Feb 28, 2020
And follow-up question: is there any instructions how to contribute to bokeh source code?
Or is it just "create branch -> test -> create pull request" routine?
bryevdv commentedon Feb 28, 2020
@Tikonderoga yes exactly PRs from forks are the standard way to proceed
Add tests for issues #9522, #9703 and #9724
Add tests for issues #9522, #9703 and #9724 (#9891)
github-actions commentedon Oct 27, 2024
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.