Incorporating uploaded data into a callback in Dash - python

First I made a simple app with fixed data:
import base64
import datetime
import io
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import dash_table
import plotly.graph_objs as go
import pandas as pd
external_stylesheets = ['css stylesheet test.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
df = pd.read_csv('myfile.csv')
text = 'Col1'
vals = df.groupby([text]).size()
available_indicators = ['Col1', 'Col2', 'Col3', 'Col4', 'Col5', 'Col6', 'Col7']
status_vals = df['Status'].unique()
app.layout = html.Div([
html.Div([
html.H1("Welcome to the Dashboard"),
html.P("Learning Dash is fun!")
], ),
dcc.Graph(id='donut-with-slider'),
dcc.Dropdown(
id='Category',
options=[{'label': i, 'value': i} for i in available_indicators],
value='Col1'),
dcc.Dropdown(
id='Status',
options=[{'label': i, 'value': i} for i in status_vals],
value='Filled'),
dcc.Slider(
id='year-slider',
min=df['Year'].min(),
max=df['Year'].max(),
value=df['Year'].max(),
marks={str(Year): str(Year) for Year in df['Year'].unique()},
step=None
),
])
#app.callback(
Output('donut-with-slider', 'figure'),
Input('year-slider', 'value'),
Input('Category', 'value'),
Input('Status', 'value'))
def update_figure(selected_year, Category, Status):
df2 = df[df.Year == selected_year]
df3 = df2[df2.Status == Status]
text = Category
vals = df3.groupby([text]).size()
fig = go.Figure(data=[go.Pie(labels=vals.index.values, values=vals, hole=0.3)])
fig.update_layout(transition_duration=500)
return fig
if __name__ == '__main__':
app.run_server(debug=True)
And all is well. What I'm struggling to do is adapt this so that data can be uploaded using dcc.Upload, and then incorporated into the donut-with-slider plot so it remains interactive. Importantly there is also a lot of pandas legwork that has to go into cleaning up the uploaded data. This is what I have so far that is not working (updated to try to send to json, but still getting same list error):
#ideally I wouldn't need to start this way, with the last
#saved data, because uploaded files might have new
#values, but I can't think of how else to do this
df = pd.read_csv('Most_Recent_Version.csv')
text = 'Col1'
vals = df.groupby([text]).size()
available_indicators = ['Col1', 'Col2', 'Col3', 'Col4', 'Col5', 'Col6', 'Col7']
status_vals = df['Status'].unique()
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'
},
multiple=True
),
html.Div([
html.H1("Welcome to the Dashboard"),
html.P("Learning Dash is so interesting!!")
], ),
dcc.Graph(id='donut-with-slider'),
dcc.Dropdown(
id='Category',
options=[{'label': i, 'value': i} for i in available_indicators],
value='Col1'),
dcc.Dropdown(
id='Status',
options=[{'label': i, 'value': i} for i in status_vals],
value='Filled'),
dcc.Slider(
id='year-slider',
min=df['Year'].min(),
max=df['Year'].max(),
value=df['Year'].max(),
marks={str(Year): str(Year) for Year in df['Year'].unique()},
step=None),
html.Div(id='output-data-upload'),
])
#app.callback(
Output('intermediate-value', 'children'),
[Input('upload-data', 'filename'), Input('upload-data', 'contents')])
def parse_contents(contents, filename):
if contents is not None:
content_type, content_string = contents.split(',')
decoded = base64.b64decode(content_string)
try:
if 'csv' in filename:
#if they upload a csv, the file is already clean
df = pd.read_excel(io.BytesIO(decoded), header=0)
elif 'xls' in filename:
df = pd.read_excel(io.BytesIO(decoded), sheet_name=tabs, header=1, usecols='B:AC')
#lines and lines of pandas cleanup
#save new Most_Recent_Version.csv
cleaned.to_csv('Most_Recent_Version.csv', index=None)
return cleaned.to_json(date_format='iso', orient='split')
except Exception as e:
print(e)
return html.Div([
'There was an error processing this file.'
])
else:
return dash.no_update
#app.callback(
Output('donut-with-slider', 'figure'),
[Input('upload-data', 'contents'),
Input('upload-data', 'filename')],
Input('Category', 'value'),
Input('Status', 'value'),
Input('year-slider', 'value'))
def update_figure(contents, filename, Category, Status, selected_year):
if json_updated_data is not None:
dff = pd.read_json(json_updated_data, orient='split')
df2 = dff[dff.Year == selected_year]
df3 = df2[df2.Status == Status]
text = Category
vals = df3.groupby([text]).size()
fig = go.Figure(data=[go.Pie(labels=vals.index.values, values=vals, hole=0.3)])
fig.update_layout(transition_duration=500)
return fig
if __name__ == '__main__':
app.run_server(debug=True)
In addition to callback where I commented about the list type, the error it's throwing lies with content_type, content_string = contents.split(',') in parse_data(): AttributeError: 'list' object has no attribute 'split'
Maybe some of this is connected, why is everything constantly getting converted to a list? What am I missing here? I tried having a second callback to update the output but that didn't seem to help either.

