Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,6 @@ pyrightconfig.json

# End of https://www.toptal.com/developers/gitignore/api/python
**/_version.py

# VS Code
.vscode/
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -273,7 +283,8 @@ Initial implementation of the lyDATA library.
<!-- generated by git-cliff -->
<!-- markdownlint-disable-file MD024 -->

[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
Expand All @@ -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
17 changes: 9 additions & 8 deletions src/lydata/accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"]
Expand All @@ -912,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
36 changes: 36 additions & 0 deletions tests/test_infer_levels.py
Original file line number Diff line number Diff line change
@@ -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"
Loading