Why doesn't the config for plotly doesn't work in dash? - plotly-dash

When I use fig.show, the mode bar buttons do show up but then when I try to do the same with dash, it doesn't work. I tried so many options and it just doesn't seem to work. The documentation is incomplete as well. These are the inbuilt buttons by the way, not custom.
I saw this in a tutorial:
fig.show(config={'modeBarButtonsToAdd':['drawline',
'drawopenpath',
'drawclosedpath',
'drawcircle',
'drawrect',
'eraseshape'
]})
Then, I tried to put the same in dash instead of fig.show, but it wouldn't work!
app.layout = html.Div([
dcc.Graph(
id='mainPlot',
figure=fig,
config={'modeBarButtonsToAdd':['drawline',
'drawopenpath',
'drawclosedpath',
'drawcircle',
'drawrect',
'eraseshape'
]}
)
])
Here is the MWE: I want to pass the config with modeBarButtonsToAdd through config via the app.layout and not fig.show. It works with fig.show as the buttons are inbuilt but doesn't work with the app.layout config pass as I get errors that say the buttons need to be defined with name, click functionality, etc. and are being treated as custom buttons as opposed to enabling inbuilt buttons.
import dash
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash()
app.layout = html.Div(children=[
dcc.Graph(
id='example-graph',
figure={
'data': [
{'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
{'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'},
],
'layout': {
'title': 'Dash Data Visualization'
}
}
)
])
if __name__ == '__main__':
app.run_server(debug=True)

Installing the latest version of dash should fix your issue.

Related

In Dash Plotly how do I upload CSV with dcc.Upload to store using dcc.Store and then display table?

haven’t found a good answer to this issue I am having. Please help!
My goal is to upload CSV via dcc.Upload, store it in dcc.Store, and then display table.
I am taking the code from dcc.Upload tutorial here and inserting a dcc.Store component. The goal is to have two callbacks. The first one would have upload data as input and store as output. The second callback would take store as input and output to a html.Div.
See the code below that is not working. Please help!
import base64
import datetime
import io
import dash
from dash.dependencies import Input, Output, State
from dash import dcc, html, dash_table
import pandas as pd
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
dcc.Upload(
id='upload-data',
children=html.Div([
'Drag and Drop or ',
html.A('Select Files')
]),
style={
'width': '100%',
'height': '60px',
'lineHeight': '60px',
'borderWidth': '1px',
'borderStyle': 'dashed',
'borderRadius': '5px',
'textAlign': 'center',
'margin': '10px'
},
# Allow multiple files to be uploaded
multiple=True
),
dcc.Store(id='store'),
html.Div(id='output-data-upload'),
])
#Goal here is to store the csv from upload into dcc.Store
#app.callback(Output('store', 'data'),
Input('upload-data', 'contents'),
State('upload-data', 'filename'),
State('upload-data', 'last_modified'))
def update_output(contents, list_of_names, list_of_dates):
content_type, content_string = contents.split(',')
decoded = base64.b64decode(content_string)
df = pd.read_csv(io.StringIO(decoded.decode('utf-8')))
return df.to_json(date_format='iso', orient='split') #I have to convert to json format to store in dcc.Store
#Goal here is to then extract dataframe from dcc.store by reading json and output a table
#app.callback(
Output('output-data-upload', 'children'),
Input('store', 'data')
)
def output_from_store(stored_data):
df = pd.read_json(stored_data, orient='split')
return html.Div([
dash_table.DataTable(
df.to_dict('records'),
[{'name': i, 'id': i} for i in df.columns]
),
html.Hr(), # horizontal line
])
if __name__ == '__main__':
app.run_server()
I followed the dcc.Upload tutorial and tried inserting the dcc.Store into it.
Your code was missing a few adjustments to run as expected.
First, as sometimes the values can be inside a list you need to get only the value inside the list, in this case, it's in index 0.
content_string = contents[0].split(',')
Second, to avoid some warnings I have added a condition that will not run the element if the upload values are empty.
if [contents, list_of_names, list_of_dates] == [None, None, None]:
return dash.no_update
You can also use it in your callback declaration:
prevent_initial_call=True
And as a tip, I would suggest you use debug=True n your development environment, so you can have better feedback about what's going wrong, making it easier to debug your code to fix the error
app.run_server(debug=True)
Full code based on your example:
import base64
import datetime
import io
import dash
from dash.dependencies import Input, Output, State
from dash import dcc, html, dash_table
import pandas as pd
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
dcc.Upload(
id='upload-data',
children=html.Div([
'Drag and Drop or ',
html.A('Select Files')
]),
style={
'width': '100%',
'height': '60px',
'lineHeight': '60px',
'borderWidth': '1px',
'borderStyle': 'dashed',
'borderRadius': '5px',
'textAlign': 'center',
'margin': '10px'
},
# Allow multiple files to be uploaded
multiple=True
),
dcc.Store(id='store'),
html.Div(id='output-data-upload'),
])
import dash
#Goal here is to store the csv from upload into dcc.Store
#app.callback(Output('store', 'data'),
Input('upload-data', 'contents'),
State('upload-data', 'filename'),
State('upload-data', 'last_modified'),
prevent_initial_call=True)
def update_output(contents, list_of_names, list_of_dates):
if [contents, list_of_names, list_of_dates] == [None, None, None]:
return dash.no_update
content_type, content_string = contents[0].split(',')
decoded = base64.b64decode(content_string)
df = pd.read_csv(io.StringIO(decoded.decode('utf-8')))
return df.to_json(date_format='iso', orient='split') #I have to convert to json format to store in dcc.Store
#Goal here is to then extract dataframe from dcc.store by reading json and output a table
#app.callback(
Output('output-data-upload', 'children'),
Input('store', 'data'),
prevent_initial_call=True
)
def output_from_store(stored_data):
df = pd.read_json(stored_data, orient='split')
return html.Div([
dash_table.DataTable(
df.to_dict('records'),
[{'name': i, 'id': i} for i in df.columns]
),
html.Hr(), # horizontal line
])
if __name__ == '__main__':
app.run_server(debug=True)
I hope it can be helpful for you.
Regards,
Leonardo

