3
votes

Here is my dataframe:

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:

trace2 = go.Bar(x=df["Date"], y=df[["B_item"]])
trace3 = go.Bar(x=df["Date"], y=df[["C_item"]])

list_updatemenus = [{'label': 'All',
  'method': 'update',
  'args': [{'visible': [True, True]}, {'title': 'All'}]},
 {'label': 'Chanel_1',
  'method': 'update',
  'args': [{'visible': [True, False]}, {'title': 'Chanel_1'}]},
 {'label': 'Chanel_2',
  'method': 'update',
  'args': [{'visible': [False, True]}, {'title': 'Chanel_2'}]}]


data = [trace1,trace2,trace3]

layout=go.Layout(title='Distribution of Sales by Region',updatemenus=list([dict(buttons= list_updatemenus)]),width=1000,height=800,barmode='group')


fig = go.Figure(data,layout)

fig.show()

And not getting the desired output:Plot 1

As it filters the graph by the "A_item", "B_item" and "C_item" while I would like to filter it by the Channel_type col as mentioned.

So the ideal result would be the below graph, but with the dropdown menu that changes the graph based on Channel_type :

Plot 2

I am able to solve the problem with Ipywidgets in the Jupyter notebook, but 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:

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, and I need to share it.

So the ideal result would be to wrap the last figure within the Plotly fig only without the ipywidgets.

Any help be really appreciated!

Thank you!

1
So it sounds like you want to be able to use a dropdown menu with the same functionality as clicking the individual traces in the legend. Do you want multiple channels to be able to be selected from the dropdown or do you only want one channel at a time? - Derek O
Hi Derek the ideal would be : dropdown menu: option1 = channel_1, option2 = channel_2, option3 = both channel_1 and channel_2 thank you! - RamboJ
And yea exactly the same layout for the remaining part of the graph. Dropdown - is just a filter for a channel_type col. The only thing is that I'd like to have it within the plotly fig() and not with ipython widgets thank you! - RamboJ

1 Answers

4
votes

The most important thing to note is that for go.Bar, if you have n dates in the x parameter and you pass a 2D array of dimension (m, n) to the y parameter of go.Bar, Plotly understands to create a grouped bar chart with each date n having m bars.

For your DataFrame, something like df[df['Channel_type'] == "Channel_1"][items].T.values will reshape it as needed. So we can apply this to the y field of args that we pass the to the buttons we make.

Credit to @vestland for the portion of the code making adjustments to the buttons to make it a dropdown.

import pandas as pd
import plotly.graph_objects as go

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":["Channel_1", "Channel_1", "Channel_1", "Channel_1", "Channel_1", "Channel_1", "Channel_1", 
                                   "Channel_2", "Channel_2", "Channel_2", "Channel_2", "Channel_2", "Channel_2", "Channel_2"]
                   })

fig = go.Figure()

colors = ['#636efa','#ef553b','#00cc96']
items = ["A_item","B_item","C_item"]
for item, color in zip(items, colors):
    fig.add_trace(go.Bar(
        x=df["Date"], y=df[item], marker_color=color
        ))

# one button for each df column
# slice the DataFrame and apply transpose to reshape it correctly
updatemenu= []
buttons=[]
for channel in df['Channel_type'].unique():
    buttons.append(dict(method='update',
                        label=channel,
                        args=[{
                        'y': df[df['Channel_type'] == channel][items].T.values
                        }])
                  )

## add a button for both channels
buttons.append(dict(
    method='update',
    label='Both Channels',
    args=[{
    'y': df[items].T.values
    }])
)

# some adjustments to the updatemenu
# from code by vestland
updatemenu=[]
your_menu=dict()
updatemenu.append(your_menu)
updatemenu[0]['buttons']=buttons
updatemenu[0]['direction']='down'
updatemenu[0]['showactive']=True

fig.update_layout(updatemenus=updatemenu)

fig.show()

enter image description here