A nonexistent object was used in an Input of a Dash callback - python-3.x

I'm new in Plotly/Dash. I'm trying to build a sentiment analysis Dashboard.
I would like to represent the "sentiment" in a graph (realtime data: from a table) I reached my fist goal so far:
I choose: trump in my SQL.
#app.callback(
Output("live-graph", "figure"),[Input('sentiment_term', 'value'), Input('graph-update', 'n_intervals')]
)
#Output("wind-speed", "figure")
#Input("wind-speed-update", "n_intervals")
#Input("sentiment_term", "value")
#Input("graph-update", "n_intervals")]
def gen_wind_speed(self, sentiment_term):
conn = sqlite3.connect('twitter.db')
c = conn.cursor()
df = pd.read_sql("SELECT * FROM sentiment WHERE tweet LIKE '%trump%' ORDER BY unix DESC LIMIT 1000", conn)
df.sort_values('unix', inplace=True)
df['sentiment_smoothed'] = df['sentiment'].rolling(int(len(df)/5)).mean()
df.dropna(inplace=True)
X = df.unix.values[-100:]
Y = df.sentiment_smoothed.values[-100:]
data = plotly.graph_objs.Scatter(
x=X,
y=Y,
name='Scatter',
mode= 'lines+markers'
)
return {'data': [data],'layout' : go.Layout(xaxis=dict(range=[min(X),max(X)]),
yaxis=dict(range=[min(Y),max(Y)]),)}
Now as second goal I would like to generate (just beside the graph) a table with the data (in this case twits) used in the graph.
But when I add the table I got:
A nonexistent object was used in an Input of a Dash callback. The id of this object is recent-table-update and the property is n_intervals. The string ids in the current layout are: [live-graph, sentiment_term, graph-update, bin-slider, bin-auto, bin-size, wind-histogram, recent-tweets-table]
Which I don´t understand since I used the same Input/Output "logic" that before
Complete code,, apologizes if it does´t look organized enough
import os
import pathlib
import numpy as np
import pandas as pd
import datetime as dt
import dash
import dash_core_components as dcc
import dash_html_components as html
import sqlite3
import plotly
import plotly.graph_objs as go
from dash.exceptions import PreventUpdate
from dash.dependencies import Input, Output, State
from scipy.stats import rayleigh
conn = sqlite3.connect('twitter.db', check_same_thread=False)
app_colors = {
'pageBackground': '#272B30',
'background': '#0C0F0A',
'text': '#6699ff',
'sentiment-plot':'#41EAD4',
'volume-bar':'#6699ff',
'someothercolor':'#80aaff',
'papercolor':'#1E2022',
'plotcolor':'#262626',
'fillcolor':'#ff6666',
'gridcolor': '#737373',
'backgroundTableHeaders': '#001133',
'backgroundTableRows': '#002266'
}
POS_NEG_NEUT = 0.1
MAX_DF_LENGTH = 100
GRAPH_INTERVAL = os.environ.get("GRAPH_INTERVAL", 5000)
app = dash.Dash(
__name__,
meta_tags=[{"name": "viewport", "content": "width=device-width, initial-scale=1"}],
)
server = app.server
app_colorsor = {"graph_bg": "#082255", "graph_line": "#007ACE"}
app.layout = html.Div(
[
# header
html.Div(
[
html.Div(
[
html.H4("Twitter STREAMING", className="app__header__title"),
html.P(
"This app continually queries a SQL database and displays live charts of twitter sentiment analysis.",
className="app__header__title--grey",
),
],
className="app__header__desc",
),
html.Div(
[
html.Img(
src=app.get_asset_url("twitter_logo.png"),
className="app__menu__img",
)
],
className="app__header__logo",
),
],
className="app__header",
),
html.Div(
[
#Plot sentiment
html.Div(
[
html.Div(
[html.H6("Plot sentiment", className="graph__title")]
),
dcc.Graph(
id="live-graph",
animate=False
),
dcc.Input(
id="sentiment_term",
value="twitter",
type="text"
),
dcc.Interval(
id="graph-update",
interval=1*1000,
n_intervals=0
),
],
className="two-thirds column container",
),
html.Div(
[
# Recent tweets table
html.Div(
[
html.Div(
[
html.H6(
"Recent tweets table",
className="graph__title",
)
]
),
html.Div(
[
dcc.Slider(
id="bin-slider",
min=1,
max=60,
step=1,
value=20,
updatemode="drag",
marks={
20: {"label": "20"},
40: {"label": "40"},
60: {"label": "60"},
},
)
],
className="slider",
),
html.Div(
[
dcc.Checklist(
id="bin-auto",
options=[
{"label": "Auto", "value": "Auto"}
],
value=["Auto"],
inputClassName="auto__checkbox",
labelClassName="auto__label",
),
html.P(
"# of Bins: Auto",
id="bin-size",
className="auto__p",
),
],
className="auto__container",
),
#it says wind because I got it from a wind direction Dash template
dcc.Graph(
id="wind-histogram",
figure=dict(
layout=dict(
plot_bgcolor=app_colorsor["graph_bg"],
paper_bgcolor=app_colorsor["graph_bg"],
)
),
),
],
className="graph__container first",
),
# Table that I would like to add
html.Div(
[
html.H6(
"Recent tweets table",
className="graph__title",
)
]
),
html.Div(id="recent-tweets-table", children=[
html.Thead(html.Tr( children=[], style={'color': app_colorsors['text']})),
html.Tbody([html.Tr(children=[], style={'color': app_colorsors['text'],
'background-color': app_colors['backgroundTableRows'],
'border':'0.2px', 'font - size':'0.7rem'}
],
className='col s12 m6 l6', style={'width':'98%', 'margin-top':30, 'margin-left':15,
'margin-right':15,'max-width':500000}
),
],
className="one-third column histogram__direction",
),
],
className="app__content",
),
],
className="app__container",
)
dcc.Interval(
id='recent-table-update',
interval=2*1000,
n_intervals=0),
#def get_current_time():
#now = dt.datetime.now()
#total_time = (now.hour * 3600) + (now.minute * 60) + (now.second)
#return total_time
def df_resample_sizes(df, maxlen=MAX_DF_LENGTH):
df_len = len(df)
resample_amt = 100
vol_df = df.copy()
vol_df['volume'] = 1
ms_span = (df.index[-1] - df.index[0]).seconds * 1000
rs = int(ms_span / maxlen)
df = df.resample('{}ms'.format(int(rs))).mean()
df.dropna(inplace=True)
vol_df = vol_df.resample('{}ms'.format(int(rs))).sum()
vol_df.dropna(inplace=True)
df = df.join(vol_df['volume'])
return df
#app.callback(
Output("live-graph", "figure"),[Input('sentiment_term', 'value'), Input('graph-update', 'n_intervals')]
)
#Output("wind-speed", "figure")
#Input("wind-speed-update", "n_intervals")
#Input("sentiment_term", "value")
#Input("graph-update", "n_intervals")]
def gen_wind_speed(self, sentiment_term):
conn = sqlite3.connect('twitter.db')
c = conn.cursor()
df = pd.read_sql("SELECT * FROM sentiment WHERE tweet LIKE '%trump%' ORDER BY unix DESC LIMIT 1000", conn)
df.sort_values('unix', inplace=True)
df['sentiment_smoothed'] = df['sentiment'].rolling(int(len(df)/5)).mean()
df.dropna(inplace=True)
X = df.unix.values[-100:]
Y = df.sentiment_smoothed.values[-100:]
data = plotly.graph_objs.Scatter(
x=X,
y=Y,
name='Scatter',
mode= 'lines+markers'
)
return {'data': [data],'layout' : go.Layout(xaxis=dict(range=[min(X),max(X)]),
yaxis=dict(range=[min(Y),max(Y)]),)}
def generate_table(df, max_rows=20):
return html.Table(className="responsive-table",
children=[
html.Thead(
html.Tr(
children=[
html.Th(col.title()) for col in df.columns.values],
style={'color': app_colorsors['text'],
'background-color': app_colorsors['backgroundTableHeaders']}
)
),
html.Tbody(
[
html.Tr(
children=[
html.Td(data) for data in d
], style={'color': app_colorsors['text'],
'background-color': quick_color(d[2]),
'border':'0.2px', 'font - size':'0.7rem'}
)
for d in df.values.tolist()])
]
)
#app.callback(Output("recent-tweets-table", "children"),[Input('sentiment_term','value'), Input('recent-table-update', 'n_intervals')]
)
def update_recent_tweets(self, sentiment_term):
genTable = html.Table()
try:
conn = sqlite3.connect('twitter.db')
df = pd.read_sql("SELECT UnixTime, Tweet, Polarity FROM %s ORDER BY UnixTime DESC LIMIT 20" % (RunConfig.tableName), conn)
if len(df)>0:
df['Date'] = pd.to_datetime(df['UnixTime'], unit='ms')
df = df.drop(['UnixTime'], axis=1)
df = df[['Date', 'Tweet', 'Polarity']]
df.Polarity = df.Polarity.round(3)
genTable = generate_table(df, max_rows=10)
except Exception as e:
with open('errors.txt','a') as f:
f.write("update_recent_tweets: " + str(e))
f.write('\n')
return genTable
if __name__ == "__main__":
app.run_server(debug=True)

I think the problem is that you are introducing a non-existing (not yet) element in the callback as input. The element is the 'recent-table-update' which doesn't exist until you update the table. To avoid this you should introduce it in the callback as a State (it doesn't trigger the callback and the code will not break). More info: link
Here is your code:
#app.callback(
Output("recent-tweets-table", "children"),
[Input('sentiment_term','value'),
Input('recent-table-update', 'n_intervals')]
)
Here is my suggested code:
#app.callback(
Output("recent-tweets-table", "children"),
Input('sentiment_term','value'),
State('recent-table-update', 'n_intervals')
)

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 update multiple Y-axis in plotly dash

