Python Plotly chart update with two dropdowns
It's just about being systematic around the list comprehensions. Below fully works, allows selection of any column and updates appropriate axis title.
import pandas as pd
import numpy as np
import plotly.express as px
temp = pd.DataFrame(np.random.randint(0, 1000, (100, 10)))
col_list = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
temp.columns = col_list
fig = px.scatter(temp, x="A", y="B")
fig.update_layout(
updatemenus=[
{
"buttons": [
{
"label": c,
"method": "update",
"args": [
{axis: [temp[c]]},
{f"{axis}axis": {"title": {"text": c}}},
],
}
for c in temp.columns
],
"x": 0 if axis == "x" else 0.1,
"y": 1.2,
}
for axis in "xy"
]
)
Plotly Dash: Graphs not updating based on Dropdown selection
There are two typos in your code:
- In the list of options of the dropdown with
id='input-type'
the value of the first option is set to'.OPT1'
instead of'OPT1'
, which is why the callback always returns the output corresponding to'OPT2'
, i.e. it always returns the line charts. - In the definition of
line_fig
under'OPT1'
thecolor
is set equal to'Reporting Airline'
instead of'Reporting_Airline'
.
Note also that there is no need to include the children
of 'plot1'
, 'plot2'
, 'plot3'
, 'plot4'
and 'plot5'
as State
in the callback.
Updated code:
# Import required libraries
import pandas as pd
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
import plotly.express as px
# Create a dash application
app = dash.Dash(__name__)
# Clear the layout and do not display exception till callback gets executed
app.config.suppress_callback_exceptions = True
# Read the airline data into pandas dataframe
airline_data = pd.read_csv(
'https://cf-courses-data.s3.us.cloud-object-storage.appdomain.cloud/IBMDeveloperSkillsNetwork-DV0101EN-SkillsNetwork/Data%20Files/airline_data.csv',
encoding='ISO-8859-1',
dtype={'Div1Airport': str, 'Div1TailNum': str, 'Div2Airport': str, 'Div2TailNum': str}
)
# List of years
year_list = [i for i in range(2005, 2021, 1)]
def compute_data_choice_1(df):
'''
Function that takes airline data as input and create 5 dataframes based on the grouping condition to be used for plottling charts and grphs.
Argument:
df: Filtered dataframe
Returns:
Dataframes to create graph.
'''
# Cancellation Category Count
bar_data = df.groupby(['Month', 'CancellationCode'])['Flights'].sum().reset_index()
# Average flight time by reporting airline
line_data = df.groupby(['Month', 'Reporting_Airline'])['AirTime'].mean().reset_index()
# Diverted Airport Landings
div_data = df[df['DivAirportLandings'] != 0.0]
# Source state count
map_data = df.groupby(['OriginState'])['Flights'].sum().reset_index()
# Destination state count
tree_data = df.groupby(['DestState', 'Reporting_Airline'])['Flights'].sum().reset_index()
return bar_data, line_data, div_data, map_data, tree_data
def compute_data_choice_2(df):
'''
This function takes in airline data and selected year as an input and performs computation for creating charts and plots.
Arguments:
df: Input airline data.
Returns:
Computed average dataframes for carrier delay, weather delay, NAS delay, security delay, and late aircraft delay.
'''
avg_car = df.groupby(['Month', 'Reporting_Airline'])['CarrierDelay'].mean().reset_index()
avg_weather = df.groupby(['Month', 'Reporting_Airline'])['WeatherDelay'].mean().reset_index()
avg_NAS = df.groupby(['Month', 'Reporting_Airline'])['NASDelay'].mean().reset_index()
avg_sec = df.groupby(['Month', 'Reporting_Airline'])['SecurityDelay'].mean().reset_index()
avg_late = df.groupby(['Month', 'Reporting_Airline'])['LateAircraftDelay'].mean().reset_index()
return avg_car, avg_weather, avg_NAS, avg_sec, avg_late
# Application layout
app.layout = html.Div(children=[
html.H1(
children='US Domestic Airline Flights Performance',
style={'textAlign': 'center', 'color': '#503D36', 'font-size': 40}
),
html.Div([
html.Div([
html.H2('Report Type:', style={'margin-right': '2em'}),
dcc.Dropdown(
id='input-type',
options=[
{'label': 'Yearly Airline Performance Report', 'value': 'OPT1'},
{'label': 'Yearly Airline Delay Report', 'value': 'OPT2'}
],
value='OPT1',
placeholder='Select a report type',
style={'width': '80%', 'padding': '3px', 'font-size': '20px', 'text-align-last': 'center'}
)
], style={'display': 'flex'}),
html.Div([
html.H2('Choose Year:', style={'margin-right': '2em'}),
dcc.Dropdown(
id='input-year',
options=[{'label': i, 'value': i} for i in year_list],
value=year_list[0],
placeholder='Select a year',
style={'width': '80%', 'padding': '3px', 'font-size': '20px', 'text-align-last': 'center'}
),
], style={'display': 'flex'}),
]),
html.Div(id='plot1'),
html.Div(
children=[
html.Div(id='plot2'),
html.Div(id='plot3')
],
style={'display': 'flex'}
),
html.Div(
children=[
html.Div(id='plot4'),
html.Div(id='plot5')
],
style={'display': 'flex'}
),
])
@app.callback([Output('plot1', 'children')],
[Output('plot2', 'children'),
Output('plot3', 'children'),
Output('plot4', 'children'),
Output('plot5', 'children')],
[Input('input-type', 'value'),
Input('input-year', 'value')])
def get_graph(chart, year):
df = airline_data[airline_data['Year'] == int(year)]
if chart == 'OPT1':
bar_data, line_data, div_data, map_data, tree_data = compute_data_choice_1(df)
bar_fig = px.bar(bar_data,
x='Month',
y='Flights',
color='CancellationCode',
title='Monthly Flight Cancellation')
line_fig = px.line(line_data,
x='Month',
y='AirTime',
color='Reporting_Airline',
title='Average monthly flight time (minutes) by airline')
pie_fig = px.pie(div_data,
values='Flights',
names='Reporting_Airline',
title='% of flights by reporting airline')
map_fig = px.choropleth(map_data,
locations='OriginState',
color='Flights',
hover_data=['OriginState', 'Flights'],
locationmode='USA-states',
color_continuous_scale='GnBu',
range_color=[0, map_data['Flights'].max()])
map_fig.update_layout(title_text='Number of flights from origin state',
geo_scope='usa')
tree_fig = px.treemap(tree_data, path=['DestState', 'Reporting_Airline'],
values='Flights',
color='Flights',
color_continuous_scale='RdBu',
title='Flight count by airline to destination state')
return [dcc.Graph(figure=tree_fig),
dcc.Graph(figure=pie_fig),
dcc.Graph(figure=map_fig),
dcc.Graph(figure=bar_fig),
dcc.Graph(figure=line_fig)]
else:
avg_car, avg_weather, avg_NAS, avg_sec, avg_late = compute_data_choice_2(df)
carrier_fig = px.line(avg_car,
x='Month',
y='CarrierDelay',
color='Reporting_Airline',
title='Average carrrier delay time (minutes) by airline')
weather_fig = px.line(avg_weather,
x='Month',
y='WeatherDelay',
color='Reporting_Airline',
title='Average weather delay time (minutes) by airline')
nas_fig = px.line(avg_NAS,
x='Month',
y='NASDelay',
color='Reporting_Airline',
title='Average NAS delay time (minutes) by airline')
sec_fig = px.line(avg_sec,
x='Month',
y='SecurityDelay',
color='Reporting_Airline',
title='Average security delay time (minutes) by airline')
late_fig = px.line(avg_late,
x='Month',
y='LateAircraftDelay',
color='Reporting_Airline',
title='Average late aircraft delay time (minutes) by airline')
return [dcc.Graph(figure=carrier_fig),
dcc.Graph(figure=weather_fig),
dcc.Graph(figure=nas_fig),
dcc.Graph(figure=sec_fig),
dcc.Graph(figure=late_fig)]
if __name__ == '__main__':
app.run_server(debug=True, host='127.0.0.1')
Plotly: How to update plotly data using dropdown list for line graph?
I've made a preliminary setup using your full datasample, and I think I've got it figured out. The challenge here is that px.line
will group your data by the color
argument. And that makes it a bit harder to edit the data displayed using a dropdownmenu with a direct reference to the source of your px.line
plot.
But you can actually build multiple px.line
figures for the different datasets, and "steal" the data with the correct structure for your figure there. This will give you these figures for the different dropdown options:
I'm a bit worried that the second plot might be a bit off, but I'm using the date you've provided which looks like this for first_timebuyers
:
So maybe it makes sense after all?
Below is the complete code without your data. We can talk about the details and further tweaking tomorrow. Bye for now.
import numpy as np
import pandas as pd
import plotly.express as px
from pandas import Timestamp
all_dwellings=pd.DataFrame(<yourData>)
first_timebuyers = pd.DataFrame(<yourData>)
# datagrab 1
lineplt_all = px.line(data_frame = all_dwellings,
x='Date',
y='Average House Price (£)',
color= 'Country',
hover_name='Country',
color_discrete_sequence=['rgb(23, 153, 59)','rgb(214, 163, 21)','rgb(40, 48, 165)', 'rgb(210, 0, 38)']
)
# datagrab 2
lineplt_first = px.line(data_frame = first_timebuyers,
x='Date',
y='Average House Price (£)',
color= 'Country',
hover_name='Country',
color_discrete_sequence=['rgb(23, 153, 59)','rgb(214, 163, 21)','rgb(40, 48, 165)', 'rgb(210, 0, 38)']
)
### Your original setup
lineplt = px.line(data_frame = all_dwellings,
x='Date',
y='Average House Price (£)',
color= 'Country',
hover_name='Country',
color_discrete_sequence=['rgb(23, 153, 59)','rgb(214, 163, 21)','rgb(40, 48, 165)', 'rgb(210, 0, 38)']
)
updatemenus = [
{'buttons': [
{
'method': 'restyle',
'label': 'All Dwellings',
'args': [{'y': [dat.y for dat in lineplt_all.data]}]
},
{
'method': 'restyle',
'label': 'First Time Buyers',
'args': [{'y': [dat.y for dat in lineplt_first.data]}]
}
],
'direction': 'down',
'showactive': True,
}
]
lineplt = lineplt.update_layout(
title_text='Average House Price in UK (£)',
title_x=0.5,
#plot_bgcolor= 'rgb(194, 208, 209)',
xaxis_showgrid=False,
yaxis_showgrid=False,
hoverlabel=dict(font_size=10, bgcolor='rgb(69, 95, 154)',
bordercolor= 'whitesmoke'),
legend=dict(title='Please click legend item to remove <br>or add to plot',
x=0,
y=1,
traceorder='normal',
bgcolor='LightSteelBlue',
xanchor = 'auto'),
updatemenus=updatemenus
)
lineplt = lineplt.update_traces(mode="lines", hovertemplate= 'Date = %{x} <br>' + 'Price = £%{y:.2f}')
lineplt.show()
Plotly: Scatter plot with dropdown menu to change data and calculated annotation
The solution for me was to change to a single dropdown button that select pairs of variables (i.e. changes both x and y). One caveat to this is when dealing with large datasets, as the number of combinations can get pretty big, but for my case (~20 columns) it was fine.
from scipy import stats
def corr_annotation(x, y):
pearsonr = stats.pearsonr(x, y)
return 'r = {:.2f} (p = {:.3f})'.format(pearsonr[0], pearsonr[1])
# Prep random data
import pandas as pd
import numpy as np
np.random.seed(12)
data = pd.DataFrame(dict(
A=np.random.randint(11, size=10),
B=np.random.randint(11, size=10),
C=np.random.randint(11, size=10),
D=np.random.randint(11, size=10)
))
# Create base figure
import plotly.express as px
fig = px.scatter(data, x='A', y='B')
fig.add_annotation(dict(text=corr_annotation(data['A'], data['B']),
showarrow=False,
yref='paper', xref='paper',
x=0.99, y=0.95))
# Create buttons
import itertools
buttons = []
for x, y in itertools.combinations(data.columns, 2):
buttons.append(dict(method='update',
label='{} x {}'.format(x, y),
args=[{'x': [data[x]],
'y': [data[y]]},
{'xaxis': {'title': x},
'yaxis': {'title': y},
'annotations': [dict(text=corr_annotation(data[x], data[y]),
showarrow=False,
yref='paper', xref='paper',
x=0.99, y=0.95)]}]
)
)
# Update and show figure
fig.update_layout(updatemenus=[dict(buttons=buttons, direction='down', x=0.1, y=1.15)])
fig.show()
Plotly dropdown selection does not update plots correctly
Are you looking for something like:
Sort data and add dropdown to df
import plotly.graph_objects as go
import pandas as pd
df = df.sort_values(["Region_title", "Room_number", "Year_quarter"])\
.reset_index(drop=True)
df["dropdown"] = df.apply(lambda x: '{} - Room nbr {}'.format(x['Region_title'], x["Room_number"]),
axis=1)
Traces and buttons
colors_list = ['#1f77b4', # muted blue
'#ff7f0e', # safety orange
'#2ca02c', # cooked asparagus green
'#d62728', # brick red
'#9467bd', # muted purple
'#8c564b', # chestnut brown
'#e377c2', # raspberry yogurt pink
'#7f7f7f', # middle gray
'#bcbd22', # curry yellow-green
'#17becf' # blue-teal
]
dfs = list(df.groupby('dropdown'))
first_title = dfs[0][0]
traces = []
buttons = []
for i,d in enumerate(dfs):
visible = [False]*4
visible[i] = True
name = d[0]
traces.append(
go.Scatter(x = d[1]["Year_quarter"],
y = d[1]["Price"],
text = "Average price",
hoverinfo = "x+y",
mode = 'lines+markers',
visible = True if i==0 else False,
name = name,
marker = {'color':colors_list[i%len(colors_list)],
'size': 10,
'opacity': 0.5,
'line': {'width': 0.5,
'color': 'white'}
}
))
buttons.append(dict(label=name,
method="update",
args=[{"visible":visible},
{"title":f"Title {name}"}]))
updatemenus = [{'active':0, "buttons":buttons}]
fig = go.Figure(data=traces,
layout=dict(updatemenus=updatemenus))
fig.update_layout(title=first_title, title_x=0.5)
fig.update_yaxes(range=[0, df["Price"].max()*1.2])
fig.show()
Here the idea is to split df by the buttons you want in your dropdown and play with visible traces.
How to update data of multiple scatter plots with dropdown buttons in plotly python?
- consistency is always the key when building these types of figure and menus
- it's far simpler to achieve consistency with Plotly Express so I have switched to this instead of graph objects
- build two figures for the two data frames, then integrate them. Name is overridden to ensure legend appears as you want
- having built figure, have all required information within it to build menu as nested list comprehensions
import numpy as np
import pandas as pd
import plotly.express as px
scen3_df = pd.DataFrame(np.random.randint(10, 20, (100, 8)), columns=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])
orig_df = pd.DataFrame(np.random.randint(0, 10, (100, 8)), columns=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])
# generate equivalent figures for both data frames
figs = [
px.line(df, y=df.columns)
.update_traces(line_color=color, name=name)
.for_each_trace(lambda t: t.update(visible=t.legendgroup == df.columns[0]))
for df, color, name in zip(
[scen3_df, orig_df], ["blue", "red"], ["Scenario 3", "Original"]
)
]
# construct overall figure
fig = (
figs[0]
.add_traces(figs[1].data)
.update_layout(xaxis_title="Time (Julian Day)", yaxis_title="Gate Height (m)")
)
# build the menu
fig.update_layout(
updatemenus=[
{
"buttons": [
{
"label": col,
"method": "update",
"args": [
{"visible": [t.legendgroup == col for t in fig.data]},
{"title": f" Gate operation at {col}"},
],
}
for col in scen3_df.columns
]
}
]
)
Related Topics
Remove All Duplicate Rows Including the "Reference" Row
Best Way to Transpose Data.Table
Imported a CSV-Dataset to R But the Values Becomes Factors
Add a New Column to a Dataframe Using Matching Values of Another Dataframe
Is There a Logical Way to Think About List Indexing
Changing Million/Billion Abbreviations into Actual Numbers? Ie. 5.12M -> 5,120,000
Writing Robust R Code: Namespaces, Masking and Using the '::' Operator
Case-Insensitive Search of a List in R
How to Retrieve Outlook Inbox Emails Using R Rdcomclient
Stumped on How to Scrape the Data from This Site (Using R)
Add a Box for the Na Values to the Ggplot Legend for a Continuous Map
Rolling Sum by Another Variable in R
How to Filter Rows Based on Difference in Dates Between Rows in R
Rle-Like Function That Catches "Run" of Adjacent Integers
Counting the Frequency of an Element in a Data Frame
How to Replace Na with Most Recent Non-Na by Group
For the Same Code, Labels (Q1, Median) Appear on One Computer But Don't Appear on Another Computer