Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Docs] Dynamic filters #891

Merged
merged 12 commits into from
Nov 25, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!--
A new scriv changelog fragment.

Uncomment the section that is right (remove the HTML comment wrapper).
-->

### Highlights ✨

- Filters update automatically when underlying dynamic data changes. ([#879](https://github.com/mckinsey/vizro/pull/879))
antonymilne marked this conversation as resolved.
Show resolved Hide resolved

<!--
### Removed

- A bullet item for the Removed category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX. ([#1](https://github.com/mckinsey/vizro/pull/1))

-->
<!--
### Added

- A bullet item for the Added category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX. ([#1](https://github.com/mckinsey/vizro/pull/1))

-->
<!--
### Changed

- A bullet item for the Changed category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX. ([#1](https://github.com/mckinsey/vizro/pull/1))

-->
<!--
### Deprecated

- A bullet item for the Deprecated category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX. ([#1](https://github.com/mckinsey/vizro/pull/1))

-->
<!--
### Fixed

- A bullet item for the Fixed category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX. ([#1](https://github.com/mckinsey/vizro/pull/1))

-->
<!--
### Security

- A bullet item for the Security category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX. ([#1](https://github.com/mckinsey/vizro/pull/1))

-->
86 changes: 76 additions & 10 deletions vizro-core/docs/pages/user-guides/data.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ Since dynamic data sources must always be added to the data manager and referenc

### Configure cache

By default, each time the dashboard is refreshed a dynamic data function executes again. In fact, if there are multiple graphs on the same page using the same dynamic data source then the loading function executes _multiple_ times, once for each graph on the page. Hence, if loading your data is a slow operation, your dashboard performance may suffer.
By default, each time the dashboard is refreshed a dynamic data function executes again. Data loading is batched so that a dynamic data function that supplies multiple graphs on the same page will only execute _once_ on page refresh. Even with this batching, if loading your data once is still a slow operation, your dashboard performance may suffer.
antonymilne marked this conversation as resolved.
Show resolved Hide resolved

antonymilne marked this conversation as resolved.
Show resolved Hide resolved
The Vizro data manager has a server-side caching mechanism to help solve this. Vizro's cache uses [Flask-Caching](https://flask-caching.readthedocs.io/en/latest/), which supports a number of possible cache backends and [configuration options](https://flask-caching.readthedocs.io/en/latest/#configuring-flask-caching). By default, the cache is turned off.

Expand Down Expand Up @@ -220,7 +220,7 @@ By default, when caching is turned on, dynamic data is cached in the data manage

If you would like to alter some options, such as the default cache timeout, then you can specify a different cache configuration:

```py title="Simple cache with timeout set to 10 minutes"
```python title="Simple cache with timeout set to 10 minutes"
data_manager.cache = Cache(config={"CACHE_TYPE": "SimpleCache", "CACHE_DEFAULT_TIMEOUT": 600})
```

Expand Down Expand Up @@ -268,8 +268,11 @@ data_manager["no_expire_data"].timeout = 0

### Parametrize data loading

You can supply arguments to your dynamic data loading function that can be modified from the dashboard.
For example, if you are handling big data then you can use an argument to specify the number of entries or size of chunk of data.
You can supply arguments to your dynamic data loading function that can be modified from the dashboard. For example:
antonymilne marked this conversation as resolved.
Show resolved Hide resolved
- To load different versions of the same data.
- To handle big data you can use an argument that controls the amount of data that is loaded. This effectively pre-filters data before it reaches the Vizro dashboard.
antonymilne marked this conversation as resolved.
Show resolved Hide resolved

In general, a parametrized dynamic data source should always return a pandas DataFrame with a fixed schema (column names and types). This ensures that page components and controls continue to work as expected when the parameter is changed on screen.

To add a parameter to control a dynamic data source, do the following:

Expand Down Expand Up @@ -333,14 +336,77 @@ Parametrized data loading is compatible with [caching](#configure-cache). The ca

You cannot pass [nested parameters](parameters.md#nested-parameters) to dynamic data. You can only target the top-level arguments of the data loading function, not the nested keys in a dictionary.

### Filter update limitation
### Filters

By default, when a [filter](filters.md) depends on dynamic data, the available selector values update on page refresh to reflect the latest dynamic data. The mechanism for this, including caching, works exactly like other non-control components such as `vm.Graph`. However, unlike such components, a filter can depend on multiple data sources.

Vizro looks at the data source for each component in the filter's `targets`. If at least one of these is a dynamic data source then the filter behaves dynamically and the filter's selector updates on page refresh. Remember that when `targets` is not explicitly specified, a filter applies to all the components on a page that use a DataFrame including `column`.
antonymilne marked this conversation as resolved.
Show resolved Hide resolved
antonymilne marked this conversation as resolved.
Show resolved Hide resolved

When the page is refreshed, the behaviour of a dynamic filter is as follows:
antonymilne marked this conversation as resolved.
Show resolved Hide resolved

- The filter's selector updates its available values:
- For [categorical selectors](selectors.md#categorical-selectors), `options` updates.
- For [numerical selectors](selectors.md#numerical-selectors), `min` and `max` update.
- The value selected on screen by a dashboard user _does not_ change. If the selected value is not present in the new set of available values then it is still selected, but the filtering operation might result in an empty DataFrame.
- The `targets` of the filter and selector type do not change (for example, a `Dropdown` cannot turn into `RadioItems`).

For example, let us add two filters to the [dynamic data example](#dynamic-data) above:

!!! example "Dynamic filters"

```py hl_lines="20 21"
from vizro import Vizro
import pandas as pd
import vizro.plotly.express as px
import vizro.models as vm

from vizro.managers import data_manager

def load_iris_data():
iris = pd.read_csv("iris.csv")
return iris.sample(50)
antonymilne marked this conversation as resolved.
Show resolved Hide resolved

data_manager["iris"] = load_iris_data

If your dashboard includes a [filter](filters.md) then the values shown on a filter's [selector](selectors.md) _do not_ update while the dashboard is running. This is a known limitation that will be lifted in future releases, but if is problematic for you already then [raise an issue on our GitHub repo](https://github.com/mckinsey/vizro/issues/).
page = vm.Page(
title="Update the chart and filters on page refresh",
components=[
vm.Graph(figure=px.box("iris", x="species", y="petal_width", color="species"))
],
controls=[
vm.Filter(column="species"), # (1)!
vm.Filter(column="sepal_length"), # (2)!
],
)

This limitation is why all arguments of your dynamic data loading function must have a default value. Regardless of the value of the `vm.Parameter` selected in the dashboard, these default parameter values are used when the `vm.Filter` is built. This determines the type of selector used in a filter and the options shown, which cannot currently be changed while the dashboard is running.
dashboard = vm.Dashboard(pages=[page])

Although a selector is automatically chosen for you in a filter when your dashboard is built, remember that [you can change this choice](filters.md#changing-selectors). For example, we could ensure that a dropdown always contains the options "setosa", "versicolor" and "virginica" by explicitly specifying your filter as follows.
Vizro().build(dashboard).run()
```

```py
vm.Filter(column="species", selector=vm.Dropdown(options=["setosa", "versicolor", "virginica"])
1. This filter implicitly controls the dynamic data source `"iris"`, which supplies the `data_frame` to the targeted `vm.Graph`. On page refresh, Vizro reloads this data, looks at the values found in the `"species"` column and sets the categorical selector's `options` accordingly.
antonymilne marked this conversation as resolved.
Show resolved Hide resolved
2. Similarly, on page refresh, Vizro looks at the `"sepal_length"` column of the reloaded data and sets new `min` and `max` values for the numerical selector.

If you have a filter that depends on dynamic data but do not want the available values to change when the dynamic data changes then you should manually specify the `selector`'s `options` field (categorical selector) or `min` and `max` fields (numerical selector). In the above example, this could be achieved as follows:

```python title="Override selector options to make a dynamic filter static"
controls = [
vm.Filter(column="species", selector=vm.Dropdown(options=["setosa", "versicolor", "virginica"])),
vm.Filter(column="sepal_length", selector=vm.RangeSlider(min=4.3, max=7.9)),
]
```

If you [specify a custom selector](filters.md#changing-selectors) for a dynamic filter without manually specifying `options` (categorical selector) or `min` and `max` (numerical selector) then the selector remains dynamic. For example:

```python title="Dynamic filter with custom selector is still dynamic"
controls = [
vm.Filter(column="species", selector=vm.Checklist()),
vm.Filter(column="sepal_length", selector=vm.Slider()),
]
antonymilne marked this conversation as resolved.
Show resolved Hide resolved
```

When Vizro initially builds a filter that depends on parametrized dynamic data loading, data is loaded using the default argument values. This data is used to perform initial validation, check which data sources contain the specified `column` (unless `targets` is explicitly specified) and determine the type of selector to use (unless `selector` is explicitly specified).
antonymilne marked this conversation as resolved.
Show resolved Hide resolved

!!! note

When a dynamic data parameter is changed on screen, the data underlying a dynamic filter can change. Currently this change affects page components such as `vm.Graph` but does not affect the available values shown in a dynamic filter, which only update on page refresh. This functionality will be coming soon!
6 changes: 3 additions & 3 deletions vizro-core/docs/pages/user-guides/filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
This guide shows you how to add filters to your dashboard. One main way to interact with the charts/components on your page is by filtering the underlying data. A filter selects a subset of rows of a component's underlying DataFrame which alters the appearance of that component on the page.

The [`Page`][vizro.models.Page] model accepts the `controls` argument, where you can enter a [`Filter`][vizro.models.Filter] model.
This model enables the automatic creation of [selectors](../user-guides/selectors.md) (such as Dropdown, RadioItems, Slider, ...) that operate upon the charts/components on the screen.
This model enables the automatic creation of [selectors](selectors.md) (such as `Dropdown`, `RadioItems`, `Slider`, ...) that operate on the charts/components on the screen.

By default, filters that control [dynamic data](data.md#dynamic-data) are [updated while the dashboard is running](data.md#filters) when the underlying data changes.
antonymilne marked this conversation as resolved.
Show resolved Hide resolved

## Basic filters

Expand Down Expand Up @@ -121,8 +122,7 @@ Currently available selectors are [`Checklist`][vizro.models.Checklist], [`Dropd
For further customizations, you can always refer to the [`Filter`][vizro.models.Filter] reference. Some popular choices are:

- select which component the filter will apply to by using `targets`
- select what the target column type is, hence choosing the default selector by using `column_type`
antonymilne marked this conversation as resolved.
Show resolved Hide resolved
- choose options of lower level components, such as the `selector` models
- specify options of the `selector` chosen

Below is an advanced example where we only target one page component, and where we further customize the chosen `selector`.

Expand Down