I am trying to read sensor values from raspberry pi sense hat and live plotting 3 sensor data on same y-axis and time on x-axis. But only one graph is visible out of 3. For this example I am using a random number generator for sensor values. How to show live data for all 3 values on y-axis? What am I doing wrong?
# from dash.dependencies import Output, Event
import dash
from dash import dcc, html
from dash.dependencies import Input, Output
import random
import plotly.graph_objs as go
from collections import deque
# define a function to get CPU temperature data
def check_CPU_temp():
a = random.randint(0,9)
b = random.randint(0,9)
c = random.randint(0,9)
return a, b, c
X = deque(maxlen=50)
X.append(1)
acc_X = deque(maxlen=50)
acc_Y = deque(maxlen=50)
acc_Z = deque(maxlen=50)
a, b, c = check_CPU_temp()
acc_X.append(a)
acc_Y.append(b)
acc_Z.append(c)
def update_values(acc_X, acc_Y, acc_Z):
a1, b1, c1 = check_CPU_temp()
acc_X.append(a1)
acc_Y.append(b1)
acc_Z.append(c1)
return acc_X, acc_Y, acc_Z
app = dash.Dash(__name__)
app.layout = html.Div(
[
dcc.Graph(id='live-graph', animate=True),
dcc.Interval(
id='refresh',
interval=1 * 1000,
n_intervals=0
),
]
)
#app.callback(Output('live-graph', 'figure'), [Input("refresh", "n_intervals")])
def update_graph_scatter(n_intervals):
X.append(X[-1] + 1)
update_values(acc_X, acc_Y, acc_Z)
data_x = go.Scatter(
x=list(X),
y=list(acc_X),
name='acc x',
showlegend=True,
mode= 'lines',
yaxis='y'
)
data_y = go.Scatter(
x=list(X),
y=list(acc_Y),
name='acc y',
showlegend=True,
mode= 'lines',
yaxis='y1'
)
data_z = go.Scatter(
x=list(X),
y=list(acc_Z),
name='acc z',
showlegend=True,
mode= 'lines',
yaxis='y2'
)
layout = go.Layout(xaxis=dict(range=[min(X), max(X)],),
yaxis=dict(
range=[0, 10],
title='y'),
yaxis1=dict(
range=[0, 10],
side='right',
title='y1'),
yaxis2=dict(
range=[0, 10],
side='left',
title='y2'))
return {'data': [data_x, data_y, data_z], 'layout': layout}
if __name__ == '__main__':
app.run_server(debug=True, host='192.168.1.16', port='8050')
I was able to solve this using fig.addtrace()
import dash
from datetime import datetime as dt
from dash.dependencies import Output, Input
from dash import dcc
from dash import html
from collections import deque
from plotly.subplots import make_subplots
import plotly.graph_objs as go
from sense_hat import SenseHat
sense = SenseHat()
sense.set_imu_config(True, True, True) # accelerometer, magnetometer , gyroscope
sense.clear()
# define a function to get acceleration data
def check_acc_data():
acc = sense.get_accelerometer_raw()
return round(acc["x"],3), round(acc["y"],3), round(acc["z"],3)
def update_values(times, acc_X, acc_Y, acc_Z):
times.append(dt.strptime(str(dt.now()), '%Y-%m-%d %H:%M:%S.%f'))
a1, b1, c1 = check_acc_data()
acc_X.append(a1)
acc_Y.append(b1)
acc_Z.append(c1)
return times, acc_X, acc_Y, acc_Z
times = deque(maxlen=50)
acc_X = deque(maxlen=50)
acc_Y = deque(maxlen=50)
acc_Z = deque(maxlen=50)
update_values(times, acc_X, acc_Y, acc_Z)
app = dash.Dash(__name__)
app.layout = html.Div(
[
dcc.Graph(id='example_graph', figure={}),
dcc.Interval(
id='graph-update',
interval=250,
n_intervals=0
),
]
)
#app.callback(
Output("example_graph", "figure"),
[Input('graph-update', 'n_intervals')]
)
def update_figure(n):
update_values(times, acc_X, acc_Y, acc_Z)
fig = make_subplots(specs=[[{"secondary_y": True}]])
fig.add_trace(
go.Scatter(x=list(times), y=list(acc_X), mode="lines+markers", name='Acc X'),
secondary_y=False
)
fig.add_trace(
go.Scatter(x=list(times), y=list(acc_Y), mode="lines+markers", name='Acc Y'),
secondary_y=True,
)
fig.add_trace(
go.Scatter(x=list(times), y=list(acc_Z), mode="lines+markers", name='Acc Z'),
secondary_y=True,
)
# fig.update_yaxes(secondary_y=False)
# fig.update_yaxes(secondary_y=True)
fig.update_layout(yaxis=dict(range=[-2.5,2.5]),
yaxis1=dict(range=[-2.5,2.5]),
yaxis2=dict(range=[-2.5,2.5]))
return fig
if __name__ == '__main__':
app.run_server(host='192.168.1.16',port='8050')

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

