Plotly Dash Graph With Multiple Dropdown Inputs Not Working - python-3.x

I’m trying to create a time-series Dash line graph that has multiple interactive dropdown user input variables. I would ideally like each of the dropdown inputs to allow for multiple selections.
While I’m able to create the drop down menus successfully, the chart isn’t updating like I’d like. When I allow the dropdowns to have multiple selections, I get an error that arrays are different lengths. And when I limit the dropdowns to one selection, I get an error that [‘Vendor_Name’] is not in index. So this may be two separate problems.
Graph that doesn’t work:
Snippet of Excel data imported into DF
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import pandas as pd
#import plotly.graph_objs as go
df = pd.read_csv("Data.csv", sep = "\t")
df['YearMonth'] = pd.to_datetime(df['YearMonth'], format = '%Y-%m')
cols = ['Product_1', 'Product_2', 'Product_3']
vendor = df['Vendor'].unique()
app = dash.Dash('Data')
app.layout = html.Div([
html.Div([
html.Div([
html.Label('Product'),
dcc.Dropdown(
id = 'product',
options = [{
'label' : i,
'value' : i
} for i in cols],
multi = True,
value = 'Product_1'
),
]),
html.Div([
html.Label('Vendor'),
dcc.Dropdown(
id = 'vendor',
options = [{
'label' : i,
'value' : i
} for i in vendor],
multi = True,
value = 'ABC')
,
]),
]),
dcc.Graph(id = 'feature-graphic')
])
#app.callback(Output('feature-graphic', 'figure'),
[Input('product', 'value'),
Input('vendor', 'value')])
def update_graph(input_vendor, input_column):
df_filtered = df[df['Vendor'] == input_vendor]
##also tried setting an index because of the error I was getting. Not sure if necessary
df_filtered = df_filtered.set_index(['Vendor'])
traces = []
df_by_col = df_filtered[[input_column, 'YearMonth']]
traces.append({
'x' :pd.Series(df_by_col['YearMonth']),
'y' : df_by_col[input_column],
'mode' : 'lines',
'type' : 'scatter',
'name' :'XYZ'}
)
fig = {
'data': traces,
'layout': {'title': 'Title of Chart'}
}
return fig
if __name__ == '__main__':
app.run_server(debug=False)
Thanks in advance for helping! Still new-ish to Python, but very excited about Dash’s capabilities. I’ve been able to create other graphs with single inputs, and have read through documentation.

Here is the approach I followed: (editing common example available in google with my approach):
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash(__name__)
all_options = {
'America': ['New York City', 'San Francisco', 'Cincinnati'],
'Canada': [u'Montréal', 'Toronto', 'Ottawa']
}
app.layout = html.Div([
dcc.Dropdown(
id='countries-dropdown',
options=[{'label': k, 'value': k} for k in all_options.keys()],
value='America', #default value to show
multi=True,
searchable=False
),
dcc.Dropdown(id='cities-dropdown', multi=True, searchable=False, placeholder="Select a city"),
html.Div(id='display-selected-values')
])
#app.callback(
dash.dependencies.Output('cities-dropdown', 'options'),
[dash.dependencies.Input('countries-dropdown', 'value')])
def set_cities_options(selected_country):
if type(selected_country) == 'str':
return [{'label': i, 'value': i} for i in all_options[selected_country]]
else:
return [{'label': i, 'value': i} for country in selected_country for i in all_options[country]]
if __name__ == '__main__':
app.run_server(debug=True)
Workaround here is: When there is single input present in parent dropdown, the value is in string format. But for multiple values, it comes in list format.
This code also work perfectly and gets updated automatically even when you click on cross option to remove any selected option.
Note: I have used 'placeholder' attribute instead of defining default value for it as it made no sense in this case. But you can also update the value dynamically in similar way.

