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: new Negative Controls implementation using strategies and manager #74

Closed
wants to merge 17 commits into from

Conversation

jjjermiah
Copy link
Contributor

@jjjermiah jjjermiah commented Dec 5, 2024

Negative Controls Refactor Module

Overview

The negative_controls_refactor module provides a flexible framework for applying negative control
strategies
to medical imaging data, with a focus on radiomics. This framework allows for targeted
manipulation of images by combining control strategies with specific region definitions. The design
emphasizes modularity, extensibility, and ease of use.

With this module, users can:

  • Apply predefined or custom negative control strategies to 2D or 3D medical images.
  • Target specific regions (e.g., ROI, non-ROI, or the entire image) for the transformations.
  • Easily define and register their own strategies for specialized use cases.

Core Concepts

Negative Control Strategies

Negative control strategies define the type of manipulation applied to the image. These include:

  1. Shuffled: Randomly shuffles pixel values within the specified region.
  2. Sampled: Samples pixel values from the image's existing distribution.
  3. Randomized: Generates entirely new pixel values within the range of the original image.

Users can also define custom negative control strategies by subclassing the NegativeControlStrategy
abstract base class and implementing the transform method.

Region Strategies

Region strategies define where the negative control strategy is applied. These include:

  1. Full: Applies the strategy to the entire image.
  2. ROI (Region of Interest): Targets only the region specified by a mask.
  3. Non-ROI: Targets everything outside the region specified by a mask.

Region strategies are implemented by subclassing RegionStrategy and overriding the __call__ method.

How It Works

Workflow

  1. Define the Negative Control Strategies (e.g., Shuffled, Randomized).
  2. Define the Region Strategies (e.g., ROI, Non-ROI).
  3. Use the NegativeControlManager to manage and apply these strategies to an image.

The NegativeControlManager handles:

  • Mapping string-based inputs to their respective strategies.
  • Applying all combinations of strategies (control × region) to a base image and mask.

Registry System

The module uses registries to map strategy names (strings) to their respective classes:

  • NEGATIVE_CONTROL_REGISTRY: Maps negative control strategy names (e.g., "shuffled") to classes.
  • REGION_REGISTRY: Maps region strategy names (e.g., "roi") to classes.

This design allows for extensibility, as new strategies can be added and registered dynamically.

Example Usage

Using Manager

from pathlib import Path
from negative_controls_refactor.manager import NegativeControlManager


# Create base image and mask
base_image = ...
mask = ...

# Define strategies
negative_control_types = ["shuffled", "randomized", "sampled"]
region_types = ["full", "roi", "non_roi"]

# Instantiate the manager
manager = NegativeControlManager.from_strings(
    negative_control_types=negative_control_types,
    region_types=region_types,
    random_seed=42,
)

#  Iterate through results
for transformed_image, control_name, region_name in manager.apply(base_image, mask):
    print(f"Applied {control_name} control to {region_name} region.")
    # do something with transformed_image 

Use strategies directly

from readii.negative_controls_refactor import NonROIRegion, RandomizedControl

randomized_control = RandomizedControl(random_seed = 10)
result_image = randomized_control(base_image, mask, NonROIRegion())

Summary by CodeRabbit

  • New Features

    • Introduced a Jupyter notebook for visualizing and saving negative control strategies applied to images.
    • Added new classes for negative control strategies: ShuffledControl, SampledControl, and RandomizedControl.
    • Implemented region strategies: FullRegion, ROIRegion, and NonROIRegion.
    • Added new dependencies to enhance development capabilities.
  • Improvements

    • Enhanced linting and formatting configuration.
    • Improved documentation and error handling across various modules.
    • Streamlined code structure and clarity in the negative controls module.
  • Chores

    • Cleaned up and organized imports in several files.

@jjjermiah jjjermiah requested a review from strixy16 December 5, 2024 17:45
Copy link
Contributor

coderabbitai bot commented Dec 5, 2024

Walkthrough

