I've been trying to understand this for days. I'm asking a user to input X_axis data, Y_axis data, and a graph they want to display.
My current version displays only one graph based on the user's choice. I want it to be able to display multiple graphs simultaneously(ex: pie and line charts). I added 'multi=True' to have an option to choose multiple graphs(commented out right now as it gives the error: "Callback error updating my_graph.figure", UnboundLocalError: local variable 'fig' referenced before assignment). I know I need to create multiple Outputs from the callback functions but I can't figure out how. Can someone please help me out? Thanks!!
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
import pandas as pd
from dash.exceptions import PreventUpdate
df_data = pd.read_json("test.json")
app = dash.Dash(__name__)
app.layout = html.Div([
html.P("Choose data1:"),
dcc.Dropdown(
id='x_axis',
options=[{'value': x, 'label': x}
for x in df_data.keys()],
clearable=False,
style={'width':'40%'}
),
html.P("Choose data2:"),
dcc.Dropdown(
id='y_axis',
options=[{'value': x, 'label': x}
for x in df_data.keys()],
clearable=False,
style={'width':'40%'}
),
html.P("Choose a graph to display:"),
dcc.Dropdown(
id='graph',
options=[{'value': 'pie', 'label': 'Pie chart'},
{'value': 'line', 'label': 'Line chart'},
{'value': 'bar', 'label': 'Bar chart'},
{'value': 'scatter', 'label': 'Scatter chart'},
{'value': '2dhistogram', 'label': '2dhistogram chart'}],
clearable=False,
style={'width':'40%'},
#multi=True
),
dcc.Graph(id='my_graph', figure={}),
])
@app.callback(
Output("my_graph", "figure"),
[Input("x_axis", "value"),
Input("y_axis", "value"),
Input("graph", "value")])
def generate_chart(x_axis, y_axis, graph):
if not x_axis:
raise PreventUpdate
if not y_axis:
raise PreventUpdate
if not graph:
raise PreventUpdate
dff = df_data
if graph=="pie":
fig = px.pie(dff, values=y_axis, names=x_axis, title="Pie Chart")
elif graph=="line":
fig = px.line(dff, x=x_axis, y=y_axis, title="Line Chart")
elif graph=="bar":
fig = px.bar(dff, x=x_axis, y=y_axis, title="Bar Chart")
elif graph=="scatter":
fig = px.scatter(dff, x=x_axis, y=y_axis, title="Scatter Chart")
elif graph=="2dhistogram":
fig = px.density_heatmap(dff, x=x_axis, y=y_axis, nbinsx=20, nbinsy=20,
color_continuous_scale="Viridis", title="2D Histogram Chart")
else:
fig = px.pie(dff, values=y_axis, names=x_axis, title="Pie Chart")
return fig
app.run_server(debug=True)
Sample json file:
{
"Names": {
"0": "Alice",
"1": "Robert",
"2": "Garry",
"3": "Nate",
"4": "Karen",
"5": "Nick"
},
"Address": {
"0": "21 Main St",
"1": "19 Third St",
"2": "4 Church St",
"3": "5 High St",
"4": "9 Elm St",
"5": "06 Washingtom St"
},
"AreaCode": {
"0": "777",
"1": "421",
"2": "768",
"3": "345",
"4": "888",
"5": "123"
}}
CodePudding user response:
You have added multi=True
to get multiple inputs from the user, it still doesn't change the fact that the function will only return a figure object with a single plot.
I feel subplots is the solution.
You can create subplots like this
fig = make_subplots(rows=1, cols=len(graph))
counter = 1
Then use individual if conditions and add traces, by using a counter.
if "scatter" in graph:
fig.add_trace(
go.Scatter(x=dff['x_axis'], y=dff['y_axis']),
row=1, col=counter )
counter = 1
if "pie" in graph:
fig.add_trace(
go.Pie(labels=dff['x_axis'], values=dff['y_axis']),
row=1, col=counter )
counter = 1
...
...
...
CodePudding user response:
Well you are close, the code you provide works great and sets most everything needed up correctly! (few minor things needed correction which I show first below)
Your working code as is — single plots @ a time
import dash
from dash import dcc
from dash import html
import pandas as pd
import plotly.express as px
from dash.dependencies import Input
from dash.dependencies import Output
from dash.exceptions import PreventUpdate
df_data = pd.read_json("test.json")
app = dash.Dash(__name__)
app.layout = html.Div(
[
html.P("Choose data1:"),
dcc.Dropdown(
id="x_axis",
options=[{"value": x, "label": x} for x in df_data.keys()],
clearable=False,
style={"width": "40%"},
),
html.P("Choose data2:"),
dcc.Dropdown(
id="y_axis",
options=[{"value": x, "label": x} for x in df_data.keys()],
clearable=False,
style={"width": "40%"},
),
html.P("Choose a graph to display:"),
dcc.Dropdown(
id="graph",
options=[
{"value": "pie", "label": "Pie chart"},
{"value": "line", "label": "Line chart"},
{"value": "bar", "label": "Bar chart"},
{"value": "scatter", "label": "Scatter chart"},
{"value": "2dhistogram", "label": "2dhistogram chart"},
],
clearable=False,
style={"width": "40%"},
# multi=True
),
dcc.Graph(id="my_graph", figure={}),
]
)
@app.callback(
Output("my_graph", "figure"),
[
Input("x_axis", "value"),
Input("y_axis", "value"),
Input("graph", "value"),
],
)
def generate_chart(x_axis, y_axis, graph):
if not x_axis:
raise PreventUpdate
if not y_axis:
raise PreventUpdate
if not graph:
raise PreventUpdate
dff = df_data
if graph == "pie":
fig = px.pie(dff, values=y_axis, names=x_axis, title="Pie Chart")
elif graph == "line":
fig = px.line(dff, x=x_axis, y=y_axis, title="Line Chart")
elif graph == "bar":
fig = px.bar(dff, x=x_axis, y=y_axis, title="Bar Chart")
elif graph == "scatter":
fig = px.scatter(dff, x=x_axis, y=y_axis, title="Scatter Chart")
elif graph == "2dhistogram":
fig = px.density_heatmap(
dff,
x=x_axis,
y=y_axis,
nbinsx=20,
nbinsy=20,
color_continuous_scale="Viridis",
title="2D Histogram Chart",
)
else:
fig = px.pie(dff, values=y_axis, names=x_axis, title="Pie Chart")
return fig
app.run_server(debug=True, dev_tools_hot_reload=True)
Dropdown option change:
Dropdown option change & data x, y options change:
All I had to change were a couple indentation mistakes, the importing of the dash component library html
(lower case not upper), and yeah otherwise it just needed a little fixing as far as proper indentation (which may have just been a copy paste onto SO issue) — great job! Of course it doesn't really make any sense to display pie charts for area codes because they are nominal values, not truly quantitative measurements, but as far as proof of principle goes for making a decently complex interactive Dash web app you set it up all correctly, it seems to me.
But now for the multiple graphs at once...a few more changes will be needed.
Modification of code to implement display of up to 5 possible total graphs
Now this isn't 100% ideal (e.g., it'd be better if no blank graphs were displayed) but hopefully it helps get you on the right track and progressing with the customization of your project