How do you plot a ohlc chart with a set range (invariable) of 1 hour using dash-plotly?

This is my code:
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objs as go
from dash.dependencies import Input, Output
from datetime import timedelta
file_name = 'temp_copy.csv'
file_path = 'C:\\Users\\xxx\\' + str( file_name )
df = pd.read_csv( 'file_path' )
df['period_close'] = pd.to_datetime( df.period_close )
df['year'] = pd.DatetimeIndex( df['period_close'] ).year
df['month'] = pd.DatetimeIndex( df['period_close'] ).month
df['month_str'] = df.month.apply(str).str.zfill(2)
df['slider_id'] = ( df.year - 2017 ) * 12 + df.month # has to be linear
df['slider_label'] = df.year.apply(str).str.cat( df.month_str ).apply(int)
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
ids = df.slider_id.unique().tolist()
labels = [{ 'label': str( i ) } for i in df.slider_label.unique().tolist() ]
marks = dict(zip( ids, labels ))
app.layout = html.Div(children=[
html.H1(children='Hello Dash'),
html.Div(children='''
Dash: A web application framework for Python.
'''),
dcc.Graph(id="my-graph"),
html.Div([
dcc.RangeSlider(id="select-range",
marks=marks,
min=df.slider_id.min(),
max=df.slider_id.max(),
value=[7, 8]
)
],
style={"padding-top": 100,}
)
], className="container")
#app.callback(
Output("my-graph", 'figure'),
[Input("select-range", 'value')]
)
def update_figure(selected):
print( selected[0] )
fromYear = int( selected[0] / 12 + 2017 )
fromMonth = int( selected[0] % 12 )
toYear = int( selected[1] / 12 + 2017 )
toMonth = int( selected[1] % 12 )
dff = df[
( df.year >= fromYear )
& ( df.month >= fromMonth )
& ( df.year <= toYear )
& ( df.month <= toMonth )
]
trace = go.Candlestick(x=dff['period_close'],
open=dff['price'],
high=dff['high'],
low=dff['low'],
close=dff['close'],
increasing={'line': {'color': '#00CC94'}},
decreasing={'line': {'color': '#F50030'}}
)
return {
'data': [trace],
'layout': go.Layout(
title=f"Prices",
xaxis={
'rangeslider': {
'visible': True,
'range': [ df.period_close.min(), df.period_close.min() + timedelta(hours=1) ]
},
'autorange': True,
},
yaxis={
"title": f'Price'
}
)}
server = app.server # the Flask app
if __name__ == '__main__':
app.run_server(debug=True)
I can not provide you with the data, but it starts at 2017 and provides the columns: open, high, low, close, period_close. I found the rangeslider wouldnt do what I wanted, although i may have done something wrong. It required an int value, in a linear format for equal spacing so i have simply got the year - 2017 + month to have a linear spread.
My problem in detail:
I have a huge dataset of 1 minute OHLC data from 2017 (July) through to Jan 2019. I want to be able to plot a 1 hour (roughly) window across my screen, with a range slider underneath that i can drag to get different timeframes. If possible, I would like the slider to not be variable in width ( ~1 hour or so ).
I don't know of a way to make the range slider lock in the width. However, you could just use the normal slider.
dcc.Slider(
min=0,
max=19,
marks={
x: '{} to {}'.format(x, x+1)
for x in range(20)
},
value=0
)
You'd have to change it, of course, but that's the general idea. Would that work?

Plotly Dash Graph With Multiple Dropdown Inputs Not Working

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()

Resources