From 5db669986427792f1a6203eefbd48ed290800dff Mon Sep 17 00:00:00 2001 From: Roman Ludwig <48687784+rmnldwg@users.noreply.github.com> Date: Sat, 12 Jul 2025 11:42:38 +0200 Subject: [PATCH 1/3] test: add doc- & unittest for inferring superlevel Related: #5 --- .gitignore | 3 +++ src/lydata/accessor.py | 11 ++++++----- tests/test_infer_levels.py | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 tests/test_infer_levels.py diff --git a/.gitignore b/.gitignore index add37ec..0437039 100644 --- a/.gitignore +++ b/.gitignore @@ -175,3 +175,6 @@ pyrightconfig.json # End of https://www.toptal.com/developers/gitignore/api/python **/_version.py + +# VS Code +.vscode/ diff --git a/src/lydata/accessor.py b/src/lydata/accessor.py index 6cb8b70..2b3d19c 100644 --- a/src/lydata/accessor.py +++ b/src/lydata/accessor.py @@ -881,11 +881,11 @@ def infer_superlevels( original DataFrame. >>> df = pd.DataFrame({ - ... ('MRI', 'ipsi' , 'Ia' ): [True , False, False, None], - ... ('MRI', 'ipsi' , 'Ib' ): [False, True , False, None], - ... ('MRI', 'contra', 'IIa'): [False, False, None , None], - ... ('MRI', 'contra', 'IIb'): [False, True , True , None], - ... ('CT' , 'ipsi' , 'I' ): [True , False, False, None], + ... ('MRI', 'ipsi' , 'Ia' ): [True , False, False, None, None ], + ... ('MRI', 'ipsi' , 'Ib' ): [False, True , False, None, False], + ... ('MRI', 'contra', 'IIa'): [False, False, None , None, None ], + ... ('MRI', 'contra', 'IIb'): [False, True , True , None, False], + ... ('CT' , 'ipsi' , 'I' ): [True , False, False, None, None ], ... }) >>> df.ly.infer_superlevels(modalities=["MRI"]) # doctest: +NORMALIZE_WHITESPACE MRI @@ -895,6 +895,7 @@ def infer_superlevels( 1 True True 2 False True 3 None None + 4 None None """ modalities = modalities or list(get_default_modalities().keys()) sides = sides or ["ipsi", "contra"] diff --git a/tests/test_infer_levels.py b/tests/test_infer_levels.py new file mode 100644 index 0000000..80e38c5 --- /dev/null +++ b/tests/test_infer_levels.py @@ -0,0 +1,36 @@ +"""Check that inferring sub- and super-levels works correctly.""" + +import pandas as pd +import pytest + +import lydata # noqa: F401 + + +@pytest.fixture +def mock_data() -> pd.DataFrame: + """Create a mock dataset for testing.""" + return pd.DataFrame({ + ("MRI", "ipsi", "Ia" ): [True , False, False, None, None ], + ("MRI", "ipsi", "Ib" ): [False, True , False, None, False], + ("MRI", "contra", "IIa"): [False, False, None , None, None ], + ("MRI", "contra", "IIb"): [False, True , True , None, False], + ("CT", "ipsi", "I" ): [True , False, False, None, None ], + }) + + +def test_infer_superlevels(mock_data: pd.DataFrame) -> None: + """Check that superlevels are inferred correctly.""" + inferred = mock_data.ly.infer_superlevels(modalities=["MRI"]) + + expected_ipsi_I = [True, True, False, None, None] + expected_contra_II = [False, True, True, None, None] + + for example in range(len(mock_data)): + assert ( + inferred.iloc[example].MRI.ipsi.I + == expected_ipsi_I[example] + ), f"{example = } mismatch for ipsi I" + assert ( + inferred.iloc[example].MRI.contra.II + == expected_contra_II[example] + ), f"{example = } mismatch for contra II" From 2579af260644cdfbe9421cfb1958b49f657eb29b Mon Sep 17 00:00:00 2001 From: Roman Ludwig <48687784+rmnldwg@users.noreply.github.com> Date: Sat, 12 Jul 2025 11:44:35 +0200 Subject: [PATCH 2/3] fix: super = `None` when subs = `False` & `None` When sublevels a and b report `None` and `False` respectively, we cannot conclude that the level was healthy. Fixes: #5 --- src/lydata/accessor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lydata/accessor.py b/src/lydata/accessor.py index 2b3d19c..96f9dc6 100644 --- a/src/lydata/accessor.py +++ b/src/lydata/accessor.py @@ -913,14 +913,14 @@ def infer_superlevels( sublevel_cols = [(modality, side, sublevel) for sublevel in sublevels] try: - are_all_healthy = ~self._obj[sublevel_cols].any(axis=1) + is_unknown = self._obj[sublevel_cols].isna().any(axis=1) is_any_involved = self._obj[sublevel_cols].any(axis=1) - is_unknown = self._obj[sublevel_cols].isna().all(axis=1) + are_all_healthy = ~is_unknown & ~is_any_involved except KeyError: continue result.loc[are_all_healthy, (modality, side, superlevel)] = False - result.loc[is_any_involved, (modality, side, superlevel)] = True result.loc[is_unknown, (modality, side, superlevel)] = None + result.loc[is_any_involved, (modality, side, superlevel)] = True return result From 6bca49f0b05f94c84d587223cfcdb35f20a2381e Mon Sep 17 00:00:00 2001 From: Roman Ludwig <48687784+rmnldwg@users.noreply.github.com> Date: Sat, 12 Jul 2025 11:49:21 +0200 Subject: [PATCH 3/3] chore: update changelog --- CHANGELOG.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 440dbee..de0f732 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. +## [0.3.1] - 2025-07-12 + +### ๐Ÿ› Bug Fixes + +- A superlevel must be unknown, when one sublevel reports "healthy" and the other "unknown". Fixes [#5]. + +### ๐Ÿงช Testing + +- Add doc- & unittest for correctly inferring superlevel. Related to [#5]. + ## [0.3.0] - 2025-06-26 Th Python package `lydata` is now pulled out of the data repository [lyDATA](https://github.com/lycosystem/lydata) and will be maintained in the repository [lydata-package](https://github.com/lycosystem/lydata-package). @@ -273,7 +283,8 @@ Initial implementation of the lyDATA library. -[0.3.0]: https://github.com/lycosystem/lydata/compare/8ae13..0.3.0 +[0.3.1]: https://github.com/lycosystem/lydata-package/compare/0.3.0..0.3.1 +[0.3.0]: https://github.com/lycosystem/lydata-package/compare/8ae13..0.3.0 [0.2.5]: https://github.com/lycosystem/lydata/compare/0.2.4..0.2.5 [0.2.4]: https://github.com/lycosystem/lydata/compare/0.2.3..0.2.4 [0.2.3]: https://github.com/lycosystem/lydata/compare/0.2.2..0.2.3 @@ -291,3 +302,4 @@ Initial implementation of the lyDATA library. [#2]: https://github.com/lycosystem/lydata/issues/2 [#4]: https://github.com/lycosystem/lydata/issues/4 [#13]: https://github.com/lycosystem/lydata/issues/13 +[#5]: https://github.com/lycosystem/lydata-package/issues/5