Selection tools not working with multiple glyphs and data sources in a single figure

Hi All,

Apologies if this is a FAQ but I have not yet found a solution to this problem:

I am running Bokeh server 1.0.4 on Tornado 5.1.1.

I have a figure to which multiple circle and square glyphs have been added, each with their own data source. I did this because I wanted to have an interactive legend where I can click to hide/show any one glyph. (I save the renderer for each glyph to a list as they are created and add them all to a legend using the list.)

All of this is working just fine, but now I could like to use the selection tools (lasso, box, poly) to select points that fall in a given region of the figure. But I find that if the selection region *does not happen to include any of the points for a particular glyph, then all the points in that glyph remain “on” (selected). *

For example, if I select a small region like this

temp-selection.png

several points outside the region remain selected (not expected), but if I select a larger region like this:

temp-selection2.png

all of the points in the region are selected and all those outside are not (as I would expect).

As I said, I think the failure in the first case is due to some data sources not having any points at all in the selection region. The selection tool should deselect them all, but instead it leaves them selected (ignores them).

Do I have to do something to link all of these glyphs together so the selection tools do not ignore some of them or is this a bug? Do I have to deselect everything first? ??

Thank you in advance for your help!

-Doug

here is an example that demonstrates this problem:

import pandas as pd
from bokeh.palettes import Spectral4
from bokeh.plotting import figure, output_file, show
from bokeh.sampledata.stocks import AAPL, IBM, MSFT, GOOG
p = figure(plot_width=800, plot_height=250,
tools=(‘pan, lasso_select, reset’),
active_drag=‘lasso_select’,
x_axis_type=“datetime”)
p.title.text = ‘Click on legend entries to mute the corresponding lines’**for data, name, color in zip([AAPL, IBM, MSFT, GOOG], [“AAPL”, “IBM”, “MSFT”, “GOOG”], Spectral4):
df = pd.DataFrame(data)
df[
‘date’] = pd.to_datetime(df[‘date’])
p.circle(df[
‘date’], df[‘close’], line_width=2, color=color, alpha=0.8,
*#selection_color=‘black’,
* nonselection_color=
‘gray’,
muted_color=
‘gray’, muted_alpha=0.2, legend=name)
p.legend.location = "top_left"p.legend.click_policy="mute"output_file(“interactive_legend.html”, title=
“interactive_legend.py example”**)

show§

While selecting with the lasso:

temp-while selecting.png

after releasing the mouse:

temp-after selecting.png

Notice that the points for MSFT and GOOG remain selected even though none of them were inside the selection region.

I would like them to become unselected in this case.

Thank you for your help!

···

On Wednesday, February 27, 2019 at 5:12:20 PM UTC-5, Doug Wood wrote:

Hi All,

Apologies if this is a FAQ but I have not yet found a solution to this problem:

I am running Bokeh server 1.0.4 on Tornado 5.1.1.

I have a figure to which multiple circle and square glyphs have been added, each with their own data source. I did this because I wanted to have an interactive legend where I can click to hide/show any one glyph. (I save the renderer for each glyph to a list as they are created and add them all to a legend using the list.)

All of this is working just fine, but now I could like to use the selection tools (lasso, box, poly) to select points that fall in a given region of the figure. But I find that if the selection region *does not happen to include any of the points for a particular glyph, then all the points in that glyph remain “on” (selected). *

For example, if I select a small region like this

several points outside the region remain selected (not expected), but if I select a larger region like this:

all of the points in the region are selected and all those outside are not (as I would expect).

As I said, I think the failure in the first case is due to some data sources not having any points at all in the selection region. The selection tool should deselect them all, but instead it leaves them selected (ignores them).

Do I have to do something to link all of these glyphs together so the selection tools do not ignore some of them or is this a bug? Do I have to deselect everything first? ??

Thank you in advance for your help!

-Doug

Hi Doug,