Dash drop down menu graph

I'm attempting to implement a live-update graph that is only displayed if the user selected it from the drop down menu. I've made a callback with input from "intervals" and from the dropdown menu "oxygen". I thought if "value" is "0" then no chart has been selected and I could return "null"(???), and if 'value' = '1', then the graph would update. Is this possible? or is my approach incorrect?
dcc.Graph(id='live-graph2', animate=False),
dcc.Interval(
id='graph-update',
interval=10000,
n_intervals=0
)),
dbc.Col(html.Div([
dcc.Dropdown(
id='oxygen',
options=[{'label': s, 'value': s}
for s in main_graph.keys()],
value=[],
multi=True),
#app.callback(
dash.dependencies.Output('live-graph2', 'figure'),
[dash.dependencies.Input('oxygen', 'value'),
dash.dependencies.Input('graph-update', 'n_intervals')],
)
def update_graph_scatter_2(n,value):
if value == 0:
....
else:
data = {}
I would approach the problem differently.
If you don't want to display the graph at all when no dropdown option is selected then the Graph component doesn't need to be in the layout by default.
Instead you could create a container component that your Graph component is appended to dynamically based on the option values of your Dropdown component.
Below is a simplified example using sample data, adjust per your requirements:
import dash
import dash_html_components as html
import dash_core_components as dcc
import plotly.express as px
import dash_bootstrap_components as dbc
df = px.data.iris()
app = dash.Dash(__name__)
app.layout = html.Div(
[
dcc.Interval(id="graph-update", interval=10000, n_intervals=0),
html.Div(id="graph-container"),
dbc.Col(
html.Div(
[
dcc.Dropdown(
id="oxygen",
options=[{"label": s, "value": s} for s in [1, 2, 3]],
value=[],
multi=True,
)
]
)
),
]
)
#app.callback(
dash.dependencies.Output("graph-container", "children"),
[
dash.dependencies.Input("oxygen", "value"),
dash.dependencies.Input("graph-update", "n_intervals"),
],
)
def update_graph_scatter_2(value, n):
if value:
# Change the line below to dynamically create the figure based on value
fig = px.scatter(df, x="sepal_width", y="sepal_length")
return dcc.Graph(id="live-graph2", figure=fig)
return html.Div()

Dash Plotly: Highlighting point on the graph

I was googl'ing around trying to find solution for the following question (no luck so far). I am using Plotly Dash callback in order to build a graph:
#app.callback(
Output("graph", "figure"),
[Input("some-input", "value")],
[State("some-state", "value")])
def build_graph(input_value, state_value):
// computing data for graph_figure
return graph_figure
Now, I want to have another callback which will highlight a specific point on the graph (or add/remove a point) based on some input condition. I struggle to figure out what to use for Output in this case? Because I cannot output graph.figure again (Dash does not allow output to the same component from different callbacks). And re-drawing entire graph seems to be inefficient.
I will appreciate any suggestions.
It is possible to change the layout of your graph without redrawing the whole graph by using this library: https://github.com/jimmybow/mydcc
There is an example how to use it here: https://github.com/jimmybow/mydcc#3-mydccrelayout-
I have prepared a small example that adds an annotation on button click. Don't forget to pip install mydcc beforehand. I had to add a cache - in form of an invisible div - to preserve the old annotations when adding a new one.
import dash
import dash_core_components as dcc
import dash_html_components as html
import mydcc
import random
import json
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
initial_layout = {'title': 'Dash Data Visualization'}
app.layout = html.Div(children=[
html.H1(children='Hello Dash'),
html.Div(children='''
Dash: A web application framework for Python.
'''),
dcc.Graph(
id='example-graph',
figure={
'data': [{'x': [1, 2, 3], 'y': [4, 1, 2], 'name': 'Test'}],
'layout': initial_layout
}
),
mydcc.Relayout(id="rrr", aim='example-graph'),
html.Button('Add random annotation', id='button'),
html.Div(id='user-cache', style={'display': 'none'},
children=json.dumps(initial_layout)),
])
#app.callback(
[dash.dependencies.Output('rrr', 'layout'),
dash.dependencies.Output('user-cache', 'children')],
[dash.dependencies.Input('button', 'n_clicks')],
[dash.dependencies.State('user-cache', 'children')])
def update_graph_annotations(n_clicks, layout):
if n_clicks is not None:
layout = json.loads(layout)
if not 'annotations' in layout:
layout['annotations'] = []
layout['annotations'].append(dict(
x=random.uniform(0, 1) * 2 + 1,
y=random.uniform(0, 1) * 2 + 1,
xref="x",
yref="y",
text="Annotation" + str(n_clicks),
showarrow=True
))
return layout, json.dumps(layout)
return dash.no_update, dash.no_update
if __name__ == '__main__':
app.run_server(debug=True)