The pull request includes modifications to several configuration and source files. The ruff.toml file has been updated to adjust linting rules and file inclusions. A new Jupyter notebook, viz_neg_controls.ipynb, has been introduced for visualizing negative control strategies. The pyproject.toml file has been enhanced with new dependencies and task definitions. Additionally, multiple classes and methods related to negative control strategies and region strategies have been added across various modules, improving the overall structure and functionality of the codebase.

Changes

File Change Summary
config/ruff.toml Updated cache-dir, modified include and extend-exclude lists, expanded select list, commented out [lint] section.
notebooks/viz_neg_controls.ipynb Introduced functions for image generation, a visualization class, interactive visualization features, and example usage.
pyproject.toml Added dependencies under development and style features, defined linting and formatting tasks.
src/readii/feature_extraction.py Enhanced documentation, refined parameter types, and modified default parameter values and error handling.
src/readii/negative_controls.py Organized imports, refined docstrings, improved error handling, and removed commented-out code.
src/readii/negative_controls_refactor/__init__.py Introduced a new module with imports and a public interface definition.
src/readii/negative_controls_refactor/abstract_classes.py Added RegionStrategy and NegativeControlStrategy abstract classes with defined methods.
src/readii/negative_controls_refactor/manager.py Defined NegativeControlManager class for managing negative control strategies with registries.
src/readii/negative_controls_refactor/negative_controls.py Introduced ShuffledControl, SampledControl, and RandomizedControl classes with their respective transform methods.
src/readii/negative_controls_refactor/regions.py Added FullRegion, ROIRegion, and NonROIRegion classes implementing the RegionStrategy interface.

Possibly related PRs

Poem

🐰 In the land where code does play,
New strategies hop in, bright as day.
With images and masks, we create and see,
Linting and tasks, all set free!
A rabbit's joy in every line,
Hopping through changes, oh so fine! 🐇


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 274dd86 and 745808b.

