Closed gtauzin closed 1 week ago
Hi @gtauzin 👋
I see what caused ambiguities (thanks for pointing out to this). I'm happy to improve the configuration UX (and the documentation) to make it more clear for all other users as well.
How should I think about a control's targets? I understand that when there are no actions, the selectors' value is directly mapped to the targets. But when there are actions, it seems to be ignored. Is this correct?
Yes, that's correct. Here's related content I posted in the previous Issue -> https://github.com/mckinsey/vizro/issues/799? So, control's "column" and "targets" are just the "configuration shortcut" for users so they don't have to write the entire configuration like this:
vm.Filter(
column="species",
targets=["graph"],
selector=vm.Dropdown(
options=[{"label": i, "value": i} for i in ["setosa", "versicolor", "virginica"]],
actions=[
vm.Action(
function=_filter(filter_column="species", targets=["graph"], filter_function=_filter_isin)
)
]
)
)
but can only do something like:
vm.Filter(column="species")
and the rest of the configuration is internally calculated by Vizro. So, how Vizro calculates the entire model based on the single column="species"
input, you can find in the src/vizro/models/_controls/filter.py
.
Also, it's worth to mention the main difference between the predefined
and custom
action here.
Here are some examples of the already predefined actions:
_filter
- _filter(filter_column="species", targets=["graph"], filter_function=_filter_isin),
export_data
- export_data(targets=['graph']),
filter_interaction
- filter_interaction(targets=['graph'])
The get_min_nop
is the great example of the custom
action.
The main difference between predefined and custom actions is that for the predefined actions its inputs
and outputs
are calculated based on the function arguments (like "targets"
). So, you don't have to add "outputs"
to the export_data
or list its "inputs"
like "all filter selectors on the page that target exported chart" (all this is internally calculated by Vizro).
In case of the custom actions, Vizro can't automatically calculate its "inputs"
and "outputs"
because Vizro doesn't know the "nature" of this action.
So, to summarise it: Based on some controls's arguments (like parameter "targets" argument), Vizro creates the predefined action vm.Action(function=_parameter(targets=["graph"]))
and assigns it to the parameter selector.actions
(if some other actions are not already assigned to the parameter selector component). Based on the action's argument "targets" (_parameter(targets=["graph"])
), Vizro calculates the action's outputs (-> outputs=["graph.figure"]
). So, the predefined actions (actions that have arguments but don't have "inputs" and "outputs" defined) are converted to the actions that have "inputs" and "outputs" defined (Vizro calculates this based on the function arguments like "targets").
"Yes, we can say that any predefined action is also a custom action, but custom actions are not the predefined actions. 😄"
Whey you say: "targets are ignored", you're correct. Targets are "ignored" if some other action is assigned to the "parameter.selector.actions" (this assigned action overwrites the default one). The "targets" parameter argument matters only if you don't set the "parameter.selector.actions" (in that case Vizro will add a _parameter
action and assign it to the "parameter.selector.actions").
...switching the order of both controls will raise an error as the previous control has not yet been defined
Can you send me an example of this? Somehow I can't reproduce it..
Thank you so much @petar-qb for the detailed answer, it's much clearer now :)
Regarding the ordering, sorry my example was indeed not clear.
I have made another one in PyCafe:
from vizro import Vizro
import pandas as pd
import vizro.plotly.express as px
import vizro.models as vm
from vizro.models.types import capture
from vizro.managers import data_manager
def load_iris_data(number_of_points=10):
iris = px.data.iris()
return iris.sample(number_of_points)
data_manager["iris"] = load_iris_data
@capture("action")
def get_min_nop(value):
return value * 10
page = vm.Page(
title="Update the chart on page refresh",
components=[
vm.Graph(id="graph", figure=px.box("iris", x="species", y="petal_width", color="species"))
],
controls=[
vm.Parameter(
# I want this first so I have to "find" a target that is different from the
# one of the other controls and is already fully defined: for readability
# I would have liked to use `targets=["slider-nop.min"]`
# targets=["slider-nop.min"], # Using this will raise
# Alternatively, as there is a custom action and targets is ignored I would have liked to use
# targets=None, # This would let me do whatever w.r.t. ordering controls
targets=["graph.x"], # Introducing a fake target is the only way I could put this control first
selector=vm.Slider(
id="dropdown-ms",
min=0, max=3, step=1, value=0, title="Min slider",
actions=[
vm.Action(
function=get_min_nop(),
inputs=["dropdown-ms.value"],
outputs=["slider-nop.min"],
)
],
),
),
vm.Parameter(
targets=["graph.data_frame.number_of_points"],
selector=vm.Slider(
id="slider-nop",
min=10, max=100, step=10, value=10,
),
),
],
)
dashboard = vm.Dashboard(pages=[page])
Vizro().build(dashboard).run()
I think what confuse me is that when I have a control with a custom action, the targets
is ignored, but it still plays a role as it needs to be valid and can influence the validity of the order of the controls as well. In my example, for readability purposes, I first tried to set targets=["slider-nop.min"]
as it is the action's output and thus seemed more natural. This is valid only when it is in 2nd position, because in 1st, "slider-nop" is not yet defined. To put it in first, I can't write targets=None
, as None is not a valid target, so I have to get creative and ask myself: "What is a valid, already defined target that I can put here so that the validation does not raise?". In the example I randomly ended putting targets=["graph.x"]
. Do I understand this properly? How would you advise I handle such a case?
Maybe a side question: Is there any way I can move/reorganize the selectors on the dashboard? Like changing the order, the position, the component they are on, etc...
Thanks again!
I think what confuse me is that when I have a control with a custom action, the targets is ignored, but it still plays a role as it needs to be valid and can influence the validity of the order of the controls as well.
Ah I see! You're confused because "targets" argument is still validated even it's not practically used. That's already noticed bad UX and will be solved with https://github.com/mckinsey/vizro/pull/363 PR.
So, "targets" argument is actually only used as a "configuration shortcut" for users when vm.Filter
and vm.Parameter
components are defined. Based on this "targets" argument, outputs for private predefined _filter
and _parameter
action are calculated.
However, let's see how can you make your app working. Generally, the order of the components and actions in Vizro should not make any issues (except this weird targets issue you mentioned). Also, the vm.Filter
and the vm.Parameter
controls are made to be used just as shortcuts for _filter
and _parameter
actions. If you want to add a custom action to the control on the left side of the screen, then there's no need to use vm.Filter
or vm.Parameter
, but you should use the e.g. vm.Slider
directly. (P.S. don't forget to explicitly add the vm.Slider
as allowed type - see below)
from vizro import Vizro
import pandas as pd
import vizro.plotly.express as px
import vizro.models as vm
from vizro.models.types import capture
from vizro.managers import data_manager
def load_iris_data(number_of_points=10):
iris = px.data.iris()
return iris.sample(number_of_points)
data_manager["iris"] = load_iris_data
@capture("action")
def get_min_nop(value):
return value * 10
# TODO - Important: Explicitly add the type to the Page.controls
vm.Page.add_type("controls", vm.Slider)
page = vm.Page(
title="Update the chart on page refresh",
components=[
vm.Graph(id="graph", figure=px.box("iris", x="species", y="petal_width", color="species"))
],
controls=[
vm.Slider(
id="dropdown-ms",
min=0, max=3, step=1, value=0, title="Min slider",
actions=[
vm.Action(
function=get_min_nop(),
inputs=["dropdown-ms.value"],
outputs=["slider-nop.min"],
)
],
),
vm.Parameter(
targets=["graph.data_frame.number_of_points"],
selector=vm.Slider(
id="slider-nop",
min=10, max=100, step=10, value=10,
),
),
],
)
dashboard = vm.Dashboard(pages=[page])
Vizro().build(dashboard).run()
Thanks @petar-qb! This looks very neat.
Question
Hi, First of all thank you so much for the work you put in vizro, it seems to be absolutely amazing!
I am making my first dashboards and I am getting confused about the relationship between a control's targets and its actions' outputs. The example below showcases the points that confuse me, but my questions are:
Code/Examples
The code below can be ran on PyCafe
Thank you!
Which package?
vizro
Code of Conduct