Skip to content

support for multiple slice positions in 2D heatmaps with automatic layout #87

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

zenWai
Copy link
Contributor

@zenWai zenWai commented Apr 1, 2025

Description

What is this PR

  • Addition of a new feature

Why is this PR needed?

solves #54

What does this PR do?

  • Allow position parameter to accept a list of positions in 2D format
  • Generate plt.Figure with up to 25 positions each in a grid layout.

Example

picture picture_25 picture_25_hides

Is this a breaking change?

No

Does this PR require an update to the documentation?

no

Checklist:

  • The code has been tested locally
  • Tests have been added to cover all new functionality (unit & integration)
  • The documentation has been updated to reflect any changes
  • The code has been formatted with pre-commit

zenWai added 2 commits April 1, 2025 03:30
- Allow position parameter to accept a list of positions in 2D format
- Generate plt.Figure with up to 25 positions each in a grid layout.
- Add validations to decrease some human error.
- Add some assert for mipy, self.slicer depends on position parameter value(s) and can be None
Copy link

codecov bot commented Apr 1, 2025

Codecov Report

Attention: Patch coverage is 15.09434% with 45 lines in your changes missing coverage. Please review.

Project coverage is 63.17%. Comparing base (4e3bf9e) to head (8a2d08c).

Files with missing lines Patch % Lines
brainglobe_heatmap/heatmaps.py 15.38% 44 Missing ⚠️
brainglobe_heatmap/planner.py 0.00% 1 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (4e3bf9e) and HEAD (8a2d08c). Click for more details.

HEAD has 15 uploads less than BASE
Flag BASE (4e3bf9e) HEAD (8a2d08c)
20 5
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #87      +/-   ##
==========================================
- Coverage   70.03%   63.17%   -6.86%     
==========================================
  Files           5        5              
  Lines         327      372      +45     
==========================================
+ Hits          229      235       +6     
- Misses         98      137      +39     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@zenWai
Copy link
Contributor Author

zenWai commented Apr 1, 2025

Ready for review, will leave it as draft for now.
Need special attention to these two aspects I'm unsure about:

  • The self.slicer initialization as None followed by conditional assignment, this pattern feels a bit awkward but I'm not sure of a better approach.
  • Using position values as default titles when self.title is None, feels a bit akward and is confusing, at same time I see value on users understanding what they're looking at and a better way to handle all choices does not come to my mind.
    Currently self.title=None does default title with positions, self.title="" would have empty title, self.title="title" makes all subplots with given title wich is akward, maybe.
    Probably could make self.title give a title for the figure instead?!

Thanks

Copy link
Member

@alessandrofelder alessandrofelder left a comment

Choose a reason for hiding this comment

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

Hey @zenWai

Sorry that other work commitments and Easter holidays have meant it's taken me a long time to look at this.
In general, I like that this PR makes it a lot easier for users to make 2D subplots in a standard way! I've made some nitpicky comments (sorry!) and asked some clarifying questions :)

My two-cents on your questions:

The self.slicer initialization as None followed by conditional assignment, this pattern feels a bit awkward but I'm not sure of a better approach.

An alternative approach I can think of is that in the 2D case, self.slicer is always a list, and we cover the current case by making it a list of length 1 when users pass a single value. That way, we don't need self.multiple_slicers, and internally always make the code iterate over self.slicer - what do you think?

Using position values as default titles when self.title is None, feels a bit akward and is confusing, at same time I see value on users understanding what they're looking at and a better way to handle all choices does not come to my mind.
Currently self.title=None does default title with positions, self.title="" would have empty title, self.title="title" makes all subplots with given title wich is akward, maybe.
Probably could make self.title give a title for the figure instead?!

I don't mind position values as default titles or empty title as is now - I think this is fine.
But 25x the same title is definitely NOT what we want. So I would vote to make self.title be a title for the figure by default? Given that IIUC the PR is backwards compatible, if people want more control over subplot titles they can script that as they could before in the old example?

@@ -127,6 +127,7 @@ def __init__(
magnitudes as the values.
position : list, tuple, np.ndarray, float
Position of the plane in the atlas.
list of positions create multiple slices.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
list of positions create multiple slices.
List of positions create multiple slices.

typo.

"List of positions not supported in 3D format. "
"Did you mean to use a tuple as a 3D position?"
)
if len(position) <= 1:
Copy link
Member

Choose a reason for hiding this comment

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

I think it should be OK to pass a list of length 1 (but not an empty list)? If you agree, please adapt accordingly?

@@ -275,6 +297,9 @@ def show(self, **kwargs) -> Union[Scene, plt.Figure]:
Creates a 2D plot or 3D rendering of the heatmap
"""
if self.format == "3D":
assert (
Copy link
Member

Choose a reason for hiding this comment

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

is there a case where self.slicer is ever still None after running __init__? I don't see it, so not sure this is needed?

# Create a list of scenes to plot
# Note: it's important to keep reference to the scenes to avoid a
# segmentation fault
scenes = []
Copy link
Member

Choose a reason for hiding this comment

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

maybe still worth keeping this example too, for advanced users (like when users want more control over figure size, or more than 25 subplots... or something else) to have more flexibility?

@alessandrofelder
Copy link
Member

Hey @zenWai - let us know whether you're planning to respond/address my comments (no rush - it took me long enough to even get to review! 😅) or you'd like the core team to take this on (when they find time)

@zenWai
Copy link
Contributor Author

zenWai commented Jun 23, 2025

Thanks for the pings, I will revisit it this weekend, I believe I want to

Hey @zenWai
An alternative approach I can think of is that in the 2D case, self.slicer is always a list, and we cover the current case by making it a list of length 1 when users pass a single value. That way, we don't need self.multiple_slicers, and internally always make the code iterate over self.slicer - what do you think?

For sure, this sounds like a good approach, I will implement it and see if I run into any edge issues.

I don't mind position values as default titles or empty title as is now - I think this is fine. But 25x the same title is definitely NOT what we want. So I would vote to make self.title be a title for the figure by default? Given that IIUC the PR is backwards compatible, if people want more control over subplot titles they can script that as they could before in the old example?

Sounds good, I will make the self.title provide a title for the figure would make things easier and makes sense.

I will take a full look this next weekend hopfully have it ready with tests👋

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants