MultiSelect widget with multiple options for each value

Hello! I would like to use the MultiSelect panel widget with multiple options for each value, and then use the selected option to render a plot, here is a simple test case of what I’m willing to do:

How can I have the same behavior in case of MultiSelect (display only unique values of the keys) and to link the widget to the plot should I use in this case @pn.depends(ticker.param.options) instead of value?
I’m kinda new to Panel and Python, I will appreciate your help, Thank you

Hi, if you want to display only unique triples for your MultiSelect, you can use the drop_duplicates function when you set the options.

multi_select = pn.widgets.MultiSelect(name='Test', 
                          value=test_list,
                          options= test_dtf[test_list].drop_duplicates().values.tolist())

For linking the selector with a plot, you can make a param.Parameterized class. In that case you can use a param.ListSelector instead of a pn.widgets.MultiSelect.

Here is an example :

import panel as pn
import pandas as pd
import param 
import holoviews as hv
import hvplot.pandas

class AppTest(param.Parameterized):
    test_dict = {"key1" : ['A']*20,
                 "key2" : [1]*20,
                 "key3": [1,2]*10,
                 "value" : [100]*20}

    test_dtf = pd.DataFrame(test_dict)
    test_list = ['key1','key2','key3']
    multi_select = param.ListSelector(objects=test_dtf[test_list].drop_duplicates().values.tolist())

    @param.depends('multi_select')
    def view(self):
        if not self.multi_select:
            return hv.Curve([])
        else:
            df=pd.DataFrame(self.multi_select,columns=['val','x','y'])
            return df.hvplot.scatter('x','y')

pn.extension()
viewer = AppTest()
pn.Row(pn.Param(viewer.param),viewer.view)
1 Like

@AurelienSciarra Thank you so much! I would like to know what is the difference between param.Parameterized class and the @pn.depends , and if I could use the last one in this cas?

@param.depends is used for linking param objects and functions , but param widgets only works in a param.Parameterized class.

@pn.depends links panel.widgets and function. You can use it inside or outside a param.Parameterized class. In this case you can replace the @param.depends(‘multi_select’) by @param.depends(‘multi_select.value’) and replace the param multi_select by a panel.widgets.

Like that :

import panel as pn
import pandas as pd
import param 
import holoviews as hv
import hvplot.pandas

class AppTest(param.Parameterized):
    test_dict = {"key1" : ['A']*20,
                 "key2" : [1]*20,
                 "key3": [1,2]*10,
                 "value" : [100]*20}

    test_dtf = pd.DataFrame(test_dict)
    test_list = ['key1','key2','key3']
    default_value=test_dtf[test_list].drop_duplicates().values.tolist()[0]
    multi_select = pn.widgets.MultiSelect(name='Test', value=[default_value]   ,options= test_dtf[test_list].drop_duplicates().values.tolist())
    
    
    @pn.depends('multi_select.value')
    def view(self):
        if len(self.multi_select.value) ==0:
            return hv.Curve([])
        else:
            df=pd.DataFrame(self.multi_select.value,columns=['val','x','y'])
            return df.hvplot.scatter('x','y')

pn.extension()
viewer = AppTest()
pn.Row(viewer.multi_select,viewer.view) 

If you want to know if you can do it with pn.depends() and without a param.Parameterized class. I’ll need more detail on what you want to do (what type of function do you want to link). Since I always make a class when I want to create a small app, I don’t really know how to do it this way. Plus in my opinion It’s easier to manage the interaction with param than using panel widgets.

1 Like

