-
Notifications
You must be signed in to change notification settings - Fork 1k
feat: Add tooltip to Altair agent portrayal (#2795) #2838
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
base: main
Are you sure you want to change the base?
feat: Add tooltip to Altair agent portrayal (#2795) #2838
Conversation
Performance benchmarks:
|
Hi this is my first time contributing to Mesa , is this acceptable? , what changes do I need to make to my commit to get the PR merged? |
ec00505
to
79a5b64
Compare
for more information, see https://pre-commit.ci
79a5b64
to
bc039e4
Compare
@coderabbitai review |
✅ Actions performedReview triggered.
|
WalkthroughAdds tooltip support to agent portrayals and Altair rendering, updates the example app to show Wealth legend and agent tooltips, removes a post-processing hook, and adjusts data handling in draw_agents to records-based with legend customization and shape domain extraction. Tests updated to pass tooltip data. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant App as Example App
participant SR as SpaceRenderer.draw_agents
participant AB as AltairBackend
participant Altair as Altair Chart
App->>SR: draw_agents(..., legend_title="Wealth", tooltips in portrayals)
SR->>AB: prepare data + call draw_agents(records)
AB->>AB: Build records with colors, shapes, tooltip
AB->>Altair: Create chart, encodings (color/shape/tooltip), legend config
Altair-->>App: Interactive chart with wealth legend and tooltips
note over AB,Altair: Tooltip keys derived from first valid tooltip<br/>Shape domain inferred from observed data
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (2)
mesa/visualization/backends/altair_backend.py (2)
217-248
: The records-based approach improves clarity.The refactoring from numpy-centric to pandas records-based DataFrame construction makes the code more maintainable and easier to extend with additional per-agent fields like tooltips.
However, consider extracting the record-building logic into a helper method if this pattern is reused elsewhere or if the method grows further:
def _build_agent_record(self, i: int, arguments: dict) -> dict: """Build a single agent record dictionary from arguments arrays.""" record = { "x": arguments["loc"][i][0], "y": arguments["loc"][i][1], "size": arguments["size"][i], "shape": arguments["shape"][i], "opacity": arguments["opacity"][i], "strokeWidth": arguments["strokeWidth"][i] / 10, "original_color": arguments["color"][i], } tooltip = arguments["tooltip"][i] if tooltip: record.update(tooltip) # Determine fill and stroke colors if arguments["filled"][i]: record["viz_fill_color"] = arguments["color"][i] record["viz_stroke_color"] = ( arguments["stroke"][i] if isinstance(arguments["stroke"][i], str) else None ) else: record["viz_fill_color"] = None record["viz_stroke_color"] = arguments["color"][i] return record
227-227
: Document or parameterize the strokeWidth scaling factor.The hardcoded division by 10 to scale strokeWidth for the continuous domain lacks context and may not be appropriate for all use cases.
Consider either:
- Adding a comment explaining why this scaling is necessary for Altair's continuous domain
- Making it a parameter if users need different scaling
Example:
- "strokeWidth": arguments["strokeWidth"][i] / 10, # Scale for continuous domain + "strokeWidth": arguments["strokeWidth"][i] / 10, # Scale to [0, 0.1] range for Altair's continuous domain
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
mesa/examples/basic/boltzmann_wealth_model/app.py
(2 hunks)mesa/visualization/backends/altair_backend.py
(6 hunks)mesa/visualization/components/portrayal_components.py
(2 hunks)tests/test_backends.py
(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-20T16:44:20.677Z
Learnt from: Sahil-Chhoker
PR: projectmesa/mesa#2803
File: mesa/visualization/backends/altair_backend.py:312-553
Timestamp: 2025-06-20T16:44:20.677Z
Learning: In mesa/visualization/backends/altair_backend.py, the author prefers to keep the draw_propertylayer method intact despite its complexity, as they believe breaking it into smaller functions would divert attention from the main logic flow. The method is well-documented and follows a clear sequence of operations for property layer rendering.
Applied to files:
mesa/visualization/backends/altair_backend.py
🔇 Additional comments (9)
tests/test_backends.py (1)
251-251
: LGTM!The addition of the tooltip field with None values appropriately tests the new tooltip support without requiring specific tooltip data.
mesa/visualization/components/portrayal_components.py (1)
58-58
: LGTM!The tooltip attribute addition is well-integrated, follows the existing pattern for optional attributes, and has an appropriate type hint.
mesa/examples/basic/boltzmann_wealth_model/app.py (2)
13-17
: Excellent example of the new tooltip feature!The agent portrayal demonstrates both the numeric color mapping and the tooltip dictionary effectively, providing a clear template for users.
48-54
: LGTM!The draw_agents call appropriately configures the visualization with colormap parameters and the new legend_title parameter for clear legend labeling.
mesa/visualization/backends/altair_backend.py (5)
1-5
: LGTM!The enhanced module docstring accurately reflects the expanded visualization capabilities.
83-83
: LGTM!The tooltip field is properly initialized and collected through both the dict-based and AgentPortrayalStyle-based portrayal paths.
Also applies to: 138-138, 194-194
276-296
: LGTM!The color handling logic correctly distinguishes between numeric (quantitative) and categorical (nominal) color encodings, with appropriate scale and legend configurations for each type.
300-300
: LGTM!Using
dropna().unique().tolist()
correctly extracts the set of shapes present in the data for the domain specification.
284-289
: LGTM!The gradient legend configuration with title, orientation, type, and length provides a clear, user-friendly visualization for numeric color scales.
tooltip_list = ["x", "y"] | ||
# This is the corrected line: | ||
if any(t is not None for t in arguments["tooltip"]): | ||
first_valid_tooltip = next((t for t in arguments["tooltip"] if t), None) | ||
if first_valid_tooltip: | ||
tooltip_list.extend(first_valid_tooltip.keys()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix inconsistency in tooltip truthiness checks.
Lines 260 and 261 use different checks (is not None
vs truthiness), which could cause unexpected behavior if users pass empty dictionaries as tooltips.
Apply this diff to make the checks consistent:
- if any(t is not None for t in arguments["tooltip"]):
- first_valid_tooltip = next((t for t in arguments["tooltip"] if t), None)
+ if any(t is not None for t in arguments["tooltip"]):
+ first_valid_tooltip = next((t for t in arguments["tooltip"] if t is not None), None)
This ensures that {}
(empty dict) is treated consistently across both checks.
🤖 Prompt for AI Agents
In mesa/visualization/backends/altair_backend.py around lines 258 to 263, the
tooltip checks are inconsistent: the any() uses "is not None" while the next()
filter uses truthiness, causing empty dicts to be treated differently; change
the next() generator to filter with "is not None" (and keep the subsequent
conditional to check for None) so empty dicts aren't skipped by truthiness and
first_valid_tooltip is compared against None before extending tooltip_list.
Note: vmin and vmax are the lower and upper bounds for the colorbar and the data is | ||
normalized between these values for color/colormap rendering. If they are not | ||
normalized between these values for color/colorbar rendering. If they are not |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the typo in the comment.
The comment incorrectly mentions "color/colorbar" but should say "color/colormap" since normalization applies to the color or colormap attributes, not the colorbar boolean flag.
Apply this diff to correct the comment:
- normalized between these values for color/colorbar rendering. If they are not
+ normalized between these values for color/colormap rendering. If they are not
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
normalized between these values for color/colorbar rendering. If they are not | |
normalized between these values for color/colormap rendering. If they are not |
🤖 Prompt for AI Agents
In mesa/visualization/components/portrayal_components.py around line 95, the
comment has a typo saying "color/colorbar" but normalization applies to the
color or colormap attributes; update the comment to read "color/colormap"
instead. Make the single-word replacement in the comment so it now mentions
"color/colormap rendering" (or equivalent) to accurately reflect that
normalization targets color/colormap, not the colorbar boolean.
Summary
This PR adds a
tooltip
attribute toAgentPortrayalStyle
, enabling agent-specific information to be displayed on hover in Altair-based visualizations.Motive
This feature resolves issue #2795, which requested an easy way to display agent information (like health, status, etc.) directly in the visualization. It is highly useful for debugging and for closely following the state of specific agents during a simulation.
Implementation
The implementation consists of two main changes:
AgentPortrayalStyle
inportrayal_components.py
was extended to include atooltip: dict | None = None
attribute.collect_agent_data
anddraw_agents
methods inaltair_backend.py
were updated to process this newtooltip
data. The backend now correctly creates a Pandas DataFrame with the tooltip columns and adds them to the Altair chart'stooltip
encoding channel.Usage Examples
Users can now pass a dictionary to the
tooltip
attribute within theiragent_portrayal
function.This provides a much richer user experience for inspecting agents.
Before:
Hovering over an agent only displayed basic coordinate information.

After:
Hovering over an agent now displays the custom information defined in the

tooltip
dictionary.Additional Notes
As discussed in the original issue, this implementation is specific to the Altair backend. A similar feature for the Matplotlib backend is not feasible in an elegant way at this time.