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

Feat/dynamic filter #879

Draft
wants to merge 37 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
9449c73
Reduce to just one load function in filter.py
antonymilne Nov 4, 2024
b37f38f
Refactor set methods and add in __call__
antonymilne Nov 4, 2024
3d6f570
Fix tests
antonymilne Nov 4, 2024
68f6f61
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 4, 2024
9e6f62e
Add numpy lower bound
antonymilne Nov 4, 2024
574169b
Fix tests
antonymilne Nov 5, 2024
568dc58
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 5, 2024
b13e574
Fix tests
antonymilne Nov 5, 2024
01307bd
Add new tests and lint
antonymilne Nov 5, 2024
2ac895b
Final tidy
antonymilne Nov 5, 2024
2e4e0e2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 5, 2024
983940b
Fix min/max=0 bug
antonymilne Nov 6, 2024
13f022e
Move _get_targets_data_and_config into _get_modified_page_figures wit…
antonymilne Nov 6, 2024
a07fda4
Dynamic before filter tidy changes
petar-qb Nov 6, 2024
f2bd362
Turn _get_targets_data_and_config into _get_targets_data - tests pass
antonymilne Nov 6, 2024
1dbacb0
Turn _create_target_arg_mapping into _filter_dot_separated_string - t…
antonymilne Nov 6, 2024
f93d780
categorical selectors after the tidy/dynamic-filter
petar-qb Nov 6, 2024
4d0fb0e
Split up filtered and unfiltered data, create _multi_load, rework Fil…
antonymilne Nov 7, 2024
1a859ff
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 7, 2024
2b3b0ae
Support for Slider and RangeSlider to work as RadioItems
petar-qb Nov 7, 2024
30aa52a
Merge remote-tracking branch 'origin/main' into tidy/dynamic-filter-2
antonymilne Nov 7, 2024
d097733
Lint and small fixes
antonymilne Nov 7, 2024
0f5b096
pull changes from tidy/dynamic-filter-2
petar-qb Nov 8, 2024
d3c677e
dynamic filters implemented as on_page_load targets
petar-qb Nov 12, 2024
1d602ea
Propagating data_frame parameter values as load–kwargs from the model…
petar-qb Nov 13, 2024
2a94f6c
Merge main into the feature branch
petar-qb Nov 13, 2024
e066fdb
More improvements
petar-qb Nov 15, 2024
9e5a239
Reverting: Sending DFP values from the MM to Filter.pre_build DM._mul…
petar-qb Nov 15, 2024
5f161aa
Merge branch 'main' of https://github.com/mckinsey/vizro into feat/dy…
petar-qb Nov 15, 2024
cfdf4fc
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 15, 2024
8ca5382
Minor code cleaning
petar-qb Nov 15, 2024
472f7a8
Solving conflicts
petar-qb Nov 15, 2024
1e3ba7f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 15, 2024
41adc9e
Minor refactoring
petar-qb Nov 15, 2024
b912bb0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 15, 2024
1d2a9c5
Minor refactoring
petar-qb Nov 15, 2024
d1bd246
Merge branch 'feat/dynamic-filter' of https://github.com/mckinsey/viz…
petar-qb Nov 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion vizro-ai/changelog.d/new_fragment.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Uncomment the section that is right (remove the HTML comment wrapper).
<!--
### {{ cat }}

- A bullet item for the {{ cat }} 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))
- A bullet item for the {{ cat }} 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))

-->
{% endfor -%}
268 changes: 237 additions & 31 deletions vizro-core/examples/scratch_dev/app.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,255 @@
"""Dev app to try things out."""

import time
import yaml

import dash
import pandas as pd
from flask_caching import Cache

import vizro.models as vm
import vizro.plotly.express as px
from vizro import Vizro
from vizro._themes._color_values import COLORS