Thank you so much , you are so helful! In fact I discovered Panel, reading this Medium article (https://towardsdatascience.com/how-to-build-a-time-series-dashboard-in-python-with-panel-altair-and-a-jupyter-notebook-c0ed40f02289) , so I would like to use the same example, but instead of 1 key (here company), I’m using a list of keys, and for each key a list of values, here is the notebook:
https://github.com/bendoesdata/panel-altair-dashboard/blob/master/panel-altair-demo.ipynb

I didn’t understand clearly what you want to do, you said that you consider Company as a key and you want to use multiple key with for each key multiple values. So I was wondering if multi_select is really what you need.

If you have a small number of keys you can use a selector for each key, you will have all the combinations.
In that case you just need to add those new selector to pn.depends and to the signature of the get_plot function.

Another case : If you only need to chose a couple (key, value) you can check this topic

But if you’re good with your MultiSelect, replacing ticker.param.value by multi_select.param.value would do the work. You will need to check if multi_select is empty , if you have selected only 1 value and the case where you have selected several values. Also I have made a mistake with the instantiation of the MultiSelect widget :

multi_select = pn.widgets.MultiSelect(name='Test', 
                          value = test_dtf[test_list].drop_duplicates().values.tolist()[0],
                          options = test_dtf[test_list].drop_duplicates().values.tolist())

First of all, I do appreciate your insightful comments, I don’t have a fixed number of keys nor values for each key, so I can’t create a selector for each one.
I would like to do this dynamically using MultiSelect but it doesn’t seem to work for this example when I use " multi_select.param.value", the plot does not show in the output, when I checked I found that the selection was empty! here is an example of my code:
https://colab.research.google.com/drive/1I9_bcJTwcOqK1teEJvfslZb-Xv8Zn3M9?usp=sharing
I’m working on this issue to understand the reason why it doesn’t work and I will try the couple ( key, value ) as well, if you have any other suggestions please let me know, and Thank you again.

I can’t use altair on my computer because of javascript error, so I haven’t tested your code yet, but I think you can’t use nested list in transform_filter(). A solution would be to apply your filters on the dataframe instead of applying them on the Chart. Here is an example with hvplot :

import panel as pn
import pandas as pd
import datetime as dt
from vega_datasets import data
import numpy as np
import hvplot.pandas


dtf = data.stocks()
dtf=pd.DataFrame(dtf)
dtf['key2']=[1]*560
dtf['key3']=[2]*560
title='title'
subtitle='subtitle'

test_list = ['symbol','key2','key3']
multi_select = pn.widgets.MultiSelect(name='Test',value = dtf[test_list].drop_duplicates().values.tolist()[0],
                                     options = dtf[test_list].drop_duplicates().values.tolist())

date_range_slider = pn.widgets.DateRangeSlider(name='Date Range slider',start=dt.datetime(2001,1,1), end=dt.datetime(2010,1,1),
                                                value=(dt.datetime(2001,1,1),dt.datetime(2010,1,1)))

@pn.depends(multi_select.param.value,date_range_slider.param.value)
def get_plot(multi_select,date_range):
    df = dtf
    df['date'] = pd.to_datetime(df['date'])
    
    start_date = date_range_slider.value[0]
    end_date = date_range_slider.value[1]
    mask = (df['date'] > start_date) & (df['date'] <= end_date)
    df = df.loc[mask]
    
    if len(multi_select)==0:
        _multi_select=[]
    elif isinstance(multi_select[0],str):
        _multi_select=[multi_select]
    else:
        _multi_select=multi_select
    
    ms=list(map(lambda x : ','.join(np.array(x).astype(str)),_multi_select))
    df=df[df[test_list].apply(lambda x : ",".join(x.astype(str).values) in ms,axis=1)]
    return df.hvplot.area('date','price').opts(color='#0c1944',alpha=0.8)


dashboard = pn.Row(pn.Column(title,subtitle,multi_select,date_range_slider),get_plot)

dashboard
1 Like

Thank you @AurelienSciarra, I discovered with you another amazing library hvplot!
You’re right transform_filter can’t be used for nested list, I tested the example you gave me, and it works fine with multiple keys, however it doesn’t when working with one or multiple keys (!= str), so I changed the condition to include this use case as well but the output hvplot is empty:

import panel as on
import pandas as pd
import datetime as dt
from vega_datasets import data
import numpy as np
import hvplot.pandas


dtf = data.stocks()
dtf=pd.DataFrame(dtf)
dtf['key2']=[1]*560
#dtf['key3']=[2]*560
title='title'
subtitle='subtitle'

#test_list = ['symbol','key2','key3']
test_list = ['key2']
multi_select = pn.widgets.MultiSelect(name='Test',value = 
dtf[test_list].drop_duplicates().values.tolist()[0],
                                 options = dtf[test_list].drop_duplicates().values.tolist())

date_range_slider = pn.widgets.DateRangeSlider(name='Date Range 
slider',start=dt.datetime(2001,1,1), end=dt.datetime(2010,1,1),
                                            value=(dt.datetime(2001,1,1),dt.datetime(2010,1,1)))

@pn.depends(multi_select.param.value,date_range_slider.param.value)
def get_plot(multi_select,date_range):
print(len(multi_select))
df = dtf
df['date'] = pd.to_datetime(df['date'])

start_date = date_range_slider.value[0]
end_date = date_range_slider.value[1]
mask = (df['date'] > start_date) & (df['date'] <= end_date)
df = df.loc[mask]

if len(multi_select)==0:
    _multi_select=[]
elif len(multi_select)==1:
    _multi_select=[multi_select]
else:
    _multi_select= multi_select

ms=list(map(lambda x : ','.join(np.array(x).astype(str)),_multi_select))
df=df[df[test_list].apply(lambda x : ",".join(x.astype(str).values) in ms,axis=1)]
return df.hvplot.area('date','price').opts(color='#0c1944',alpha=0.8)


dashboard = pn.Row(pn.Column(title,subtitle,multi_select,date_range_slider),get_plot)

dashboard

In your example multi_select = [[1]] which verify len(multi_select)==1 but that means _multi_select = [[[1]]].
That will cause an error when we compute ms.

You need to change the condition by :

if len(multi_select)==0:
        _multi_select=[]
elif len(np.shape(multi_select))<=1:
        _multi_select=[multi_select]
else:
        _multi_select=multi_select 

And sort values by date

return df.sort_values('date').hvplot.area('date','price').opts(color='#0c1944',alpha=0.8)

with those changes it would works with one or multiple keys

1 Like

I’m so grateful for your help :pray: