pythonpandasmatplotlibipywidgetsmatplotlib-widget

How to use a dropdown widget to highlight selected categorical variable in stacked bar chart?


I am learning matplotlib and ipywidgets and attempt to design an interactive bar chart, such that the selected category can be highlighted.

Data Example

Assuming I have a dataframe:

import pandas as pd
import matplotlib.pyplot as plot

data = {"Production":[10000, 12000, 14000],
        "Sales":[9000, 10500, 12000]}
index = ["2017", "2018", "2019"]

df = pd.DataFrame(data=data, index=index)
df.plot.bar(stacked=True,rot=15, title="Annual Production Vs Annual Sales")

The resulting stacked bar chart looks like below:

enter image description here

What I am after

If we select production in the dropdown list, the blue bars will be highlighted by adding a box (or a frame) surrounding it. Similar should happen to Sales if it is selected.

Question

I am not sure if ipywidgets and matplotlib are enough to fulfill this feature, or do we need other package to make it? If possible to do with those two packages, could anyone share some clues? Thanks!


Solution

  • Here is one quick way to do it with ipywidgets and matplotlib:

    import ipywidgets as widgets
    import matplotlib.pyplot as plt
    import pandas as pd
    from IPython import display
    
    
    def select(*args):
        """Helper function to draw red border around bars."""
        display.clear_output(wait=True)
        display.display(dropdown)
        if dropdown.value == "All":
            indices = []
        elif dropdown.value == "Sales":
            indices = [0, 2, 4]
        else:
            indices = [1, 3, 5]
        for i, bar in enumerate(ax.containers):
            if i in indices:
                plt.setp(bar, edgecolor="red", linewidth=3)
            else:
                plt.setp(bar, edgecolor=None)
        display.display(fig)
    
    
    df = pd.DataFrame(
        data={"Production": [10000, 12000, 14000], "Sales": [9000, 10500, 12000]},
        index=["2017", "2018", "2019"],
    )
    
    # Initialize plot
    fig, ax = plt.subplots()
    colors = ["blue", "orange"]
    labels = ["Sales", "Production"]
    
    for i in df.index:
        bottom = 0
        for j, item in enumerate(df.loc[i, :]):
            ax.bar(x=i, height=item, bottom=bottom, color=colors[j], label=labels[j])
            bottom += item
        labels = [None, None]  # To avoid repetition in legend
    
    ax.legend(loc="upper left")
    fig.suptitle("Annual Production Vs Annual Sales")
    
    # Add dropdown menu above plot
    dropdown = widgets.Dropdown(
        options=["All", *df.columns],
        disabled=False,
    )
    dropdown.observe(select)
    display.display(dropdown)
    

    This is what you get from running the above code in the same Jupyter notebook cell:

    enter image description here

    enter image description here

    enter image description here