Adding Input to a dropdown from uploaded data

My first objective was to upload and parse a file (which I have accomplished with the sample code provided from dcc.Upload documentation). I now want to take that parsed data and display them in a drop down menu where later the end user can select an option from the dropdown and have a graph display. My current issue is I am not able to tie the parsed data to the dropdown and I am not sure why. I am new to Dash and any help in this matter would be greatly appreciated.

Hi @KofiM

You need to bild a callback where the Output property is (‘my_dropdown’, ‘options’) and into the callback generate a variable with this format:

[{'label': 'option-1', 'value': 'op-1'}, {'label': 'option-2', 'value': 'op-2'}, {'label': 'option-n', 'value': 'op-n'}]

To get the elements from your file, if you are using a Pandas DataFrame and want to provide the elements of a series named “Name”:

   data_serie = my_table.Name.sort_values()
   options = [{'label': i, 'value': i} for i in data_serie]

   return options

I tried something like this, but to no avail. Is there an issue with how I am using the callback?


import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
import pandas as pd
import numpy as np
from dash.dependencies import Input, Output, State


app=dash.Dash(__name__)
app.layout=html.Div(
    children=[
        html.Div(children=[
            html.Div(className='four columns div-users-controls',
                     children=[html.P('Load Platemap'),
                               html.Div(className='upload_cmpnme_data',children=[
                                   dcc.Upload(id='upload-cmp_nme',children=html.Div([html.A('Select File')]),
                                              style={'width': '100%',
                                             'height': '60px',
                                             'lineHeight': '60px',
                                             'borderWidth': '1px',
                                             'borderStyle': 'dashed',
                                             'borderRadius': '5px',
                                             'textAlign': 'center',
                                             'margin': '10px'},multiple=True),
                                     html.Div(
                                  className='div-for-dropdown',
                                  children=[
                                      dcc.Dropdown(id='dropdwn',                                                   
                                                   placeholder="Select Compound",
                                                   multi=True,
                                                   style={'backgroundColor':'#1E1E1E'},
                                                   className='drpdwn-guy'),],
                                      style={'color':'#1E1E1E','margin':'10px'})
                                                                      
                      ]),],)],)],)
                                        
def take_to_dict(array):
    lst_to_dict=[]
    for i in range(len(array[0])):
        lst_to_dict.append({'label':array[:,i],'value':array[:,i]})
    return lst_to_dict

#Parse the data for relevant columns
def parse_platemap(file_contents,fnme):
    f_t,f_content=file_contents.split(',')
    decode=base64.b64decode(f_content)
    try:
        cmp_data=pd.read_csv(io.StringIO(decode.decode('utf-8')),sep=',',header=None)
        cmpd_col=np.array([cmp_data.iloc[2:17,0]])
        cmpd_lot=np.array([cmp_data.iloc[2:17,1]])
        cmpd_tie=np.concatenate((cmpd_col,cmpd_lot),axis=0)       
        
        creat_dict=take_to_dict(cmpd_tie)
        
    except:
        
        return html.Div("Data load Failed")
    return html.Div("Data loaded")
                               
@app.callback(Output('dropdwn','options'),
              Input('upload-cmp_nme', 'contents'),
              State('upload-cmp_nme','filename'))                             
                        
def update_opt(content,file_name):
    if content is not None:
        options=[parse_platemap(a,b) for a,b in
                 zip(content,file_name)]
        return options


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

Hi @KofiM

Tip for shearing code:
image

To find errors you can print elements in the callback that will be showed in the prompt command line.
For example in your code you can add this prints:

def update_opt(content,file_name):
    print(content, file_name)
    if content is not None:
       options=[parse_platemap(a,b) for a,b in zip(content,file_name)]
   print(options)

   return options

And review if the options variable is showed with the same format I writted before.
Take present that you need to use prevent initial callback to avoid trigger the callback in first loading or in your case you can assign an empty ‘options’ using an ‘else:’ condition (in case content in None)

Ty for code sharing tip.

I’m sorry, I am not sure I follow your solution. Could you please clarify

just print the elements and see if they are showed the information as expected, also add an ‘options’ in the if statement for content equal to None:

@app.callback(Output('dropdwn','options'),
              Input('upload-cmp_nme', 'contents'),
              State('upload-cmp_nme','filename'))                             
def update_opt(content,file_name):
    print(content, file_name)
    if content is not None:
       options=[parse_platemap(a,b) for a,b in zip(content,file_name)]
    else:
       options=[{'label':"", 'value': ""}]
   print(options)

   return options

Then see if the callback is taking the content and the file name you are expecting and then if the options is bilding the apropiate format to send to the options output [{‘label’:“option-1”, ‘value’: “op-1”}, etc.]
An additional tip: In the callback the def line must be written in the first line affter the @app.callback.
Now I see that in your code there are a space line between them.

Hi Eduardo,

Seems like my data is being passed to the dropdown but the values are not displaying…

image

Show what is printing with print(options).

Well, you need to change the way you are building the options variable because you are assigning arrays to the label and value options instead of strings.

When I specify the index, I am able to pass that specific element to the dropdown successfully. But when I attempt to pass all the elements it seems to fail. Can you not assign multiple inputs at once? also I am able to pass an array if I specify the index is this a bug given that I’m passing np.arrays and not strings?

try:
        cmp_data=pd.read_csv(io.StringIO(decode.decode('utf-8')),sep=',',header=None)
        cmpd_col=np.array([cmp_data.iloc[2:18,0]])
        cmpd_lot=np.array([cmp_data.iloc[2:18,1]])
        cmpd_tie=np.concatenate((cmpd_col,cmpd_lot),axis=0)
        

        OPTIONS=take_to_dict(cmpd_tie)
        
    except:
        
        return html.Div("Data load Failed")
    #print(type(OPTIONS))
    return OPTIONS[1]```

image

Solution found, I had to specify the index in the update_function after the callback, and not in my parsing function. Thank you Eduardo!

def update_output(contents,file_names):
    #print(contents,file_names)
    
    if contents is not None:
        options=[parse_platemap(a,b) for a,b in zip(contents,file_names)]
    else:
        options=[{'label':"",'value':""}]
    
    print(options)
    return options[0]
1 Like

I’m sorry, I am not sure I follow your solution. Could you please clarify get-vidmate.com instasave.onl

My options variable is actually a list of dictionaries (16 total), when I originally returned options (return options) it was passed as a list of 1 element. Specifying the index fixed that and returns the 16 elements I originally wanted (return options[0]).

I’m sorry, I am not sure I follow your solution. Could you please clarify

@feedtaletwo

As I previously mentioned the option property needs the following format:

[{'label': 'option-1', 'value': 'op-1'}, {'label': 'option-2', 'value': 'op-2'}, {'label': 'option-n', 'value': 'op-n'}]

It means two elements for each options: Label and value.