Plotly: Updating Data with Dropdown Selection

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' the color 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:

Sample Image

Sample Image

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:

Sample Image

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
]
}
]
)

Sample Image



Related Topics



Leave a reply



Submit