📒 Files selected for processing (1)
  • src/readii/feature_extraction.py (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/readii/feature_extraction.py

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

codecov bot commented Dec 5, 2024

Codecov Report

Attention: Patch coverage is 7.43243% with 137 lines in your changes missing coverage. Please review.

Project coverage is 48.96%. Comparing base (3bcaa28) to head (745808b).
Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
...dii/negative_controls_refactor/abstract_classes.py 0.00% 47 Missing ⚠️
...ii/negative_controls_refactor/negative_controls.py 0.00% 33 Missing ⚠️
src/readii/negative_controls_refactor/regions.py 0.00% 23 Missing ⚠️
src/readii/negative_controls_refactor/manager.py 0.00% 21 Missing ⚠️
src/readii/negative_controls.py 55.55% 8 Missing ⚠️
src/readii/negative_controls_refactor/__init__.py 0.00% 5 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main      #74       +/-   ##
===========================================
- Coverage   60.92%   48.96%   -11.97%     
===========================================
  Files          10       15        +5     
  Lines         540      674      +134     
===========================================
+ Hits          329      330        +1     
- Misses        211      344      +133     

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

@jjjermiah
Copy link
Contributor Author

Just tested out a new region (idk if dilation is the right way) but it works pretty straightforward

class NonROIWithBorderRegion(RegionStrategy):
	"""Region strategy to apply control outside the ROI, including a 2-pixel border."""

	region_name = "non_roi_with_border"
	# Add a 2-pixel border around the ROI
	padding = 2

	def __call__(self, image_array: np.ndarray, mask_array: np.ndarray) -> np.ndarray:
		"""
		Apply the region mask to the image array, adding a border of 2 pixels around the ROI.

		Parameters
		----------
		image_array : np.ndarray
			The base image as a numpy array.
		mask_array : np.ndarray
			The binary mask defining the ROI.

		Returns
		-------
		np.ndarray
			A binary mask with 1s outside the ROI and its 2-pixel border.
		"""
		# Ensure mask_array is binary (0s and 1s)
		binary_mask = (mask_array > 0).astype(np.uint8)
		
		roi_with_border = binary_dilation(binary_mask, iterations=self.padding)

		# Create the inverse mask for everything outside the ROI and its border
		region_mask = np.where(roi_with_border > 0, 0, 1)

		# Validate that there are non-zero pixels in the resulting mask
		if not region_mask.any():
			msg = "ROI with border mask is all 0s. No pixels outside ROI and its border to apply control."
			raise ValueError(msg)

		return region_mask

combined

Comment on lines +52 to +61
def apply(self, base_image: T, mask: T) -> Iterator[tuple[T, str, str]]:
"""Apply the negative control strategies to the region strategies."""
for control_strategy, region_strategy in product(
self.negative_control_strategies, self.region_strategies
):
yield (
control_strategy(base_image, mask, region_strategy),
control_strategy.name(),
region_strategy.name(),
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I need a bit more documentation explaining how this works. I can see that it's applying the negative controls to the images, but I'm not sure how it's doing it.


region_name = "full"

def __call__(self, image_array: np.ndarray, mask_array: np.ndarray) -> np.ndarray:
Copy link
Collaborator

Choose a reason for hiding this comment

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

image_array isn't actually used in any of the region strategies. Can it be removed?

jjjermiah and others added 3 commits December 11, 2024 14:04
Co-authored-by: Katy Scott <k.l.scott16@gmail.com>
@strixy16 strixy16 marked this pull request as ready for review December 11, 2024 19:48
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (8)
src/readii/negative_controls_refactor/negative_controls.py (1)

43-43: Use consistent naming conventions for variable names

Variable names like imgDimensions, flatArrImage, randNumGen, minVoxelVal, and maxVoxelVal use mixed camelCase and snake_case styles. For consistency with PEP 8 style guidelines, consider using snake_case for variable names.

Also applies to: 46-46, 49-49, 69-69, 72-72, 73-73, 76-76

notebooks/viz_neg_controls.ipynb (1)

223-225: Update deprecated Matplotlib function tostring_rgb to buffer_rgba

The fig.canvas.tostring_rgb() function is deprecated in Matplotlib 3.8 and will be removed in 3.10. Use fig.canvas.buffer_rgba() instead, and adjust the code to handle the RGBA format.

Apply this diff to update the code:

             # Convert plot to image
-            img = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
+            img = np.frombuffer(fig.canvas.buffer_rgba(), dtype=np.uint8)
+            img = img.reshape(fig.canvas.get_width_height()[::-1] + (4,))
+            img = img[:, :, :3]  # Discard the alpha channel
src/readii/negative_controls_refactor/regions.py (3)

11-15: Consider using np.ones_like(mask_array, dtype=bool) for memory efficiency

The region mask is used for boolean operations, so using a boolean dtype would be more memory efficient than the default float64.

-    region_mask = np.ones_like(mask_array)
+    region_mask = np.ones_like(mask_array, dtype=bool)

23-29: Optimize binary mask creation and validation

The implementation is correct but could be optimized by:

  1. Using boolean dtype for memory efficiency
  2. Combining the binary conversion and validation
-    region_mask = np.where(mask_array > 0, 1, 0)
-    if not region_mask.any():
+    region_mask = mask_array > 0
+    if not np.any(region_mask):
         msg = "ROI mask is all 0s. No pixels in ROI to apply negative control."
         raise ValueError(msg)
     return region_mask

37-43: Optimize binary mask creation and validation

Similar to ROIRegion, the implementation could be optimized for memory efficiency.

-    region_mask = np.where(mask_array > 0, 0, 1)
-    if not region_mask.any():
+    region_mask = ~(mask_array > 0)
+    if not np.any(region_mask):
         msg = "Non-ROI mask is all 0s. No pixels outside ROI to apply negative control."
         raise ValueError(msg)
     return region_mask
config/ruff.toml (2)

3-5: Remove duplicate cache-dir entry

The cache-dir configuration is duplicated.

cache-dir = "~/.cache/ruff"

-# iteratively adding files to the include list
-cache-dir = "~/.cache/ruff"
-

111-113: Remove duplicate McCabe complexity rule

The McCabe complexity rule "C" is listed twice in the select list.

-  # Check for McCabe complexity
-  # https://docs.astral.sh/ruff/rules/complex-structure/
-  "C",
src/readii/feature_extraction.py (1)

Line range hint 9-9: Remove unused import.

The logger import from readii.utils is not used in this file.

-from readii.utils import logger
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 3bcaa28 and c92a384.

⛔ Files ignored due to path filters (1)
  • pixi.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • config/ruff.toml (5 hunks)
  • notebooks/viz_neg_controls.ipynb (1 hunks)
  • pyproject.toml (4 hunks)
  • src/readii/feature_extraction.py (12 hunks)
  • src/readii/negative_controls.py (12 hunks)
  • src/readii/negative_controls_refactor/__init__.py (1 hunks)
  • src/readii/negative_controls_refactor/abstract_classes.py (1 hunks)
  • src/readii/negative_controls_refactor/manager.py (1 hunks)
  • src/readii/negative_controls_refactor/negative_controls.py (1 hunks)
  • src/readii/negative_controls_refactor/regions.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
src/readii/negative_controls.py

9-9: readii.utils.logger imported but unused

Remove unused import: readii.utils.logger

(F401)

src/readii/feature_extraction.py

38-38: SyntaxError: Simple statements must be separated by newlines or semicolons


38-38: SyntaxError: Simple statements must be separated by newlines or semicolons


38-38: SyntaxError: Simple statements must be separated by newlines or semicolons


38-38: SyntaxError: Simple statements must be separated by newlines or semicolons


38-38: SyntaxError: Compound statements are not allowed on the same line as simple statements


38-38: SyntaxError: Expected 'in', found name


38-38: SyntaxError: Expected ':', found name


38-38: SyntaxError: Simple statements must be separated by newlines or semicolons


38-38: SyntaxError: Simple statements must be separated by newlines or semicolons


38-38: SyntaxError: Simple statements must be separated by newlines or semicolons


38-38: SyntaxError: Simple statements must be separated by newlines or semicolons


38-38: SyntaxError: Simple statements must be separated by newlines or semicolons


38-38: SyntaxError: Simple statements must be separated by newlines or semicolons


38-38: SyntaxError: Simple statements must be separated by newlines or semicolons


38-38: SyntaxError: Simple statements must be separated by newlines or semicolons


38-39: SyntaxError: Expected an identifier


41-42: SyntaxError: Expected an expression


43-43: SyntaxError: Unexpected indentation


43-43: SyntaxError: Simple statements must be separated by newlines or semicolons


43-43: SyntaxError: Simple statements must be separated by newlines or semicolons


43-43: SyntaxError: Simple statements must be separated by newlines or semicolons


43-43: SyntaxError: Simple statements must be separated by newlines or semicolons


43-43: SyntaxError: Simple statements must be separated by newlines or semicolons


43-43: SyntaxError: Simple statements must be separated by newlines or semicolons


43-43: SyntaxError: Simple statements must be separated by newlines or semicolons


43-43: SyntaxError: Compound statements are not allowed on the same line as simple statements


43-43: SyntaxError: Expected an expression


43-44: SyntaxError: Expected an identifier


45-45: SyntaxError: Unexpected indentation


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


46-46: SyntaxError: Expected a statement


47-47: SyntaxError: Unexpected indentation


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Compound statements are not allowed on the same line as simple statements


47-47: SyntaxError: Expected an expression


47-47: SyntaxError: Expected 'in', found name


47-47: SyntaxError: Expected ':', found name


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Compound statements are not allowed on the same line as simple statements


47-47: SyntaxError: Expected ',', found name


47-48: SyntaxError: Expected an identifier


48-48: SyntaxError: Expected ',', found dedent


49-49: SyntaxError: Unexpected indentation


49-49: SyntaxError: Simple statements must be separated by newlines or semicolons


49-49: SyntaxError: Simple statements must be separated by newlines or semicolons


49-49: SyntaxError: Simple statements must be separated by newlines or semicolons


49-49: SyntaxError: Simple statements must be separated by newlines or semicolons


49-49: SyntaxError: Simple statements must be separated by newlines or semicolons


49-49: SyntaxError: Compound statements are not allowed on the same line as simple statements


49-49: SyntaxError: Expected 'in', found name


49-50: SyntaxError: Expected an identifier


51-51: SyntaxError: Expected a statement


52-53: SyntaxError: Expected an expression


54-54: SyntaxError: Unexpected indentation


54-54: SyntaxError: Simple statements must be separated by newlines or semicolons


54-54: SyntaxError: Simple statements must be separated by newlines or semicolons


54-54: SyntaxError: Simple statements must be separated by newlines or semicolons


54-54: SyntaxError: Compound statements are not allowed on the same line as simple statements


54-54: SyntaxError: Expected ',', found name


54-54: SyntaxError: Expected ',', found name


54-54: SyntaxError: Expected ',', found name


54-54: SyntaxError: Expected ',', found name


54-55: SyntaxError: Expected an identifier


55-55: SyntaxError: Expected ',', found dedent


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-85: SyntaxError: Expected an identifier


86-87: SyntaxError: Expected ',', found newline


87-88: SyntaxError: Expected an expression


89-89: SyntaxError: Unexpected indentation


89-89: SyntaxError: Simple statements must be separated by newlines or semicolons


89-89: SyntaxError: Simple statements must be separated by newlines or semicolons


89-89: SyntaxError: Simple statements must be separated by newlines or semicolons


89-89: SyntaxError: Simple statements must be separated by newlines or semicolons


89-90: SyntaxError: Expected an identifier


90-90: SyntaxError: Expected a statement


91-91: SyntaxError: Unexpected indentation


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Compound statements are not allowed on the same line as simple statements


91-91: SyntaxError: Expected ',', found name


91-92: SyntaxError: Expected an identifier


92-92: SyntaxError: Expected ',', found dedent


93-93: SyntaxError: Unexpected indentation


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Compound statements are not allowed on the same line as simple statements


93-94: SyntaxError: Expected an identifier


94-94: SyntaxError: Expected ',', found dedent


95-95: SyntaxError: Unexpected indentation


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


96-96: SyntaxError: Expected a statement


96-96: SyntaxError: Expected a statement


97-97: SyntaxError: Unexpected indentation


97-97: SyntaxError: Simple statements must be separated by newlines or semicolons


97-97: SyntaxError: Simple statements must be separated by newlines or semicolons


97-97: SyntaxError: Simple statements must be separated by newlines or semicolons


97-97: SyntaxError: Simple statements must be separated by newlines or semicolons


97-97: SyntaxError: Simple statements must be separated by newlines or semicolons


97-97: SyntaxError: Compound statements are not allowed on the same line as simple statements


97-97: SyntaxError: Expected 'in', found name


97-98: SyntaxError: Expected an identifier


99-99: SyntaxError: Expected a statement


100-101: SyntaxError: Expected an expression


102-102: SyntaxError: Unexpected indentation


102-102: SyntaxError: Simple statements must be separated by newlines or semicolons


102-102: SyntaxError: Simple statements must be separated by newlines or semicolons


102-102: SyntaxError: Simple statements must be separated by newlines or semicolons


102-103: SyntaxError: Expected an identifier


103-103: SyntaxError: Expected a statement


124-124: SyntaxError: Simple statements must be separated by newlines or semicolons


124-124: SyntaxError: Simple statements must be separated by newlines or semicolons


124-124: SyntaxError: Simple statements must be separated by newlines or semicolons


124-124: SyntaxError: Simple statements must be separated by newlines or semicolons


124-124: SyntaxError: Compound statements are not allowed on the same line as simple statements


124-124: SyntaxError: Expected 'in', found name


124-124: SyntaxError: Expected ':', found name


124-124: SyntaxError: Simple statements must be separated by newlines or semicolons


124-124: SyntaxError: Simple statements must be separated by newlines or semicolons


124-124: SyntaxError: Simple statements must be separated by newlines or semicolons


124-125: SyntaxError: Expected an identifier


150-150: SyntaxError: Expected an indented block after if statement


470-470: SyntaxError: Expected ',', found name


470-470: SyntaxError: Unparenthesized generator expression cannot be used here

🔇 Additional comments (12)
src/readii/negative_controls_refactor/manager.py (1)

52-61: Provide additional documentation for the apply method

As previously noted, adding more detailed documentation to the apply method would help clarify how negative control and region strategies are combined and applied to images.

src/readii/negative_controls_refactor/regions.py (1)

11-11: Remove unused parameter image_array

The image_array parameter is not used in any of the region strategies.

src/readii/negative_controls_refactor/__init__.py (1)

1-20: LGTM! Well-organized module initialization

The module initialization is clean, well-documented, and follows best practices:

  • Clear module docstring
  • Organized imports
  • Comprehensive __all__ list exporting all necessary components
src/readii/negative_controls.py (4)

29-29: LGTM! Efficient type checking implementation.

The use of a tuple in isinstance check is more efficient than multiple individual checks.


214-215: LGTM! Improved error message handling.

Good practice to store error messages in variables before raising exceptions. This improves maintainability and makes it easier to update error messages.

Also applies to: 227-228


286-287: LGTM! Consistent error message handling.

Follows the same improved pattern of storing error messages in variables before raising exceptions.

Also applies to: 299-300


356-357: LGTM! Consistent error handling pattern.

Maintains the improved pattern of storing error messages in variables before raising exceptions.

Also applies to: 359-360

src/readii/feature_extraction.py (5)

40-54: LGTM! Well-documented function parameters and return type.

The comprehensive docstring clearly describes the function's purpose, parameters, and return value.

🧰 Tools
🪛 Ruff (0.8.2)

41-42: SyntaxError: Expected an expression


43-43: SyntaxError: Unexpected indentation


43-43: SyntaxError: Simple statements must be separated by newlines or semicolons


43-43: SyntaxError: Simple statements must be separated by newlines or semicolons


43-43: SyntaxError: Simple statements must be separated by newlines or semicolons


43-43: SyntaxError: Simple statements must be separated by newlines or semicolons


43-43: SyntaxError: Simple statements must be separated by newlines or semicolons


43-43: SyntaxError: Simple statements must be separated by newlines or semicolons


43-43: SyntaxError: Simple statements must be separated by newlines or semicolons


43-43: SyntaxError: Compound statements are not allowed on the same line as simple statements


43-43: SyntaxError: Expected an expression


43-44: SyntaxError: Expected an identifier


45-45: SyntaxError: Unexpected indentation


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


45-45: SyntaxError: Simple statements must be separated by newlines or semicolons


46-46: SyntaxError: Expected a statement


47-47: SyntaxError: Unexpected indentation


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Compound statements are not allowed on the same line as simple statements


47-47: SyntaxError: Expected an expression


47-47: SyntaxError: Expected 'in', found name


47-47: SyntaxError: Expected ':', found name


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Simple statements must be separated by newlines or semicolons


47-47: SyntaxError: Compound statements are not allowed on the same line as simple statements


47-47: SyntaxError: Expected ',', found name


47-48: SyntaxError: Expected an identifier


48-48: SyntaxError: Expected ',', found dedent


49-49: SyntaxError: Unexpected indentation


49-49: SyntaxError: Simple statements must be separated by newlines or semicolons


49-49: SyntaxError: Simple statements must be separated by newlines or semicolons


49-49: SyntaxError: Simple statements must be separated by newlines or semicolons


49-49: SyntaxError: Simple statements must be separated by newlines or semicolons


49-49: SyntaxError: Simple statements must be separated by newlines or semicolons


49-49: SyntaxError: Compound statements are not allowed on the same line as simple statements


49-49: SyntaxError: Expected 'in', found name


49-50: SyntaxError: Expected an identifier


51-51: SyntaxError: Expected a statement


52-53: SyntaxError: Expected an expression


54-54: SyntaxError: Unexpected indentation


54-54: SyntaxError: Simple statements must be separated by newlines or semicolons


54-54: SyntaxError: Simple statements must be separated by newlines or semicolons


54-54: SyntaxError: Simple statements must be separated by newlines or semicolons


54-54: SyntaxError: Compound statements are not allowed on the same line as simple statements


54-54: SyntaxError: Expected ',', found name


54-54: SyntaxError: Expected ',', found name


54-54: SyntaxError: Expected ',', found name


54-54: SyntaxError: Expected ',', found name


80-102: LGTM! Improved type annotation and documentation.

Good improvements:

  1. Changed segBoundingBox type from tuple to np.ndarray for better type accuracy
  2. Added comprehensive docstring with clear parameter descriptions
🧰 Tools
🪛 Ruff (0.8.2)

84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-84: SyntaxError: Expected ',', found name


84-85: SyntaxError: Expected an identifier


86-87: SyntaxError: Expected ',', found newline


87-88: SyntaxError: Expected an expression


89-89: SyntaxError: Unexpected indentation


89-89: SyntaxError: Simple statements must be separated by newlines or semicolons


89-89: SyntaxError: Simple statements must be separated by newlines or semicolons


89-89: SyntaxError: Simple statements must be separated by newlines or semicolons


89-89: SyntaxError: Simple statements must be separated by newlines or semicolons


89-90: SyntaxError: Expected an identifier


90-90: SyntaxError: Expected a statement


91-91: SyntaxError: Unexpected indentation


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Simple statements must be separated by newlines or semicolons


91-91: SyntaxError: Compound statements are not allowed on the same line as simple statements


91-91: SyntaxError: Expected ',', found name


91-92: SyntaxError: Expected an identifier


92-92: SyntaxError: Expected ',', found dedent


93-93: SyntaxError: Unexpected indentation


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Simple statements must be separated by newlines or semicolons


93-93: SyntaxError: Compound statements are not allowed on the same line as simple statements


93-94: SyntaxError: Expected an identifier


94-94: SyntaxError: Expected ',', found dedent


95-95: SyntaxError: Unexpected indentation


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


95-95: SyntaxError: Simple statements must be separated by newlines or semicolons


96-96: SyntaxError: Expected a statement


96-96: SyntaxError: Expected a statement


97-97: SyntaxError: Unexpected indentation


97-97: SyntaxError: Simple statements must be separated by newlines or semicolons


97-97: SyntaxError: Simple statements must be separated by newlines or semicolons


97-97: SyntaxError: Simple statements must be separated by newlines or semicolons


97-97: SyntaxError: Simple statements must be separated by newlines or semicolons


97-97: SyntaxError: Simple statements must be separated by newlines or semicolons


97-97: SyntaxError: Compound statements are not allowed on the same line as simple statements


97-97: SyntaxError: Expected 'in', found name


97-98: SyntaxError: Expected an identifier


99-99: SyntaxError: Expected a statement


100-101: SyntaxError: Expected an expression


102-102: SyntaxError: Unexpected indentation


102-102: SyntaxError: Simple statements must be separated by newlines or semicolons


102-102: SyntaxError: Simple statements must be separated by newlines or semicolons


102-102: SyntaxError: Simple statements must be separated by newlines or semicolons


120-120: LGTM! Improved parameter handling and error checks.

Good improvements:

  1. Changed default parameter to None for better flexibility
  2. Added clear error handling for missing parameter files

Also applies to: 149-157


281-281: LGTM! Updated error message for accuracy.

Error message now correctly references 'radiogenomic_output' instead of 'readii_output'.


Line range hint 382-406: LGTM! Enhanced function documentation.

The comprehensive docstring clearly describes the function's purpose, parameters, and return value.

Comment on lines +38 to +40
NEGATIVE_CONTROL_REGISTRY[control_type](random_seed=random_seed)
for control_type in negative_control_types
]
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for invalid strategy names

Currently, the code assumes that the provided control_type and region_type exist in the registries. If an invalid name is provided, a KeyError will be raised. Consider adding error handling to provide a more informative error message.

Also applies to: 43-45

Comment on lines +72 to +81
minVoxelVal = np.min(image_array)
maxVoxelVal = np.max(image_array)

# Set the random seed for np random generator
randNumGen = np.random.default_rng(seed=self.random_seed)

# Generate random array with same dimensions as baseImage with values ranging from the minimum to maximum inclusive of the original image
random3DArr = randNumGen.integers(
low=minVoxelVal, high=maxVoxelVal, endpoint=True, size=imgDimensions,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure image_array contains integer values for randNumGen.integers

The randNumGen.integers method requires integer values for the low and high parameters. If image_array contains floating-point values, this could result in an error or unexpected behavior. Consider converting image_array to integers or using randNumGen.uniform for floating-point ranges.

Apply this diff to ensure integer values are used:

 def transform(self, image_array: np.ndarray) -> np.ndarray:
     """Randomly generate pixel values."""
     # Get array dimensions to reshape back to
-    imgDimensions = image_array.shape
+    img_dimensions = image_array.shape

+    # Ensure image array is of integer type
+    image_array = image_array.astype(int)

     # Get min and max values to set as range for random values
-    minVoxelVal = np.min(image_array)
-    maxVoxelVal = np.max(image_array)
+    min_voxel_val = np.min(image_array)
+    max_voxel_val = np.max(image_array)

     # Set the random seed for np random generator
-    randNumGen = np.random.default_rng(seed=self.random_seed)
+    rand_num_gen = np.random.default_rng(seed=self.random_seed)

     # Generate random array with same dimensions as baseImage with values ranging from the minimum to maximum inclusive of the original image
-    random3DArr = randNumGen.integers(
-        low=minVoxelVal, high=maxVoxelVal, endpoint=True, size=imgDimensions,
+    random_array = rand_num_gen.integers(
+        low=min_voxel_val, high=max_voxel_val, endpoint=True, size=img_dimensions,
     )
-    return random3DArr
+    return random_array
📝 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.

Suggested change
minVoxelVal = np.min(image_array)
maxVoxelVal = np.max(image_array)
# Set the random seed for np random generator
randNumGen = np.random.default_rng(seed=self.random_seed)
# Generate random array with same dimensions as baseImage with values ranging from the minimum to maximum inclusive of the original image
random3DArr = randNumGen.integers(
low=minVoxelVal, high=maxVoxelVal, endpoint=True, size=imgDimensions,
)
# Ensure image array is of integer type
image_array = image_array.astype(int)
# Get min and max values to set as range for random values
min_voxel_val = np.min(image_array)
max_voxel_val = np.max(image_array)
# Set the random seed for np random generator
rand_num_gen = np.random.default_rng(seed=self.random_seed)
# Generate random array with same dimensions as baseImage with values ranging from the minimum to maximum inclusive of the original image
random_array = rand_num_gen.integers(
low=min_voxel_val, high=max_voxel_val, endpoint=True, size=img_dimensions,
)

pyproject.toml Outdated
Comment on lines 64 to 66
jupyterlab = ">=4.3.1,<5"
pixi-kernel = ">=0.5.1,<0.6"
ipykernel = ">=6.29.5,<7"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove duplicate dependency entries

These dependencies are already declared in lines 64-66:

  • jupyterlab
  • pixi-kernel
  • ipykernel

Apply this diff to remove the duplicates:

-jupyterlab = ">=4.3.1,<5"
-pixi-kernel = ">=0.5.1,<0.6"
-ipykernel = ">=6.29.5,<7"

@jjjermiah jjjermiah closed this Dec 13, 2024
@jjjermiah jjjermiah deleted the jjjermiah/sandbox/neg-nifti branch December 13, 2024 16:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

2 participants