Jupyter Notebook Widgets: Create dependent dropdowns - python

I want to create 2 dropdown widgets in my Jupyter Notebook. The dropdown content is taken from a dataframe.
Let's say I have a pandas dataframe consisting of 3 categorical variables 'a', 'b', 'c'. 'a' has 3 subtypes 'a1','a2' and 'a3'. 'b' and 'c' are similar to a in the sense that they also have their own subtypes. I want to create 2 dropdown widgets: the first dropdown widget will have ['a','b','c'], and the second dropdown widget will display subtypes depending on what variable the user selects for the first widget.
I honestly have any idea how to do this. I'll try to write out some codes for this:
import pandas as pd
from IPython.display import *
import ipywidgets as widgets
from ipywidgets import *
# Create the dataframe
df = pd.DataFrame([['a1','a2','a3'],
['b1','b2','b3'],
['c1','c2','c3']], index = ['a','b','c']).transpose()
# Widgets
widget1 = Dropdown(options = ['a','b','c'])
display(widget1)
widget2 = Dropdown(???????)
display(widget2)
And depending on what I select for the two dropdown widgets, I want some function executed.
Any help is appreciated.

I found out how to do this. I hope this helps for anyone else who's also looking to do the same thing.
x_widget = Dropdown(options = ['a','b','c'])
y_widget = Dropdown()
# Define a function that updates the content of y based on what we select for x
def update(*args):
y_widget.options = df[x_widget.value].unique().tolist()
x_widget.observe(update)
# Some function you want executed
def random_function():
...
interact(random_function,
x = x_widget,
y = y_widget);

Related

Jupyter dropdown menu to choose graph based on column name

I would like to make a dropdown menu that shows each column name from the dataset, and the user can choose a column to visualize the data.
With Python this code is:
for i in df.columns:
plt.figure(figsize = (10,6))
plt.title("Count vs "+i)
sns.histplot(data = df, x = i, element = 'bars')
I have tried the following code, which does not account for each column name, nor does it output charts similar to the above code.
def f(x):
return x
def f(age):
df.loc[age].iplot(
xTitle='Year',
yTitle='Count of {}'.format(age),
title='Count per Age'
)
interact(f, age=df.index)

Generate linked dropdown ipywidget in a for loop -> one per multiindex level

