Passing Figure object to Graph in plotly dash - python

When I try to pass a Figure object to the dcc.Graph() in my layout, I get en error that says:
dash.exceptions.InvalidCallbackReturnValue: The callback ..graph.figure.. is a multi-output.
Expected the output type to be a list or tuple but got:
Figure({# the content of the figure})
my code is like:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.express as px
app.layout = html.Div([
dcc.Graph(
id='graph'
)
])
#app.callback(
[
Output('graph', 'figure'),
],
[
Input('my-input', 'value')
]
)
def gen_graph(value):
dff = # my filtered df
fig = px.line(dff, x='x_var', y='y_var')
return fig
Feels like I'm missing something in how the Figure should be passed to the dcc.Graph(). Any ideas?

You structured your Output as a list, that makes it a multi-output callback. Just change it like this:
#app.callback(
Output('graph', 'figure'),
[
Input('my-input', 'value')
]
)
def gen_graph(value):
...
Alternatively, you could wrap your output in brackets to make it a list (return [fig]). Either way should work fine.

Related

Display "5 items chosen" instead of "Red", "Blue", "Yellow" .... in a dropdown Dash Python?

I want to create a table in Dash where it is possible to choose multiple values in a specific column. My goal is to do it with a regular table and then add dropdowns for filtering.
However, when a dropdown is added and the size of the choices is bigger than the dropdown it adds "rows". Is it possible to let the choices that been made to be translated into number of choices made instead?
I wonder is it is possible to do something similiar to the Basic example in
https://mdbootstrap.com/docs/standard/extended/multiselect/
I have tried something like this
from dash import Dash, dcc, html, Input, Output
from plotly.express import data
import pandas as pd
df = data.medals_long()
app = Dash(__name__)
app.layout = html.Div([
dcc.Dropdown(df.columns, id='pandas-dropdown-1', multi=True),
html.Div(id='pandas-output-container-1')
])
#app.callback(
Output('pandas-output-container-1', 'children'),
Output('pandas-dropdown-1', 'search_value'),
Input('pandas-dropdown-1', 'value')
)
def update_output(value):
return f'You have selected {value}', f'{len(value)} values have been chosen'
if __name__ == '__main__':
app.run_server(debug=True)

How does only shows a white referencial on dash application?

