Skip to content

Commit fca9309

Browse files
committed
preserve modality index names in names_make_unique()
closes #146
1 parent 2eaa318 commit fca9309

4 files changed

Lines changed: 25 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,11 @@ 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.3.9] (Unreleased)
1212

13-
### Changed
13+
### Fixed
1414

15-
- `update()` no longer automatically pulls obs/var columns from individual modalities by default. Set `mudata.set_options(pull_on_update=true)`
16-
to restore the old behavior. Use `pull_obs/pull_var` and `push_obs/push_var` for more flexibility.
17-
- 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
18-
`mudata.settings.override` as context manager for local settings overrides.
15+
- `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.
1916

2017
## [0.3.8]
2118

@@ -180,7 +177,7 @@ To copy the annotations explicitly, you will need to use `pull_obs()` and/or `pu
180177

181178
Initial `mudata` release with `MuData`, previously a part of the `muon` framework.
182179

183-
[0.4.0]: https://github.com/scverse/mudata/releases/tag/v0.4.0
180+
[0.3.9]: https://github.com/scverse/mudata/releases/tag/v0.3.9
184181
[0.3.8]: https://github.com/scverse/mudata/releases/tag/v0.3.8
185182
[0.3.7]: https://github.com/scverse/mudata/releases/tag/v0.3.7
186183
[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
@@ -1299,14 +1299,19 @@ def _names_make_unique(self, attr: Literal["obs", "var"]):
12991299
kj = mods[j]
13001300
if len(getattr(self._mod[ki], namesattr).intersection(getattr(self._mod[kj], namesattr))) > 0:
13011301
warnings.warn(
1302-
"Modality names will be prepended to obs_names since there are identical obs_names in different modalities.",
1302+
f"Modality names will be prepended to {namesattr} since there are identical {namesattr} in different modalities.",
13031303
stacklevel=1,
13041304
)
13051305
for m, mod in self._mod.items():
13061306
setattr(mod, namesattr, m + ":" + getattr(mod, namesattr).astype(str))
13071307
raise StopIteration() # break out of both loops
13081308

1309-
setattr(self, namesattr, pd.Index([]).append([getattr(mod, namesattr) for mod in self._mod.values()]))
1309+
attrval = getattr(self, attr)
1310+
1311+
# self._set_names replaces the names of each modality. Unnecessary here, since the names are coming directly from the modalities
1312+
attrval.index = pd.Index([], name=attrval.index.name).append(
1313+
[getattr(mod, namesattr) for mod in self._mod.values()]
1314+
)
13101315

13111316
def obs_names_make_unique(self):
13121317
"""

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
@@ -105,24 +105,30 @@ def test_names_make_unique(mdata: md.MuData):
105105
namesfun = getattr(mdata, f"{oattr}_names_make_unique")
106106

107107
mods = mdata.mod_names
108-
names = getattr(mdata.mod[mods[0]], namesattr).to_list()
109-
names[1] = names[0]
110-
setattr(mdata.mod[mods[0]], namesattr, names)
108+
names = getattr(mdata.mod[mods[0]], namesattr)
109+
nameslist = names.to_list()
110+
nameslist[1] = nameslist[0]
111+
setattr(mdata.mod[mods[0]], namesattr, pd.Index(nameslist, name=names.name))
111112
namesfun()
112113
assert mdata.shape[1 - mdata.axis] == sum(mod.shape[1 - mdata.axis] for mod in mdata.mod.values())
113114
assert getattr(mdata, namesattr).is_unique
115+
assert getattr(mdata.mod[mods[0]], namesattr).name == names.name
114116

117+
modality_index_names = {}
115118
for mod in mods[:2]:
116-
names = getattr(mdata.mod[mod], namesattr).to_list()
117-
names[1] = names[0] = "testname"
118-
setattr(mdata.mod[mod], namesattr, names)
119+
names = getattr(mdata.mod[mod], namesattr)
120+
nameslist = names.to_list()
121+
nameslist[1] = nameslist[0] = "testname"
122+
setattr(mdata.mod[mod], namesattr, pd.Index(nameslist, name=names.name))
123+
modality_index_names[mod] = names.name
119124
with pytest.warns(UserWarning, match="Modality names will be prepended"):
120125
namesfun()
121126
assert mdata.shape[1 - mdata.axis] == sum(mod.shape[1 - mdata.axis] for mod in mdata.mod.values())
122127
assert getattr(mdata, namesattr).is_unique
123128
for m, mod in mdata.mod.items():
124129
assert getattr(mod, namesattr).is_unique
125130
assert (getattr(mod, namesattr).str[: len(m) + 1] == f"{m}:").all()
131+
assert getattr(mod, namesattr).name == modality_index_names[m]
126132

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

0 commit comments

Comments
 (0)