Getting Started with Gunz-CM#

version: 1.0.0 status: active

Install#

pip install gunz-cm

Requires Python 3.11 or later. For GPU-accelerated 3D reconstruction and super-resolution, also install the optional extras:

pip install gunz-cm[3dr]      # CPU PyTorch
pip install gunz-cm[3dr-gpu]  # CUDA PyTorch
pip install gunz-cm[ren]      # CPU super-resolution
pip install gunz-cm[ren-gpu]  # CUDA super-resolution

Load your first contact matrix#

A Hi-C contact matrix lives in one of several file formats. gunz-cm’s unified load_cm_data(...) function reads any of them and returns a matrix in your preferred format:

from gunz_cm import load_cm_data
from gunz_cm.consts import Balancing, DataStructure

# Replace with your file path. The function infers the format from
# the extension (.hic, .cool, .mcool, .csv, .coo, .gzcm, .pkl, .npy).
fpath = "data/sample.mcool"

cm = load_cm_data(
    fpath=fpath,
    bin_size_bp=1_000_000,         # 1 Mb resolution
    region1="chr1",                # chromosome
    region2="chr1",                # same-chromosome contact matrix
    balancing=Balancing.NONE,      # raw counts; or KR, VC, VC_SQRT
    output_format=DataStructure.COO,  # sparse SciPy matrix
)

print(f"Shape: {cm.shape}, Non-zero entries: {cm.nnz}")

The output is a scipy.sparse.coo_matrix. Convert to a dense array with the standard scipy method:

dense = cm.toarray()                  # 2D numpy array

Inspect a file before loading#

Before loading, probe a file to see what’s inside. For multi-resolution .mcool files, list the available zoom levels; for any cooler file, list the chromosome names and lengths:

from gunz_cm.loaders import get_chrom_infos
import cooler

# Chromosome names and lengths (works for any cooler file)
chrom_info = get_chrom_infos(fpath)
# {'chr1': {'name': 'chr1', 'length': 248956422}, ...}

# Resolution (read directly from the file attribute)
binsize = cooler.Cooler(fpath).binsize
print(f"Resolution: {binsize:,} bp")

Run with synthetic data (no files required)#

If you don’t have a Hi-C file handy, the tutorials generate synthetic data inline. Here’s the same load call against a synthetic 3x3 contact matrix:

import tempfile
from pathlib import Path

import cooler
import numpy as np
import pandas as pd

from gunz_cm import load_cm_data
from gunz_cm.consts import Balancing, DataStructure

# Create a 3x3 synthetic .cool file
tmpdir = Path(tempfile.mkdtemp(prefix="gunz_cm_demo_"))
fpath = str(tmpdir / "synthetic.cool")

bins = pd.DataFrame({
    "chrom": ["chr1"] * 3,
    "start": [0, 1_000_000, 2_000_000],
    "end":   [1_000_000, 2_000_000, 3_000_000],
})
rows, cols, counts = [], [], []
for i in range(3):
    for j in range(i, 3):
        # Distance-decaying contacts
        weight = 100.0 * np.exp(-abs(bins["start"].iloc[i] - bins["start"].iloc[j]) / 1_000_000)
        rows.append(i); cols.append(j); counts.append(weight)
pixels = pd.DataFrame({"bin1_id": rows, "bin2_id": cols, "count": counts})
cooler.create_cooler(fpath, bins, pixels, symmetric_upper=True, mode="w")

# Load as a DataFrame
df = load_cm_data(
    fpath=fpath, bin_size_bp=1_000_000, region1="chr1",
    balancing=Balancing.NONE, output_format=DataStructure.DF,
)
print(df)

Filter and balance the matrix#

Raw counts have visibility and mappability biases. Drop empty rows/columns and apply Knight-Ruiz (KR) matrix balancing:

import numpy as np
from gunz_cm.preprocs import filter_empty_rowcols

# Load as a sparse COO matrix
cm = load_cm_data(
    fpath=fpath, bin_size_bp=1_000_000, region1="chr1",
    balancing=Balancing.NONE, output_format=DataStructure.COO,
)
sparse_matrix = filter_empty_rowcols(cm)  # drop empty bins

