Skip to content

Commit 7529ffc

Browse files
committed
preserve modality index names in names_make_unique()
closes #146 (cherry picked from commit 4cadfd01bb24e378333edbb83e0e85af8b44c623)
1 parent 86e7ab7 commit 7529ffc

4 files changed

Lines changed: 29 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning][].
88
[keep a changelog]: https://keepachangelog.com/en/1.1.0/
99
[semantic versioning]: https://semver.org/spec/v2.0.0.html
1010

11-
## [0.4.0] (unreleased)
11+
## [0.4.0] (Unreleased)
1212

1313
### Changed
1414

@@ -17,6 +17,12 @@ and this project adheres to [Semantic Versioning][].
1717
- The settings API has changed. Use e.g. `mudata.settings.pull_on_update = True` instead of `mudata.set_options(pull_on_update=True)` and use
1818
`mudata.settings.override` as context manager for local settings overrides.
1919

20+
## [0.3.9] (Unreleased)
21+
22+
### Fixed
23+
24+
- `make_obs_names_unique` and `make_var_names_unique` now preserve the `.obs.index.name` / `.var.index.name` of each modality and of the MuData object.
25+
2026
## [0.3.8]
2127

2228
### Fixed
@@ -181,6 +187,7 @@ To copy the annotations explicitly, you will need to use `pull_obs()` and/or `pu
181187
Initial `mudata` release with `MuData`, previously a part of the `muon` framework.
182188

183189
[0.4.0]: https://github.com/scverse/mudata/releases/tag/v0.4.0
190+
[0.3.9]: https://github.com/scverse/mudata/releases/tag/v0.3.9
184191
[0.3.8]: https://github.com/scverse/mudata/releases/tag/v0.3.8
185192
[0.3.7]: https://github.com/scverse/mudata/releases/tag/v0.3.7
186193
[0.3.6]: https://github.com/scverse/mudata/releases/tag/v0.3.6

src/mudata/_core/mudata.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -868,14 +868,19 @@ def _names_make_unique(self, attr: Literal["obs", "var"]):
868868
kj = mods[j]
869869
if len(getattr(self._mod[ki], namesattr).intersection(getattr(self._mod[kj], namesattr))) > 0:
870870
warnings.warn(
871-
"Modality names will be prepended to obs_names since there are identical obs_names in different modalities.",
871+
f"Modality names will be prepended to {namesattr} since there are identical {namesattr} in different modalities.",
872872
stacklevel=1,
873873
)
874874
for m, mod in self._mod.items():
875875
setattr(mod, namesattr, m + ":" + getattr(mod, namesattr).astype(str))
876876
raise StopIteration() # break out of both loops
877877

878-
setattr(self, namesattr, pd.Index([]).append([getattr(mod, namesattr) for mod in self._mod.values()]))
878+
attrval = getattr(self, attr)
879+
880+
# self._set_names replaces the names of each modality. Unnecessary here, since the names are coming directly from the modalities
881+
attrval.index = pd.Index([], name=attrval.index.name).append(
882+
[getattr(mod, namesattr) for mod in self._mod.values()]
883+
)
879884

880885
def obs_names_make_unique(self):
881886
"""

tests/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ def mdata(rng: np.random.Generator, request: pytest.FixtureRequest) -> MuData:
6060
[f"{attr}_{i}" for i in rng.choice(mod.shape[axis], size=mod.shape[axis], replace=False)],
6161
)
6262
setattr(mod, f"{oattr}_names", [f"{modname}_{oattr}_{i}" for i in range(mod.shape[1 - axis])])
63+
mod1.obs.index.name = "fizz"
64+
mod2.var.index.name = "buzz"
6365
mdata = MuData(mods, axis=axis)
6466
mdata.obs["arange"] = np.arange(mdata.n_obs)
6567
return mdata

tests/test_obs_var.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,24 +118,30 @@ def test_names_make_unique(mdata: md.MuData):
118118
namesfun = getattr(mdata, f"{oattr}_names_make_unique")
119119

120120
mods = mdata.mod_names
121-
names = getattr(mdata.mod[mods[0]], namesattr).to_list()
122-
names[1] = names[0]
123-
setattr(mdata.mod[mods[0]], namesattr, names)
121+
names = getattr(mdata.mod[mods[0]], namesattr)
122+
nameslist = names.to_list()
123+
nameslist[1] = nameslist[0]
124+
setattr(mdata.mod[mods[0]], namesattr, pd.Index(nameslist, name=names.name))
124125
namesfun()
125126
assert mdata.shape[1 - mdata.axis] == sum(mod.shape[1 - mdata.axis] for mod in mdata.mod.values())
126127
assert getattr(mdata, namesattr).is_unique
128+
assert getattr(mdata.mod[mods[0]], namesattr).name == names.name
127129

130+
modality_index_names = {}
128131
for mod in mods[:2]:
129-
names = getattr(mdata.mod[mod], namesattr).to_list()
130-
names[1] = names[0] = "testname"
131-
setattr(mdata.mod[mod], namesattr, names)
132+
names = getattr(mdata.mod[mod], namesattr)
133+
nameslist = names.to_list()
134+
nameslist[1] = nameslist[0] = "testname"
135+
setattr(mdata.mod[mod], namesattr, pd.Index(nameslist, name=names.name))
136+
modality_index_names[mod] = names.name
132137
with pytest.warns(UserWarning, match="Modality names will be prepended"):
133138
namesfun()
134139
assert mdata.shape[1 - mdata.axis] == sum(mod.shape[1 - mdata.axis] for mod in mdata.mod.values())
135140
assert getattr(mdata, namesattr).is_unique
136141
for m, mod in mdata.mod.items():
137142
assert getattr(mod, namesattr).is_unique
138143
assert (getattr(mod, namesattr).str[: len(m) + 1] == f"{m}:").all()
144+
assert getattr(mod, namesattr).name == modality_index_names[m]
139145

140146
with pytest.raises(TypeError, match="axis="):
141147
getattr(mdata, f"{attr}_names_make_unique")()

0 commit comments

Comments
 (0)