pastry = pd.DataFrame(
{
"pastry": [
"Scones",
"Bagels",
"Muffins",
"Cakes",
"Donuts",
"Cookies",
"Croissants",
"Eclairs",
"Brownies",
"Tarts",
"Macarons",
"Pies",
],
"Profit Ratio": [-0.10, -0.15, -0.05, 0.10, 0.05, 0.20, 0.15, -0.08, 0.08, -0.12, 0.02, -0.07],
}
from vizro.managers import data_manager
from functools import partial

print("INITIALIZING")

SPECIES_COLORS = {"setosa": "#00b4ff", "versicolor": "#ff9222", "virginica": "#3949ab"}
BAR_CHART_CONF = dict(x="species", color="species", color_discrete_map=SPECIES_COLORS)
SCATTER_CHART_CONF = dict(x="sepal_length", y="petal_length", color="species", color_discrete_map=SPECIES_COLORS)

# Relevant for the "page_6" only
FILTER_COLUMN = "species"
# FILTER_COLUMN = "sepal_length"
# FILTER_COLUMN = "date_column"


def load_from_file(filter_column=None, parametrized_species=None):
# Load the full iris dataset
df = px.data.iris()
df["date_column"] = pd.date_range(start=pd.to_datetime("2024-01-01"), periods=len(df), freq="D")

if parametrized_species:
return df[df["species"].isin(parametrized_species)]

with open("data.yaml", "r") as file:
data = yaml.safe_load(file)
data = data or {}

filter_column = filter_column or FILTER_COLUMN
if filter_column == "species":
final_df = pd.concat(
objs=[
df[df[filter_column] == "setosa"].head(data.get("setosa", 0)),
df[df[filter_column] == "versicolor"].head(data.get("versicolor", 0)),
df[df[filter_column] == "virginica"].head(data.get("virginica", 0)),
],
ignore_index=True,
)
elif filter_column == "sepal_length":
final_df = df[
df[filter_column].between(
data.get("min"),
data.get(
"max",
),
inclusive="both",
)
]
elif filter_column == "date_column":
date_min = pd.to_datetime(data.get("date_min"))
date_max = pd.to_datetime(data.get("date_max"))
final_df = df[df[filter_column].between(date_min, date_max, inclusive="both")]
else:
raise ValueError("Invalid FILTER_COLUMN")

return final_df


data_manager["load_from_file"] = load_from_file
data_manager["load_from_file_species"] = partial(load_from_file, filter_column="species")
data_manager["load_from_file_sepal_length"] = partial(load_from_file, filter_column="sepal_length")
data_manager["load_from_file_date_column"] = partial(load_from_file, filter_column="date_column")


# TODO-DEV: Turn on/off caching to see how it affects the app.
# data_manager.cache = Cache(config={"CACHE_TYPE": "SimpleCache", "CACHE_DEFAULT_TIMEOUT": 5})


homepage = vm.Page(
title="Homepage",
components=[
vm.Card(text="This is the homepage."),
],
)

page_1 = vm.Page(
title="Dynamic vs Static filter",
components=[
vm.Graph(
id="p1-G-1",
figure=px.bar(data_frame="load_from_file_species", **BAR_CHART_CONF),
),
vm.Graph(
id="p1-G-2",
figure=px.scatter(data_frame=px.data.iris(), **SCATTER_CHART_CONF),
),
],
controls=[
vm.Filter(id="p1-F-1", column="species", targets=["p1-G-1"], selector=vm.Dropdown(title="Dynamic filter")),
vm.Filter(id="p1-F-2", column="species", targets=["p1-G-2"], selector=vm.Dropdown(title="Static filter")),
vm.Parameter(
targets=["p1-G-1.x", "p1-G-2.x"],
selector=vm.RadioItems(options=["species", "sepal_width"], title="Simple X-axis parameter"),
),
],
)

page = vm.Page(
title="Charts UI",

page_2 = vm.Page(
title="Categorical dynamic selectors",
components=[
vm.Graph(
figure=px.bar(
pastry.sort_values("Profit Ratio"),
orientation="h",
x="Profit Ratio",
y="pastry",
color="Profit Ratio",
color_continuous_scale=COLORS["DIVERGING_RED_CYAN"],
id="p2-G-1",
figure=px.bar(data_frame="load_from_file_species", **BAR_CHART_CONF),
),
],
controls=[
vm.Filter(id="p2-F-1", column="species", selector=vm.Dropdown()),
vm.Filter(id="p2-F-2", column="species", selector=vm.Dropdown(multi=False)),
vm.Filter(id="p2-F-3", column="species", selector=vm.Checklist()),
vm.Filter(id="p2-F-4", column="species", selector=vm.RadioItems()),
vm.Parameter(
targets=["p2-G-1.x"],
selector=vm.RadioItems(
options=["species", "sepal_width"], value="species", title="Simple X-axis parameter"
),
),
],
)

dashboard = vm.Dashboard(pages=[page])

page_3 = vm.Page(
title="Numerical dynamic selectors",
components=[
vm.Graph(
id="p3-G-1",
figure=px.bar(data_frame="load_from_file_sepal_length", **BAR_CHART_CONF),
),
],
controls=[
vm.Filter(id="p3-F-1", column="sepal_length", selector=vm.Slider()),
vm.Filter(id="p3-F-2", column="sepal_length", selector=vm.RangeSlider()),
vm.Parameter(
targets=["p3-G-1.x"],
selector=vm.RadioItems(
options=["species", "sepal_width"], value="species", title="Simple X-axis parameter"
),
),
],
)

page_4 = vm.Page(
title="[TO BE DONE IN THE FOLLOW UP PR] Temporal dynamic selectors",
components=[
vm.Graph(
id="p4-G-1",
figure=px.bar(data_frame="load_from_file_date_column", **BAR_CHART_CONF),
),
],
controls=[
vm.Filter(id="p4-F-1", column="date_column", selector=vm.DatePicker(range=False)),
vm.Filter(id="p4-F-2", column="date_column", selector=vm.DatePicker()),
vm.Parameter(
targets=["p4-G-1.x"],
selector=vm.RadioItems(
options=["species", "sepal_width"], value="species", title="Simple X-axis parameter"
),
),
],
)

page_5 = vm.Page(
title="Parametrised dynamic selectors",
components=[
vm.Graph(
id="p5-G-1",
figure=px.bar(data_frame="load_from_file_species", **BAR_CHART_CONF),
),
],
controls=[
vm.Filter(id="p5-F-1", column="species", targets=["p5-G-1"], selector=vm.Checklist()),
vm.Parameter(
targets=[
"p5-G-1.data_frame.parametrized_species",
# TODO: Uncomment the following target and see the magic :D
# Is this the indicator that parameter.targets prop has to support 'target' definition without the '.'?
# "p5-F-1.",
],
selector=vm.Dropdown(
options=["setosa", "versicolor", "virginica"], multi=True, title="Parametrized species"
),
),
vm.Parameter(
targets=[
"p5-G-1.x",
# TODO: Uncomment the following target and see the magic :D
# "p5-F-1.",
],
selector=vm.RadioItems(
options=["species", "sepal_width"], value="species", title="Simple X-axis parameter"
),
),
],
)


page_6 = vm.Page(
title="Page to test things out",
components=[
vm.Graph(id="graph_dynamic", figure=px.bar(data_frame="load_from_file", **BAR_CHART_CONF)),
vm.Graph(
id="graph_static",
figure=px.scatter(data_frame=px.data.iris(), **SCATTER_CHART_CONF),
),
],
controls=[
vm.Filter(
id="filter_container_id",
column=FILTER_COLUMN,
targets=["graph_dynamic"],
# targets=["graph_static"],
# selector=vm.Dropdown(id="filter_id"),
# selector=vm.Dropdown(id="filter_id", value=["setosa"]),
# selector=vm.Checklist(id="filter_id"),
# selector=vm.Checklist(id="filter_id", value=["setosa"]),
# TODO-BUG: vm.Dropdown(multi=False) Doesn't work if value is cleared. The persistence storage become
# "null" and our placeholder component dmc.DateRangePicker can't process null value. It expects a value or
# a list of values.
# SOLUTION -> Create the "Universal Vizro placeholder component".
# TEMPORARY SOLUTION -> set clearable=False for the dynamic Dropdown(multi=False)
# selector=vm.Dropdown(id="filter_id", multi=False), ->
# selector=vm.Dropdown(id="filter_id", multi=False, value="setosa"),
# selector=vm.RadioItems(id="filter_id"),
# selector=vm.RadioItems(id="filter_id", value="setosa"),
# selector=vm.Slider(id="filter_id"),
# selector=vm.Slider(id="filter_id", value=5),
# selector=vm.RangeSlider(id="filter_id"),
# selector=vm.RangeSlider(id="filter_id", value=[5, 7]),
),
vm.Parameter(
targets=["graph_dynamic.x"],
selector=vm.RadioItems(options=["species", "sepal_width"], title="Simple X-axis parameter"),
),
],
)

dashboard = vm.Dashboard(pages=[homepage, page_1, page_2, page_3, page_4, page_5, page_6])

if __name__ == "__main__":
Vizro().build(dashboard).run()
app = Vizro().build(dashboard)

print("RUNNING\n")

app.run(dev_tools_hot_reload=False)
13 changes: 13 additions & 0 deletions vizro-core/examples/scratch_dev/data.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Choose from 0-50
setosa: 5
versicolor: 10
virginica: 15

# Choose from: 4.8 to 7.4
min: 5
max: 7

# Choose from:
# 2020-01-01 to 2020-05-29
date_min: 2024-01-01
date_max: 2024-05-29
Comment on lines +1 to +13
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusting these values, the input data is changed for the scratch/app.py example.

Loading
Loading