I would like to select part of a pandas MultiIndex DataFrame to apply some post-processing (mainly to plot some graphs).
To do so, I would like to use one DropDown ipywidgets per MultiIndex level. And I would like to have a function capable of generate this widgets no matter the number of levels the DataFrame have.
Let's assume we have the following DataFrame (MultiIndex with 3 levels):
d = {'num_legs': [4, 4, 2, 2],
'num_wings': [0, 0, 2, 2],
'class': ['mammal', 'mammal', 'mammal', 'bird'],
'animal': ['cat', 'dog', 'bat', 'penguin'],
'locomotion': ['walks', 'walks', 'flies', 'walks']}
df = pd.DataFrame(data=d)
df = df.set_index(['class', 'locomotion', 'animal'])
I would like to have a first dropdown widget allowing to choose among "mammal" or "bird". The second dropdown widget, will then allow the choice among "walks" and "flies" depending on the first choice (actually, choosing bird will give only "walks" as possible choice because of DataFrame data). The third one would propose the available animals depending on the previous choices.
Here follows my code...nearly working : the first two dropdowns interact correctly but the subsequent ones keeps staying empty...
def multi_index_dropdown(df,n_of_levels_to_expands=None) :
n_of_levels_to_expands = df.index.nlevels
dd = {}
dd[0] = widgets.Dropdown(options=df.index.unique(level=0),
description=df.index.names[0],
disabled=False)
def make_update(ln) :
def update_level(*args) :
dd[ln].options = df.loc[dd[ln-1].value].index.unique(level=0)
return update_level
update_levels = {}
for level_n in range(1,n_of_levels_to_expands) :
dd[level_n] = widgets.Dropdown(description=df.index.names[level_n],
disabled=False)
update_levels[level_n] = make_update(level_n)
dd[level_n-1].observe(update_levels[level_n],'value')
display(dd[0],dd[1],dd[2])
return dd
choice = multi_index_dropdown(df)
What's going wrong here?
Additional information:
I am working with jupyter lab (I do not think this is important...).
This is the first time I use a function (make_update) to create function in a for loop. However, I understood it was useful to pass the proper "level_n" argument...hoping I used it right!
My bad, it was just an error in the update_level function (I did not undo some changes I made to simplify my problem) :
It should have been as follows :
def update_level(*args) :
xs = tuple(dd[i].value for i in range(ln))
dd[ln].options = df.loc[xs].index.unique(level=0)
By the time, I have restructured my function in a class. The whole working code is the following :
class multi_index_dropdown(object) :
def __init__(self, df, n_of_levels_to_expands=None) :
if n_of_levels_to_expands is None :
self.n_of_levels_to_expands = df.index.nlevels
else:
self.n_of_levels_to_expands = n_of_levels_to_expands
self.multiple_dd = {}
self.df = df
self.create()
def make_update(self,ln) :
def update_level(*args) :
xs = tuple(self.multiple_dd[i].value for i in range(ln))
self.multiple_dd[ln].options = self.df.loc[xs].index.unique(level=0)
return update_level
def create(self) :
self.multiple_dd[0] = widgets.Dropdown(
options=self.df.index.unique(level=0),
description=self.df.index.names[0],
disabled=False)
self.update_levels = {}
for level_n in range(1,self.n_of_levels_to_expands) :
self.multiple_dd[level_n] = widgets.Dropdown(
description=self.df.index.names[level_n],
disabled=False)
self.update_levels[level_n] = self.make_update(level_n)
self.multiple_dd[level_n-1].observe(self.update_levels[level_n],'value')
display(self.multiple_dd[0],self.multiple_dd[1],self.multiple_dd[2])
choice = multi_index_dropdown(df)

Python Streamlit - filter pandas dataframe without rerun entire script

I have the following code:
import streamlit as st
import pandas as pd
#define data
d = {'id': ['a', 'b', 'c'], 'data': [3, 4,6]}
df = pd.DataFrame(data=d)
#create sidebar input
with st.sidebar.form("my_form"):
a = st.slider('sidebar for testing', 5, 10, 9)
calculate = st.form_submit_button('Calculate')
if calculate:
df['result'] = df['data'] + a
st.write(df)
#no issues up to this point. When I move the slider in 10 the output in 16 stays on the web page
########debug############
# I am trying to select an 'id' from the dropdown and use that to filter df, but when I select a value from the dropdown,
# the script runs again and the output disappears
filter = st.selectbox('filter data', df['id'].unique())
st.write(df[df['id'] == filter])
I would like to filter the Pandas dataframe using a drop down menu to select the id I am interested in, but when I use the drop down the code reruns.
Any idea how I can solve this?
PS I also tried enclosing the entire computation in a function and adding the #st.cache decorator, but without success. I would appreciate it if anyone could show me how it’s done.
I was able to get this behavior by not using the submit button. Streamlit reruns the script from top to bottom any time there's user input, so the form submit resets as well.
d = {'id': ['a', 'b', 'c'], 'data': [3, 4, 6]}
df = pd.DataFrame(data=d)
a = st.slider('sidebar for testing', 5, 10, 9)
df['result'] = df['data'] + a
st.write(df)
# Now this will show the filtered row in the dataframe as you change the inputs
filter = st.selectbox('filter data', df['id'].unique())
st.write(df[df['id'] == filter])
For more complicated workflows, I'd refactor this and cache data that gets loaded in, but for filtering your dataframe, this should work.
Streamlit always re-runs the code on each user-submission. You can however solve this with st.session_state, which allows sharing states between reruns. Its api is a lot like a standard python dictionary.
Here is your example with st.session_state:
import streamlit as st
import pandas as pd
#define data
d = {'id': ['a', 'b', 'c'], 'data': [3, 4,6]}
df = pd.DataFrame(data=d)
#create sidebar input
with st.sidebar.form("my_form"):
a = st.slider('sidebar for testing', 5, 10, 9)
calculate = st.form_submit_button('Calculate')
# Initialization
if 'button_pressed' not in st.session_state:
st.session_state['button_pressed'] = False
# Changes if calculated button is pressed
if calculate:
st.session_state['button_pressed'] = True
# Conditional on session_state instead
if st.session_state['button_pressed']:
df['result'] = df['data'] + a
st.write(df)
#no issues up to this point. When I move the slider in 10 the output in 16 stays on the web page
########debug############
# I am trying to select an 'id' from the dropdown and use that to filter df, but when I select a value from the dropdown,
# the script runs again and the output disappears
filter = st.selectbox('filter data', df['id'].unique())
st.write(df[df['id'] == filter])

