WillKoehrsen / Bokeh-Python-Visualization

A Bokeh project developed for learning and teaching Bokeh interactive plotting!
361 stars 295 forks source link

CheckboxGroup active list not updating #4

Open fabhlc opened 6 years ago

fabhlc commented 6 years ago

Hi,

I'm trying to replicate the second in your series of tutorials but am having trouble with the CheckboxGroup part. While the initial carriers load and appear, there seems to be a disconnect that's preventing the CheckboxGroup active list from updating data displayed in the histogram. So when I check or uncheck a box, it won't be reflected in the histogram.

I've removed the binwidth and range sliders to cut down on the problem. Otherwise the code below is essentially as the original in your github.

def` histogram_bokeh(flights):

    def make_dataset(carrier_list, range_start = -60, range_end = 120, bin_width=5):

        #check to make sure start is less than end:
        assert range_start < range_end, "Start must be less than end!"

        by_carrier = pd.DataFrame(columns=['proportion', 'left', 'right',
                                          'f_proportion', 'f_interval',
                                          'name', 'color'])
        range_extent = range_end - range_start

        for i, carrier_name in enumerate(carrier_list):

            #subset to the carrier
            subset = flights[flights['name'] == carrier_name]

            # Create a histogram
            arr_hist, edges = np.histogram(subset['arr_delay'],
                                          bins = int(range_extent / bin_width),
                                          range = [range_start, range_end])

            # Divide the counts by the total to get a proportion and create df
            arr_df = pd.DataFrame({'proportion': arr_hist / np.sum(arr_hist),
                                  'left': edges[:-1],
                                  'right': edges [1:]})

            #Format the proportion
            arr_df['f_proportion'] = ['%0.5f' % proportion for proportion in arr_df['proportion']]

            #Format the interval
            arr_df['f_interval'] = ['%d to %d minutes' % (left, right) for left,
                                    right in zip(arr_df['left'], arr_df['right'])]
            #Assign the carrier for labels
            arr_df['name'] = carrier_name
            #Colour each carrier differently
            arr_df['color'] = Category20_16[i]
            #Add to the overall dataframe
            by_carrier = by_carrier.append(arr_df)

        # Overall dataframe
        by_carrier = by_carrier.sort_values(['name','left'])

        #Convert dataframe to column data source
        return ColumnDataSource(by_carrier)

    def make_plot(src):
        # Blank plot with correct labels
        p = figure(plot_width = 700, plot_height = 700,
                  title = "Histogram of Arrival Delays by Carrier",
                  x_axis_label = 'Delay (min)', y_axis_label = 'Proportion')

        # Quad glyphs to create a histogram
        p.quad(source = src, bottom = 0, top = 'proportion', left = 'left', right = 'right',
              color = 'color', fill_alpha = 0.7, hover_fill_color = 'color', legend = 'name',
              hover_fill_alpha = 1.0, line_color = 'black')

        # HoverTool
        hover = HoverTool(tooltips=[('Carrier', '@name'),
                                   ('Delay', '@f_interval'),
                                   ('Proportion', '@f_proportion')])
        p.add_tools(hover)
        return p

    def update(attr, old, new):
        carriers_to_plot = [carrier_selection.labels[i] for i in carrier_selection.active]
        new_src = make_dataset(carriers_to_plot)
        src.data.update(new_src.data)    

    carrier_selection = CheckboxGroup(labels = available_carriers, active=[0,1,9])
    carrier_selection.on_change('active', update)

    initial_carriers = [carrier_selection.labels[i] for i in carrier_selection.active]
    src = make_dataset(initial_carriers)

    p = make_plot(src)

    controls = WidgetBox(carrier_selection)

    # Create a row layout
    layout = row(controls, p)

    # make a tab with the layout
    tab = Panel(title = "histogram", child = layout) 
    tabs = Tabs(tabs=[tab]) #add tab2 to list for additional tabs

    return tabs

show(histogram_bokeh(flights))

Note that I found I need Panels in the last section to make it work. Otherwise, essentially the same. I suspect there might be something in the update function that's causing it to not work?

I am using bokeh 0.12.16.

Thanks!

WillKoehrsen commented 6 years ago

The code here seems to work for generating the histogram by itself. The code you show I think is from the complete application, while the code linked to is for the histogram by itself. Are you trying to run the entire application, or just the histogram?

(To run use bokeh serve --show histogram.py from the command line in the directory with histogram.py)

The complete update function is

def update(attr, old, new):
        carriers_to_plot = [carrier_selection.labels[i] for i in carrier_selection.active]

        new_src = make_dataset(carriers_to_plot,
                                                        range_start = range_select.value[0],
                                                        range_end = range_select.value[1],
                                                        bin_width = binwidth_select.value)

        src.data.update(new_src.data)

so it looks like you have the right update function. I would try going to the code I linked above and trying to run that by itself. The application code has to be formatted slightly differently than the stand-alone plot which might explain the issue.

fabhlc commented 6 years ago

I'm trying to run the entire application as per your second tutorial (minus the range and bin width sliders widgets) in order to get the CheckboxGroup widget to work.

The initial histogram gets created just fine - it's the updating action that doesn't work. (e.g. clicking on checkboxes don't result in any action).

Thanks

p.e. I use Jupyter Notebook so I've been creating the plots inline.

fabhlc commented 6 years ago

Update: this works on Bokeh servers, which I wasn't using before. Cheers!

WillKoehrsen commented 6 years ago

That's good to hear!

Sorry, I was trying to say that it should work in a Bokeh server before, but I don't think I was very clear. If you're trying to run it in a Jupyter Notebook in the future, there's a slightly different set of steps to get the interactivity.

jazib-jamil commented 4 years ago

Hi, I read all the comments. I'm trying to run it on Jupyter/Spyder. I want to know the difference needed to make code work. Thanks

markgithub111 commented 4 years ago

I had a similar problem to the one fabhlc did. The updating problem happened because of the deprecation of "legend." I struggled to find the solution but eventually figured out that you had to use legend_field, not legend_group. The solution is explained here: https://docs.bokeh.org/en/latest/docs/reference/plotting.html.

My question is different though. I have this running in the Jupyter notebook, but I would like to run the code in a browser, not in the Jupyter notebook (so that I can give the plot to someone else to use). I can't figure out how to do that. I've used the output to html feature on the passively interactive plot (from Part I of the article) and that works fine. But the actively interactive one will not save to an html file. I would be happy to learn how to do this. To be clear, I don't care what format the output is on, I just want people to just click on an icon and get the interactive visualization to work. Like I do in Plotly.