Related

'float' object not iterable, dcc.Upload in Dash

This is a strange error, and one that has been asked before here but unfortunately went unanswered.
I have taken the code from the Dash official documentation here, which allows the user to upload a csv or xls file and view it as a datatable in the dash web app. I've copied and pasted the code below:
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
),
html.Div(id='output-data-upload'),
])
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(
df.to_dict('records'),
[{'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
if __name__ == '__main__':
app.run_server(debug=True)
The above code works flawlessly, as one would expect from code taken from the official documentation. Below is my code, which works perfectly except for the feature taken from the above documentation. I didn't change the code at all.
import pandas as pd
import numpy as np
import plotly.express as px
import dash
from dash import html, Dash, dcc, dash_table
from dash.dependencies import Input, Output, State
import dash_bootstrap_components as dbc
import dash_split_pane
import base64
import io
import datetime
from pandas.tseries.offsets import BDay
import plotly.graph_objects as go
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
app = Dash(__name__, external_stylesheets=external_stylesheets)
columnNames = ["Blood Lactate", "Velocity (km/h)", "Stage Finish Time (MM:SS)"]
df = pd.DataFrame(columns=columnNames)
df.rename_axis("Stage", inplace=True, axis=0)
columnIds = ["bloodLactate", "velocity", "stageFinishTime"]
# ------------------------------------------------------------------------
input_types = ['number', 'number', 'text']
row1 = html.Div(
[
dbc.Row([
dbc.Col([
html.Div([
html.P("Blood Lactate:", style={"margin-left":20}),
dcc.Input(
id="bloodLactateId",
type="number",
placeholder="insert Blood Lactate",
minLength=0, maxLength=50,
autoComplete='on',
disabled=False,
readOnly=False,
required=False,
size=20,
style={"margin-right":20}
)
], style=
{
"display":"flex",
"justify-content":"space-between",
"align-items":"baseline",
"margin-top":20
}
)
])
])
]
)
row2 = html.Div(
[
dbc.Row([
dbc.Col([
html.Div([
html.P("Velocity (km/h):", style={"margin-left":20}),
dcc.Input(
id="velocityId",
type="number",
placeholder="insert Velocity",
minLength=0, maxLength=50,
autoComplete='on',
disabled=False,
readOnly=False,
required=False,
size="20",
style={"margin-right":20}
)
], style={
"display":"flex",
"justify-content":"space-between",
"align-items":"baseline"})
]),
])
]
)
row3 = html.Div(
[
dbc.Row([
dbc.Col([
html.Div([
html.P("Stage Finish Time (MM:SS):",
style={"margin-left":20}),
dcc.Input(
id="stageFinishTimeId",
type="text",
placeholder="insert (MM:SS)",
minLength=0, maxLength=50,
autoComplete='on',
disabled=False,
readOnly=False,
required=False,
size="20",
style={"margin-right":20}
)
], style={"display":"flex",
"justify-content":"space-between",
"align-items":"baseline"})
]),
])
]
)
row4 = html.Div(
dbc.Row(
html.Button('Add Row',
id='add_row',n_clicks=0),
style={"text-align":"center"}
)
)
row5 = html.Div([
dcc.Upload(
id="upload-data", children=html.Div([
'Drag and Drop COSMED file or ', html.A('Select Files')
] ),
style={
'width':'80%',
"lineHeight":"60px",
'borderWidth':'1px',
'borderStyle':'dashed',
'borderRadius':'5px',
'text-align':'center',
'margin-left':'auto',
'margin-right':'auto',
'margin-top':40,
}
)
], style={"align-content":'center'})
table = html.Div([
dbc.Row([
dbc.Col([html.H5('Results',
className='text-center',
style={"margin-left":20}),
dash_table.DataTable(
id='table-container_3',
data=[],
columns=[{"name":i_3,"id":i_3,'type':'numeric'} for i_3 in df.columns],
style_table={'overflow':'scroll','height':600},
style_cell={'textAlign':'center'},
row_deletable=True,
editable=True),
],width={'size':12,"offset":0,'order':1})
]), html.Div(id='output-data-upload')
])
pane1 = html.Div([
row1,
html.Br(),
row2,
html.Br(),
row3,
html.Br(),
row4,
html.Br(),
row5
])
pane2 = html.Div(
table
)
app.layout = dash_split_pane.DashSplitPane(
children=[pane1, pane2],
id="splitter",
split="vertical",
size=500
)
#copy pasted code from docs ======================================================
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(
df.to_dict('records'),
[{'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('table-container_3', 'data'),
Output('bloodLactateId', 'value'),
Output('velocityId', 'value'),
Output('stageFinishTimeId', 'value'),
Input('add_row', 'n_clicks'),
State('table-container_3', 'data'),
State('table-container_3', 'columns'),
State('bloodLactateId', 'value'),
State('velocityId', 'value'),
State('stageFinishTimeId', 'value'))
def add_row(n_clicks, rows, columns, selectedBloodLactate, selectedVelocity,
selectedStageFinishTime):
if n_clicks > 0:
rows.append({c['id']: r for c,r in zip(columns,
[selectedBloodLactate,
selectedVelocity,
selectedStageFinishTime])})
return rows, '', '', ''
#copy pasted code ===============================================================
#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
#=================================================================================
if __name__ == '__main__':
app.run_server(debug=False)
Here is a photo of what the app looks like, to get an idea.
When I load the app it runs as expected, I can add rows, but when I upload a csv file I get the following error:
File "C:\Users\harry\OneDrive\Documents\coding\fypWebApp\dashApp\app.py", line 255, in update_output
zip(list_of_contents, list_of_names, list_of_dates)]
TypeError: 'float' object is not iterable
It seems the problem lies in one of the lists in the update_output function. Also, I get the following notice in VSCode.
This notice does not appear when I have just the code taken from the documentation in a standalone file (as seen in the image below), only when I enter the code into my code.
I don't know where to start to fix this problem, and it seems like it requires a good understanding of Dash to solve. Any help is appreciated.
After long debugging, the problem is that list_of_dates is a float number which represents the last modified for the uploaded file. There is a list comprehension in the callback which iterates through this float number, which does not make sense to iterate through a number, and it eventually throws an error. To solve this problem, all you should do is to replace the callback function with the following:
def update_output(list_of_contents, list_of_names, list_of_dates):
if list_of_contents is not None:
children = [parse_contents(list_of_contents, list_of_names, list_of_dates)]
return children
Inside the function parse_contents, I added the delimiter=";" to read the CSV file properly.
if 'csv' in filename:
# Assume that the user uploaded a CSV file
df = pd.read_csv(
io.StringIO(decoded.decode('utf-8')), delimiter=";") #<-- this line.
Finally, I reduced the size of the upper table to show the table of the CSV file.
Output

Dash -Callback with multiple inputs (input and dropdown) in Datatable

I'm new to Dash and Python. I have an app with a dropdown and a search input however I cannot get the callback to get both inputs to work. Currently either only the dropdown will work or just the input. I would like to first select the dropdown and then be able to search for text within the Datatable.
Below is my code.
import pandas as pd
import dash
import dash_table
import dash_core_components as dcc
import dash_bootstrap_components as dbc
import dash_html_components as html
import pathlib
from dash.dependencies import Input, Output
df = pd.read_csv('data.csv',encoding='cp1252')
env_list = df["Environment"].unique()
PAGE_SIZE = 20
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]
)
def description_card():
"""
:return: A Div containing logo.
"""
return html.Div(
id="description-card",
children=[
html.Img(id="logo", src=app.get_asset_url("logo.png"), height=80)
],style={'textAlign': 'center'}
)
def generate_control_card():
"""
:return: Descriptions
"""
return html.Div(
id="env-card",
children=[
html.H3("Welcome to the Package Catalog"),
]
)
app.layout = html.Div(
id="app-container",
children=[
# Banner
html.Div(
id="banner",
className="banner",
children=[description_card(),generate_control_card()
],
),
html.Div([
html.P([
html.Label("Select Environment", style={"background": "#F5F5F5"}),
dcc.Dropdown(
id='env-select',
options=[{'label': i, 'value': i} for i in env_list],
value=env_list[0],
)]),
html.Div([
html.P([
html.Label("Search for a package and description in the search box.", style={'display':'inline-block', 'background': '#F5F5F5'}),
dcc.Input(value='', id='filter-input', placeholder='Filter', debounce=True)
]),
dash_table.DataTable(css=[{'selector': '.row', 'rule': 'margin: 0'}],
id='datatable-paging',
columns=[
{"name": i, "id": i} for i in df.columns # sorted(df.columns)
],
style_header={
'backgroundColor': 'F5F5F5',
'fontWeight': 'bold'
},
page_current=0,
page_size=PAGE_SIZE,
page_action='custom',
sort_action='custom',
sort_mode='single',
sort_by=[]
)
]),
]),
])
#app.callback(
Output('datatable-paging', 'data'),
[
Input('datatable-paging', 'page_current'),
Input('datatable-paging', 'page_size'),
Input('datatable-paging', 'sort_by'),
Input('env-select', 'value'),
Input('filter-input', 'value')
])
def update_table(page_current, page_size, sort_by, environment_input, filter_string,
):
# Filter
dff = df[df.Environment==environment_input]
dff = df[df.apply(lambda row: row.str.contains(filter_string, regex=False).any(), axis=1)]
# Sort
if len(sort_by):
dff = dff.sort_values(
sort_by[0]['column_id'],
ascending=sort_by[0]['direction'] == 'asc',
inplace=False
)
return dff.iloc[
page_current * page_size:(page_current + 1) * page_size
].to_dict('records')
Change this line
dff = df[df.apply(lambda row: row.str.contains(filter_string, regex=False).any(), axis=1)]
to this
dff = dff[df.apply(lambda row: row.str.contains(filter_string, regex=False).any(), axis=1)]
In the line before the one shown above you apply your dropdown filter and store the filtered result as dff. So by using df instead of dff you're essentially discarding the dropdown filter result.

Returning a completely new layout to same page in Dash

I am trying to set up a dash app which has 2 layers:
1st page has a couple of input forms, and based on these inputs - app_layout
2nd page - which has a different set of intputs (layout_more_inputs) which pop up depending on what 1st page input is.
I am trying to return layout_more_inputs from a callback but it doesn't work.
app.layout = html.Div([
html.H3('welcome to app'),html.Br(),
dcc.Input(id='input-00-state', type='text', value='QQQ'),
dcc.Input(id='input-01-state', type='text', value='MOVE'),
html.Button(id='submit-button-state', n_clicks=0, children='Go!'),
html.Div(id='output-state'),
dcc.Graph(id='graph-with-slider'),
])
layout_more_inputs = html.Div([
dcc.Input(id='input-10-state', type='number', value='0.11'),
dcc.Input(id='input-11-state', type='number', value=0.12),
html.Button(id='submit-button-state2', n_clicks=0, children='Go Go!'),
])
#front page - 0
#app.callback(
Output('container', 'children'),
Input('submit-button-state', 'n_clicks'),
State('input-00-state', 'value'),
State('input-01-state', 'value'),
)
def ask_for_more_inputs(n_clicks,asset_str,event_str):
print("input summary:")
print(n_clicks,asset_str,event_str)
return layout_more_inputs #<<--DOES NOT WORK
#front page - 1
#app.callback(
Output('graph-with-slider', 'figure'),
Output('output-state', 'children'),
Input('submit-button-state2', 'n_clicks'),
State('input-10-state', 'value'),
State('input-11-state', 'value'),
)
def more_inputs(n_clicks,input0,input1):
d = {'x': [input0, input1], 'y': [input0, input1]}
df = pd.DataFrame(data=d)
filtered_df = df
fig = px.scatter(filtered_df, x="x", y="y")
fig.update_layout(transition_duration=500)
return fig, u'''Button pressed {} times, 1 is "{}", and 2 is "{}"'''.format(n_clicks,state1,state2)
if __name__ == '__main__':
app.run_server(debug=True)
I'm not 100% on what exactly is is you're attempting to deliver, but one options would be to use Dash's dcc.Tabs property.
I've started & laid the groundwork, sort of, for you:
import sys
import dash
from dash import html
from dash import dcc
from dash import no_update
from dash.dependencies import Input, Output, State
# app = dash.Dash(__name__)
app = JupyterDash()
layout = html.Div([
html.H3('welcome to app'),
html.Br(),
dcc.Input(id='input-00-state', type='text', value='QQQ'),
dcc.Input(id='input-01-state', type='text', value='MOVE'),
html.Button(id='submit-button-state', n_clicks=0, children='Go!'),
html.Div(id='output-state'),
dcc.Graph(id='graph-with-slider'),
])
layout_more_inputs = html.Div([
dcc.Input(id='input-10-state', type='number', value=0.11),
dcc.Input(id='input-11-state', type='number', value=0.12),
html.Button(id='submit-button-state2', n_clicks=0, children='Go Go!'),
])
tabs = [
dcc.Tab(label="Front Page - 0", children=layout),
dcc.Tab(label="Front Page - 1", children=layout_more_inputs)
]
multitab_layout = [dcc.Tabs(id="tabs", children=tabs)]
app.layout = html.Div(
[html.Div(id="multitab_layout", children=multitab_layout)])
# front page - 0
#app.callback(
Output('container', 'children'),
Input('submit-button-state', 'n_clicks'),
[State('input-00-state', 'value'),
State('input-01-state', 'value')]
)
def ask_for_more_inputs(n_clicks, asset_str, event_str):
print("input summary:")
print(n_clicks, asset_str, event_str)
return layout_more_inputs # <<--DOES NOT WORK
# front page - 1
#app.callback([
Output('graph-with-slider', 'figure'),
Output('output-state', 'children')
], Input('submit-button-state', 'n_clicks'),
Input('submit-button-state2', 'n_clicks'), [
State('input-00-state', 'value'),
State('input-01-state', 'value'),
State('input-10-state', 'value'),
State('input-11-state', 'value')
])
def more_inputs(n_clicks, n_cliks2, input0, input1, state0, state1, state10,
state11):
d = {'x': [input0, input1], 'y': [input0, input1]}
df = pd.DataFrame(data=d)
filtered_df = df
fig = px.scatter(filtered_df, x="x", y="y")
fig.update_layout(transition_duration=500)
return fig, u'''Button pressed {} times, 1 is "{}", and 2 is "{}"'''.format(
n_clicks, state1, state2)
if __name__ == '__main__':
app.run_server(debug=True)
using your provided code; but not entirely sure where exactly you'd want it to go from here. In terms of UI/EX behavior etc. Perhaps this may be enough of a clue to get you going as you need to...