1 input data
The data as it is in the csv is hard to loop.
And I would argue that it is the main reason your code does not work,
because you seem to understand the fundamental code structure.
Having put on my SQL glasses I think you should try to get it to sth like
Date, Vendor, ProductName, Value
2 callback input types change
multi is tricky because it changes switches between returning a str if only 1 item is selected and list if more than one is selected
3 callback return type
you code returns a dict but the callback declared figure as the return type
but here is the code with debugging traces of print() and sleep()
import pandas as pd
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objs as go
import time
df = pd.read_csv("Data.csv", sep="\t")
df['YearMonth'] = pd.to_datetime(df['YearMonth'], format='%Y-%m')
products = ['Product_1', 'Product_2', 'Product_3']
vendors = df['Vendor'].unique()
app = dash.Dash('Data')
app.layout = html.Div([
html.Div([
html.Div([
html.Label('Product'),
dcc.Dropdown(
id='product',
options=[{'label' : p, 'value' : p} for p in products],
multi=True,
value='Product_1'
),
]),
html.Div([
html.Label('Vendor'),
dcc.Dropdown(
id='vendor',
options=[{'label': v, 'value': v} for v in vendors],
multi=True,
value='ABC'
),
]),
]),
dcc.Graph(id='feature-graphic', figure=go.Figure())
])
#app.callback(
Output('feature-graphic', 'figure'),
[Input('product', 'value'),
Input('vendor', 'value')])
def update_graph(input_product, input_vendor):
# df_filtered[['Product_1', 'YearMonth']]
if type(input_product) == str:
input_product = [input_product]
if type(input_vendor) == str:
input_vendor= [input_vendor]
datasets = ['']
i = 1
for vendor in input_vendor:
df_filtered = df[df['Vendor'] == vendor]
for product in input_product:
datasets.append((df_filtered[['YearMonth', 'Vendor', product]]).copy())
datasets[i]['ProductName'] = product
datasets[i].rename(columns={product: 'Value'}, inplace=True)
i += 1
datasets.pop(0)
print(datasets)
traces = ['']
for dataset in datasets:
print(dataset)
time.sleep(1)
traces.append(
go.Scatter({
'x': dataset['YearMonth'],
'y': dataset['Value'],
'mode': 'lines',
'name': f"Vendor: {dataset['Vendor'].iloc[0]} Product: {dataset['ProductName'].iloc[0]}"
}))
traces.pop(0)
layout = {'title': 'Title of Chart'}
fig = {'data': traces, 'layout': go.Layout(layout)}
return go.Figure(fig)
if __name__ == '__main__':
app.run_server()
quick and dirty disclosure:
If you handle the 1. issue it will dramatically simplify everything.
So I'd try to isolate the pd.DataFrame() juggling out of the callback and into the upper I/O part.
1) don't use counters in for loops
2) my variable names aren't the best either
3) the following style is caveman's python and there must be a better way:
traces = ['']
traces.append(this_and_that)
traces.pop(0)
Generally:
using print(input_variable) and print(type(input_variable))
gets my wheels most of the time out of the mud.
after all
you should notice that each trace got its individual name which will show up in the legend. Clicking on the name in the legend will add or remove the trace without the need for#app.callback()

Related

scatter plot with Dash

I am coding a Dash page to plot some data for myself.
I want to have some dynamic features on some of my plots.
On my dash I have one dropdown, one pie chart and one scatter chart.
Here is my code:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objs as go
app = dash.Dash()
app.layout = html.Div([
html.Label(['Select the Type:']),
dcc.Dropdown(
id='type_filter',
options=[{'label': i, 'value': i} for i in df.Type.unique()],
value='All'
),
dcc.Graph(
id='allocation_chart'
),
dcc.Graph(
id='allocation_history_chart'
),
])
#app.callback(
Output('allocation_chart','figure'),
[
Input('type_filter', 'value')
]
)
def update_allocation(type_):
if type_ == 'All':
filtered = df
else:
mask = df['Type'] == type_
filtered = df.loc[mask]
data = [
go.Pie(
labels=filtered['Asset'],
values=filtered['Total']
)
]
return {
'data': data,
'layout': go.Layout(title='Asset Allocation by Type')
}
#app.callback(
Output('allocation_history_chart','figure'),
[
Input('type_filter','value')
]
)
def update_allocation_history(type_):
if type_ == 'All':
filtered = df_index
else:
mask = df['Type'] == type_
filtered = df_index.loc[mask]
data = go.Figure()
for ticker in filtered.index:
data.add_trace(
go.Scatter(
x = filtered.loc[ticker].index,
y = filtered.loc[ticker],
name = ticker
)
)
return {
'data': [data],
'layout': go.Layout(title='History of Asset')
}
if __name__ == '__main__':
app.run_server()
here is the Dash:
Here is the scatter chart ran in my Notebook:
Everything is working - Dash is running well, i can see the dropdown and the pie chart - except the scatter chart - the second one.
I absolutely don't understand why because when I run the scatter chart in my Notebook it's working pretty well.
If you have any idea, don't hesitate.