How Dynamically populate the DropDown from the data uploaded through Plotly Dash dcc upload option

I have uploaded the data using Plotly Dash dcc upload option. I am not able to dynamically populated the unique columns of uploaded dataframe as part of DropDown
I am able to successfuly upload the file using dcc upload option; i am able to see the output in dash table. but using same input, i am not able to see the output from dropdown.
the dataframe, i have uploaded with the columns of (country, continent, population)
i should able to get the unique columns dropdown
this is my code:
import base64
import datetime
import io
import plotly.graph_objs as go
import dash
import dash_html_components as html
import dash_core_components as dcc
import dash_table
import dash_table_experiments as dte
from dash.dependencies import Input, Output, State
import pandas as pd
app = dash.Dash()
app.layout = html.Div([
html.Div([
dcc.Upload(
id='upload-data',
children=html.Div([
'Drag and Drop or ',
html.A('Select Files')
]),
multiple=True
),
html.Div(id='output-data-upload'),
])
dcc.Tab(label='Discrete Info', children=[
html.Div([
dcc.Dropdown(
id='select_column',
),
]),
html.Div([
dcc.Graph(
id='bar1',
hoverData={'points': [{'customdata': ''}]},
)
], style={'width': '100%', 'display': 'inline-block', 'padding': '0 10'}),
]),
])
def parse_contents(contents, filename, date):
content_type, content_string = contents.split(',')
decoded = base64.b64decode(content_string)
try:
if 'csv' in filename:
# Assume that the user uploaded a CSV file
df = pd.read_csv(
io.StringIO(decoded.decode('utf-8')))
elif 'xls' in filename:
# Assume that the user uploaded an excel file
df = pd.read_excel(io.BytesIO(decoded))
except Exception as e:
print(e)
return html.Div([
'There was an error processing this file.'
])
return html.Div([
html.H5(filename),
html.H6(datetime.datetime.fromtimestamp(date)),
dash_table.DataTable(
data=df.to_dict('records'),
columns=[{'name': i, 'id': i} for i in df.columns]
),
html.Hr(), # horizontal line
# For debugging, display the raw contents provided by the web browser
html.Div('Raw Content'),
html.Pre(contents[0:200] + '...', style={
'whiteSpace': 'pre-wrap',
'wordBreak': 'break-all'
})
])
#app.callback(Output('output-data-upload', 'children'),
[Input('upload-data', 'contents')],
[State('upload-data', 'filename'),
State('upload-data', 'last_modified')])
def update_output (list_of_contents, list_of_names, list_of_dates):
if list_of_contents is not None:
children = [
parse_contents(c, n, d) for c, n, d in
zip(list_of_contents, list_of_names, list_of_dates)]
return children
#app.callback(Output('select_column', 'options'
[Input('output-data-upload', 'children'')]
def dd_update (options)
return(options)
This is a little tricky without specific code to reference, but the basic idea is to set up a callback that will update the options prop of your dcc.Dropdown. You can have that callback triggered (Input) by the upload, make it read in the dataframe, obtain the columns, and then output the properly formatted list of dictionaries.
Please use a following simple example that solves your problem
import base64
import io
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
from dash.dependencies import Input, Output
app = dash.Dash()
app.layout = html.Div([
dcc.Upload(
id='upload-data',
children=html.Div([
'Drag and Drop or ',
html.A('Select Files')
]),
style={
'width': '100%',
'height': '60px',
'lineHeight': '60px',
'borderWidth': '1px',
'borderStyle': 'dashed',
'borderRadius': '5px',
'textAlign': 'center',
'margin': '10px'
},
),
dcc.Dropdown(
id='dropdown',
options=[],
value=None),
])
def parse_data(contents, filename):
content_type, content_string = contents.split(',')
decoded = base64.b64decode(content_string)
try:
if 'csv' in filename:
# Assume that the user uploaded a CSV or TXT file
df = pd.read_csv(
io.StringIO(decoded.decode('utf-8')))
elif 'xls' in filename:
# Assume that the user uploaded an excel file
df = pd.read_excel(io.BytesIO(decoded))
elif 'txt' or 'tsv' in filename:
# Assume that the user upl, delimiter = r'\s+'oaded an excel file
df = pd.read_csv(
io.StringIO(decoded.decode('utf-8')), delimiter = r'\s+')
except Exception as e:
print(e)
return html.Div([
'There was an error processing this file.'
])
return df
#app.callback(
Output('dropdown', 'options'),
[Input('upload-data', 'contents'),
Input('upload-data', 'filename')])
def update_options(contents, filename):
if contents:
df = parse_data(contents, filename)
df = df.set_index(df.columns[0])
lst = [{'label': i, 'value': i} for i in df.columns]
return lst
else:
return []
if __name__ == '__main__':
app.run_server(debug=True)
Here is my working code, very basic, that you can use as a reference
html.Div(children=[
html.Div(children=[
dcc.Dropdown(id='dropdown_students_cols', options=[{'label': i, 'value': i} for i in list(filter(lambda x: x!='userid', list(df)))], value='component'),
]),
html.Div(children=[
dcc.Dropdown(id='dropdown_row_names'),
]),
])
#app.callback(
Output(component_id='dropdown_row_names', component_property='options'),
[Input(component_id='dropdown_students_cols', component_property='value')],)
def get_row_names(column_name='component'):
row_names = df[column_name].unique().tolist()
lst = [{'label': i, 'value': i} for i in row_names]
print(lst)
return lst
The print statement in the callback returns a list of dictionaries that I can use for options:
[{'label': 'mod_glossary', 'value': 'mod_glossary'}, {'label': 'mod_quiz', 'value': 'mod_quiz'},...]
Don't pay attention to lambda function in the original dropdown id='dropdown_students_cols', I just needed to exclude this column from selection.
When you make selection in the original dropdown id='dropdown_students_cols', it invokes the callback function with the selection as an argument.
This selection gives me a required column in the df and then I build a list of unique rows in this column.
Then I iterate over this list to build a list of dictionaries with label:value
The print statement confirms that the list is built successfully
Then I return that list as options for the child dropdown id='dropdown_row_names'
So you can use another trigger as Input, in your case Upload, pass values into the callback, process the data as you need and then return a list if dictionaries as options for the dropdown.
I suggest you trim your code to a vary basic working example and then slowly build up from it.

