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
9 changes: 6 additions & 3 deletions src/adata/conversion/cellbender.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ def place_global(key: str, path: str, item: h5py.Dataset) -> None:
rows = index
elif n_latent in (0, n_barcodes):
rows = None
if index_path is not None:
# A filtered 0.3+ file still lists every analysed droplet, which
# places nothing here; CellBender's loader keeps it in uns.
other[_INDEX] = index_path
else:
raise ValueError(
f"Cannot tell which barcode each of CellBender's {n_latent:,} latent "
Expand Down Expand Up @@ -641,10 +645,9 @@ def _write_uns(root: Any, handle: h5py.File, latents: _Latents) -> None:
meta = write_mapping(root["uns"], "cellbender")
write_scalar(meta, "version", latents.version)
for key, path in latents.other.items():
# A setting stays the one-element array CellBender stores it as,
# as its own loader gives it.
value = np.asarray(handle[path][...])
if value.ndim == 1 and value.size == 1:
# CellBender wraps each scalar setting in a one-element array.
value = value.reshape(())
if value.ndim == 0:
item = value.item()
write_scalar(meta, key, item.decode("utf-8", "replace") if isinstance(item, bytes) else item)
Expand Down
20 changes: 19 additions & 1 deletion tests/test_cellbender.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def test_each_latent_lands_on_its_own_barcode(tmp_path, version, filtered, suffi
np.testing.assert_allclose(got.var["ambient_expression"], truth.ambient)
uns = got.uns["cellbender"]
assert uns["version"] == version
assert float(uns["target_false_positive_rate"]) == 0.01
assert np.ravel(uns["target_false_positive_rate"]).tolist() == [0.01]
if version == "0.3":
assert "features_analyzed_inds" in uns, "metadata stays in uns"
assert "features_analyzed_inds" not in got.var
Expand Down Expand Up @@ -164,6 +164,24 @@ def test_latents_land_right_when_read_in_many_blocks(tmp_path, filtered, monkeyp
assert np.array_equal(got.layers["cellranger"].toarray(), _raw_counts()[rows])


@pytest.mark.parametrize("filtered", [False, True], ids=["full", "filtered"])
@pytest.mark.parametrize("version", ["0.3", "0.2"])
def test_uns_holds_what_cellbender_stored_as_its_loader_gives_it(tmp_path, version, filtered):
"""Checked against real CellBender 0.3.2 output, uns differed from
CellBender's own loader twice: one-element settings were unwrapped to
scalars, so `uns[...][0]` broke, and a filtered file's
barcode_indices_for_latents, which places no rows there, was dropped."""
truth = _cellbender(tmp_path, version=version, filtered=filtered)
out = tmp_path / "out.h5ad"
_ok(truth.path, "-o", out)
uns = _read(out).uns["cellbender"]
# A scalar in 0.2, a one-element array since.
assert np.shape(uns["target_false_positive_rate"]) == (() if version == "0.2" else (1,))
# 0.2 drops the index from filtered files; 0.3+ keeps every analysed droplet's.
kept = version != "0.2" and filtered
assert ("barcode_indices_for_latents" in uns) == kept


def test_cellbender_0_2_on_cell_ranger_2_input(tmp_path):
truth = _cellbender(tmp_path, version="0.2", cellranger_v2=True)
out = tmp_path / "out.h5ad"
Expand Down
Loading