首页 > 解决方案 > 通过下拉列表上传 csv 文件后访问列

问题描述

我有一个破折号应用程序,我正在尝试上传任意 csv,然后通过下拉列表访问所述数据文件的列。我有两个下拉菜单。访问列后,我想计算两者之间的相关性并绘制它们。但是,我一直试图弄清楚如何访问下拉列表中上传的数据列。我有一个工作的 Shiny 应用程序可以做到这一点,但我正在尝试用 Dash 复制它。我对 Dash 真的很陌生,所以这可能是一个简单的修复。我的代码如下!

import os
import io
import json
import dash
import base64
import plotly
import datetime
import operator
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import dash_core_components as dcc
import dash_html_components as html
import dash_table_experiments as dte
from dash.dependencies import Input, Output, State

app = dash.Dash()
app.scripts.config.serve_locally = True
app.config['suppress_callback_exceptions'] = 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': '15%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'left',
            'margin': '10px'
        },
        multiple=False),

    html.Div([
        html.H5("First Column"),
        dcc.Dropdown(
            id = 'y-dropdown',
            options = [],
        )
    ]),

    html.Div([
        html.H5("Second Column"),
        dcc.Dropdown(
            id = 'x-dropdown',
            options = [],
        )
    ]),    

    html.Br(),
    html.Button(
        id = 'propagate-button',
        n_clicks = 0,
        children = 'Propagate Table Data'
    ),

    html.Br(),
    html.H5("Updated Table"),
    html.Div(
        dte.DataTable(rows = [{}], id = 'table')
    ),

    html.Div(
    dcc.Graph(
        id = 'graph'
        )
    )
])

## Functions 
# File upload function
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 None

    return df

## Callbacks 
# Table creation
@app.callback(Output('table', 'rows'),
              [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.css.append_css({
    "external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"
})

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

标签: plotlyplotly-dash

解决方案


我认为您不能创建具有多个输出的单个回调,因此您需要为每个下拉列表和表格创建单独的回调。请参考以下代码。

import os
import io
import json
import dash
import base64
import plotly
import datetime
import operator
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import dash_core_components as dcc
import dash_html_components as html
import dash_table_experiments as dte
from dash.dependencies import Input, Output, State

app = dash.Dash()
app.scripts.config.serve_locally = True
app.config['suppress_callback_exceptions'] = 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': '15%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'left',
            'margin': '10px'
        },
        multiple=False),

    html.Div([
        html.H5("First Column"),
        dcc.Dropdown(
            id = 'y-dropdown',
            options = [],
        )
    ]),

    html.Div([
        html.H5("Second Column"),
        dcc.Dropdown(
            id = 'x-dropdown',
            options = [],
        )
    ]),    

    html.Br(),
    html.Button(
        id = 'propagate-button',
        n_clicks = 0,
        children = 'Propagate Table Data'
    ),

    html.Br(),
    html.H5("Updated Table"),
    html.Div(
        dte.DataTable(rows = [{}], id = 'table')
    ),

    html.Div(
    dcc.Graph(
        id = 'graph'
        )
    )
])

## Functions 
# File upload function
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 None
    return df

## Callbacks 
# Table creation
@app.callback(Output('table', 'rows'),
              [Input('upload-data', 'contents'),
               Input('upload-data', 'filename')])
def update_output(contents, filename):
    if contents is not None:
        df = parse_contents(contents, filename)
        columns = df.columns.values.tolist()
        if df is not None:
            return df.to_dict('records')
        else:
            return [{}]
    else:
        return [{}]

# update y-dropdown
@app.callback(Output('y-dropdown', 'options'),
              [Input('upload-data', 'contents'),
               Input('upload-data', 'filename')])
def update_y_dropdown(contents, filename):
    if contents is not None:
        df = parse_contents(contents, filename)
        columns = df.columns.values.tolist()
        if df is not None:
            return [ {'label': x, 'value': x} for x in columns ]
        else:
            return []
    else:
        return []

# update x-dropdown
@app.callback(Output('x-dropdown', 'options'),
              [Input('upload-data', 'contents'),
               Input('upload-data', 'filename')])
def update_x_dropdown(contents, filename):
    if contents is not None:
        df = parse_contents(contents, filename)
        columns = df.columns.values.tolist()
        if df is not None:
            return [ {'label': x, 'value': x} for x in columns ]
        else:
            return []
    else:
        return []

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

推荐阅读