How can I create tabs and subtabs in Dash?

I'm trying to create two tabs with two subtabs each in Dash but I'm having problems.
Here is my code:
import dash
import dash_html_components as html
import dash_core_components as dcc
app = dash.Dash()
# Create a Dash layout
app.layout = html.Div([
html.Div(
html.H1('My Dashboard')
),
dcc.Tabs(id="tabs", value='Tab1', children=[
dcc.Tab(label='Tab 1', id='tab1', value='Tab1', children =[
dcc.Tabs(id="subtabs1", value='Subtab1', children=[
dcc.Tab(label='Sutab 1', id='subtab1', value='Subtab1'),
dcc.Tab(label='Sutab 2', id='subtab2', value='Subtab2'),
]),
]),
dcc.Tab(label='Tab 2', id='tab2', value= 'Tab2', children=[
dcc.Tabs(id="subtabs2", value='Subtab4', children=[
dcc.Tab(label='Sutab 4', id='subtab4', value='Subtab4'),
dcc.Tab(label='Sutab 5', id='subtab5', value='Subtab5'),
]),
])
])
])
if __name__ == '__main__':
app.run_server(debug=True)
When I run this application the subtabs in the first tab work as expected. However, I can't navigate to any subtabs in the second tab. Why is this happening? Any help would be appreciated. Thank you.
As it stands, with Dash you would need to assign two separate callbacks and then check the values, and return the respective output when trying to do this.
It doesn't change the layout on the page whatsoever. That way you can check the values of the tabs and assign the desired callbacks (in this case traversing between tabs/subtabs).
The following code should achieve what you are looking to do :).
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
app = dash.Dash()
# Create a Dash layout
app.layout = html.Div([
html.Div(
html.H1('My Dashboard')
),
dcc.Tabs(id='tabs', value='Tab1', children=[
dcc.Tab(label='Tab 1', id='tab1', value='Tab1', children =[
]),
dcc.Tab(label='Tab 2', id='tab2', value='Tab2', children=[
])
])
])
#app.callback(Output('tab1', 'children'),
[Input('tabs', 'value')])
def update_tabs(value):
if value == 'Tab1':
return dcc.Tabs(id="subtabs1", value='Subtab1', children=[
dcc.Tab(label='Subtab 1', id='subtab1', value='Subtab1'),
dcc.Tab(label='Subtab 2', id='subtab2', value='Subtab2'),
]),
#app.callback(Output('tab2', 'children'),
[Input('tabs', 'value')])
def update_tabs(value):
if value == 'Tab2':
return dcc.Tabs(id="subtabs2", value='Subtab4', children=[
dcc.Tab(label='Subtab 4', id='subtab4', value='Subtab4'),
dcc.Tab(label='Subtab 5', id='subtab5', value='Subtab5')
]),
if __name__ == '__main__':
app.run_server(debug=True)