muon: Multimodal Omics Analysis Framework

repository·main·Indexed 43 days ago

https://github.com/scverse/muon

A Python framework for multimodal omics analysis designed to handle data from multiple biological modalities, such as RNA and ATAC, within a unified MuData object structure. It integrates with scanpy for unimodal analysis and provides specialized modules for ATAC-seq (muon.atac) and CITE-seq (muon.prot), as well as tools for Multi-Omics Factor Analysis (MOFA) via mu.tl.mofa().

Tokens
9.8K
Snippets
59
Records
68
Agent score
54%

What's inside muon

  1. How MuData objects work

    main

    In muon, multimodal data is managed using mudata.MuData objects. A MuData object acts as a container for multiple modalities. Each modality is stored as an AnnData object within the .mod attribute.

    Key characteristics:

    • Modalities: Accessible via .mod or as a shorthand directly on the MuData object.
    • Metadata: .obs (observations/cells) and .var (variables/features) contain annotations. Columns from individual modalities are prefixed with the modality name (e.g., rna:n_genes) to avoid collisions, unless names are identical across modalities, in which case they are merged.
    • Multidimensional Annotations: .obsm stores embeddings (like UMAP or MOFA) and boolean vectors indicating sample availability per modality.
    • Shape: The mdata.shape is the sum of observations and variables across all modalities. Observations with the same name are treated as the same sample across modalities, while variables are counted as belonging uniquely to their specific modality.
    from muon import MuData
    
    # Accessing modalities
    # mdata.mod['rna'] or mdata['rna'] returns an AnnData object
  2. How MuData containers and AnnData work together

    main

    The MuData class acts as a container for multimodal measurements. Instead of extending a single data format, muon treats unimodal measurements as independent, fully functional AnnData objects stored within the MuData container.

    This design allows:

    1. Independent Operation: You can operate on individual modalities (like RNA or ATAC) using standard AnnData workflows.
    2. Cross-modality Storage: The container can store information that requires all modalities, such as joint embeddings or cell annotations generated from multiple data types.
    3. Transparency: It builds on existing HDF5-based formats and widely adopted software like scanpy.
  3. Understand the MuData data structure

    main

    muon is built around MuData objects, which are designed for multimodal omics analysis. A MuData object acts as a container for multiple modalities, where each individual modality is represented as an AnnData object. This structure allows you to perform both multimodal analysis on the whole object and unimodal analysis on specific assays using standard scanpy functions.

    from muon import MuData
    
    # Creating a MuData object from multiple AnnData modalities
    mdata = MuData({'rna': adata_rna, 'atac': adata_atac})
  4. Use muon preprocessing, tools, and plotting modules

    main

    Muon organizes its functionality into several submodules for different stages of analysis:

    • mu.pp: Preprocessing functions.
    • mu.tl: Tools and statistical methods.
    • mu.pl: Plotting and visualization functions.
  5. Perform in-place filtering with muon.pp.filter_obs and muon.pp.filter_var

    main

    To avoid the memory overhead of copying AnnData objects during subsetting (which occurs when using scanpy.pp.filter_cells or slicing syntax like adata[cell_ids]), muon provides in-place filtering functions. These functions modify the object directly.

    Usage Patterns

    1. Filtering by condition: Use a lambda function to define criteria for an observation or variable column.
    2. Filtering by names: Pass a list of specific observation or variable names to keep.
    3. Filtering by boolean mask: Pass a boolean vector (e.g., from adata.var.include_feature.values).

    Important Constraints

    • No Views: In-place filtering is not defined for AnnData views.
    • Backed Objects: When using backed objects, calling these functions will read the requested subset of .X into memory, meaning the object will no longer be backed.
    # Filter observations in-place using a condition
    mu.pp.filter_obs(adata, 'total_counts', lambda x: (x >= 10000) & (x <= 50000))
    
    # Filter observations in-place using a list of names
    obs_names_subset = ['AAACAGCCAATCCCTT-1', 'AAACAGCCAATGCGCT-1']
    mu.pp.filter_obs(adata, obs_names_subset)
    
    # Filter variables in-place using a boolean mask
    mu.pp.filter_var(adata, adata.var.include_feature.values)
  6. Install muon in development mode for hacking

    main

    To work on the muon source code without reinstalling after every change, perform a development-mode install using flit. This symlinks files from your Python package directory to your working directory.

    1. Install flit.
    2. Navigate to the muon project directory.
    3. Run flit install -s.
    flit install -s
  7. Use omic-specific modules for modality analysis

    main

    To perform analysis specific to certain sequencing protocols (like ATAC-seq or protein/epitope data), muon provides specialized modules. It is a common pattern to import these modules using short abbreviations to keep the code concise.

    When using these modules:

    1. In-place modification: Most functions expect the AnnData or MuData object as the first argument and modify it in place by default.
    2. API Consistency: Modules follow the module.pp (preprocessing) and module.tl (tools) pattern similar to scanpy.
    3. Compatibility: Functions often accept both AnnData and MuData objects, using default keys like 'rna', 'atac', or 'prot' to identify modalities.
    # Example: Using the ATAC-seq module
    from muon import atac as ac
    
    # Access the 'atac' modality from a MuData object 'mdata'
    atac = mdata.mod["atac"]
    
    # Run a preprocessing step (e.g., TF-IDF transformation)
    ac.pp.tfidf(atac)
    
    # Run a tool (e.g., Latent Semantic Indexing)
    ac.tl.lsi(atac)
  8. Install development dependencies using Pip

    main

    You can manage environments manually using pip by creating a virtual environment and installing the package with development, test, and documentation extras.

    python3 -m venv .venv
    source .venv/bin/activate
    pip install -e ".[dev,test,doc]"
  9. Install development dependencies using uv

    main

    If you prefer uv, you can initialize a virtual environment in the .venv directory. Note that uv supports only a single environment per project at a time, so you will need to mix dependencies for testing and documentation.

    uv sync --all-extras
  10. Update version numbers for releases

    main

    Before releasing, update the version number in pyproject.toml following Semantic Versioning:

    • MAJOR: Incompatible API changes.
    • MINOR: Backwards compatible functionality additions.
    • PATCH: Backwards compatible bug fixes.

    After updating, commit the changes and tag the commit as vX.X.X (e.g., v0.1.9). Pushing the tag triggers the GitHub workflow to create a release on PyPI.

  11. Run automated tests with pytest

    main

    Tests are implemented using pytest. You can run them via your IDE or through the command line depending on your environment manager:

    Using Hatch:

    hatch test          # Test with highest supported Python version
    hatch test --all    # Test with all supported Python versions

    Using uv:

    uv run pytest

    Using Pip:

    source .venv/bin/activate
    pytest
    hatch test
    hatch test --all
    uv run pytest
    source .venv/bin/activate
    pytest