Creating dd menus based on DataFrame

Hi I was trying to make multiple dropdown menus based on some columns of my DataFrame. But Im having some inefficient results in the time that takes to update the options of each dd menu because they all interact with each other. For example:
image

Let’s say I have this dataset and I want to update a secondary dd menu based on the person dd menu so if a choose person 1 the secondary dropdown menu should show me which car options I have available. At this point, nothing seems to be difficult. My problem is when I try to add the select all option in order to show all cars available for all the persons or if all will like to select all cars in order to check which IDs I have available for all cars.
I’m dealing with a lot of columns so I will like to have the select all option per each column but that takes me to a point where I started creating a bunch of conditions and then return that result to the second dropdown menu to update his options and imagine that multiple times.
So I was trying to see if there’s a way to map all options including the select all option to a dictionary so I just access to the specific option that I’m looking for but I can’t map the select all option to any value of my dataset because there is are no rows with select all.

Hello @seventy77,

Not sure if this is something you are after, but try this out:

import dash
from dash import Dash, html, dcc, Input, Output, State, ALL, MATCH, ctx
import pandas as pd

df = pd.DataFrame({'Col1': [1,1,1,2,2,2,3,3,3,4,4,4],
                   'Col2': [2,2,3,3,1,1,4,1,3,3,4,1],
                   'ID': [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]})

app = Dash(__name__)

app.layout = html.Div([dcc.Dropdown(id={'index': 0, 'type':'tierDropdowns'}, options=df['Col1'].unique()),
                       html.Div(id={'index':0, 'type':'tierDivs'})])

@app.callback(
    Output({'index': MATCH, 'type':'tierDivs'}, 'children'),
    Input({'index': MATCH, 'type':'tierDropdowns'}, 'value'),
    State({'index': ALL, 'type':'tierDropdowns'}, 'value'),
    prevent_initial_call=True
)
def makeDropDowns(v, i):
    if v:
        try:
            dff = df
            for x in range(ctx.triggered_id.index+1):
                print(x)
                col = dff.columns[x]
                dff = dff.query(f'{col}=={i[x]}')
            print(dff)
            return [dcc.Dropdown(id={'index': len(i), 'type':'tierDropdowns'},
                                 options=dff[df.columns[len(i)]].unique()),
                           html.Div(id={'index':len(i), 'type':'tierDivs'})]
        except:
            pass
    return []


app.run(debug=True)

This will continue to provide dropdowns through each column until there are none left. And if you clear one near the top, it removes all the other below it.

2 Likes

Hi @jinnyzor thanks for your solution. This looks very robust and sharp, I just got a final question, how if I will like to select not a specific option I mean maybe the user wants to check all the results for the first filter and then a specific result for the second filter. How do I map the ‘select_all’ option of the first in order to show me all available options in the second filter. This Sounds very easy for a second filter, but I was dealing with something like this:

This was my solution but I started to do a bunch of conditions that covered all possible scenarios in which the user can
select but this is so inefficient because I was evaluating 3 filter variables and in the next filter that I would like to add I have to evaluate 4 filters because this depends on the filter that I just created and I would need to start evaluating again all possible scenarios

Hey @seventy77,

Yes, this can work, here is a way to allow the select all.

import dash
from dash import Dash, html, dcc, Input, Output, State, ALL, MATCH, ctx
import pandas as pd

df = pd.DataFrame({'Col1': [1,1,1,2,2,2,3,3,3,4,4,4],
                   'Col2': [2,2,3,3,1,1,4,1,3,3,4,1],
                   'ID': [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]})

app = Dash(__name__)

app.layout = html.Div([dcc.Dropdown(id={'index': 0, 'type':'tierDropdowns'}, options=['select all']+[i for i in df['Col1'].unique()]),
                       html.Div(id={'index':0, 'type':'tierDivs'})])

@app.callback(
    Output({'index': MATCH, 'type':'tierDivs'}, 'children'),
    Input({'index': MATCH, 'type':'tierDropdowns'}, 'value'),
    State({'index': ALL, 'type':'tierDropdowns'}, 'value'),
    prevent_initial_call=True
)
def makeDropDowns(v, i):
    if v:
        try:
            dff = df
            for x in range(ctx.triggered_id.index+1):
                print(x)
                col = dff.columns[x]
                if v != 'select all':
                    dff = dff.query(f'{col}=={i[x]}')
            print(dff)
            return [dcc.Dropdown(id={'index': len(i), 'type':'tierDropdowns'},
                                 options=['select all']+[i for i in dff[df.columns[len(i)]].unique()]),
                           html.Div(id={'index':len(i), 'type':'tierDivs'})]
        except:
            pass
    return []


app.run(debug=True)

I also started messing around with allowing multiple selections, which got quite interesting, as well as allowing the user to select which columns to filter… Got very tricky.

1 Like

Let me try that @jinnyzor I appreciate your help a lot :). I am quite new in this dash world so I’m still learning. Got so many questions :P. How do you control the style of the dd that you are creating dynamically instead of vertically maybe you would like to have some of the prompts out horizontally and other verticals. Or maybe you would like to have a title for each dd created in order to know with which variable I’m dealing with

1 Like

The easiest way would to be using a component like dbc, which would allow for using rows and columns along with labels.

https://dash-bootstrap-components.opensource.faculty.ai/docs/components/layout/

Thanks!
@jinnyzor I found a little bug:

I selected option 1 in the first filter and select all in the second filter and

Seems like is not updating the correct options on the last dd menu based on columns 2 select all option.

The correct result is showed on the right side of the picture

Change:

if v != 'select all':if i[x] != 'select all':

1 Like

I’m struggling to access each component that you create dynamically. Is there a way to define first all the components and then fill in their values as you did? Maybe this is not that critical but at least I would like to not lose control of what I’m creating

How do you mean?

It uses pattern-matching id’s, so any id you’d have to reference that exactly.

I need to study the concept of pattern matching. At this point in time it looks hard for me how can I interact with other callballcks because all the dd menus are coming from the same function so I don’t have separated dash components to say for example:
Figure update if id-filter_1 is updated.

Is the figure based off of the same info and you want to apply the filters to it?

yes correct and other data tables should be sensible of these changes

You can output the altered dff to the other data tables, and these tables can drive the figures.

1 Like