# Knight-Ruiz matrix balancing: a Sinkhorn-style loop that finds
# per-bin weights w such that diag(w) @ A @ diag(w) has uniform
# row sums. This teaching version operates in-memory on a small
# matrix; for large datasets use the streaming
# `gunz_cm.normalization.kr_normalize_chunked` instead.
A = sparse_matrix.toarray().astype(float)
w = np.ones(A.shape[0])
for _ in range(200):
    row_sums = A @ w
    row_sums = np.where(row_sums == 0, 1.0, row_sums)
    w_new = w / np.sqrt(row_sums)
    if np.max(np.abs(w_new - w)) < 1e-5:
        break
    w = w_new

balanced = (A * w[:, None]) * w[None, :]
print(f"Row sums after KR: {balanced.sum(axis=1).round(4)}")
# Row sums are now equal across rows (uniform marginals)

Convert to another format#

Convert to COO text, GZCM v3 (compressed tiles), or MEMMAP for archival or downstream tooling:

from gunz_cm.converters import convert_to_cm_coo
from pathlib import Path

# Write a COO text file (tab-delimited: row, col, count)
convert_to_cm_coo(
    input_fpath=fpath,
    output_fpath=Path(tmpdir / "chr1.coo"),
    region1="chr1",
    bin_size_bp=1_000_000,
    balancing=Balancing.NONE,         # synthetic data has no KR weights
    on_conflict="overwrite",         # v2.25.0: replaces deprecated overwrite=True
)

# Read it back
df = load_cm_data(
    fpath=str(tmpdir / "chr1.coo"),
    bin_size_bp=1_000_000,
    region1="chr1",
    balancing=Balancing.NONE,
    output_format=DataStructure.DF,
)
print(f"Round-trip pixels: {len(df)}")

Visualize the result#

Render the matrix as a heatmap with the visualizations module:

import matplotlib
matplotlib.use("Agg")  # non-interactive backend
import matplotlib.pyplot as plt
import numpy as np
from gunz_cm import load_cm_data
from gunz_cm.consts import Balancing, DataStructure

# Load and visualize
cm = load_cm_data(
    fpath=fpath, bin_size_bp=1_000_000, region1="chr1",
    balancing=Balancing.NONE, output_format=DataStructure.COO,
)
matrix = cm.toarray().astype(float)

# Direct matplotlib: log-scaled heatmap
fig, ax = plt.subplots(figsize=(6, 5))
im = ax.imshow(
    np.log1p(matrix),
    cmap="hot_r",
    aspect="equal",
    interpolation="nearest",
)
ax.set_title("log(1 + count) contact matrix")
fig.colorbar(im, ax=ax, label="log(counts)")
plt.savefig(tmpdir / "matrix.png", dpi=150, bbox_inches="tight")

What’s next?#

This page covered a minimal end-to-end session: install → load → inspect → balance → convert → visualize. To go deeper on any of these stages, read the dedicated user-guide pages:

  • Load more formats (HIC, COOL, MCOOL, GZCM, PICKLE, NPY): see the :doc:user_guide/loaders page and the :doc:tutorials/generated/01_core_operations/01_load_hic tutorial.

  • Filter and balance: see :doc:user_guide/preprocs and :doc:user_guide/normalization, plus the :doc:tutorials/generated/02_preprocessing_normalization/02_balance_kr tutorial.

  • Convert and archive: see :doc:user_guide/converters and the :doc:tutorials/generated/01_core_operations/03_convert tutorial.

  • 3D reconstruction and metrics: see :doc:user_guide/reconstructions and :doc:user_guide/metrics, plus the :doc:tutorials/generated/prototypes/tutorial_3d_reconstruction tutorial.

  • All tutorials (load → convert → visualize → random downsample): see the :doc:tutorials/index page.

For full API reference, see :doc:gunz_cm or the per-module pages in :doc:user_guide/index. For a 5-minute overview of the library’s mental model, see :doc:quickstart.

To report bugs or request features, open an issue on GitHub at https://github.com/sXperfect/gunz-cm/issues.