From 1ba291a008c3c2c8f562be2ea845295e66796446 Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 22 Jan 2024 20:24:22 -0500 Subject: [PATCH 1/6] feat: Implement Altair version of grid visualization _draw_grid is derived from https://github.com/Princeton-CDH/simulating-risk/blob/907c290e12c97b28aa9ce9c80ea7fc52a4f280ae/simulatingrisk/hawkdove/server.py#L114-L171 Co-authored-by: rlskoeser --- mesa/experimental/components/altair.py | 52 ++++++++++++++++++++++++++ mesa/experimental/jupyter_viz.py | 9 +++++ 2 files changed, 61 insertions(+) create mode 100644 mesa/experimental/components/altair.py diff --git a/mesa/experimental/components/altair.py b/mesa/experimental/components/altair.py new file mode 100644 index 00000000000..3b249923b9e --- /dev/null +++ b/mesa/experimental/components/altair.py @@ -0,0 +1,52 @@ +import contextlib +from typing import Optional + +import pandas as pd +import solara + +with contextlib.suppress(ImportError): + import altair as alt + +@solara.component +def SpaceAltair(model, agent_portrayal, dependencies: Optional[list[any]] = None): + space = getattr(model, "grid", None) + if space is None: + # Sometimes the space is defined as model.space instead of model.grid + space = model.space + chart = _draw_grid(space, agent_portrayal) + solara.FigureAltair(chart) + + +def _draw_grid(space, agent_portrayal): + def portray(g): + all_agent_data = [] + for content, (x, y) in space.coord_iter(): + if not content: + continue + if not hasattr(content, "__iter__"): + # Is a single grid + content = [content] # noqa: PLW2901 + for agent in content: + # use all data from agent portrayal, and add x,y coordinates + agent_data = agent_portrayal(agent) + agent_data["x"] = x + agent_data["y"] = y + all_agent_data.append(agent_data) + return all_agent_data + + all_agent_data = portray(space) + df = pd.DataFrame(all_agent_data) + chart = ( + alt.Chart(df) + .mark_point(filled=True) + .encode( + # no x-axis label + x=alt.X("x", axis=None), + # no y-axis label + y=alt.Y("y", axis=None), + size=alt.Size("size"), + color=alt.Color("color"), + ) + # .configure_view(strokeOpacity=0) # hide grid/chart lines + ) + return chart diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index a6ae318a822..9d29e08fb60 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -6,6 +6,7 @@ import solara from solara.alias import rv +import mesa.experimental.components.altair as components_altair import mesa.experimental.components.matplotlib as components_matplotlib from mesa.experimental.UserParam import Slider @@ -28,6 +29,10 @@ def Card( components_matplotlib.SpaceMatplotlib( model, agent_portrayal, dependencies=[current_step.value] ) + elif space_drawer == "altair": + components_altair.SpaceAltair( + model, agent_portrayal, dependencies=[current_step.value] + ) elif space_drawer: # if specified, draw agent space with an alternate renderer space_drawer(model, agent_portrayal) @@ -113,6 +118,10 @@ def render_in_jupyter(): components_matplotlib.SpaceMatplotlib( model, agent_portrayal, dependencies=[current_step.value] ) + elif space_drawer == "altair": + components_altair.SpaceAltair( + model, agent_portrayal, dependencies=[current_step.value] + ) elif space_drawer: # if specified, draw agent space with an alternate renderer space_drawer(model, agent_portrayal) From 542f761dee3891ef2aeee640a8562082891d50a9 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 28 Jan 2024 15:06:55 -0500 Subject: [PATCH 2/6] Make color and size optional --- mesa/experimental/components/altair.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mesa/experimental/components/altair.py b/mesa/experimental/components/altair.py index 3b249923b9e..75e89efea5f 100644 --- a/mesa/experimental/components/altair.py +++ b/mesa/experimental/components/altair.py @@ -44,9 +44,16 @@ def portray(g): x=alt.X("x", axis=None), # no y-axis label y=alt.Y("y", axis=None), - size=alt.Size("size"), - color=alt.Color("color"), ) # .configure_view(strokeOpacity=0) # hide grid/chart lines ) + + has_color = hasattr(all_agent_data[0], "color") + if has_color: + chart = chart.encode(color=alt.Color("color")) + + has_size = hasattr(all_agent_data[0], "size") + if has_size: + chart = chart.encode(size=alt.Size("size")) + return chart From 14af7c8290296a795facd72b18622dcb2cd8507b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:17:10 +0000 Subject: [PATCH 3/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/components/altair.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesa/experimental/components/altair.py b/mesa/experimental/components/altair.py index 75e89efea5f..9d790dc3aa8 100644 --- a/mesa/experimental/components/altair.py +++ b/mesa/experimental/components/altair.py @@ -7,6 +7,7 @@ with contextlib.suppress(ImportError): import altair as alt + @solara.component def SpaceAltair(model, agent_portrayal, dependencies: Optional[list[any]] = None): space = getattr(model, "grid", None) From 1cbf5019d7d8123a0edd266e2d3af668c6f2c904 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 28 Jan 2024 20:15:24 -0500 Subject: [PATCH 4/6] Altair viz: Convert from DF -> alt.Data --- mesa/experimental/components/altair.py | 32 ++++++++++++-------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/mesa/experimental/components/altair.py b/mesa/experimental/components/altair.py index 9d790dc3aa8..2d10fc30363 100644 --- a/mesa/experimental/components/altair.py +++ b/mesa/experimental/components/altair.py @@ -1,7 +1,6 @@ import contextlib from typing import Optional -import pandas as pd import solara with contextlib.suppress(ImportError): @@ -36,25 +35,24 @@ def portray(g): return all_agent_data all_agent_data = portray(space) - df = pd.DataFrame(all_agent_data) + encoding_dict = { + # no x-axis label + "x": alt.X("x", axis=None, type="ordinal"), + # no y-axis label + "y": alt.Y("y", axis=None, type="ordinal"), + } + has_color = "color" in all_agent_data[0] + if has_color: + encoding_dict["color"] = alt.Color("color", type="nominal") + has_size = "size" in all_agent_data[0] + if has_size: + encoding_dict["size"] = alt.Size("size", type="quantitative") + chart = ( - alt.Chart(df) + alt.Chart(alt.Data(values=all_agent_data), encoding=alt.Encoding(**encoding_dict)) .mark_point(filled=True) - .encode( - # no x-axis label - x=alt.X("x", axis=None), - # no y-axis label - y=alt.Y("y", axis=None), - ) + .properties(width=space.width * 15, height=space.height * 15) # .configure_view(strokeOpacity=0) # hide grid/chart lines ) - has_color = hasattr(all_agent_data[0], "color") - if has_color: - chart = chart.encode(color=alt.Color("color")) - - has_size = hasattr(all_agent_data[0], "size") - if has_size: - chart = chart.encode(size=alt.Size("size")) - return chart From 2fd4486efbbcb9b794ee35ef2200331f9be82beb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:39:59 +0000 Subject: [PATCH 5/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/components/altair.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mesa/experimental/components/altair.py b/mesa/experimental/components/altair.py index 2d10fc30363..bd220019f0e 100644 --- a/mesa/experimental/components/altair.py +++ b/mesa/experimental/components/altair.py @@ -49,7 +49,9 @@ def portray(g): encoding_dict["size"] = alt.Size("size", type="quantitative") chart = ( - alt.Chart(alt.Data(values=all_agent_data), encoding=alt.Encoding(**encoding_dict)) + alt.Chart( + alt.Data(values=all_agent_data), encoding=alt.Encoding(**encoding_dict) + ) .mark_point(filled=True) .properties(width=space.width * 15, height=space.height * 15) # .configure_view(strokeOpacity=0) # hide grid/chart lines From e5cbd6b14d000a07ede3ca55160414d9feb1cdbf Mon Sep 17 00:00:00 2001 From: rht Date: Thu, 8 Feb 2024 03:30:07 -0500 Subject: [PATCH 6/6] Altair viz: Hardcode width/height consistent with Card width/height --- mesa/experimental/components/altair.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/experimental/components/altair.py b/mesa/experimental/components/altair.py index bd220019f0e..2b08485b848 100644 --- a/mesa/experimental/components/altair.py +++ b/mesa/experimental/components/altair.py @@ -53,7 +53,7 @@ def portray(g): alt.Data(values=all_agent_data), encoding=alt.Encoding(**encoding_dict) ) .mark_point(filled=True) - .properties(width=space.width * 15, height=space.height * 15) + .properties(width=280, height=280) # .configure_view(strokeOpacity=0) # hide grid/chart lines )