Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
holoviz
GitHub Repository: holoviz/panel
Path: blob/main/doc/tutorials/intermediate/parameters.md
2012 views

Reactive Parameters

Welcome to the world of reactive parameters in Panel! In this section, we'll delve into the powerful capabilities offered by Parameters in the HoloViz ecosystem.

Understanding Parameters

Panel and other projects in the HoloViz ecosystem build on the foundation provided by Param. Param offers a robust framework for adding validation, documentation, and interactivity to projects. Similar to projects like Pydantic in some aspects, but Param focuses on providing APIs that simplify the expression of complex dependencies, reactivity, and UI interactions.

In this section, we won't delve into the inner workings of Param but rather focus on understanding the fundamentals. By the end of this section, we will:

  • Understand the difference between the Parameter value and the Parameter object.

  • Know how to use Parameter objects, bound functions, and reactive expressions as proxies or references for their current value.

import panel as pn pn.extension('tabulator')

Exploring Parameters

Parameters serve as objects expressing the semantics of a value of an attribute on an object, encompassing not only Python types but also broader semantics.

Let's define a simple Parameterized class with a value Parameter:

import param class Text(param.Parameterized): value = param.String(default='', allow_None=True, doc="The text value")

Now, let's create an instance and examine its current value:

text = Text(value="Are you a Python programmer? If so, you need Param!") text.value

We can also inspect its Parameter instance:

text.param.value

The Utility of Parameters

Parameters act as references to underlying values, making them invaluable for enhancing UI interactivity.

Parameters imbue validation, documentation, and interactivity into Panel, with most Panel components built as Parameterized classes with Parameters.

For instance, consider the value Parameter of the Panel TextInput widget:

class TextInput(pn.widgets.Widget): ... value = param.String(default='', allow_None=True, doc=""" Initial or entered text value updated when <enter> key is pressed.""")

To explore all parameters on a Parameterized class, we inspect the .param namespace:

pn.widgets.TextInput.param

Utilizing Parameters

Let's now delve into practical usage scenarios.

Accessing Parameter Values

Consider working with a TextInput widget:

text_input = pn.widgets.TextInput(value='A string!') text_input

We can access its current value:

text_input.value

Additionally, we can access the Parameter instance, acting as a proxy or reference for the value:

text_input.param.value

Setting Parameter Values

We can set it to a new value:

text_input.value = 'Hello World!'

For setting multiple parameter values, use the .param.update method:

text_input.param.update(value='Updated!', width=85);

Or use it as a context manager for temporary value setting:

from time import sleep with text_input.param.update(value='Temporary'): sleep(1)

Validation

Parameters perform validation, raising errors for invalid assignments:

import traceback as tb try: text_input.value = 3.14 except ValueError as e: tb.print_exc()

Parameters as References

Parameters serve as proxies for underlying Parameter values, facilitating interactive declarations.

Consider a simple example using the TextInput widget:

import panel as pn pn.extension() text_in = pn.widgets.TextInput(value='Hello world!') text_out = pn.pane.Markdown(text_in.param.value) pn.Column(text_in, text_out).servable()

Observe how changes in TextInput are automatically reflected in the Markdown output.

This also works when using it for other parameters, e.g. we can add a switch to toggle the visibility of some component:

visible = pn.widgets.Switch(value=True) pn.Row(visible, pn.pane.Markdown('Hello World!', visible=visible)).servable()

Many parameters that accept a container such as a dictionary or list can also resolve references when they are nested, e.g. if we declare a styles dictionary one of the values can be a widget:

color = pn.widgets.ColorPicker(value='red') md = pn.pane.Markdown('Some Text!', styles={'color': color}) pn.Row(color, md).servable()

Notice that we passed in the widget object directly instead of the .param.value. This is possible because widgets are treated as a proxy of their value parameter just like a Parameter is treated as a proxy for current value.

Transforming Parameters

Transforming parameters allows for complex value processing pipelines, offering immense flexibility.

For instance, we can create a reactive format string, filling in values based on widget input:

import panel as pn pn.extension() text_input = pn.widgets.TextInput(value='World') text = pn.rx('**Hello {}!**').format(text_input) md = pn.pane.Markdown(text) pn.Row(text_input, md).servable()

Similarly, we can make a DataFrame reactive:

import pandas as pd import panel as pn pn.extension() data_url = 'https://assets.holoviz.org/panel/tutorials/turbines.csv.gz' df = pn.cache(pd.read_csv)(data_url) dfrx = pn.rx(df) dfrx.head(2)

Please explore the potential by replacing 2 with an instance of an IntSlider.

:::{dropdown} Solution

import pandas as pd import panel as pn pn.extension() data_url = 'https://assets.holoviz.org/panel/tutorials/turbines.csv.gz' df = pn.cache(pd.read_csv)(data_url) dfrx = pn.rx(df) slider = pn.widgets.IntSlider(value=2, start=1, end=10) pn.panel(dfrx.head(slider)).servable()