Use Holoviz Panel Dropdown value to query dataframe

I am trying to use a Holoviz Panel dropdown widget value to query a dataframe. The dataframe however does not reflect the change in the dropdown value. I added a markdown widget to check if the change in the dropdown value is being captured - It seems to be. However, I can't figure out how to update the dataframe. I am a complete beginner to programming, just trying to learn. Any help is appreciated.
import pandas as pd
import panel as pn
pn.extension()
# Dataframe
df = pd.DataFrame({'CcyPair':['EUR/USD', 'AUD/USD' ,'USD/JPY'],
'Requester':['Client1', 'Client2' ,'Client3'],
'Provider':['LP1', 'LP2' ,'LP3']})
# Dropdown
a2 = pn.widgets.Select(options=list(df.Provider.unique()))
# Query dataframe based on value in Provider dropdown
def query(x=a2):
y = pn.widgets.DataFrame(df[(df.Provider==x)])
return y
# Test Markdown Panel to check if the dropdown change returns value
s = pn.pane.Markdown(object='')
# Register watcher and define callback
w = a2.param.watch(callback, ['value'], onlychanged=False)
def callback(*events):
print(events)
for event in events:
if event.name == 'value':
df1 = query(event.new)
s.object = event.new
# Display Output
pn.Column(query, s)
Output Image
Inspired by the self-answer, the following code produces a select box containing the list of providers and a dataframe filtered on that selection. It was tested on Panel version 0.13.1.
Note that the watch=True suggestion in the self-answer wasn't necessary.
import pandas as pd
import panel as pn
pn.extension()
# Dataframe
df = pd.DataFrame({
'CcyPair':['EUR/USD', 'AUD/USD' ,'USD/JPY'],
'Requester':['Client1', 'Client2' ,'Client3'],
'Provider':['LP1', 'LP2' ,'LP3']
})
# Dropdown
providers = list(df.Provider.unique())
select_widget = pn.widgets.Select(options=providers)
# Query dataframe based on value in Provider dropdown
#pn.depends(select_widget)
def query(x):
filtered_df = pn.widgets.DataFrame(df[df.Provider==x])
return filtered_df
# Display Output
pn.Column(select_widget, query)
Figured it out, turned out I just needed to add #pn.depends above my query function. Once I added pn.depends(a2, watch=True), the dataframe was filtered based on a2 input. The callback and watcher were unnecessary.

Show/Hide Bokeh widgets based on another widget

I have a checkbox A that has two options, and I want to display different widgets based on the options. So when click option 1 only widgets B shows, and click option 2 only widget C shows. I was able to hide widgets B and C with css_classes = ['hidden'] but don't know how to make them show conditionally. I figured it to be something like this:
if 0 in Checkbox_A.active:
wiget_B.show()
else:
widget_C.show()
In Bokeh, you can either link the whole models together or link signals. Assuming you're using bokeh serve, something like this will work:
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import Div, CheckboxGroup
d1 = Div(text="First widget")
d2 = Div(text="Second widget", visible=False)
c = CheckboxGroup(labels=["Display the first widget"], active=[0])
def checkbox_changed(attr, old, new):
b = (new == [0])
d1.visible = b
d2.visible = not b
c.on_change('active', checkbox_changed)
curdoc().add_root(column(c, d1, d2))
But it's possible to do something like that purely on the JavaScript side.

Categories