I am not sure there is any simple way to avoid this behavior. The reason for it is this: Bokeh only draws points as "unselected" if *some* selection has been made at all on a glyph's data source. Since you are not sharing a data source between glyphs, and the lasso you make does not intersect the MSFT or GOOG glyphs, there is no selection for those glyph's data source, therefore their appearance is unchanged.

Offhand I can openly think of two possible options:

* share a CDS between all the glyphs. This is only really practical if all the time series have the same length, and even then be aware that the selection indices are *unioned* across every glyph that shares a source, which may not be what you want

* use a CustomJS callback on the glyph's data source selections to brute force whatever muting/visual change you want to perform on other glyphs:

  https://bokeh.pydata.org/en/latest/docs/user_guide/interaction/callbacks.html#customjs-for-selections

Thanks,

Bryan

···

On Mar 3, 2019, at 12:44 PM, Doug Wood <[email protected]> wrote:

import pandas as pd

from bokeh.palettes import Spectral4
from bokeh.plotting import figure, output_file, show
from bokeh.sampledata.stocks import AAPL, IBM, MSFT, GOOG

p = figure(plot_width=800, plot_height=250,
           tools=('pan, lasso_select, reset'),
           active_drag='lasso_select',
           x_axis_type="datetime")
p.title.text = 'Click on legend entries to mute the corresponding lines'

for data, name, color in zip([AAPL, IBM, MSFT, GOOG], ["AAPL", "IBM", "MSFT", "GOOG"], Spectral4):
    df = pd.DataFrame(data)
    df['date'] = pd.to_datetime(df['date'])
    p.circle(df['date'], df['close'], line_width=2, color=color, alpha=0.8,
             #selection_color='black',
             nonselection_color='gray',
             muted_color='gray', muted_alpha=0.2, legend=name)

p.legend.location = "top_left"
p.legend.click_policy="mute"

output_file("interactive_legend.html", title="interactive_legend.py example")

show(p)

Hi Bryan,
Thank you for your reply (and for all the work you are doing on Bokeh)!

The problem is that I am separating the data into separate data sources so I can use the interactive legends feature as per

https://bokeh.pydata.org/en/latest/docs/user_guide/interaction/legends.html

Here is the an example from that page which I modified to add a lasso tool to try it out:

import pandas as pd

from bokeh.palettes import Spectral4
from bokeh.plotting import figure, output_file, show
from bokeh.sampledata.stocks import AAPL, IBM, MSFT, GOOG

p = figure(plot_width=800, plot_height=250,
tools=(‘pan, lasso_select, reset’),
active_drag=‘lasso_select’,
x_axis_type=“datetime”)
p.title.text = ‘Click on legend entries to mute the corresponding lines’

for data, name, color in zip([AAPL, IBM, MSFT, GOOG], [“AAPL”, “IBM”, “MSFT”, “GOOG”], Spectral4):
df = pd.DataFrame(data)
df[‘date’] = pd.to_datetime(df[‘date’])
p.circle(df[‘date’], df[‘close’], line_width=2, color=color, alpha=0.8,
nonselection_color=‘gray’,
muted_color=‘gray’, muted_alpha=0.2, legend=name)

p.legend.location = “top_left”
p.legend.click_policy=“mute”

output_file(“interactive_legend.html”, title=“interactive_legend.py example”)

show§

``

Yes, I understand why it is happening, but it would be great if interactive legends and selections worked together. I am actually using a list of data sources (like the 4 data sets the example) across multiple graphs. When I select points in one plot (even in multiple glyphs), they are selected in all the other graphs because I am sharing the same list of data sources. But all I need now is for the lasso tool to deselect any points in the glyphs that have no intersection at all with the selection region when the user makes a section in one of the graphs.

