-
Notifications
You must be signed in to change notification settings - Fork 0
Refactor API for better consistency and documentation #4
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?
Conversation
We are beginning to build out all of the same functionality of the R package.
The confidence intervals and plotting don't work correctly when the bounds are forced to (0, 2pi). Therefore we allow negative angles for the CIs only.
- Standardize naming with ssm_analyze instead of ssm_analyse - Add comprehensive docstrings using NumPy style - Add examples to key functions - Fix circular imports between modules - Add centralized parameter validation - Improve typing with more specific return annotations - Add consistent plot methods to SSMResults
…analyse functions
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.
Pull Request Overview
This PR represents a major refactoring of the circumplex package, transitioning from a monolithic design to a modular architecture. The changes split the code into separate modules for analysis, visualization, utilities, and data structures, while adding comprehensive test coverage and new instrument definitions.
- Modularized codebase by splitting
circumplex.pyintossm_analysis.py,visualization.py,utils.py, andssm_results.py - Added comprehensive test suite with visualization, analysis, and notebook validation tests
- Introduced new instrument definitions (IIPSC) and enhanced existing ones with normative data support
Reviewed Changes
Copilot reviewed 25 out of 31 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_visualisation.py | New test suite for visualization functions including circular plots and profile plots |
| tests/test_ssm_analysis.py | New comprehensive tests for SSM analysis with parametrized test cases |
| tests/test_notebooks.py | New notebook structure validation tests |
| tests/test_analysis.py | Removed legacy test file, functionality moved to test_ssm_analysis.py |
| src/circumplex/visualization.py | New module containing all plotting functions (ssm_plot, ssm_plot_circle, ssm_profile_plot) |
| src/circumplex/utils.py | New utility module with mathematical functions (cosine_form, r2_score, angle_median) |
| src/circumplex/ssm_results.py | New SSMResults class with plotting and data access methods |
| src/circumplex/ssm_analysis.py | New analysis module with ssm_analyze and related functions |
| src/circumplex/instrument.py | Enhanced instrument handling with normative data support and improved API |
| src/circumplex/instruments/*.json | Updated instrument definitions with consistent field naming (inst_items) |
| src/circumplex/core/ | New core module directory (appears to be experimental/unused) |
| pyproject.toml | Updated dependencies and build system from pdm to hatchling |
| requirements*.lock | New lock files for dependency management |
| docs/tutorials/using-instruments.ipynb | New tutorial notebook for instrument usage |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -0,0 +1,9 @@ | |||
| from instrument import Instrument, instruments | |||
Copilot
AI
Nov 8, 2025
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.
The import statement uses a relative import from instrument import ... without a leading dot, which will fail. This should be either from .instrument import ... for a relative import or from circumplex.core.instrument import ... for an absolute import.
| from instrument import Instrument, instruments | |
| from .instrument import Instrument, instruments |
| if incl_amp: | ||
| ax.axhline(amplitude + elevation, color="black", linestyle="--") | ||
| ax.text( | ||
| 0, elevation + amplitude * 0.5, f"a = {amplitude:.2f}", fontsize=fontsize |
Copilot
AI
Nov 8, 2025
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.
Incorrect calculation for amplitude text position. The text placement uses elevation + amplitude * 0.5, but this doesn't properly center the text at the midpoint of the amplitude line. It should be elevation + amplitude / 2 or elevation + amplitude * 0.5 needs to be calculated from the baseline, not the elevation.
| 0, elevation + amplitude * 0.5, f"a = {amplitude:.2f}", fontsize=fontsize | |
| 0, elevation + amplitude / 2, f"a = {amplitude:.2f}", fontsize=fontsize |
|
|
||
|
|
||
| def test_ssm_plot_contrast(mock_ssm_results): | ||
| fig = visualization.ssm_plot_circle(mock_ssm_results) |
Copilot
AI
Nov 8, 2025
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.
The test function name test_ssm_plot_contrast doesn't match its implementation. It calls ssm_plot_circle instead of ssm_plot_contrast, making it a duplicate of test_ssm_plot_circle rather than testing contrast plots.
| fig = visualization.ssm_plot_circle(mock_ssm_results) | |
| fig = visualization.ssm_plot_contrast(mock_ssm_results) |
| fc=list(colors[i]) + [0.5], # face opacity at 0.5 | ||
| linestyle=row["linestyle"], | ||
| edgecolor=list(colors[i]) + [1], # edge opacity at 1 |
Copilot
AI
Nov 8, 2025
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.
The list concatenation with + [0.5] and + [1] may fail or behave unexpectedly. colors[i] returns a tuple/array from seaborn palette, so list(colors[i]) creates a list that gets concatenated with a single-element list. Consider using tuple unpacking or (*colors[i], 0.5) for cleaner code, or ensure the result is a tuple: fc=tuple(list(colors[i]) + [0.5]).
| fc=list(colors[i]) + [0.5], # face opacity at 0.5 | |
| linestyle=row["linestyle"], | |
| edgecolor=list(colors[i]) + [1], # edge opacity at 1 | |
| fc=(*colors[i], 0.5), # face opacity at 0.5 | |
| linestyle=row["linestyle"], | |
| edgecolor=(*colors[i], 1), # edge opacity at 1 |
| if __name__ == "__main__": | ||
| ######## SCRATCH ######## | ||
| from importlib.resources import files | ||
|
|
||
| import matplotlib.pyplot as plt | ||
|
|
||
| _jz2017_path = str(files("circumplex.data").joinpath("jz2017.csv")) | ||
| data = pd.read_csv(_jz2017_path) | ||
|
|
||
| results = ssm_analyze( | ||
| data=data, | ||
| scales=["PA", "BC", "DE", "FG", "HI", "JK", "LM", "NO"], | ||
| angles=(90, 135, 180, 225, 270, 315, 0, 45), | ||
| # grouping="Gender", | ||
| # contrast='model', | ||
| measures=["NARPD", "ASPD"], | ||
| # measures_labels=['Narcissistic PD', 'Antisocial PD'], | ||
| ) | ||
| print(results.scores) | ||
| # print(results) | ||
| print(results.summary()) | ||
|
|
||
| # fig = ssm_plot(results) | ||
| fig, ax = results.profile_plot(incl_elev=True) | ||
| plt.show() |
Copilot
AI
Nov 8, 2025
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.
There is executable scratch/debug code at the module level (lines 846-870) that should not be in production code. This code will execute whenever the module is imported by other modules. Move this code to a separate script or protect it with if __name__ == "__main__": guard that's already present at line 846.
|
|
||
| __all__ = [ | ||
| "ssm_analyze", | ||
| "SSMResults", |
Copilot
AI
Nov 8, 2025
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.
The name 'SSMResults' is exported by all but is not defined.
| __all__ = [ | ||
| "ssm_analyze", | ||
| "SSMResults", | ||
| "ssm_plot", |
Copilot
AI
Nov 8, 2025
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.
The name 'ssm_plot' is exported by all but is not defined.
| @@ -0,0 +1,154 @@ | |||
| from __future__ import annotations | |||
|
|
|||
| from typing import Any, Dict, List, Tuple, Optional | |||
Copilot
AI
Nov 8, 2025
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.
Import of 'Optional' is not used.
| from typing import Any, Dict, List, Tuple, Optional | |
| from typing import Any, Dict, List, Tuple |
| @@ -0,0 +1,127 @@ | |||
| from typing import List, Tuple, Union, Optional, Any | |||
Copilot
AI
Nov 8, 2025
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.
Import of 'Optional' is not used.
Import of 'Any' is not used.
| from typing import List, Tuple, Union, Optional, Any | |
| from typing import List, Tuple, Union |
| @@ -0,0 +1,489 @@ | |||
| from __future__ import annotations | |||
|
|
|||
| from typing import List, Optional, Tuple, Union, Any | |||
Copilot
AI
Nov 8, 2025
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.
Import of 'Any' is not used.
| from typing import List, Optional, Tuple, Union, Any | |
| from typing import List, Optional, Tuple, Union |
Summary
ssm_analyzeinstead ofssm_analyse)Changes
This PR refactors the API to make it more consistent and better documented, while maintaining backward compatibility. Key improvements include:
API Consistency
Documentation
Improved Interface
Testing
🤖 Generated with Claude Code