Dropdown menu for group bar chart

Hi guys,

Here is my dataframe:

import pandas as pd 
df = pd.DataFrame({"Date":["2020-01-27","2020-02-27","2020-03-27","2020-04-27", "2020-05-27", "2020-06-27", "2020-07-27",
                          "2020-01-27","2020-02-27","2020-03-27","2020-04-27", "2020-05-27", "2020-06-27", "2020-07-27"],
                   "A_item":[2, 8, 0, 1, 8, 10, 4, 7, 2, 15, 5, 12, 10, 7],
                   "B_item":[1, 7, 10, 6, 5, 9, 2, 5, 6, 1, 2, 6, 15, 8],
                   "C_item":[9, 2, 9, 3, 9, 18, 7, 2, 8, 1, 2, 8, 1, 3],
                   "Channel_type":["Chanel_1", "Chanel_1", "Chanel_1", "Chanel_1", "Chanel_1", "Chanel_1", "Chanel_1", 
                                   "Chanel_2", "Chanel_2", "Chanel_2", "Chanel_2", "Chanel_2", "Chanel_2", "Chanel_2"]
                   })

I want to plot a group Bar chart with the dropdown filter on the Channel_type col. That’s what I am trying:

import plotly.express as px

fig = px.bar(df, x='Date', y=['A_item', 'B_item', 'C_item'])

fig.update_layout(barmode='group')

fig.show()

and here is the output:

However, that graph shows the aggregated data and what I am trying to achieve is to be able to filter it by the “Channel_type” column within a dropdown menu, while the layout of the group bar chart stays the same and all 3 item cols will be present for every dropdown option. The options for the dropdown to be:

Option 1: All (“Channel_1” & “Channel_2” - aggregated and same as the graph on my picture)
Option 2: “Channel_1” only
Option 3: “Channel_2” only

I was able to achieve it with the Ipython widgets, but for my specific task, I want it within the Plotly fig()

Any suggestions on how to accomplish that?

Thank you!

Just in case if someone needs it: I am able to solve the problem with Ipywidgets in Jupyter notebook, but like I said it’s not really working for my particular task. Here is the code:

from plotly import graph_objs as go
import ipywidgets as w
from IPython.display import display

x  = 'Date'
y1 = 'A_item'
y2 = 'B_item'
y3 = 'C_item'

trace1 = {
    'x': df[x],
    'y': df[y1],
    'type': 'bar',
    'name':'A_item'
}

trace2={
    'x': df[x],
    'y': df[y2],
    'type': 'bar',
    'name':'B_item'
}

trace3 = {
    'x': df[x],
    'y': df[y3],
    'type': 'bar',
    'name':'C_item',
    
}

data = [trace1, trace2, trace3]

# Create layout for the plot
layout=dict(
    title='Channels', 
    width=1200, height=700, title_x=0.5,
    paper_bgcolor='#fff',
    plot_bgcolor="#fff",
    xaxis=dict(
        title='Date', 
        type='date', 
        tickformat='%Y-%m-%d',
        gridcolor='rgb(255,255,255)',
        zeroline= False,
    ),
    yaxis=dict(
        title='My Y-axis',
        zeroline= False
            )
        )

fig = go.FigureWidget(data=data, layout=layout)

def update_fig(change):
    aux_df = df[df.Channel_type.isin(change['new'])]
    with fig.batch_update():
        for trace, column in zip(fig.data, [y1, y2, y3]):
            trace.x = aux_df[x]
            trace.y = aux_df[column]

drop = w.Dropdown(options=[
    ('All', ['Chanel_1', 'Chanel_2']),
    ('Chanel_1', ['Chanel_1']),
    ('Chanel_2', ['Chanel_2']),
])
drop.observe(update_fig, names='value')



display(w.VBox([drop, fig]))

And here is the output:

The problem is that I am not able to wrap the VBox into an HTML file and save the dropdown menu. Also, it isn’t working in the Python shell as it is intended for the Jupyter notebook, however, I need to share it.

So the ideal result would be to wrap it within the Plotly fig only.

@Jksks

Add to your code these lines, that restyle your plot:

cols =["A_item", "B_item", "C_item"]
chan1 = [list(df[item][:7])+[None]*7  for item in cols]
chan2 = [[None]*7 + list(df[item][7:])  for item in cols]

button1 =  dict(method = "restyle",
                args = [{'y': [ df["A_item"], df["B_item"], df["C_item"] ] }],
                label = "Channel 1 & Channel 2")
button2 =  dict(method = "restyle",
                args = [{'y': chan1}],
                label = "Channel 1")
button3 =  dict(method = "restyle",
                args = [{'y': chan2}],
                label = "Channel 2")
fig.update_layout(height=450,
                  updatemenus=[dict(active=0,
                                    buttons=[button1, button2, button3])
                              ]) 

@empet Huge thanks! that’s exactly what I was looking for!