I will look into the CustomJS to run after a lasso selection and if any all of the elements of a glyph are selected then I’ll deselect them all. The problem is I will have to deal with the special case of a selection happening to include all the points of a glyph. Looking quickly at the CustomJS example, I think I could just keep a flag for each glyph/data source to keep track of whether any of its points were hit during the select, and at the end, deselect all the glyphs that never got touched.

Thanks again!
Doug

···

On Sunday, March 3, 2019 at 4:06:21 PM UTC-5, Bryan Van de ven wrote:

Hi Doug,

I am not sure there is any simple way to avoid this behavior. The reason for it is this: Bokeh only draws points as “unselected” if some selection has been made at all on a glyph’s data source. Since you are not sharing a data source between glyphs, and the lasso you make does not intersect the MSFT or GOOG glyphs, there is no selection for those glyph’s data source, therefore their appearance is unchanged.

Offhand I can openly think of two possible options:

  • share a CDS between all the glyphs. This is only really practical if all the time series have the same length, and even then be aware that the selection indices are unioned across every glyph that shares a source, which may not be what you want

  • use a CustomJS callback on the glyph’s data source selections to brute force whatever muting/visual change you want to perform on other glyphs:

      [https://bokeh.pydata.org/en/latest/docs/user_guide/interaction/callbacks.html#customjs-for-selections](https://bokeh.pydata.org/en/latest/docs/user_guide/interaction/callbacks.html#customjs-for-selections)
    

Thanks,

Bryan

On Mar 3, 2019, at 12:44 PM, Doug Wood [email protected] wrote:

import pandas as pd

from bokeh.palettes import Spectral4

from bokeh.plotting import figure, output_file, show

from bokeh.sampledata.stocks import AAPL, IBM, MSFT, GOOG

p = figure(plot_width=800, plot_height=250,

       tools=('pan, lasso_select, reset'),
       active_drag='lasso_select',
       x_axis_type="datetime")

p.title.text = ‘Click on legend entries to mute the corresponding lines’

for data, name, color in zip([AAPL, IBM, MSFT, GOOG], [“AAPL”, “IBM”, “MSFT”, “GOOG”], Spectral4):

df = pd.DataFrame(data)
df['date'] = pd.to_datetime(df['date'])
p.circle(df['date'], df['close'], line_width=2, color=color, alpha=0.8,
         #selection_color='black',
         nonselection_color='gray',
         muted_color='gray', muted_alpha=0.2, legend=name)

p.legend.location = “top_left”

p.legend.click_policy=“mute”

output_file(“interactive_legend.html”, title=“interactive_legend.py example”)

show§

Hi Doug,

I'm not aware of any reason you can't share a data source and use interactive legends. The interactive legend has nothing to with selections or data sources. It sets an explicit overall hidden or muted state (unrelated to selection shading) on the glyph itself, based only on UI interactions with the legend. Given that, it's not actually clear how selections and interactive legends should interact, or that there is necessarily one "right" answer to the question. I.e. you seem to want all other glyphs to be auto-muted when a selection is made but it's easy to imagine other people who would *not* want that. So I think the CustomJS route is probably the best option, since it affords whatever policy any user wants without out imposing on others. In your case, you can pass all the glyphs in the as args to the CustomJS, to make them accessible to the JS code, then when a selection happens you can set them to be muted or hidden yourself (basically as you describe).

Thanks,

Bryan

···

On Mar 3, 2019, at 1:48 PM, Doug Wood <[email protected]> wrote:

Hi Bryan,
Thank you for your reply (and for all the work you are doing on Bokeh)!

The problem is that I am separating the data into separate data sources so I can use the interactive legends feature as per
https://bokeh.pydata.org/en/latest/docs/user_guide/interaction/legends.html

Here is the an example from that page which I modified to add a lasso tool to try it out:

import pandas as pd

from bokeh.palettes import Spectral4
from bokeh.plotting import figure, output_file, show
from bokeh.sampledata.stocks import AAPL, IBM, MSFT, GOOG

p = figure(plot_width=800, plot_height=250,
            tools=('pan, lasso_select, reset'),
            active_drag='lasso_select',
            x_axis_type="datetime")
p.title.text = 'Click on legend entries to mute the corresponding lines'

for data, name, color in zip([AAPL, IBM, MSFT, GOOG], ["AAPL", "IBM", "MSFT", "GOOG"], Spectral4):
     df = pd.DataFrame(data)
     df['date'] = pd.to_datetime(df['date'])
     p.circle(df['date'], df['close'], line_width=2, color=color, alpha=0.8,
              nonselection_color='gray',
              muted_color='gray', muted_alpha=0.2, legend=name)

p.legend.location = "top_left"
p.legend.click_policy="mute"

output_file("interactive_legend.html", title="interactive_legend.py example")

show(p)

Yes, I understand why it is happening, but it would be great if interactive legends and selections worked together. I am actually using a list of data sources (like the 4 data sets the example) across multiple graphs. When I select points in one plot (even in multiple glyphs), they are selected in all the other graphs because I am sharing the same list of data sources. But all I need now is for the lasso tool to deselect any points in the glyphs that have no intersection at all with the selection region when the user makes a section in one of the graphs.

I will look into the CustomJS to run after a lasso selection and if any all of the elements of a glyph are selected then I'll deselect them all. The problem is I will have to deal with the special case of a selection happening to include all the points of a glyph. Looking quickly at the CustomJS example, I think I could just keep a flag for each glyph/data source to keep track of whether any of its points were hit during the select, and at the end, deselect all the glyphs that never got touched.

Thanks again!
Doug

On Sunday, March 3, 2019 at 4:06:21 PM UTC-5, Bryan Van de ven wrote:
Hi Doug,

I am not sure there is any simple way to avoid this behavior. The reason for it is this: Bokeh only draws points as "unselected" if *some* selection has been made at all on a glyph's data source. Since you are not sharing a data source between glyphs, and the lasso you make does not intersect the MSFT or GOOG glyphs, there is no selection for those glyph's data source, therefore their appearance is unchanged.

Offhand I can openly think of two possible options:

* share a CDS between all the glyphs. This is only really practical if all the time series have the same length, and even then be aware that the selection indices are *unioned* across every glyph that shares a source, which may not be what you want

* use a CustomJS callback on the glyph's data source selections to brute force whatever muting/visual change you want to perform on other glyphs:

        https://bokeh.pydata.org/en/latest/docs/user_guide/interaction/callbacks.html#customjs-for-selections

Thanks,

Bryan

> On Mar 3, 2019, at 12:44 PM, Doug Wood <[email protected]> wrote:
>
> import pandas as pd
>
> from bokeh.palettes import Spectral4
> from bokeh.plotting import figure, output_file, show
> from bokeh.sampledata.stocks import AAPL, IBM, MSFT, GOOG
>
> p = figure(plot_width=800, plot_height=250,
> tools=('pan, lasso_select, reset'),
> active_drag='lasso_select',
> x_axis_type="datetime")
> p.title.text = 'Click on legend entries to mute the corresponding lines'
>
> for data, name, color in zip([AAPL, IBM, MSFT, GOOG], ["AAPL", "IBM", "MSFT", "GOOG"], Spectral4):
> df = pd.DataFrame(data)
> df['date'] = pd.to_datetime(df['date'])
> p.circle(df['date'], df['close'], line_width=2, color=color, alpha=0.8,
> #selection_color='black',
> nonselection_color='gray',
> muted_color='gray', muted_alpha=0.2, legend=name)
>
> p.legend.location = "top_left"
> p.legend.click_policy="mute"
>
> output_file("interactive_legend.html", title="interactive_legend.py example")
>
> show(p)

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/dab558d9-c205-47dc-9e86-069344b23de9%40continuum.io.
For more options, visit https://groups.google.com/a/continuum.io/d/optout.