Harsh Vedant
Harsh Vedant

Reputation: 107

Issue with plotting uploaded CSV

I'm trying to build a dashboard using plotly/Dash that allows the user to upload a CSV file and then plot the results. I have borrowed from the examples in the dash documentation and tried to piece together an example that plots the graph but I get an error message which I've attached below.

My code is attached:

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

graph_name = 'Time series plot'
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

colors = {
    "graphBackground": "#212529",
    "background": "#000000",
    "text": "#ffffff"
}
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'),
    dcc.Graph(id='new-hydrograph')
])

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.'
        ])
    if df.shape[1] == 4:

        df = df.iloc[2:]
        df.columns = ['Datetime', 'Depth (in)', 'Velocity (feet/sec)', 'Flow (MGD)']
    else:
        df.columns = ['Datetime', 'Rain (in)']

    return df


@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('new-hydrograph', 'figure'),
            [Input('upload-data', 'contents'),
            Input('upload-data', 'filename')])

def plot_graph(contents, filename):

    df = parse_contents(contents, filename)


    trace1 = go.Scatter(
        x = df.date, 
        y = df['Depth (in)'],
        mode = 'lines',
        )
    return {
        'data': [trace1],
        'layout': go.Layout(
            title = graph_name,
            plot_bgcolor = colors["graphBackground"],
            paper_bgcolor = colors["graphBackground"])
    }



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

The error message I get is:

Traceback (most recent call last):
  File "C:\Users\hvkumar\AppData\Local\Continuum\Anacond
sk\app.py", line 1997, in __call__
    return self.wsgi_app(environ, start_response)
  File "C:\Users\hvkumar\AppData\Local\Continuum\Anacond
sk\app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "C:\Users\hvkumar\AppData\Local\Continuum\Anacond
sk\app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "C:\Users\hvkumar\AppData\Local\Continuum\Anacond
sk\_compat.py", line 33, in reraise
    raise value
  File "C:\Users\hvkumar\AppData\Local\Continuum\Anacond
sk\app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "C:\Users\hvkumar\AppData\Local\Continuum\Anacond
sk\app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "C:\Users\hvkumar\AppData\Local\Continuum\Anacond
sk\app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "C:\Users\hvkumar\AppData\Local\Continuum\Anacond
sk\_compat.py", line 33, in reraise
    raise value
  File "C:\Users\hvkumar\AppData\Local\Continuum\Anacond
sk\app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\Users\hvkumar\AppData\Local\Continuum\Anacond
sk\app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view
  File "C:\Users\hvkumar\AppData\Local\Continuum\Anacond
h-0.39.0-py3.6.egg\dash\dash.py", line 1073, in dispatch
    response.set_data(self.callback_map[output]['callbac
  File "C:\Users\hvkumar\AppData\Local\Continuum\Anacond
h-0.39.0-py3.6.egg\dash\dash.py", line 969, in add_conte
    output_value = func(*args, **kwargs)
  File "C:\Users\hvkumar\Documents\Atlanta\pressure_data
- Copy\app_1.py", line 93, in plot_graph
    df = parse_contents(contents, filename)
  File "C:\Users\hvkumar\Documents\Atlanta\pressure_data
- Copy\app_1.py", line 50, in parse_contents
    content_type, content_string = contents.split(',')
AttributeError: 'list' object has no attribute 'split'

Here's an example CSV

PRC-01   "Average=15min"     "QualityFlag=FALSE"     "QualityValue=FALSE"
DateTime     "MP1\DFINAL"    "MP1\VFINAL"    "MP1\QFINAL"
M/d/yyyy h:mm:ss tt  "inches"    "feet/sec"  "MGD"
1/1/2015 0:00   14.9748 3.69    7.976
1/1/2015 0:15   15.0504 3.64    7.934
1/1/2015 0:30   14.7672 3.79    8.047
1/1/2015 0:45   14.9844 3.68    7.97
1/1/2015 1:00   14.8416 3.52    7.53
1/1/2015 1:15   14.7108 3.55    7.497

Upvotes: 0

Views: 649

Answers (1)

Shovalt
Shovalt

Reputation: 6776

Your code had several problems:

  1. The error you gave us stems from using multiple=True in the upload field. This causes the callback function to receive lists instead of strings in the contents, filename variables. Setting to False will solve that error, and allow you to select only a single file for upload, as I assume you intend.
  2. Your CSV is tab-delimited, so must include delimiter='\t' in your pd.read_csv call.
  3. The callback function gets called once on page load even when no file is selected. In that case contents, filename are both None. I suggest checking this at the start of the callback function, before attempting to parse the input.
  4. You used the wrong name for the date column. You need df['Datetime'], according to the columns you define in parse_contents.

Points 3 and 4 are taken care of in the revised function below. Don't forget to take care of points 1 and 2.

@app.callback(Output('new-hydrograph', 'figure'),
              [Input('upload-data', 'contents'),
               Input('upload-data', 'filename')])
def plot_graph(contents, filename):
    fig = {
        'layout': go.Layout(
            title=graph_name,
            plot_bgcolor=colors["graphBackground"],
            paper_bgcolor=colors["graphBackground"])
    }

    if contents:
        df = parse_contents(contents, filename)
        trace1 = go.Scatter(
            x=df['Datetime'],
            y=df['Depth (in)'],
            mode='lines',
        )
        fig['data'] = [trace1]

    return fig

Upvotes: 1

Related Questions