:::

Now let's get a little more complex and write a whole pipeline that selects the desired columns, samples a number of random rows, and then applies some custom styling highlighting the rows with the highest value:

import pandas as pd import panel as pn import numpy as np pn.extension() data_url = 'https://assets.holoviz.org/panel/tutorials/turbines.csv.gz' df = pn.cache(pd.read_csv)(data_url) dfrx = pn.rx(df) cols = pn.widgets.MultiChoice( options=df.columns.to_list(), value=['p_name', 't_state', 't_county', 'p_year', 'p_cap'], height=300 ) nrows = pn.widgets.IntSlider(start=5, end=20, step=5, value=15, name='Samples') style = pn.rx('color: white; background-color: {color}') color = pn.widgets.ColorPicker(value='darkblue', name='Highlight color') def highlight_max(s, props=''): if s.dtype.kind not in 'f': return np.full_like(s, False) return np.where(s == np.nanmax(s.values), props, '') styled_df = dfrx[cols].sample(nrows).style.apply(highlight_max, props=style.format(color=color), axis=0) pn.pane.DataFrame(styled_df).servable()

As you can see, the Pandas code is identical to what you might have written if you were working with a regular DataFrame. However, now you can use widgets and even complex expressions as inputs.

Try replacing pn.pane.DataFrame with pn.panel to display an interactive component with widgets.

:::{dropdown} Solution

import pandas as pd import panel as pn import numpy as np pn.extension() data_url = 'https://assets.holoviz.org/panel/tutorials/turbines.csv.gz' df = pn.cache(pd.read_csv)(data_url) dfrx = pn.rx(df) cols = pn.widgets.MultiChoice( options=df.columns.to_list(), value=['p_name', 't_state', 't_county', 'p_year', 'p_cap'], height=300 ) nrows = pn.widgets.IntSlider(start=5, end=20, step=5, value=15, name='Samples') style = pn.rx('color: white; background-color: {color}') color = pn.widgets.ColorPicker(value='darkblue', name='Highlight color') def highlight_max(s, props=''): if s.dtype.kind not in 'f': return np.full_like(s, False) return np.where(s == np.nanmax(s.values), props, '') styled_df = dfrx[cols].sample(nrows).style.apply(highlight_max, props=style.format(color=color), axis=0) pn.panel(styled_df).servable()

:::

Exercise: Scale the Font Size

Write a small app where you can scale the font-size of a Markdown pane with another widget, e.g. an IntSlider. The font-size can be set using the styles parameter.

:::{hint}

  • The styles parameter only accepts dictionaries of strings but IntSlider returns int types.

  • The font-size value should be a string value in pixels, eg. "15px" is a valid font-size.

:::

:::{dropdown} Solution 1: Reactive String Formatting

import panel as pn pn.extension() intslider = pn.widgets.IntSlider(value=10, start=5, end=100, step=10, name="Font Size") styles_rx = {"font-size": pn.rx("{value}px").format(value=intslider)} markdown = pn.pane.Markdown("We love dataviz!", styles=styles_rx) pn.Column(intslider, markdown).servable()

:::

:::{dropdown} Solution 2: Reactive Function

import panel as pn pn.extension() intslider = pn.widgets.IntSlider(value=10, start=5, end=100, step=10, name="Font Size") def styles(font_size): return {"font-size": f"{font_size}px"} styles_rx = pn.rx(styles)(intslider) markdown = pn.pane.Markdown("We love dataviz!", styles=styles_rx) pn.Column(intslider, markdown).servable()

:::

::::{dropdown} Solution 3: Bound Function

import panel as pn pn.extension() intslider = pn.widgets.IntSlider(value=10, start=5, end=100, step=10, name="Font Size") def styles(font_size): return {"font-size": f"{font_size}px"} styles_bn = pn.bind(styles, font_size=intslider) markdown = pn.pane.Markdown("We love dataviz!", styles=styles_bn) pn.Column(intslider, markdown).servable()

:::{note}

pn.bind is the predecessor of pn.rx. We recommend using pn.rx over pn.bind as it's much more flexible and efficient. We include this example because you will find lots of examples in the Panel documentation and in the Panel community using pn.bind.

:::

::::

To harness the full potential of Param and Reactive Parameters in Panel, we recommend studying the Param User Guide and the ReactiveExpr reference guide.

Recap

Param serves as the backbone of Panel, providing a robust framework for adding validation, documentation, and interactivity. By grasping the fundamentals discussed here, you'll be well-equipped to leverage Panel's interactivity and reactivity effectively.

Now, we should be able to:

  • Clearly understand the distinction between Parameter values and Parameter objects.

  • Use Parameter objects, bound functions, and expressions as proxies or references for their current value.

Resources

Explanation

How-To