I'm trying to deploy some app in dash but, although the dropdowns appears correcty, the plots are not getting called.
I already tried a lot of different things, but it always goes to a white referencial...
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import numpy as np
import pandas as pd
import plotly.graph_objs as go
import plotly.express as px
import plotly.io as pio
from plotly.colors import n_colors
from plotly.subplots import make_subplots
df = pd.read_csv('movies.csv', encoding = "ISO-8859-1")
df.columns = df.columns.str.capitalize()
###################### APP ############################
app = dash.Dash(__name__)
app.layout = html.Div([
html.Div([
dcc.Graph(id = 'our_graph')
],className = 'nine columns'),
html.Div([
html.Br(),
html.Label(['Choose Country / Genre / Company:'],style={'font-weight': 'bold', "text-align": "center"}),
dcc.Dropdown(id = 'country_drop',
options = [{'label':country, 'value':country} for country in df['Country'].unique()],
value = 'USA',
multi = False,
disabled=False,
clearable=True,
searchable=True,
placeholder='Choose Country...',
className='form-dropdown',
style={'width':"90%"},
persistence='string',
persistence_type='memory'),
dcc.Dropdown(id= 'genre_drop',
options = [{'label': genre, 'value' : genre} for genre in df['Genre'].unique()],
value = 'Drama',
multi = False,
disabled=False,
clearable=True,
searchable=True,
placeholder='Choose Genre..',
className='form-dropdown',
style={'width':"90%"},
persistence='string',
persistence_type='memory'),
dcc.Dropdown(id = 'company_drop',
options = [{'label':company, 'value':company} for company in df['Company'].unique()],
value = 'Paramount Pictures',
multi = False,
disabled=False,
clearable=True,
searchable=True,
placeholder='Choose Company..',
className='form-dropdown',
style={'width':"90%"},
persistence='string',
persistence_type='memory'),
],className='three columns')
])
####################Callbacks#######################
#app.callback(
dash.dependencies.Output('our_graph', 'figure'),
[dash.dependencies.Input("company_drop", "value"),
dash.dependencies.Input("country_drop", "value"),
dash.dependencies.Input("genre_drop", "value")
]
)
def plots(country, genre,company):
new_df = df.loc[(df['Country'] == country) & (df['Genre'] == genre) & (df['Company'] == company)]
revenue_df = new_df.groupby(by = ['Year'])['Gross','Budget'].sum()
fig = px.line(revenue_df, x=revenue_df.index, y=revenue_df.columns, title = 'Which Country has the highest revenue by category?',
labels=dict(x="Year", y= 'Amount of $ in billions'))
return fig
#Run App
if __name__ == '__main__':
app.run_server(debug=False)`
If someone can help me out...i'm a begginer but although it might be simples i'm already looking for the solution into 2 days...
The reason why the data is not displayed is that the order of the drop-down and the order in which it is received by the graph function are different, so the acquired data does not exist, resulting in an error.
#app.callback(
dash.dependencies.Output('our_graph', 'figure'),
dash.dependencies.Input("company_drop", "value"),
dash.dependencies.Input("country_drop", "value"),
dash.dependencies.Input("genre_drop", "value"))
def plots(company, country, genre):
new_df = df[(df['Country'] == country) & (df['Genre'] == genre) & (df['Company'] == company)]
revenue_df = new_df.groupby(by = ['Year'])[['Gross','Budget']].sum()
revenue_df.reset_index(inplace=True)
fig = px.line(revenue_df,
x='Year',
y=['Gross', 'Budget'],
title='Which Country has the highest revenue by category?',
)
fig.update_xaxes(title_text='YEAR')
fig.update_yaxes(title_text='Amount of $ in billions')
return fig

How to keep the dropdown value after button click in Dash?

I have an application in which I want to update the dropdown values when the user inputs some text into the dropdown. So far, I managed to get the text from an Input text that the user inputs, but not from the dropdown. The dropdown keeps reseting after clicking in any part of the screen or on the button.
import dash
import dash_html_components as html
import dash_core_components as dcc
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
app = dash.Dash(
external_stylesheets=[
dbc.themes.BOOTSTRAP])
app.layout = html.Div(
[
dcc.Input(id='helper'),
dcc.Dropdown(id="my-dynamic-dropdown", options=[]),
html.Br(),
html.Br(),
html.Br(),
html.Br(),
html.Br(),
html.Br(),
html.Br(),
dbc.Button('Add to Dropdown', id='dropdown_button', n_clicks=0, color="info",
className="mr-1")
],
)
options = []
#app.callback(
Output("my-dynamic-dropdown", "options"),
Input('dropdown_button', 'n_clicks'),
[State('my-dynamic-dropdown', 'options'),
# State('helper', 'value')
State('my-dynamic-dropdown', 'search_value')
]
)
def update_options(n_clicks,existing_options,helper_value):
if helper_value is None:
print("none value")
return options
else:
print("helper value ",helper_value)
options.append({'label': helper_value, 'value': helper_value})
return options
if __name__ == "__main__":
app.run_server(debug=True)
The dropdown allows a user to search through the existing options, but what you're trying to do is not what the dropdown was designed for. You would either need to write a custom component, use some combination of dropdown and input components, or find a different solution to whatever problem you're trying to solve.

Sharing dataframe between callbacks

I am trying to share dataframe between callbacks but i keep getting this error. I want to use dcc.store to the data. Then I will have one callback filtering the data while the other callback plotting the graph.
"Callback error updating main_data.data"
My code run fine if I include everything in one callback, but it won't work once I split it.
import dash
import pathlib
import numpy as np
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
from flask import Flask
df =pd.read_csv("salesfunnela.csv")
mgr_options = df["Manager"].unique()
mgr_options = np.insert(mgr_options, 0 , 'All Managers')
server = Flask(__name__)
app = dash.Dash(server=server)
app.layout = html.Div([
dcc.Store(id='main_data'),
html.Div(
[
html.P("Div1", className="control_label"),
dcc.Dropdown(
id="Manager",
options=[{
'label': i,
'value': i
} for i in mgr_options],
value='All Managers'),
],
style={'width': '25%',
'display': 'inline-block'}),
dcc.Graph(id='funnel-graph'),
html.Div(
[
html.P("Div2", className="abc"),
],
style={'width': '25%',
'display': 'inline-block'}),
])
#app.callback(
dash.dependencies.Output('main_data', 'data'),
[dash.dependencies.Input('Manager', 'value')])
def update_data(Manager):
if Manager == "All Managers":
df_plot = df.copy()
else:
df_plot = df[df['Manager'] == Manager]
return df_plot
#app.callback(
dash.dependencies.Output('funnel-graph', 'figure'),
[dash.dependencies.Input('main_data', 'data')])
def update_graph(main_data):
pv = pd.pivot_table(
df_plot,
index=['Name'],
columns=["Status"],
values=['Quantity'],
aggfunc=sum,
fill_value=0)
traces = [go.Bar(x=pv.index, y=pv[('Quantity', t[1])], name=t[1]) for t in pv]
return {
'data': traces,
'layout':
go.Layout(
title='Customer Order Status for {}'.format(Manager),
barmode='stack')
}
if __name__ == '__main__':
app.run_server(debug=True)
Some time has passed but I hope this might help.
What is basically discussed in previous answer is to change def update_graph(main_data) to def update_graph(df_plot), or alternatively, change df_plot in the function to main_data if you like. this will most likely not solve your problem though. Since the problem is that the function update_data cannot store the data in the first place. The idea to store the filtered data somewhere is probably a good idea, instead of sending it through chained callbacks.
In the section for sharing data between callbacks in the docs/getting started guide (https://dash.plotly.com/sharing-data-between-callbacks), it says that you have to store the data as either JSON or base64 encoded binary data. A Pandas DataFrame is not binary data in an ASCII string format (base64), if you want to encode a DataFrame in base64 you should probably convert it to a string first and then encode that into base64 (e.g. https://docs.python.org/3/library/base64.html). So in your example code, to use JSON, you would have to change the return statement to
return df_plot.to_json(date_format='iso', orient='split')
in the update_data function.
Then in update_graph you would now need to convert the JSON back into Pandas DataFrame. The first few lines of that function would then look like this instead
def update_graph(main_data):
df_plot = pd.read_json(main_data, orient='split')
pv = pd.pivot_table(
df_plot,
index=['Name'],
columns=["Status"],
values=['Quantity'],
aggfunc=sum,
fill_value=0)
I hope this helps, and that it's not too late.
You probably want to read more about Chained callbacks...
Docs - https://dash.plotly.com/basic-callbacks
Scroll down to the section: Dash App With Chained Callbacks
In the docs-example, you'll notice that the data is not really passed between two callbacks.
Rather they work like event listeners, listening to updates in the DOM.
In your case, there's nothing called "main-data" in the layout, which the second callback is trying to listen to.
Try to play around with 'funnel-graph' or 'Div2' or setup another element whose updates can be tracked by these callbacks.

Plotly Dash: Display a variable inside Markdown text

I'm new to the Dash and Plotly ecosystem and began building a web-based dashboard a few days ago.
Here is a snippet of code:
import dash
import dash_html_components as html
import dash_core_components as dcc
# initialize the application
app = dash.Dash()
# define the layout of the app
app.layout = html.Div([
# add a date range selector
dcc.DatePickerRange(
id = 'my-date-picker-range',
min_date_allowed = dt(2010,1,4),
max_date_allowed = dt(2020, 12, 31),
initial_visible_month = dt(2020, 5, 23)
),
html.Div(id = 'output-container-date-picker-range'),
# add some markdown text
dcc.Markdown(f'''
This report covers the time period spanning {start_date} to {end_date}.
'''),
])
#app.callback(
dash.dependencies.Output('output-container-date-picker-range', 'children'),
[dash.dependencies.Input('my-date-picker-range', 'start_date'),
dash.dependencies.Input('my-date-picker-range', 'end_date')])
app.run_server(debug = True)
I'm attempting to display the start_date and end_date variables inside the markdown text (using an f string). Unfortunately, I'm getting the following error message:
NameError: name 'start_date' is not defined
Is it possible to include variable output in Markdown text? Thanks!
You are using a decorator (#app.callback) but you did not attach it to a function to be executed. You need to attach the decorator to the function that is responsible for updating the right div.
I think your best bet is to stick to the documentation.
This gives a similar result as what you want:
import dash
import dash_html_components as html
import dash_core_components as dcc
from datetime import datetime as dt
# initialize the application
app = dash.Dash()
# define the layout of the app
app.layout = html.Div([
# add a date range selector
dcc.DatePickerRange(
id = 'my-date-picker-range',
min_date_allowed = dt(2010,1,4),
max_date_allowed = dt(2020, 12, 31),
initial_visible_month = dt(2020, 5, 23)
),
html.Div(id = 'output-container-date-picker-range'),
])
#app.callback(
dash.dependencies.Output('output-container-date-picker-range', 'children'),
[dash.dependencies.Input('my-date-picker-range', 'start_date'),
dash.dependencies.Input('my-date-picker-range', 'end_date')])
def update_output_div(start_date, end_date):
return f"This report covers the time period spanning {start_date} to {end_date}"
app.run_server(debug = True)

Categories

Resources