Plotly Update Button to Filter Dataset - python

I am brand new to the world of Python and Plotly and have been working on a Capstone project with the following objective:
Create an interactive chart that allows the user to view US Vehicle Sales between 2019-2021
that allows the user to view the data by Body Type, Segment, Year, Make/Brand, and Individual Models.
I have learned how to add buttons using Plotly Express, though I have been having issues with making them toggle the way I want them to. Here is a snippet of my code:
segbar = px.bar(segments, x=segments.Month, y=segments.NumSold, color=segments.NumSold,
color_continuous_scale='inferno', custom_data=[segments.Segment, segments.PrimaryBodyType, segments.Month, segments.Year], barmode='group')
segbar.update_traces(hovertemplate='<b>Segment:</b> %{customdata[0]} %{customdata[1]}<br><b>Units Sold:</b> %{y:,.0f}<br>Date: %{customdata[2]} %{customdata[3]}')
segbar.update_layout(
updatemenus=[
dict(
type="dropdown",
direction="down",
bgcolor='Dark Blue',
buttons=list(
[
dict(
label="(All)",
method="update",
args=[{"y": segments.NumSold},
{"x": segments.Month}],
),
dict(
label="2021",
method="update",
args=[{"y": segments.loc[segments['Year'] == "2021", "NumSold]},
{"x": segments.loc[segments['Year] == "2021", "Month"]}]
)
]),
), dict(
type="dropdown",
direction="down"
)
], template='plotly_dark')
segbar.show()
The default view (first button) seems to be working fine, though when I select the other button to filter by rows with a "Year" value of 2021, this is the output:

You are pretty close – you just need to add another set of square brackets around the pd.Series you are passing to "y" and "x" in the args key of the dictionaries. To get the example to work, I had to modify your DataFrame slightly, but this should work with your DataFrame.
from io import StringIO
import pandas as pd
import plotly.express as px
segment_data = StringIO("""Segment|PrimaryBodyType|Month|Year|NumSold|MonthNum(Index)|Compact|
A|SUV|January|2021|254391|0|Compact|
B|SUV|January|2019|249913|0|Midsize|
C|SUV|January|2021|248762|0|Midsize|
D|SUV|January|2020|239102|0|Compact|
E|SUV|January|2020|233614|0|Compact|
""")
segments = pd.read_csv(segment_data, sep="|")
segments["Year"] = segments["Year"].astype(str)
segbar = px.bar(segments, x=segments.Month, y=segments.NumSold, color=segments.NumSold,
color_continuous_scale='inferno', custom_data=[segments.Segment, segments.PrimaryBodyType, segments.Month, segments.Year], barmode='group')
segbar.update_traces(hovertemplate='<b>Segment:</b> %{customdata[0]} %{customdata[1]}<br><b>Units Sold:</b> %{y:,.0f}<br>Date: %{customdata[2]} %{customdata[3]}')
segbar.update_layout(
updatemenus=[
dict(
type="dropdown",
direction="down",
bgcolor='Dark Blue',
buttons=list(
[
dict(
label="(All)",
method="update",
args=[{"y": [segments.NumSold]},
{"x": [segments.Month]}],
),
dict(
label="2021",
method="update",
args=[{"y": [segments.loc[segments['Year'] == "2021", "NumSold"]]},
{"x": [segments.loc[segments['Year'] == "2021", "Month"]]}]
)
]),
), dict(
type="dropdown",
direction="down"
)
], template='plotly_dark')
segbar.show()

Related

Make button coloured in plotly express

I have the following button in plotly express:
fig.update_layout(
shapes=[vertical_line],
updatemenus=[
dict(
type="buttons",
buttons=[
dict(label="Toggle Shapes",
method="relayout",
args=[{
"shapes": [vertical_line],
"annotations": vline_annotation}],
args2=[{
"shapes": rectangle_shape + [vertical_line],
"annotations": rectangle_annotation+ vline_annotation}],
),
],
)
]
)
This button is too bright for the plotly_dark theme, and is thus unreadable. I would like to change the color, however it seems this button doesn't have a parameter to set color in plotly express (to the best of my knowledge). How would I make this button green?
I think you can use the bgcolor at the updatemenus level (not the button level) (docs):
updatemenus=[
dict([
type="buttons",
buttons=[
#...
],
bgcolor = "#222",
bordercolor = "#FFF",
borderwidth = 0.5
])
]

plotly graph_objects (go) selecting two lines from two dropdowns menu to compare in the same figure

i am trying to compare two lines in the same fig in plotly go, the goal is to let the user to select two entities (i.e. countries) from two dropdown menu in the chart
this is the chart with all entities active
when i select different country in the two dropdown:
i.e. dropdown1 = Italy, dropdown2= France
the chart shows just one line (the last selected from the dropdown, so it update)but it doesn't plot the two lines in the same figure
Plot with just one line
this is the dataframe:
dataframe head
this is the code i am working on:
fig1 = go.Figure()
for column in df.columns.to_list():
fig1.add_trace(
go.Scatter(
x = df.index,
y = df[column],
name = column
)
)
button_all = dict(label = 'All',
method = 'restyle',
args = [{'visible': df.columns.isin(df.columns),
'title': 'All',
'showlegend':True}])
button_none = dict(label = 'None',
method = 'update',
args = [{'visible': df.columns.isin(df.columns),
'title': 'All',
'showlegend':True}])
def create_layout_button(column):
return dict(label = column,
method = 'restyle',
args = [{'visible': df.columns.isin([column]),
'title': column,
'showlegend': True}])
def create_layout_buttonE(column):
return dict(label = column,
method = 'update',
args = [{'visible': df.columns.isin([column]),
'title': column,
'showlegend': True}])
addAll = True
# Update plot sizing
fig1.update_layout(
width=700,
height=500,
autosize=False,
margin=dict(t=120, b=0, l=0, r=0),
)
# Add dropdowns
button_layer_1_height = 1.08
fig1.update_layout(
updatemenus=[
dict(
buttons=([button_all] * addAll) + list(df.columns.map(lambda column: create_layout_button(column))),
direction="down",
pad={"r": 10, "t": 0},
showactive=True,
x=0.1,
xanchor="left",
y=button_layer_1_height,
yanchor="top"
),
dict(
buttons=([button_none] * addAll) + list(df.columns.map(lambda column: create_layout_buttonE(column))),
direction="down",
pad={"r": 10, "t": 0},
showactive=True,
x=0.37,
xanchor="left",
y=button_layer_1_height,
yanchor="top"
),
]
)
fig1.show()
synthesized dataframe in same structure as you defined
taken a different approach. Rather than creating all traces and controlling visibility in updatemenus. Create two traces and control name and contents of y in updatemenus
import pandas as pd
import numpy as np
import plotly.graph_objects as go
# get some countries
countries = (
pd.read_csv(
"https://raw.githubusercontent.com/owid/covid-19-data/master/public/data/vaccinations/locations.csv"
)
.loc[lambda d: d["iso_code"].str.len() == 3, "location"]
.sample(20)
.sort_values()
.values
)
# build data frame of same struct
df = pd.DataFrame(
np.random.randint(200, 1500, [22, len(countries)]),
columns=countries,
index=range(2000, 2022),
)
# create a figure with two place holder traces
fig = go.Figure(
[
go.Scatter(x=df.index, y=np.full(len(df), np.nan), meta=i, name=i)
for i in range(2)
]
)
# but data for y and name in when country is selected
fig.update_layout(
updatemenus=[
{
"x": b / 3,
"y": 1.4,
"active": None,
"buttons": [
{
"label": c,
"method": "restyle",
"args": [{"y": [df[c]], "name": c}, [b]],
}
for c in df.columns
],
}
for b in range(2)
]
)

Non-responsive scatter/bar plots to the Callback function

I am very new to Plotly Dash and I cannot get a hang of the #callback function with multiple inputs linked to two (scatter/bar) graphs.
Dashboard
Sample Data
For some reason, after selecting the inputs and clicking the “Run selection” button, the graphs do not update accordingly. Does anyone know what I am doing wrong? Been reading the documentation and browsing through online examples, but could not figure this one out... I believe something is wrong with the #callback function or the way I defined the data in my function.
Thank you in advance, would highly appreciate every bit of help! :slight_smile:
import pandas as pd
import dash
import dash_bootstrap_components as dbc
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output, State
import plotly.graph_objs as go
import plotly.express as px
# load the data
data = pd.read_excel('example_data.xlsx')
data.fillna(0, inplace=True)
data['Satellite'] = data['Satellite'].astype(int)
#initiate app and set theme
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
# navigation bar
navbar = dbc.NavbarSimple(
children=[
dbc.NavItem(dbc.NavLink("Page 1", href="#")),
dbc.DropdownMenu(
children=[
dbc.DropdownMenuItem("Page 2", href="#"),
dbc.DropdownMenuItem("Page 3", href="#"),
],
nav=True,
in_navbar=True,
label="More",
),
],
brand="Sample Dash Application",
brand_href="#",
color="primary",
dark=True,
)
# Construct a dictionary of dropdown values for the days
day_options = []
for day in data['AsofDate'].unique():
# grab day_options and append to it
day_options.append({'label':str(day),'value':day})
# Construct a dictionary of dropdown values for the portfolios
portfolio_options = []
for portfolio in data['Satellite'].unique():
portfolio_options.append({'label':str(portfolio),'value':int(portfolio) or 0})
# Construct a dictionary of dropdown values for the region
region_options = []
for region in data['Region'].unique():
region_options.append({'label':str(region),'value':region})
###############################################################################
# Filter pane
# Check dash core components at https://dash.plotly.com/dash-core-components
###############################################################################
controls = [
html.Div(
[
'Select Day',
dcc.Dropdown(
id='day-dropdown',
options=day_options,
value=data['AsofDate'].min(),
clearable=False
),
]
),
html.Div(
[
'Select Portfolio',
dcc.Dropdown(
id='portfolio-dropdown',
options=portfolio_options,
value=data['Satellite'].min(),
clearable=False
),
]
),
html.Div(
[
'Select Region',
dcc.Dropdown(
id='region-dropdown',
options=region_options,
value=data['Region'].min(),
clearable=False
),
]
),
html.Br(),
dbc.Button("Run selection", color="primary", id="button-submit")
]
###############################################################################
# Add components here, e.g. graphs
###############################################################################
avp_graph = dcc.Graph(id="avp-graph", style={"height": "500px"})
bar_plot_graph = dcc.Graph(id='bar-plot-graph', style={"height": "500px"})
###############################################################################
# Container
# Check dash core components at https://dash.plotly.com/dash-core-components
###############################################################################
container = dbc.Container(
fluid=True,
children=[
html.Hr(),
html.H3('Dashboard'),
html.Hr(),
# Bootstrap Layout examples:
# https://getbootstrap.com/docs/4.0/examples/
# Bootstrap grid system: https://getbootstrap.com/docs/4.0/layout/grid/
# Under each column (2, 5, 5) you can add components.
# The left column has a width of 2 and contains the controls only.
# The middle column has a width of 5 and contains a table and a graph.
# The right column has a width of 5 and contains a table and a graph.
dbc.Row(
[
dbc.Col(
[
# See https://dash-bootstrap-components.opensource.faculty.ai/docs/components/card/
dbc.Card([
dbc.CardHeader("Filter Pane"),
dbc.CardBody(controls),
]
),
],
sm=2
),
dbc.Col(
[
html.H5('Column 1'),
avp_graph # middle column, graph 1
],
sm=5
),
dbc.Col(
[
html.H5('Column 2'),
bar_plot_graph # right column, graph 2
],
sm=5
)
]
)
]
)
# Define Layout
app.layout = html.Div([
navbar,
container
])
#app.callback(
[
Output("avp-graph", "figure"),
Output("bar-plot-graph", "figure")
],
[Input("button-submit", "n_clicks")],
[
State("day-dropdown", "value"),
State("portfolio-dropdown", "value"),
State("region-dropdown", "value"),
],
)
def run_submit(n_clicks, day, portfolio, region):
# Bar plot
mask = data["AsofDate"] == day
bar_plot_graph = px.bar(data[mask], x=data['Region'], y=data['Return'], title='Bar Plot') # barmode="group",
# Scatter Plot
avp_graph = px.scatter(
x=data['line_x'],
y=data['line_y'],
labels={"x": "x", "y": "y"},
title=f"Scatter Plot",
)
return [avp_graph, bar_plot_graph] # return the components in the order of Output Callbacks!!!
if __name__ == '__main__':
app.run_server(port=8051, debug=False)
I'm not sure how you want your plots to change, but what I can see is:
The variables portfolio and region are defined by the state of the dropdown menus, but they are not used in your callback function.
The plot avp_graph in the callback function doesn't depend on any input. Therefore, it makes sense that it remains constant independently of what you select in your dropdown menus.
The plot bar_plot_graph only depends on the variable day.

I don't want to see all the graphs before selecting a dropdown button (python plotly)

I created a dropdown menu with several graphs and everything is fine but when I start the program and have not yet selected one of the buttons, I see all the graphs and it just looks messy so I want to change this. In the default situation I would like to see only the graph that is connected to the first button. I tried to do this with the line "active = 0" but it only caused the first button to be highlighted.
Here is a snippet of my data (it shows the effect of the corona crisis on the sales index in the german manufacturing industry):
Chemicals;Mechanical engineering;Motor vehicles and motor vehicle parts;Dates
101.5;108.1;104.6;Jan-2019
101.2;105.8;105.9;Feb-2019
101;105.9;106.2;Mar-2019
101;105.6;101.2;Apr-2019
99.3;103.2;104.5;Mai-2019
99.4;103;101.9;Jun-2019
99.2;104;99.5;Jul-2019
99.4;103;102.2;Aug-2019
97.1;102.7;102.2;Sept-2019
99.7;100.6;100.9;Okt-2019
99.1;101.8;100.4;Nov-2019
98.7;99.7;101.6;Dez-2019
99.2;102.4;100.1;Jan-2020
101.5;100.1;99.8;Feb-2020
99.2;91.5;72.7;Mar-2020
90;73.9;24.8;Apr-2020
And here is a simple version of my program:
import plotly.graph_objects as go
import numpy as np
import pandas as pd
df = pd.read_csv("sales_index_manufacturing.csv", delimiter = ";")
fig = go.Figure()
fig.add_trace(
go.Scatter(x=list(df['Dates']),
y=list(df['Chemicals']),
name="Chemicals",
line=dict(color="#008b8b"))
)
fig.add_trace(
go.Scatter(x=list(df['Dates']),
y=list(df['Mechanical engineering']),
name='Mechanical engineering',
line=dict(color="#8b008b"))
)
fig.add_trace(
go.Scatter(x=list(df['Dates']),
y=list(df['Motor vehicles and motor vehicle parts']),
name='Motor vehicles and motor vehicle parts',
line=dict(color="#ffa500"))
)
fig.update_layout(
updatemenus=[
dict(
active= 0,
buttons=list([
dict(label='Chemicals',
method="update",
args=[{"visible": [True,False,False]},
{"title": "Chemicals",
"annotations": [],
'yaxis': {'title': 'sales index (2015 = 100)'}}]),
dict(label='Mechanical engineering',
method="update",
args=[{"visible": [False,True,False]},
{"title": 'Mechanical engineering',
"annotations": [],
'yaxis': {'title': 'sales index (2015 = 100'}}]),
dict(label='Motor vehicles and motor vehicle parts',
method="update",
args=[{"visible": [False,False,True]},
{"title": "Motor vehicles and motor vehicle parts",
"annotations": [],
'yaxis': {'title': 'sales index (2015 = 100)'}}]
)]
),
direction="down",
pad={"r": 10, "t": 10},
showactive=True,
x=-0.04,
xanchor="left",
y=1.132,
yanchor="top",
)
])
fig.update_layout(
legend=dict(x= 0.75,
y=1.1,
bgcolor='rgba(255, 255, 255, 0)',
bordercolor='rgba(255, 255, 255, 0)'),
width=1000,
height=600,
autosize=False,
template="plotly_white",
)
fig.show()
When you run the code, you're probably wondering what I mean by "messy," but the original program contains a lot more graphs and data, and it really looks like a mess in the default situation.
Thanks in advance for your help!

How to update choropleth map in Dash

I'm making a webapp using Python and Dash, this webapp includes a choropleth map of the world with data based on the selected year. I want to be able to change the year and with that also update the map to match that year. I prefer to do this with the Dash slider, although any other way I would appreciate as well.
I've tried updating other graphs such as line charts with the text input, and that worked, but when i changed it to a choropleth map it stopped updating. It now only creates the map but updates on it don't show up. I've put some print text in the update function and it confirmed that it is actually called when I change the input, but the graph just doesn't update.
The layout:with the dcc.input i want to update the html.Div 'my-div'
app.layout = html.Div( children=[
html.H1(
children='UN Sustainable Development goal: Poverty',
style={
'textAlign': 'center',
'color': colors
}
),
dcc.Input(id='my-id',value='30', type='text'),
html.Div(id='my-div')
,
daq.Slider(
id='my-daq-slider',
min=1,
max=sliderlength,
step=1,
),
html.Div(id='slider-output')
], style={
'textAlign': 'center'
})
The update part
#app.callback(
Output('my-div', 'children'),
[Input('my-id', 'value')])
def update_output_div(input_value):
return dcc.Graph(
id='my-div',
figure={'data': [go.Choropleth(
locations = df_pov['Country Code'],
z = df_pov.iloc[:,int(input_value)],
text = df_pov['Country Name'],
autocolorscale = True,
reversescale = False,
marker = go.choropleth.Marker(
line = go.choropleth.marker.Line(
color = 'rgb(180,180,180)',
width = 0.5
)),
colorbar = go.choropleth.ColorBar(
tickprefix = '%',
title = '% below 1.90$ '),
)],
'layout': go.Layout(
title = go.layout.Title(
text = list(df_pov)[int(input_value)]
),
geo = go.layout.Geo(
showframe = False,
showcoastlines = False,
projection = go.layout.geo.Projection(
type = 'equirectangular'
)
),
annotations = [go.layout.Annotation(
x = 0.55,
y = 0.1,
xref = 'paper',
yref = 'paper',
text = 'Source: Kaggle',
showarrow = False
)]
)
}
)
What i expected: for the choropleth to update when changing the text input, or slider input.
Actual: the map gets created once ( with the same function that should update it), but doesn't update.
Dash doesn't like new components being loaded in like this. Try initializing your graph by adding an empty graph to the layout like this:
html.Div(id='my-div', children=[
dcc.Graph(
id='my-graph-id',
figure=dict(
data=[],
layout={},
),
)
])
With the graph already on the page, you would have to change the callback so that it updates the figure prop of the Graph instead of the children of the div.
Also, you have multiple IDs that are the same. Dash doesn't like that. Make sure every ID is unique. If all of this still does not work, I may need to ask you to post some more details so I can help further. Good luck!

Categories

Resources