Search code examples
pythonpython-3.xpandasplotbokeh

Is there any way to add a MultiChoice widget for df.plot_bokeh?


What I use is just

        fig_list = []
        grouped = df.unstack().groupby(level=1, axis=1)
        for key in grouped.groups.keys():
            tmp = grouped.get_group(key).droplevel([0, 1], axis=1)
            fig = tmp.plot_bokeh(kind="line", title=key,
                           rangetool=True,
                           show_figure=False, **kwargs)
            fig_list.append(fig)

and here is df.

('IC', 'IC00') ('IC', 'IC01') ('IC', 'IC02') ('IC', 'IC03') ('IF', 'IF00') ('IF', 'IF01') ('IF', 'IF02') ('IF', 'IF03')
(Timestamp('2023-01-03 00:00:00'), 1.0) -2474 nan -1128 -4424 3327 nan -3021 -432
(Timestamp('2023-01-03 00:00:00'), 18.0) -1056 nan 7373 -1199 -1948 nan -731 -165
(Timestamp('2023-01-03 00:00:00'), 102.0) 6613 nan 4045 2683 425 nan nan nan
(Timestamp('2023-01-03 00:00:00'), 131.0) 2890 nan 5971 3882 -84 nan 361 nan
(Timestamp('2023-01-03 00:00:00'), 1000.0) 12068 nan 18309 17182 840 nan -2437 5518
(Timestamp('2023-01-04 00:00:00'), 1.0) -2388 nan -996 -4463 3053 nan -2650 -388
(Timestamp('2023-01-04 00:00:00'), 18.0) -563 nan 6464 -1385 -829 nan -1762 318
(Timestamp('2023-01-04 00:00:00'), 102.0) 6309 nan 4350 1707 508 nan nan nan
(Timestamp('2023-01-04 00:00:00'), 131.0) 2976 nan 5977 4001 -66 nan 258 nan

Do u guys have any idea on how to add a MultiChoice widget to each fig in the for loop so that I can choose the id in a interactive way?

Actually I have no idea of bokeh itself. if I need to achieve the same result as plot_bokeh, what should I do, df.stack().stack().reset_index() first?


Solution

  • In pure Bokeh, you can handle it by using a widget along with a js callback. Below is a complete example with a Check Box Button - the plots will be hidden or displayed depending on the user's selection.

    import pandas as pd
    from bokeh.io import show, output_notebook
    from bokeh.models import CustomJS, CheckboxButtonGroup, ColumnDataSource
    from bokeh.plotting import figure
    from bokeh.layouts import column, row
    import random
    import math
    output_notebook()
    
    def create_plots(doc):
        
        # create toy dataframe
        df = pd.DataFrame({
            'datetime': ['2023-01-01', '2023-01-02', '2023-01-03', '2023-01-04'] * 3,
            'id': ['id_1'] * 4 + ['id_2'] * 4 + ['id_3'] * 4,
            'value': random.sample(range(1, 1000), 12)
        })
        
        ids = list(set(df['id']))
    
        # create figure
        plots = []
        for id_ in ids:
            source = ColumnDataSource(data=df[df['id'] == id_])
            datetime = df[df['id'] == id_]['datetime']
            p = figure(title=f"Plot for {id_}", x_range=datetime, height=300, width=300)
            p.line(x='datetime', y='value', source=source)
            p.xaxis.major_label_orientation = math.radians(45)
            plots.append(p)
        
        # create widget
        checkbox_button_group = CheckboxButtonGroup(labels=ids, active=list(range(len(ids))))
        
        callback = CustomJS(args=dict(plots=plots, chkbx=checkbox_button_group), code="""
            for (let i = 0; i < plots.length; i++){
                plots[i].visible = chkbx.active.includes(i)
            }
            """)
            
        checkbox_button_group.js_on_change('active', callback)
        
        doc.add_root(column(checkbox_button_group, row(plots)))
    
    show(create_plots)
    

    This code can be tested in a Jupyter notebook. I didn't try it with plot_bokeh, though, as I'm not used to work with it.