Last active
September 1, 2021 07:47
-
-
Save MarcSkovMadsen/c457538cc0a3b60fb248a2c9dfdebdf3 to your computer and use it in GitHub Desktop.
Examples of Parent-Child related selections using HoloViz Panel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import panel as pn | |
import pandas as pd | |
import param | |
pn.extension(sizing_mode="stretch_width") | |
LOGO = "https://holoviz.org/assets/panel.png" | |
ACCENT_BASE_COLOR = "#4099da" | |
SELECTED_COLOR = "#2c6b98" | |
DATA_URL = "https://raw.githubusercontent.com/lukes/ISO-3166-Countries-with-Regional-Codes/master/all/all.csv" | |
try: | |
DATA = pd.read_csv("country_data.csv") | |
except: | |
DATA = pd.read_csv(DATA_URL) | |
DATA.to_csv("country_data.csv", index=False) | |
DATA=DATA.dropna(subset=["region"]) | |
# GENERAL, REUSABLE CODE START | |
def to_sorted_list_of_cleaned_values(lst): | |
return list(sorted(str(val) for val in lst)) | |
def get_parents(data, parent_column): | |
return to_sorted_list_of_cleaned_values(data[parent_column].unique()) | |
def get_children(parent, data, parent_column, child_column): | |
if parent: | |
return to_sorted_list_of_cleaned_values(data[data[parent_column]==parent][child_column].unique()) | |
def to_parent_child_dict(data, parent_column, child_column): | |
parents = get_parents(data, parent_column) | |
parent_child_dict = {} | |
for parent in parents: | |
parent_child_dict[parent]=get_children(parent, data, parent_column, child_column) | |
return parents, parent_child_dict | |
def configure_parent_child_parameters(obj, parent_parameter, child_parameter, parents, parent_child_map): | |
obj.param[parent_parameter].objects=parents | |
if parents: | |
setattr(obj, parent_parameter, parents[0]) | |
def _update_child_widget(parent): | |
# Depending on the use case the parent_child_map could be a lookup function instead | |
objects = parent_child_map[parent] | |
obj.param[child_parameter].objects = objects | |
if getattr(obj, child_parameter) in objects: | |
pass | |
elif objects: | |
setattr(obj, child_parameter, objects[0]) | |
pn.bind(_update_child_widget, parent=obj.param[parent_parameter], watch=True) | |
_update_child_widget(getattr(obj, parent_parameter)) | |
# GENERAL, REUSABLE CODE END | |
class Selection(param.Parameterized): | |
region = param.Selector() | |
country = param.Selector() | |
_widgets = { | |
"region": {"max_width": 293}, | |
"country": {"max_width": 293}, | |
} | |
def __init__(self, **params): | |
super().__init__(**params) | |
regions, region_country_map = to_parent_child_dict(DATA, "region", "name") | |
configure_parent_child_parameters(self, "region", "country", regions, region_country_map) | |
self._panel = pn.Param(self, widgets=self._widgets) | |
def __panel__(self): | |
return self._panel | |
selection = Selection() | |
def get_country_info(country): | |
return DATA[DATA["name"]==country].T | |
get_country_info=pn.bind(get_country_info, country=selection.param.country) | |
pn.template.FastListTemplate( | |
site="Awesome Panel", | |
logo=LOGO, | |
title="Parent-Child Selections Example", | |
sidebar=["## Selection", selection,], | |
main=[pn.Column("## Country Info", get_country_info)], | |
accent_base_color=ACCENT_BASE_COLOR, | |
header_background=ACCENT_BASE_COLOR, | |
).servable() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import panel as pn | |
import pandas as pd | |
import param | |
pn.extension(sizing_mode="stretch_width") | |
LOGO = "https://holoviz.org/assets/panel.png" | |
ACCENT_BASE_COLOR = "#4099da" | |
SELECTED_COLOR = "#2c6b98" | |
DATA_URL = "https://raw.githubusercontent.com/lukes/ISO-3166-Countries-with-Regional-Codes/master/all/all.csv" | |
try: | |
DATA = pd.read_csv("country_data.csv") | |
except: | |
DATA = pd.read_csv(DATA_URL) | |
DATA.to_csv("country_data.csv", index=False) | |
DATA=DATA.dropna(subset=["region"]) | |
# GENERAL, REUSABLE CODE START | |
def to_sorted_list_of_cleaned_values(lst): | |
return list(sorted(str(val) for val in lst)) | |
def get_parents(data, parent_column): | |
return to_sorted_list_of_cleaned_values(data[parent_column].unique()) | |
def get_children(parent, data, parent_column, child_column): | |
if parent: | |
return to_sorted_list_of_cleaned_values(data[data[parent_column]==parent][child_column].unique()) | |
def to_parent_child_dict(data, parent_column, child_column): | |
parents = get_parents(data, parent_column) | |
parent_child_dict = {} | |
for parent in parents: | |
parent_child_dict[parent]=get_children(parent, data, parent_column, child_column) | |
return parents, parent_child_dict | |
def configure_parent_child_widgets(parent_widget, child_widget, parents, parent_child_map): | |
parent_widget.options=parents | |
if parents: | |
parent_widget.value=parents[0] | |
print(parent_widget.value, parent_widget.options) | |
def _update_child_widget(parent): | |
# Depending on the use case the parent_child_map could be a lookup function instead | |
options = parent_child_map[parent] | |
child_widget.options = options | |
if child_widget.value in options: | |
pass | |
elif options: | |
child_widget.value = options[0] | |
pn.bind(_update_child_widget, parent=parent_widget, watch=True) | |
_update_child_widget(parent_widget.value) | |
# GENERAL, REUSABLE CODE START | |
region_widget = pn.widgets.Select(name="Region") | |
country_widget = pn.widgets.Select(name="Country", max_width=293) | |
regions, region_country_map = to_parent_child_dict(DATA, "region", "name") | |
configure_parent_child_widgets(region_widget, country_widget, regions, region_country_map) | |
def get_country_info(country): | |
return DATA[DATA["name"]==country].T | |
get_country_info=pn.bind(get_country_info, country=country_widget) | |
pn.template.FastListTemplate( | |
site="Awesome Panel", | |
logo=LOGO, | |
title="Parent-Child Selections Example", | |
sidebar=["## Selection", region_widget, country_widget], | |
main=[pn.Column("## Country Info", get_country_info)], | |
accent_base_color=ACCENT_BASE_COLOR, | |
header_background=ACCENT_BASE_COLOR, | |
).servable() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
An example of parent-child related selections in HoloViz Panel.
You can serve the examples via
panel serve parent_child_widgets.py
orpanel serve parent_child_parameters.py
.At first it might look like a lot of code. But most code is either something that you would have been writing for other reasons or re-useable code you could put in your
utils.py
file.For example for configuring the widgets the core code is.
This could probably be further refactored to one line.
Some things to consider when creating these kinds of relations is
nan
s, converted values to strings and sorted them.panel-parent-child-relations.mp4
For further inspiration check out