diff --git a/docs/api/cooler.rst b/docs/api/cooler.rst index f2dce02..05c2a7e 100644 --- a/docs/api/cooler.rst +++ b/docs/api/cooler.rst @@ -23,6 +23,7 @@ Cooler API .. automethod:: __init__ .. automethod:: add_pixels + .. automethod:: bins .. automethod:: chromosomes .. automethod:: finalize .. automethod:: path diff --git a/docs/api/generic.rst b/docs/api/generic.rst index f39bee4..a19910a 100644 --- a/docs/api/generic.rst +++ b/docs/api/generic.rst @@ -19,7 +19,10 @@ Generic API .. automethod:: __init__ .. automethod:: __getitem__ + .. automethod:: attributes .. automethod:: chromosomes + .. automethod:: is_hic + .. automethod:: is_mcool .. automethod:: path .. automethod:: resolutions diff --git a/docs/api/hic.rst b/docs/api/hic.rst index 488c2a1..a22e277 100644 --- a/docs/api/hic.rst +++ b/docs/api/hic.rst @@ -12,6 +12,7 @@ Hi-C API .. automethod:: __init__ .. automethod:: add_pixels + .. automethod:: bins .. automethod:: chromosomes .. automethod:: finalize .. automethod:: path diff --git a/src/bin_table.cpp b/src/bin_table.cpp index ddb12a4..89bb78e 100644 --- a/src/bin_table.cpp +++ b/src/bin_table.cpp @@ -395,6 +395,8 @@ nb::object BinTable::to_df(std::optional range, std::vector starts(n); std::vector ends(n); + const auto chrom_id_offset = static_cast(_bins->chromosomes().at(0).is_all()); + std::visit( [&](const auto& bins) { const auto [first_bin, last_bin] = !range.has_value() @@ -403,7 +405,7 @@ nb::object BinTable::to_df(std::optional range, std::size_t i = 0; std::for_each(first_bin, last_bin, [&](const auto& bin) { bin_ids[i] = bin.id(); - chrom_ids[i] = static_cast(bin.chrom().id()); + chrom_ids[i] = static_cast(bin.chrom().id() - chrom_id_offset); starts[i] = bin.start(); ends[i] = bin.end(); ++i; @@ -411,8 +413,8 @@ nb::object BinTable::to_df(std::optional range, }, _bins->get()); - return make_bin_table_df(chrom_names(), std::move(chrom_ids), std::move(starts), std::move(ends), - std::move(bin_ids)); + return make_bin_table_df(chrom_names(false), std::move(chrom_ids), std::move(starts), + std::move(ends), std::move(bin_ids)); } std::shared_ptr BinTable::get() const noexcept { return _bins; } diff --git a/src/cooler_file_writer.cpp b/src/cooler_file_writer.cpp index 3e511a9..1d0e542 100644 --- a/src/cooler_file_writer.cpp +++ b/src/cooler_file_writer.cpp @@ -72,6 +72,14 @@ const hictk::Reference &CoolerFileWriter::chromosomes() const { return ref; } +std::shared_ptr CoolerFileWriter::bins_ptr() const noexcept { + if (!_w) { + return {}; + } + + return _w->bins_ptr(); +} + void CoolerFileWriter::add_pixels(const nb::object &df) { if (!_w.has_value()) { throw std::runtime_error( @@ -196,6 +204,8 @@ void CoolerFileWriter::bind(nb::module_ &m) { nb::arg("include_ALL") = false, "Get chromosomes sizes as a dictionary mapping names to sizes.", nb::rv_policy::take_ownership); + writer.def("bins", &get_bins_from_object, "Get table of bins.", + nb::sig("def bins(self) -> hictkpy.BinTable"), nb::rv_policy::move); writer.def("add_pixels", &hictkpy::CoolerFileWriter::add_pixels, nb::sig("def add_pixels(self, pixels: pandas.DataFrame)"), nb::arg("pixels"), diff --git a/src/file.cpp b/src/file.cpp index eea9ac5..29ba846 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -194,7 +194,7 @@ static nb::dict get_hic_attrs(const hictk::hic::File &hf) { py_attrs["bin_size"] = hf.resolution(); py_attrs["format"] = "HIC"; - py_attrs["format_version"] = hf.version(); + py_attrs["format-version"] = hf.version(); py_attrs["assembly"] = hf.assembly(); py_attrs["format-url"] = "https://github.com/aidenlab/hic-format"; py_attrs["nbins"] = hf.bins().size(); @@ -305,7 +305,8 @@ void declare_file_class(nb::module_ &m) { file.def("resolution", &hictk::File::resolution, "Get the bin size in bp."); file.def("nbins", &hictk::File::nbins, "Get the total number of bins."); - file.def("nchroms", &hictk::File::nchroms, "Get the total number of chromosomes."); + file.def("nchroms", &hictk::File::nchroms, nb::arg("include_ALL") = false, + "Get the total number of chromosomes."); file.def("attributes", &file::attributes, "Get file attributes as a dictionary.", nb::rv_policy::take_ownership); diff --git a/src/hic_file_writer.cpp b/src/hic_file_writer.cpp index f6ee3da..a5eadea 100644 --- a/src/hic_file_writer.cpp +++ b/src/hic_file_writer.cpp @@ -117,6 +117,10 @@ auto HiCFileWriter::resolutions() const { const hictk::Reference &HiCFileWriter::chromosomes() const { return _w.chromosomes(); } +hictkpy::BinTable HiCFileWriter::bins(std::uint32_t resolution) const { + return hictkpy::BinTable{_w.bins(resolution)}; +} + void HiCFileWriter::add_pixels(const nb::object &df) { if (_finalized) { throw std::runtime_error( @@ -184,6 +188,8 @@ void HiCFileWriter::bind(nb::module_ &m) { nb::arg("include_ALL") = false, "Get chromosomes sizes as a dictionary mapping names to sizes.", nb::rv_policy::take_ownership); + writer.def("bins", &hictkpy::HiCFileWriter::bins, "Get table of bins for the given resolution.", + nb::sig("def bins(self, resolution: int) -> hictkpy.BinTable"), nb::rv_policy::move); writer.def("add_pixels", &hictkpy::HiCFileWriter::add_pixels, nb::sig("def add_pixels(self, pixels: pd.DataFrame) -> None"), nb::arg("pixels"), diff --git a/src/include/hictkpy/cooler_file_writer.hpp b/src/include/hictkpy/cooler_file_writer.hpp index d8ebc6f..ec90b4b 100644 --- a/src/include/hictkpy/cooler_file_writer.hpp +++ b/src/include/hictkpy/cooler_file_writer.hpp @@ -40,6 +40,7 @@ class CoolerFileWriter { [[nodiscard]] std::uint32_t resolution() const noexcept; [[nodiscard]] const hictk::Reference& chromosomes() const; + [[nodiscard]] std::shared_ptr bins_ptr() const noexcept; void add_pixels(const nanobind::object& df); diff --git a/src/include/hictkpy/hic_file_writer.hpp b/src/include/hictkpy/hic_file_writer.hpp index 0ca2242..1041b72 100644 --- a/src/include/hictkpy/hic_file_writer.hpp +++ b/src/include/hictkpy/hic_file_writer.hpp @@ -44,6 +44,7 @@ class HiCFileWriter { [[nodiscard]] auto resolutions() const; [[nodiscard]] const hictk::Reference& chromosomes() const; + [[nodiscard]] hictkpy::BinTable bins(std::uint32_t resolution) const; void add_pixels(const nanobind::object& df); diff --git a/src/include/hictkpy/pixel_selector.hpp b/src/include/hictkpy/pixel_selector.hpp index 890a3b2..54d697d 100644 --- a/src/include/hictkpy/pixel_selector.hpp +++ b/src/include/hictkpy/pixel_selector.hpp @@ -48,11 +48,10 @@ struct PixelSelector { [[nodiscard]] std::string repr() const; - using PixelCoordTuple = - std::tuple; + using GenomicCoordTuple = std::tuple; - [[nodiscard]] auto get_coord1() const -> PixelCoordTuple; - [[nodiscard]] auto get_coord2() const -> PixelCoordTuple; + [[nodiscard]] auto get_coord1() const -> GenomicCoordTuple; + [[nodiscard]] auto get_coord2() const -> GenomicCoordTuple; [[nodiscard]] nanobind::iterator make_iterable() const; [[nodiscard]] nanobind::object to_arrow(std::string_view span = "upper_triangle") const; diff --git a/src/multires_file.cpp b/src/multires_file.cpp index 15d693d..80fb65b 100644 --- a/src/multires_file.cpp +++ b/src/multires_file.cpp @@ -28,6 +28,43 @@ static std::string repr(const hictk::MultiResFile& mrf) { static std::filesystem::path get_path(const hictk::MultiResFile& mrf) { return mrf.path(); } +static nb::dict get_attrs(const hictk::hic::File& hf) { + nb::dict py_attrs; + + py_attrs["format"] = "HIC"; + py_attrs["format-version"] = hf.version(); + py_attrs["assembly"] = hf.assembly(); + py_attrs["format-url"] = "https://github.com/aidenlab/hic-format"; + py_attrs["nchroms"] = hf.nchroms(); + + for (const auto& [k, v] : hf.attributes()) { + py_attrs[nb::cast(k)] = v; + } + + return py_attrs; +} + +static nb::dict get_attrs(const hictk::cooler::MultiResFile& mclr) { + nb::dict py_attrs; + + py_attrs["format"] = mclr.attributes().format; + py_attrs["format-version"] = mclr.attributes().format_version; + py_attrs["format-url"] = "https://github.com/open2c/cooler"; + py_attrs["assembly"] = + mclr.open(mclr.resolutions().front()).attributes().assembly.value_or("unknown"); + py_attrs["nchroms"] = mclr.chromosomes().size(); + + return py_attrs; +} + +static nb::dict attributes(const hictk::MultiResFile& f) { + auto attrs = f.is_hic() ? get_attrs(f.open(f.resolutions().front()).get()) + : get_attrs(hictk::cooler::MultiResFile{f.path()}); + attrs["resolutions"] = f.resolutions(); + + return attrs; +} + bool is_mcool_file(const std::filesystem::path& path) { return bool(hictk::cooler::utils::is_multires_file(path.string())); } @@ -54,12 +91,17 @@ void declare_multires_file_class(nb::module_& m) { mres_file.def("__repr__", &multires_file::repr, nb::rv_policy::move); mres_file.def("path", &multires_file::get_path, "Get the file path.", nb::rv_policy::move); + mres_file.def("is_mcool", &hictk::MultiResFile::is_mcool, + "Test whether the file is in .mcool format."); + mres_file.def("is_hic", &hictk::MultiResFile::is_hic, "Test whether the file is in .hic format."); mres_file.def("chromosomes", &get_chromosomes_from_object, nb::arg("include_ALL") = false, "Get chromosomes sizes as a dictionary mapping names to sizes.", nb::rv_policy::take_ownership); mres_file.def("resolutions", &get_resolutions, "Get the list of available resolutions.", nb::rv_policy::take_ownership); + mres_file.def("attributes", &multires_file::attributes, "Get file attributes as a dictionary.", + nb::rv_policy::take_ownership); mres_file.def("__getitem__", &hictk::MultiResFile::open, "Open the Cooler or .hic file corresponding to the resolution given as input.", nb::rv_policy::move); diff --git a/src/pixel_selector.cpp b/src/pixel_selector.cpp index bc8518b..50c650a 100644 --- a/src/pixel_selector.cpp +++ b/src/pixel_selector.cpp @@ -66,7 +66,9 @@ std::string PixelSelector::repr() const { count_type_to_str(pixel_count)); } - return fmt::format(FMT_STRING("PixelSelector({}, {}; {}; {})"), coord1(), coord2(), + return fmt::format(FMT_STRING("PixelSelector({}:{}-{}; {}:{}-{}; {}; {})"), + coord1().bin1.chrom().name(), coord1().bin1.start(), coord1().bin2.end(), + coord2().bin1.chrom().name(), coord2().bin1.start(), coord2().bin2.end(), pixel_format == PixelFormat::COO ? "COO" : "BG2", count_type_to_str(pixel_count)); } @@ -105,16 +107,24 @@ const hictk::BinTable& PixelSelector::bins() const noexcept { return std::visit([](const auto& s) -> const hictk::BinTable& { return s->bins(); }, selector); } -auto PixelSelector::get_coord1() const -> PixelCoordTuple { - const auto c = coord1(); - return PixelCoordTuple{std::make_tuple(c.bin1.chrom().name(), c.bin1.start(), c.bin1.end(), - c.bin2.chrom().name(), c.bin2.start(), c.bin2.end())}; +[[nodiscard]] static PixelSelector::GenomicCoordTuple coords_to_tuple( + const hictk::PixelCoordinates& coords, const hictk::BinTable& bins) { + if (!coords) { + return {"ALL", 0, static_cast(bins.size())}; + } + + assert(coords.bin1.chrom() == coords.bin2.chrom()); + + return {std::string{coords.bin1.chrom().name()}, static_cast(coords.bin1.start()), + static_cast(coords.bin2.end())}; +} + +auto PixelSelector::get_coord1() const -> GenomicCoordTuple { + return coords_to_tuple(coord1(), bins()); } -auto PixelSelector::get_coord2() const -> PixelCoordTuple { - const auto c = coord2(); - return PixelCoordTuple{std::make_tuple(c.bin1.chrom().name(), c.bin1.start(), c.bin1.end(), - c.bin2.chrom().name(), c.bin2.start(), c.bin2.end())}; +auto PixelSelector::get_coord2() const -> GenomicCoordTuple { + return coords_to_tuple(coord2(), bins()); } template diff --git a/test/test_bin_table.py b/test/test_bin_table.py index 5e35ce3..3c7d3b9 100644 --- a/test/test_bin_table.py +++ b/test/test_bin_table.py @@ -61,17 +61,16 @@ def test_getters(self): bins.get_id("abc", 100) @pytest.mark.skipif( - not numpy_avail() or not pandas_avail() or not pyarrow_avail(), - reason="numpy, pandas, or pyarrow are not available", + not pandas_avail() or not pyarrow_avail(), + reason="pandas or pyarrow are not available", ) def test_vectorized_getters(self): - import numpy as np chroms = {"chr1": 1000, "chr2": 500} bins = hictkpy.BinTable(chroms, 100) - assert len(bins.get(np.array([1, 1]))) == 2 - assert len(bins.get_ids(np.array(["chr1", "chr1"]), np.array([1, 1]))) == 2 + assert len(bins.get([1, 1])) == 2 + assert len(bins.get_ids(["chr1", "chr1"], [1, 1])) == 2 @pytest.mark.skipif(not pandas_avail() or not pyarrow_avail(), reason="pandas is not available") def test_merge(self): diff --git a/test/test_file_accessors.py b/test/test_file_accessors.py index 923ea16..ee621ce 100644 --- a/test/test_file_accessors.py +++ b/test/test_file_accessors.py @@ -27,7 +27,7 @@ class TestClass: def test_attributes(self, file, resolution): f = hictkpy.File(file, resolution) assert f.resolution() == 100_000 - # assert f.nchroms() == 8 # TODO enable after merging https://github.com/paulsengroup/hictk/pull/294 + assert f.nchroms() == 8 assert f.nbins() == 1380 assert "chr2L" in f.chromosomes() diff --git a/test/test_file_creation_cool.py b/test/test_file_creation_cool.py index 518dd75..1685326 100644 --- a/test/test_file_creation_cool.py +++ b/test/test_file_creation_cool.py @@ -31,6 +31,21 @@ def setup_method(): logging.basicConfig(level="INFO", force=True) logging.getLogger().setLevel("INFO") + def test_accessors(self, file, resolution, tmpdir): + bins = hictkpy.File(file, resolution).bins() + + path = tmpdir / "test.cool" + w = hictkpy.cooler.FileWriter(path, bins) + + assert str(w).startswith("CoolFileWriter(") + assert w.path() == path + if resolution is None: + assert w.resolution() == 0 + else: + assert w.resolution() == resolution + assert w.chromosomes() == bins.chromosomes() + assert len(w.bins().to_df().compare(bins.to_df())) == 0 + def test_file_creation_thin_pixel(self, file, resolution, tmpdir): f = hictkpy.File(file, resolution) if f.bins().type() != "fixed": @@ -39,7 +54,7 @@ def test_file_creation_thin_pixel(self, file, resolution, tmpdir): df = f.fetch(join=False).to_df() expected_sum = df["count"].sum() - path = tmpdir / "test1.cool" + path = tmpdir / "test.cool" w = hictkpy.cooler.FileWriter(path, f.chromosomes(), f.resolution()) chunk_size = 1000 @@ -66,7 +81,7 @@ def test_file_creation(self, file, resolution, tmpdir): df = f.fetch(join=True).to_df() expected_sum = df["count"].sum() - path = tmpdir / "test2.cool" + path = tmpdir / "test.cool" w = hictkpy.cooler.FileWriter(path, f.chromosomes(), f.resolution()) chunk_size = 1000 @@ -91,7 +106,7 @@ def test_file_creation_bin_table(self, file, resolution, tmpdir): df = f.fetch(join=True).to_df() expected_sum = df["count"].sum() - path = tmpdir / "test2.cool" + path = tmpdir / "test.cool" w = hictkpy.cooler.FileWriter(path, f.bins()) chunk_size = 1000 @@ -119,7 +134,7 @@ def test_file_creation_float_counts(self, file, resolution, tmpdir): df["count"] += 0.12345 expected_sum = df["count"].sum() - path = tmpdir / "test3.cool" + path = tmpdir / "test.cool" w = hictkpy.cooler.FileWriter(path, f.chromosomes(), f.resolution()) chunk_size = 1000 diff --git a/test/test_file_creation_hic.py b/test/test_file_creation_hic.py index 7a0d6a4..347ef20 100644 --- a/test/test_file_creation_hic.py +++ b/test/test_file_creation_hic.py @@ -31,6 +31,20 @@ def setup_method(): logging.basicConfig(level="INFO", force=True) logging.getLogger().setLevel("INFO") + def test_accessors(self, file, resolution, tmpdir): + bins = hictkpy.File(file, resolution).bins() + if bins.type() != "fixed": + pytest.skip(f'BinTable of file "{file}" does not have fixed bins.') + + path = tmpdir / "test.hic" + w = hictkpy.hic.FileWriter(path, bins) + + assert str(w).startswith("HiCFileWriter(") + assert w.path() == path + assert w.resolutions() == [resolution] + assert w.chromosomes() == bins.chromosomes() + assert len(w.bins(resolution).to_df().compare(bins.to_df())) == 0 + def test_file_creation_thin_pixel(self, file, resolution, tmpdir): f = hictkpy.File(file, resolution) if f.bins().type() != "fixed": @@ -39,7 +53,7 @@ def test_file_creation_thin_pixel(self, file, resolution, tmpdir): df = f.fetch(join=False).to_df() expected_sum = df["count"].sum() - path = tmpdir / "test1.hic" + path = tmpdir / "test.hic" w = hictkpy.hic.FileWriter(path, f.chromosomes(), f.resolution()) chunk_size = 1000 @@ -66,7 +80,7 @@ def test_file_creation(self, file, resolution, tmpdir): df = f.fetch(join=True).to_df() expected_sum = df["count"].sum() - path = tmpdir / "test2.hic" + path = tmpdir / "test.hic" w = hictkpy.hic.FileWriter(path, f.chromosomes(), f.resolution()) chunk_size = 1000 @@ -91,7 +105,7 @@ def test_file_creation_bin_table(self, file, resolution, tmpdir): df = f.fetch(join=True).to_df() expected_sum = df["count"].sum() - path = tmpdir / "test2.hic" + path = tmpdir / "test.hic" if f.bins().type() != "fixed": with pytest.raises(Exception): hictkpy.hic.FileWriter(path, f.bins()) diff --git a/test/test_file_validators.py b/test/test_file_validators.py index a1ad54e..aa6a091 100644 --- a/test/test_file_validators.py +++ b/test/test_file_validators.py @@ -4,8 +4,6 @@ import pathlib -import pytest - import hictkpy testdir = pathlib.Path(__file__).resolve().parent @@ -17,7 +15,7 @@ class TestClass: - def test_validators(self): + def test_valid_formats(self): assert hictkpy.is_cooler(cool_file) assert not hictkpy.is_cooler(hic_file) @@ -26,3 +24,31 @@ def test_validators(self): assert hictkpy.is_scool_file(scool_file) assert not hictkpy.is_scool_file(cool_file) + + assert hictkpy.is_hic(hic_file) + assert not hictkpy.is_hic(cool_file) + + def test_invalid_formats(self): + path = pathlib.Path(__file__).resolve() + + assert not hictkpy.is_cooler(path) + assert not hictkpy.is_mcool_file(path) + assert not hictkpy.is_scool_file(path) + assert not hictkpy.is_hic(path) + + def test_invalid_files(self): + non_existing_file = testdir / "foobar.123" + assert not non_existing_file.exists() + + assert not hictkpy.is_cooler(non_existing_file) + assert not hictkpy.is_mcool_file(non_existing_file) + assert not hictkpy.is_scool_file(non_existing_file) + assert not hictkpy.is_hic(non_existing_file) + + folder = testdir + assert folder.is_dir() + + assert not hictkpy.is_cooler(folder) + assert not hictkpy.is_mcool_file(folder) + assert not hictkpy.is_scool_file(folder) + assert not hictkpy.is_hic(folder) diff --git a/test/test_multires_file_accessors.py b/test/test_multires_file_accessors.py index 17549a8..4e1e1c5 100644 --- a/test/test_multires_file_accessors.py +++ b/test/test_multires_file_accessors.py @@ -11,19 +11,39 @@ testdir = pathlib.Path(__file__).resolve().parent pytestmark = pytest.mark.parametrize( - "file", + "file,format", [ - testdir / "data" / "cooler_test_file.mcool", + (testdir / "data" / "cooler_test_file.mcool", "mcool"), + (testdir / "data" / "hic_test_file.hic", "hic"), ], ) class TestClass: - def test_attributes(self, file): + def test_accessors(self, file, format): f = hictkpy.MultiResFile(file) + assert str(f).startswith("MultiResFile(") + assert f.path() == file - assert (f.resolutions() == [100_000, 1_000_000]).all() + assert f.is_mcool() == (format == "mcool") + assert f.is_hic() == (format == "hic") assert len(f.chromosomes()) == 8 + if f.is_hic(): + resolutions = [100_000] + assert (f.resolutions() == resolutions).all() + assert f.attributes()["format"] == "HIC" + assert f.attributes()["format-version"] == 9 + assert (f.attributes()["resolutions"] == resolutions).all() + else: + resolutions = [100_000, 1_000_000] + assert (f.resolutions() == resolutions).all() + assert f.attributes()["format"] == "HDF5::MCOOL" + assert f.attributes()["format-version"] == 2 + assert (f.attributes()["resolutions"] == resolutions).all() + assert f[100_000].resolution() == 100_000 + + with pytest.raises(Exception): + f[1234] # noqa diff --git a/test/test_pixel_selector_accessors.py b/test/test_pixel_selector_accessors.py new file mode 100644 index 0000000..ada56bb --- /dev/null +++ b/test/test_pixel_selector_accessors.py @@ -0,0 +1,54 @@ +# Copyright (C) 2023 Roberto Rossini +# +# SPDX-License-Identifier: MIT + +import pathlib + +import pytest + +import hictkpy + +from .helpers import numpy_avail + +testdir = pathlib.Path(__file__).resolve().parent + +pytestmark = pytest.mark.parametrize( + "file,resolution", + [ + (testdir / "data" / "cooler_test_file.mcool", 100_000), + (testdir / "data" / "hic_test_file.hic", 100_000), + ], +) + + +@pytest.mark.skipif(not numpy_avail(), reason="numpy is not available") +class TestClass: + def test_repr(self, file, resolution): + f = hictkpy.File(file, resolution) + + sel = f.fetch() + assert str(sel) == "PixelSelector(ALL; COO; int32)" + + sel = f.fetch(join=True) + assert str(sel) == "PixelSelector(ALL; BG2; int32)" + + sel = f.fetch(count_type="float") + assert str(sel) == "PixelSelector(ALL; COO; float64)" + + sel = f.fetch("chr2L:0-10,000,000", "chr2L:5,000,000-20,000,000") + assert str(sel) == "PixelSelector(chr2L:0-10000000; chr2L:5000000-20000000; COO; int32)" + + def test_coords(self, file, resolution): + f = hictkpy.File(file, resolution) + + sel = f.fetch() + assert sel.coord1() == ("ALL", 0, len(f.bins())) + assert sel.coord1() == sel.coord2() + + sel = f.fetch("chr2L:0-10,000,000") + assert sel.coord1() == ("chr2L", 0, 10_000_000) + assert sel.coord1() == sel.coord2() + + sel = f.fetch("chr2L:0-10,000,000", "chr2L:5,000,000-20,000,000") + assert sel.coord1() == ("chr2L", 0, 10_000_000) + assert sel.coord2() == ("chr2L", 5_000_000, 20_000_000) diff --git a/test/test_singlecell_file_accessors.py b/test/test_singlecell_file_accessors.py index f48f1c4..aa7a1d9 100644 --- a/test/test_singlecell_file_accessors.py +++ b/test/test_singlecell_file_accessors.py @@ -19,13 +19,19 @@ class TestClass: - def test_attributes(self, file): + def test_accessors(self, file): f = hictkpy.cooler.SingleCellFile(file) + assert str(f).startswith("SingleCellFile(") + assert f.path() == file assert f.resolution() == 100_000 assert len(f.chromosomes()) == 20 + assert len(f.bins()) == 26398 assert len(f.cells()) == 5 assert f.attributes()["format"] == "HDF5::SCOOL" assert f["GSM2687248_41669_ACAGTG-R1-DpnII.100000.cool"].resolution() == 100_000 + + with pytest.raises(Exception): + f["ABC"] # noqa