First of all thanks for plot.ly Dash, itâs a great platform and I particularly appreciate the nice documentation!
My question follows up on the initial one - how to actually be able to use table data that has been uploaded via the dcc.Upload
component combined with dash_table_experiments
.
Reading in a local Excel file within the script as a data frame and creating a table based on this data, I was able to filter the table or use table row data to create plots. However, trying to replicate the same thing but uploading the data via dcc.Upload
, I donât seem to be able to actually access and work with the uploaded table data.
In the simple example below, Iâd like to be able to filter the table data eventually using groupby
etc. and in a first step would like to create dropdown options based on the uploaded columns. With the old way of reading in data I could simply use Input('table', 'rows')
in callbacks and transfer table rows into a df. Now this does not seem to work, neither using Input
nor State
- in any case the dropdown options do not update and remain empty.
Having the great option of directly uploading the data, I think it would be crucial to actually work with it, so would be great if anyone had some ideas!
Regarding the discussion above, I understand that global variables arenât save, however in the case of uploading data it may be useful to be able to make this uploaded data (and only this) an initial âglobalâ df that can then be accessed as usual by filtering/callback functions. Trying to achieve this in my trials, I ran into quite a few problems with callbacks not being happy with an initially empty df that then gets populated with the uploaded data, for example.
So, as much as I like the dcc.Upload
option, I feel the uploaded data cannot be accessed properly at this stage or am I missing something?
Any help would be much appreciated!
Example:
import dash_html_components as html
import dash_core_components as dcc
import dash
import plotly
import dash_table_experiments as dte
from dash.dependencies import Input, Output, State
import pandas as pd
import numpy as np
import json
import datetime
import operator
import os
import base64
import io
app = dash.Dash()
app.scripts.config.serve_locally = True
app.layout = html.Div([
html.H5("Upload Files"),
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.Br(),
html.Button(id='propagate-button', n_clicks=0, children='Propagate Table Data'),
html.Br(),
html.H5("Filter Column"),
dcc.Dropdown(id='dropdown_table_filterColumn',
multi = False,
placeholder='Filter Column'),
html.Br(),
html.H5("Updated Table"),
html.Div(id='output-data-upload'),
html.Div(dte.DataTable(rows=[{}], id='table'), style={'display': 'none'})
])
# Functions
# file upload function
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)),
# Use the DataTable prototype component:
# github.com/plotly/dash-table-experiments
dte.DataTable(rows=df.to_dict('records')),
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',
'whiteSpace': 'normal'
})
])
# Callbacks
# callback table creation
@app.callback(Output('output-data-upload', 'children'),
[Input('upload-data', 'contents'),
Input('upload-data', 'filename'),
Input('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
#callback update options of filter dropdown
@app.callback(Output('dropdown_table_filterColumn', 'options'),
[Input('propagate-button', 'n_clicks'),
Input('table', 'rows')])
def update_filter_column_options(n_clicks_update, tablerows):
if n_clicks_update < 1:
print "df empty"
return []
else:
dff = pd.DataFrame(tablerows) # <- problem! dff stays empty even though table was uploaded
print "updating... dff empty?:", dff.empty #result is True, labels stay empty
return [{'label': i, 'value': i} for i in sorted(list(dff))]
# simpler option, does not work either:
#@app.callback(Output('dropdown_table_filterColumn', 'options'),
# [Input('table', 'rows')])
#
#def update_filter_column_options(tablerows):
#
# dff = pd.DataFrame(tablerows)
# print "updating... dff empty?:", dff.empty
#
# return [{'label': i, 'value': i} for i in sorted(list(dff))] # <- problem! dff stays empty even though table was uploaded
app.css.append_css({
"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"
})
if __name__ == '__main__':
app.run_server(debug=True)