Why is Dash giving a parse error when uploading files?

…衆ロ難τιáo~ 提交于 2019-12-13 03:40:52

问题


Uploading Excel or CSV results in an error. I followed the Dash demo, but as soon as I try to extend it to do something like plotting, it doesn't work. I don't want to just show a table. The Dash_Table function was updated, so previous examples that used Dash_Table_Experiments no longer work

I've spent the whole night on stack exchange, tinkering with my code and reading other solutions. the full working code is provided below. I'd like to also add a drop down call back function to "filter" the data by a categorical variable.

 import base64
 import datetime
 import io
 import plotly.graph_objs as go
 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 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=False
  ),
  html.Div(id='output-data-upload'),
  dcc.Graph(id='graph1')
 ])


 def parse_contents(contents, filename):
   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(
        data=df.to_dict('records'),
        columns=[{'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')])
 def update_output(list_of_contents, list_of_names):
   if list_of_contents is not None:
       children = [
           parse_contents(c, n) for c, n in
           zip(list_of_contents, list_of_names)]
      return children

@app.callback(
   Output('graph1', 'figure'),
   [Input('upload-data', 'contents'),
   Input('upload-data', 'filename')])

def plot_graph(contents, filename):
   df = parse_contents(contents, filename)
   trace1 = go.Bar(
        x=df['Quarter'],
        y=df['Score'],
    )

layout = go.Layout(
    title='graph1'

)
fig = go.Figure(data = [trace1], layout=layout)
return fig

if __name__ == '__main__':
   app.run_server(debug=True)

The error I get is: Callback error updating output-data-upload.children: ValueError: not enough values to unpack (expected 2, got 1)

and

AttributeError: 'NoneType' object has no attribute 'split'

The problem appears to be with how python is handling the parser:

  def parse_contents(contents, filename):
     content_type, content_string = contents.split(',')

   decoded = base64.b64decode(content_string)

But none of the solutions seem to resolve the problem.

please help. Since so many people are struggling with this (it would seem), it would be great if we could resolve it and post a functioning code (Github?) that accomplishes what Shiny can so easily do already.


回答1:


Your code:

   children = [
       parse_contents(c, n) for c, n in
       zip(list_of_contents, list_of_names)]

And then

def parse_contents(contents, filename):
    content_type, content_string = contents.split(',')
    decoded = base64.b64decode(content_string)
    ...

Here the error happens when c in None, then the contents parameter of parse_contents is None, and the "None has no .split" error happens.

If c is not None but only has one word, then contents.split() returns just one element, and the "not enough values to unpack" error happens.

I would filter it:

pairs = zip(list_of_contents, list_of_names)
children = [parse_contents(c, n) for (c, n) in pairs if c and (len(c.split(',')) == 2)]

You could also consider doing the split outside parse_contents and change the code around it.

I would also try to log the filenames for which contents wrong, e.g. [n for (c, n) in pairs if not c or len(c.split(',')) != 2]).




回答2:


Solved it. Posting here for others to make use of:

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:
            # Assume that the user uploaded a CSV file
            df = pd.read_csv(
                io.StringIO(decoded.decode('utf-8')))
        elif 'xlsx' 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 df
else:
    return [{}]


@app.callback(Output('table', 'data'),
              [Input('upload-data', 'contents'),
              Input('upload-data', 'filename')])
def update_output(contents, filename):
    if contents is not None:
        df = parse_contents(contents, filename)
        if df is not None:
            return df.to_dict('records')
        else:
            return [{}]
    else:
        return [{}]
@app.callback(
    Output('graph1', 'figure'),
    [Input('upload-data', 'contents'),
     Input('upload-data', 'filename')])

def plot_graph(contents, filename):
    df = parse_contents(contents, filename)
    trace1 = go.Bar(
            x=df['Quarter'],
            y=df['Score'],
        )

    layout = go.Layout(
        title='graph1'

)
fig = go.Figure(data = [trace1], layout=layout)
return fig


来源:https://stackoverflow.com/questions/57677709/why-is-dash-giving-a-parse-error-when-uploading-files

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!