live data Dash plot with python

i have write a code in python to plot data in realtime. there is only one problem the code run i get no errors but there is also no plot in web page that i created.
can any one help me pleadse?
thanks
ma code
#app work
app = dash.Dash(__name__)
#html layout
app.layout = html.Div(
[
dcc.Graph(id = 'live-graph',
animate = True),
dcc.Interval(
id = 'graph-update',
interval = 1*1000,
n_intervals = 0 ),])
I recommend checking the data in raw[0] and raw1 and I do not think you need list(str()). You need also to bring the data from the database by increasing the value after LIMIT. Therefore, you should let the size of the data coming from the database increase dynamically and being controlled by n_intervals such that select * from ..... LIMIT + n and absolutely the value of n should start from 1, therefore set n_intervals to 1 accordingly. Another error in your program, you return figure which is not graph_object and dbc.Graph expects graph_object.
In the example below, I created a dataframe from CSV file, but you can understand the logic and apply it directly to your example:
from dash import Dash, html, dcc
import plotly.graph_objects as go
from dash.dependencies import Input, Output
import pandas as pd
import numpy
app = Dash(__name__)
df = pd.read_csv("data.csv")
app.layout = html.Div(
[
dcc.Graph(id='live-graph'),
dcc.Interval(
id='interval-component',
interval=1000, # in milliseconds
n_intervals = 1, # start
),
]
)
#app.callback(
Output('live-graph', 'figure'),
[Input('interval-component', 'n_intervals')]
)
def update_graph_scatter(n):
if n <= len(df):
row = df.iloc[:n,:]
w = numpy.asarray(row[df.columns[0]])
volt = numpy.asarray(row[df.columns[1]])
data = go.Scatter( x = w , y = volt , hoverinfo='text+name+y', name='Scatter', mode= 'lines+markers', )
layout = go.Layout(xaxis = dict(range=[min(w),max(w)]), yaxis = dict(range=[min(volt)-1,max(volt)+1]))
figure = go.Figure({'data' :data, 'layout' : layout})
return figure
app.run_server(debug=True, use_reloader=False)

How to connect multiple outputs to callbacks?

I'm trying to create a dashboard with a dropdown menu, with each menu item, when selected, loads 2 figures. I did the following but the output figures are the same no matter what I select in the dropdown. When I selected '3 Day Forward Return', I want it to show fig 11 and fig 12.
app = dash.Dash()
fig_names = ['1 Week Forward Return','3 Day Forward Return', 'Next-Day Forward Return ']
fig_dropdown = html.Div([
dcc.Dropdown(id='fig_dropdown',options=[{'label': x, 'value': x} for x in fig_names])])
fig_plot1 = dcc.Graph(id='macdwkly',figure = fig1, style={'display': 'inline-block'})
fig_plot2 = dcc.Graph(id='rsiwkly',figure = fig2, style={'display': 'inline-block'})
app.layout = html.Div([fig_dropdown, fig_plot1,fig_plot2])
#app.callback(Output('fig_plot', 'children'),
[Input('fig_dropdown', 'value')])
def update_output(fig_name):
return name_to_figure(fig_name)
def name_to_figure(fig_names):
figure = go.Figure()
if fig_name == '1 Week Forward Return':
fig1 = px.scatter(dfx, x=dfx.MACD, y=dfx.Wkly_FR, color=dfx['wkly_gt_0'])
fig2 = px.scatter(dfx, x=dfx.RSI, y=dfx.Wkly_FR, color=dfx['wkly_gt_0'])
elif fig_name == '3 Day Forward Return':
fig11 = px.scatter(dfx, x=dfx.MACD, y=dfx.Three_d_FR, color=dfx['tred_gt_0'])
fig12 = px.scatter(dfx, x=dfx.RSI, y=dfx.Three_d_FR, color=dfx['tred_gt_0'])
return dcc.Graph(figure=figure)
if __name__ == '__main__':
app.run_server()
You should correct Output('fig_plot', 'children') to :
[Output('macdwkly', 'figure'),Output('rsiwkly', 'figure')]
Accordingly, the return of name_to_figure should return two figures:
def name_to_figure(fig_names):
...
...
...
return (fig1 , fig2)
Also rename all variables inside name_to_figure to fig1 and
fig2 inside the if-else statements.
Also correct the variable name fig_name to fig_names inside the
conditions of if-else statements.
No need for go.Figure because you use px.scatter, instead.