Changing the label in Dash bootstrap dcc.tab based on callback children

I am using dash core components to create a dash app that includes tabs that return tables based on callbacks. I'm trying to see if it's possible to change the label on the tab itself based on the callback, though it seems like the label will only accept a string, and not a callback with and id and children.
Right now the label of the tab simply says 'Car Type' (this is just a snippet of the code):
dbc.Row([
dbc.Col(dcc.Dropdown(id='car-types', multi=False,,
options=[{'label':x, 'value':x}
for x in sorted(car_list)]),
#width={'size': 5, "offset": 1, 'order': 1}
),
html.Div([
dcc.Tabs([
dcc.Tab(label='Car Type', children=
dbc.Col([
html.Div(id="table1"
)]
)
)
#app.callback(
[Output('table1', 'children'),
Output('car_label', 'children')],
[Input('car-types', 'value')],
[State('car_manuf', 'value')],
def update_table1(a,b):
code for table,
a = code for car_label string
return html.Div([dt.DataTable(),
), a
But what if I wanted it to say "Car Type SUV" or "Car Type Sedan" based on what the output 'car_label' says, how can I change the label of the tab to say that?
I tried something like:
html.Div([
dcc.Tabs([
dcc.Tab(label='Car Type ' + (children=[],id='car_label'), children=
dbc.Col([
html.Div(id="table1"
)]
)
But obviously that won't work. Any suggestions?
Maybe something like this with a dropdown and string formatting.
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
dcc.Tabs(id='tabs-example', value='tab-1', children=[
dcc.Tab(label='', id='first-tab', value='tab-1'),
dcc.Tab(label='', id='second-tab', value='tab-2'),
]),
dcc.Dropdown(id='car-types', multi=False, value='honda',
options=[{'label':'honda', 'value':'honda'},
{'label':'toyota', 'value':'toyota'}]
),
dcc.Dropdown(id='car-types2', multi=False, value='tesla',
options=[{'label': 'jeep', 'value': 'jeep'},
{'label': 'tesla', 'value': 'tesla'}]
),
html.Div(id='tabs-example-content')
])
#app.callback(Output('tabs-example-content', 'children'),
Input('tabs-example', 'value'))
def render_content(tab):
if tab == 'tab-1':
return html.Div([
html.H3('Tab content 1...')
])
elif tab == 'tab-2':
return html.Div([
html.H3('Tab content 2...')
])
#app.callback(
Output('first-tab', 'label'),
Input('car-types', 'value')
)
def update_label(name):
return f"Car Type: {name}"
#app.callback(
Output('second-tab', 'label'),
Input('car-types2', 'value')
)
def update_label(name2):
return f"Car Type: {name2}"
if __name__ == '__main__':
app.run_server(debug=True)

Dash dropdown with linechart

I'm trying to connect a dropdown in dash with a figure. I'm using the following code:
df = pd.read_csv('https://api.statbank.dk/v1/data/mpk100/CSV?valuePresentation=Value&timeOrder=Ascending&LAND=*&Tid=*', sep=';')
df = df[df['INDHOLD'] != '..']
df['rate'] = df['INDHOLD'].str.replace(',', '.').astype(float)
df_countries = df['LAND'].unique()
df['TID'] = pd.to_datetime(df['TID']) #datetime
df.groupby('LAND')
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)
# Create server variable with Flask server object for use with gunicorn
server = app.server
app.layout = html.Div([
html.Div([
html.Div([
dcc.Dropdown(id='linedropdown',
options=[{'label': i, 'value': i} for i in df_countries],
value='DANMARK',
multi=True,
clearable=False
),
],className='six columns'),
],className='row'),
html.Div([
html.Div([
dcc.Graph(id='linechart'),
],className='six columns'),
],className='row'),
])
#app.callback(
[Output('linechart', 'figure')],
[Input('linedropdown', 'value')]
)
def update_graph(linedropval):
df_filterd = df[df['LAND'].isin(['INDHOLD'])]
#extract list of chosen countries
list_chosen_countries=df_filterd['LAND'].tolist()
#filter original df according to chosen countries
#because original df has all the complete dates
df_line = df[df['LAND'].isin(list_chosen_countries)]
line_chart = px.line(
data_frame=df_line,
x='TID',
y=linedropval,
color='LAND',
labels={'Rate':'rate', 'Datetime':'date'},
)
line_chart.update_layout(uirevision='foo')
return (line_chart)
#------------------------------------------------------------------
app.run_server()
But I'm getting the following error in the dashboard:
ValueError: All arguments should have the same length. The length of
column argument `df[color]` is 0, whereas the length of previously-
processed arguments ['TID', 'y'] is 1
and
ValueError: Value of 'y' is not the name of a column in 'data_frame'.
Expected one of ['LAND', 'TID', 'INDHOLD', 'rate'] but received: DANMARK
How can i tackle this? The dropdown includes countries with their respectice interest rates over years. This should be simple. But i have issues with the callback.
While all the columns in your dataframe are upper-case:
Index(['LAND', 'TID', 'INDHOLD', 'rate'], dtype='object')
All strings in the column 'LAND' are mixed case:
array(['Belgien', 'Bulgarien', 'Danmark', 'Estland', 'Finland',
'Frankrig', 'Grækenland', 'Irland', 'Island', 'Italien', 'Letland',
'Litauen', 'Luxembourg', 'Nederlandene', 'Norge', 'Polen',
'Portugal', 'Rumænien', 'Rusland', 'Schweiz', 'Slovakiet',
'Slovenien', 'Spanien', 'Storbritannien', 'Sverige', 'Tjekkiet',
'Tyskland', 'Ungarn', 'Østrig', 'Sydafrika', 'Canada', 'Mexico',
'USA', 'Israel', 'Indien', 'Japan', 'Sydkorea', 'Tyrkiet',
'Australien', 'New Zealand', 'Euro-dollar-renten'], dtype=object)
And you're trying to reference all countries here using only uppper-case like 'DANMARK'.
Correcting that will launch the app with a working selector. The figure doesn't quite work though since your filtering is a bit weird. Let me know what you're trying to achieve here and we can take a look at that too.
Figure / App
Complete code:
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
df = pd.read_csv('https://api.statbank.dk/v1/data/mpk100/CSV?valuePresentation=Value&timeOrder=Ascending&LAND=*&Tid=*', sep=';')
df = df[df['INDHOLD'] != '..']
df['rate'] = df['INDHOLD'].str.replace(',', '.').astype(float)
df_countries = df['LAND'].unique()
df['TID'] = pd.to_datetime(df['TID']) #datetime
df.groupby('LAND')
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)
# Create server variable with Flask server object for use with gunicorn
server = app.server
app.layout = html.Div([
html.Div([
html.Div([
dcc.Dropdown(id='linedropdown',
options=[{'label': i, 'value': i} for i in df_countries],
value='Danmark',
multi=True,
clearable=False
),
],className='six columns'),
],className='row'),
html.Div([
html.Div([
dcc.Graph(id='linechart'),
],className='six columns'),
],className='row'),
])
#app.callback(
[Output('linechart', 'figure')],
[Input('linedropdown', 'value')]
)
def update_graph(linedropval):
#global df_filtered
global df_line
df_filtered = df[df['LAND'].isin(['INDHOLD'])]
#extract list of chosen countries
list_chosen_countries=df_filterd['LAND'].tolist()
#filter original df according to chosen countries
#because original df has all the complete dates
df_line = df[df['LAND'].isin(list_chosen_countries)]
line_chart = px.line(
data_frame=df_line,
x='TID',
y=linedropval,
color='LAND',
labels={'Rate':'rate', 'Datetime':'date'},
)
line_chart.update_layout(uirevision='foo')
return (line_chart)
#------------------------------------------------------------------
# Run app and display result inline in the notebook
app.enable_dev_tools(dev_tools_hot_reload =True)
app.run_server(mode='inline', port = 8040, dev_tools_ui=True, debug=True,
dev_tools_hot_reload =True, threaded=True)

Categories