I am trying to create a dynamic filtering with Ipywidgets. The word dynamic here refers to: if an option is chosen in one widget, it will affect the choices of the remaining widgets.
Here is a toy dataset for replication purposes.
toy_data = pd.DataFrame({"LETTER": ["A", "B", "A","B","A", "B", "A","B"],
"PLANT": ["Orange", "Carrots", "Lemon","Potato","Pomelo","Yam","Lime","Radish"],
"NUMBER": [1,2,3,4,5,6,7,8]})
Creating widgets:
letter_var = widgets.Dropdown(
description="Letter: ",
options=toy_data.LETTER.unique(),
value="A",
)
def letter_filtering(change):
clear_output(wait = True)
letter = letter_var.value
new_toy_data = toy_data[toy_data.LETTER == str(letter)]
plant_var = widgets.Dropdown(description="Plant: ", options=new_toy_data.PLANT.unique(), value="Orange")
return plant_var
the purpose of the letter_filtering function is to filter the choices in the wigets for plants. That is if the letter B has been chosen for letter_var, the choices in plant_var will only be limited to the letter B. but upon implementation,
widgets.HBox([letter_var,letter_filtering])
I am receiving a trait error.
TraitError: The 'children' trait of a HBox instance contains an Instance of a TypedTuple which expected a Widget, not the function 'letter_filtering'.
I think I'm lost on how to go about this.
I am not familiar with the widget mechanism, but you can achieve this via the "interact" functionality, which I think is also simpler:
from ipywidgets import interact, fixed
toy_data = pd.DataFrame({"LETTER": ["A", "B", "A","B","A", "B", "A","B"],
"PLANT": ["Orange", "Carrots", "Lemon","Potato","Pomelo","Yam","Lime","Radish"],
"NUMBER": [1,2,3,4,5,6,7,8]})
def inner_fn(plant, df_in):
df_ = df_in if plant == 'ALL' else df_in[df_in['PLANT'] == plant]
return df_
def outer_fn(letter):
df_ = toy_data if letter == 'ALL' else toy_data[toy_data['LETTER'] == letter]
plants = ['ALL'] + sorted(df_['PLANT'].unique())
interact(inner_fn, plant=plants, df_in=fixed(df_))
letters = ['ALL'] + sorted(toy_data['LETTER'].unique())
interact(outer_fn, letter=letters)
This will create a "plant" dropdown underneath the "letter" dropdown, like so:
And when a letter is chosen, the list of plants will update, like so:
The little extra indentation of the second dropdown box is a tip-off that this is a nested mechanism and admittedly makes this technique a bit unsightly.