Interconnection of two dropdowns in plotly-dash

I have been trying to connect two dropdowns to each other i.e. If I select a value from one DropDown the path and the contents of the other DropDown should change accordingly.
e.g I have a folder of dates (06-06-2020, 07-06-2020 and 08-06-2020) so if I select a date which is 07-06-2020 the DropDown below it should display the values which are inside the date folder 07-06-2020.
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from dash.exceptions import PreventUpdate
import plotly.graph_objects as go
import os
import pandas as pd
os.chdir(r"C:\Users\ankit\Desktop\dash_assign\SessionCode")
dir_route = os.getcwd()
app = dash.Dash(__name__)
g_t = ["OHLC Bars", "Candlesticks"]
# external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
# csv_list = [files for files in os.listdir(dir_route)]
csv_list = []
date_list = []
path_list = []
for dates in os.listdir(dir_route):
date = dates
date_list.append(date)
app.layout = html.Div(
children=[
html.H1("Time Series Graph"),
dcc.Dropdown(id="select_date",
options=[{"label": dates, "value": str(dates)} for dates in date_list],
value=20180102,
style={
"margin": "10px"
}
),
dcc.Dropdown(id="sym",
placeholder="Select Symbol",
style={
"margin": "10px"
}),
# html.Button("PLOT", id='plot'),
dcc.Graph(id='graph')
]
)
#app.callback(Output('sym', component_property="options"),
[Input('select_date', 'value')])
def update_dates(dat):
lst = os.listdir(os.path.join(dir_route, dat))
for files in lst:
if files.endswith(".csv"):
files.split(" ")
new_file = files[0]
return new_file
#app.callback(Output('graph', 'figure'),
[Input('select_date', 'value'),
Input("sym", "options"),
])
def update_graph(date, symbols):
path = os.path.join(dir_route, date, symbols)
df = pd.read_csv(os.path.join(path+".csv"), index_col=False)
fig = go.Figure(go.Ohlc(x =df["TimeStamp"],
open=df['Open'],
high=df['High'],
low=df['Low'],
close=df['Close'],
increasing_line_color='#00ff00',
decreasing_line_color='#ff0000',
name="Price",
))
return fig
if __name__ == "__main__":
# fig = update_candle("A.csv")
# fig.show()
app.run_server(debug=True, port=5001)
This is my root directory and inside it i have various csv's which should appear in the DropDown once I select the dates
csv files inside one date directory
You can use a callback to update the second dropdown. Below is an example where cascading dropdown is created from table
#app.callback(
Output(component_id='id_first_dropdown', component_property='options'),
[Input(component_id='id_second_dropdown', component_property='value')]
)
def update_dp(filter_value):
sql = "Select distinct second_dropdown_options from table where first_dropdown_value='" + filter_value + "'"
df = pd.read_sql_query(sql, conn)
df = df['second_dropdown_options']
return [{'label': i, 'value': i} for i in df.unique()]

error somewhere in the code while adding colorscale in dash barplot

