bokeh / bokeh

Interactive Data Visualization in the browser, from Python
https://bokeh.org
BSD 3-Clause "New" or "Revised" License
19.12k stars 4.18k forks source link

color mapping in legend does not take current view (CDSView) into account and color mapping in circle fill is wrong after subsampling with box_select from another plot #8010

Open JenniferShelton opened 6 years ago

JenniferShelton commented 6 years ago

ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

bokeh 0.12.16 python 3.6.5 notebook 5.5.0 OSX 10.11.6 browser safari or chrome pandas 0.23.0

Description of expected behavior and the observed behavior

Here is a small dataset: x_values y_values fruit animal 1 1 apple cat 2 0 pear cat 3 1 apple dog 4 0 apple bird 5 1 kiwi cat

I would expect that if I view a subset of the data using CDSView the legend will still match the colors to the unique list of factors. I would also expect that if a plot like plot B (created from a CDSView) was filtered using the box_select tool in plot A the color mapping would adjust and still reflect the correct color.

In the example below the legend is only correct when the data is not a CDSView subset of the full ColumnDataSource. The legend is only correct in plots A and C.

screen shot 2018-06-18 at 10 02 14 pm

Also in the example below if I use box select on figure A then the color mapping shifts in figure B.

screen shot 2018-06-18 at 10 02 26 pm

Complete, minimal, self-contained example code that reproduces the issue

from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, CDSView, BooleanFilter, CategoricalColorMapper
from bokeh.layouts import gridplot
from bokeh.io import show

data = {'x_values': [1, 2, 3, 4, 5],
        'y_values': [1, 0, 1, 0, 1],
        'fruit' : ['apple', 'pear', 'apple', 'apple', 'kiwi'],
        'animal' : ['cat', 'cat', 'dog', 'bird', 'cat']}

color_mapper = CategoricalColorMapper(factors=['apple', 'pear', 'kiwi'], 
                                          palette=['#ffd92f', '#20b2aa', '#e78ac3'])

color_mapper2 = CategoricalColorMapper(factors=['cat', 'dog', 'bird'], 
                                          palette=['red', 'black', 'yellow'])

source = ColumnDataSource(data=data)

booleans = [True if val == 1 else False for val in source.data['y_values']]

view1 = CDSView(source=source, filters=[BooleanFilter(booleans)])

# Figure to select ColumnDataSource rows from woth box_select
tools = ["box_select", "reset"]
p1=figure(tools=tools,
          plot_width=400,
          plot_height=400,
         title="A. Correct fill colors and legend")
p1.circle("x_values","y_values",
          legend='fruit',
          fill_color={'field': 'fruit',
                      'transform': color_mapper},
          size=20, 
          source=source)

# Figure that is a CDS view of the source data
p=figure(plot_width=400,
        plot_height=400,
        title="B. Good fill colors until box_select is used and bad legend")
p.circle("x_values", "y_values", size=20, source=source, view=view1, 
         legend='animal',
         fill_color={'field': 'animal',
                      'transform': color_mapper2},)
# Figure that includes all the source data (no view)
p_full=figure(plot_width=400,
          plot_height=400,
        title="C. Correct fill colors and legend even after box_select")
p_full.circle("x_values", "y_values", size=20, source=source, 
         legend='animal',
         fill_color={'field': 'animal',
                      'transform': color_mapper2},)

p1.legend.location = 'center_right'
p_full.legend.location = 'center_right'
show(gridplot([[p1,p],
             [p_full]]))

Stack traceback and/or browser JavaScript console output

Screenshots or screencasts of the bug in action

bryevdv commented 6 years ago

I think a general solution to #8023 would also solve this but I'm not completely sure so will not close this as a dupe just yet.

kyleabeauchamp commented 4 years ago

I think I've observed a similar but perhaps unrelated issue (no minimal example yet, sorry) where I've got a MultiSelect piped into a CustomJSFilter piped into a CDSView that filters on the column used as the legend_group (in a p.circle()). In practice, I'm observing the un-selected legend groups show up with strange fill and outline colors in the legend.

bryevdv commented 4 years ago

Another occurrence of this I believe:

https://discourse.bokeh.org/t/update-glyph-color-from-datatable/5426/2

mattpap commented 4 years ago

Selection issue was fixed in PR #10041. The legend is still broken.