How to create two dropdowns to let the user choose which column of datatable to plot on graph x-axis and y-axis

Hello, I saw this code in the user guide and I want to ask how to create two dropdowns to let the user choose which column of datatable to plot on graph x-axis and y-axis.

import base64
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 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=‘datatable-upload’,
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’
},
),
dash_table.DataTable(id=‘datatable-upload-container’),
dcc.Graph(id=‘datatable-upload-graph’)
])

def parse_contents(contents, filename):
content_type, content_string = contents.split(’,’)
decoded = base64.b64decode(content_string)
if ‘csv’ in filename:
# Assume that the user uploaded a CSV file
return pd.read_csv(
io.StringIO(decoded.decode(‘utf-8’)))
elif ‘xls’ in filename:
# Assume that the user uploaded an excel file
return pd.read_excel(io.BytesIO(decoded))

@app.callback(Output(‘datatable-upload-container’, ‘data’),
[Input(‘datatable-upload’, ‘contents’)],
[State(‘datatable-upload’, ‘filename’)])
def update_output(contents, filename):
if contents is None:
return [{}]
df = parse_contents(contents, filename)
return df.to_dict(‘records’)

@app.callback(Output(‘datatable-upload-graph’, ‘figure’),
[Input(‘datatable-upload-container’, ‘data’)])
def display_graph(rows):
df = pd.DataFrame(rows)
return {
‘data’: [{
‘x’: df[df.columns[0]],
‘y’: df[df.columns[1]],
‘type’: ‘bar’
}]
}

if name == ‘main’:
app.run_server(debug=True)

It’s quite easy.
First you define your dropdown menu:

def data_selector(df):
    columns=df.columns
    data_options= [{'label' :k, 'value' :k} for k in columns]
    return dcc.Dropdown(
            id='data_selector',
            options=data_options,
            multi=True,
            #setting a default value, this is not required, but makes development easier.
            value=['Location_X', 'Location_Y'])

Then you need to add it to your layout by calling the function.
Then you need a callback to update the dropdown options:

@app.callback(Output('data_selector', 'options'),
              [Input('datatable-upload-container’, ‘data’)])

def update_dropdown(rows):

    df = pd.DataFrame(rows)
    print('updating menus')
    columns=df.columns
    col_labels=[{'label' :k, 'value' :k} for k in columns]
    return col_labels

And then you feed your selection to the graph:

@app.callback(Output(‘datatable-upload-graph’, ‘figure’),
[State('datatable-upload-container’, ‘data’)]),
[Input(‘data_selector’, ‘value’)])

def display_graph(value):
 df = pd.DataFrame(rows)
 return {
 ‘data’: [{
#first column that was selected
 ‘x’: df[value[0]],
#second column that was selected
 ‘y’: df[[value[1],
 ‘type’: ‘bar’
 }]
 }

@Snizl
I am new to dash actually. So I want to ask what I need to put inside the @app.callback output and input to call the def data_selector(df) function to the layout and is this code is for two dropdown menu?

For the layout you would just start with

app.layout=html.Div([data_selector(df)])

Or put it at whatever place in the layout that you want.
Of course the function needs to be defined before the layout is created. In my case I imported it from a different file.
You just call the function that I defined in my previous comment, as its return value is in the correct format
to create the layout.
This will create the dropdown menu. You don’t need to put it in an appcallback, this will be initiated on app launch. This might be a bit tricky because it takes an input argument that does not exists at this point.

I don’t know if there are better alternatives, but you could solve this either, by just creating a sample dataframe as a global variable:

global df
df=pd.DataFrame({'column1':[1, 2]})

or by only calling it once your data is uploaded.For that you first need to create a div with an id:

app.layout=html.Div([id='column_selection'])

and then combine the app callbacks for data upload and dropdown update.
But I think for the start, if you just want to familiarize yourself with dash, the global variable is easier.

@Snizl
So I combine the code, but there are two errors:
-html.Div([data_selector(df)]) (undefined name ‘data_selector’)
-df = pd.DataFrame(rows) (undefined name ‘rows’)
and the graph wont plot after i upload the file and choose which rows to plot

This is the code:

import base64
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 pandas as pd

external_stylesheets = [‘https://codepen.io/chriddyp/pen/bWLwgP.css’]

app = dash.Dash(name, external_stylesheets=external_stylesheets)

global df
df=pd.DataFrame({‘column1’:[1, 2]})

app.layout = html.Div([
dcc.Upload(
id=‘datatable-upload’,
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’
},
),
dash_table.DataTable(id=‘datatable-upload-container’),
#undefined name ‘data_selector’
html.Div([data_selector(df)]),
dcc.Graph(id=‘datatable-upload-graph’)
])

def parse_contents(contents, filename):
content_type, content_string = contents.split(’,’)
decoded = base64.b64decode(content_string)
if ‘csv’ in filename:
# Assume that the user uploaded a CSV file
return pd.read_csv(
io.StringIO(decoded.decode(‘utf-8’)))
elif ‘xls’ in filename:
# Assume that the user uploaded an excel file
return pd.read_excel(io.BytesIO(decoded))

def data_selector(df):
columns=df.columns
data_options= [{‘label’ :k, ‘value’ :k} for k in columns]
return dcc.Dropdown(
id=‘data_selector’,
options=data_options,
multi=True,
#setting a default value, this is not required, but makes development easier.
value=[‘Location_X’, ‘Location_Y’])

@app.callback(Output(‘data_selector’, ‘options’),
[Input(‘datatable-upload-container’, ‘data’)])

def update_dropdown(rows):

df = pd.DataFrame(rows)
print('updating menus')
columns=df.columns
col_labels=[{'label' :k, 'value' :k} for k in columns]
return col_labels

@app.callback(Output(‘datatable-upload-container’, ‘data’),
[Input(‘datatable-upload’, ‘contents’)],
[State(‘datatable-upload’, ‘filename’)])
def update_output(contents, filename):
if contents is None:
return [{}]
df = parse_contents(contents, filename)
return df.to_dict(‘records’)

@app.callback(Output(‘datatable-upload-graph’, ‘figure’),
[Input(‘data_selector’, ‘value’)],
[State(‘datatable-upload-container’, ‘data’)])

def display_graph(value):
#undefined name ‘rows’
df = pd.DataFrame(rows)
return {
‘data’: [{
#first column that was selected
‘x’: df[value[0]],
#second column that was selected
‘y’: df[value[1]],
‘type’: ‘bar’
}]
}

if name == ‘main’:
app.run_server(debug=False)