I have written some code to represent a barplot with a colorscale, in dash. The expected result looks something like this but with only one dropdown menu
https://brandatastudio.shinyapps.io/visualization-practica/
, therefore I wrote the following code:
#!/usr/bin/env python
import dash
import dash_html_components as html
import dash_core_components as dcc
import plotly.offline as pyo
import plotly.graph_objs as go
import pandas as pd
from dash.dependencies import Input, Output
app = dash.Dash()
datause = pd.read_csv("datawind.csv" , sep = ",")
app.layout = html.Div([
dcc.Graph(
id = 'barplot1'),
dcc.Dropdown(
id="dropmenu" ,
options=[{'label' : i , 'value' : i } for i in datause.columns.values
],
value='Total Investment ($ Millions)')
],
)
#app.callback(Output(component_id = 'barplot1', component_property = 'figure'),
[Input(component_id = 'dropmenu' , component_property = 'value')])
def update_figure(dropmenuinput):
potato = dropmenuinput
return {
'data': [
go.Bar(
x = datause["State"],
y = datause[potato],
mode = 'markers',
marker = {
"color" : datause[potato],
"colorbar" : dict(title = "hola"),
"colorscale" : 'Viridis'
})],
'layout': go.Layout(
title = "pene"
,xaxis={'title': "state"},
yaxis ={'title' : potato}) }
if __name__ == '__main__':
app.run_server(debug=True)
Instead, the obtained results ends up looking like this
After testing and retesting, I discover that the main error of the code seems to be in this section
mode = 'markers',
marker = dict(
size=16,
color = datause['Total Investment ($ Millions)'],
colorbar = dict(title = "hola"),
colorscale = 'Viridis'
))]
If I remove that section of the code, the dashboard looks something like it should, without the colorscale of course.
So, the question is, what is failing in my insertion of the colorscale? I have been checking different posts of how to add them
https://community.plot.ly/t/add-a-colorbar-to-scatter-plot-in-python/13349
https://plot.ly/python/colorscales/#custom-contour-plot-colorscale
and I find no examples applied to dash dashboard, or barplots, only here can I hope to find help.
Notes: Heres is the information regarding to the enviroment and packages affecting:
chardet==3.0.4
click==6.7
Cython==0.28.2
dash==0.21.0
dash-core-components==0.22.1
dash-html-components==0.10.0
dash-renderer==0.12.1
decorator==4.3.0
nbformat==4.4.0
numpy==1.14.2
pandas==0.22.0
pandas-datareader==0.6.0
plotly==2.5.1
python-dateutil==2.7.2
pytz==2018.4
requests==2.18.4
urllib3==1.22
Werkzeug==0.14.1
Here is the dataset Iḿ using https://drive.google.com/open?id=1Cr74jKf2FHDA7XVAblQX3d9_dVHfXyVG
The only problem I see is the line
mode = 'markers',
inside the go.Bar. If you go to the reference you will see that Bar traces do not have this attribute. If it still doesn't work I suggest updating plotly, as version 3 was a major upgrade to the library (actually I would recommend updating even if it works).
Here is a minimal example from a notebook:
trace = go.Bar(
x = [1, 2, 3, 4],
y = [1, 2, 3, 4],
marker = {
"color" : [1, 2, 3, 4],
"colorbar" : dict(),
"colorscale" : 'Viridis',
"showscale": True
}
)
I have had a similar issue when adding a color scale to a Map using Dash and the way I resolved this was by finding out the underlying colour schemes by running the code:
import plotly.express as px
print(px.colors.sequential.Plasma)
['#0d0887', '#46039f', '#7201a8', '#9c179e', '#bd3786', '#d8576b', '#ed7953', '#fb9f3a', '#fdca26', '#f0f921']
Then my command in Dash is:
"colorscale":[[0.0,'#0d0887'], [0.1,'#46039f'], [0.2,'#7201a8'], [0.3,'#9c179e'], [0.4,'#bd3786'], [0.5,'#d8576b'], [0.6,'#ed7953'], [0.7,'#fb9f3a'], [0.8,'#fdca26'], [1.0,'#f0f921']]

Resources