diff --git a/.gitignore b/.gitignore index 1a25dbe..fe606d8 100644 --- a/.gitignore +++ b/.gitignore @@ -89,8 +89,8 @@ instance/ # Scrapy stuff: .scrapy -# Sphinx documentation -docs/_build/ +# MkDocs build output +site/ # PyBuilder .pybuilder/ @@ -198,6 +198,10 @@ cython_debug/ # Cursor rules directory .cursor/rules/ +### Claude Code ### +.claude/ +CLAUDE.md + ### Python Patch ### # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration poetry.toml @@ -214,3 +218,5 @@ data/compressed/*mzML.gz # Ignore example results directory to avoid large files examples/results/ + +.DS_Store diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 1b097d5..84fa6c4 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -11,10 +11,5 @@ python: - method: pip path: . -sphinx: - configuration: docs/source/conf.py - #fail_on_warning: true - -formats: - - pdf - - epub +mkdocs: + configuration: mkdocs.yml diff --git a/README.md b/README.md index a79b20e..ad7acd7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # OptiMHC -**OptiMHC** is an optimum rescoring pipeline for immunopeptidomics data. It enhances peptide identification by integrating multiple feature generators and machine learning-based rescoring. +**An optimum rescoring pipeline for immunopeptidomics data that significantly enhances peptide identification performance.** + +OptiMHC integrates multiple rescoring features with machine learning-based rescoring to maximize the number of confidently identified peptides from mass spectrometry experiments. ## Quick Start diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index d0c3cbf..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = source -BUILDDIR = build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/api/cli.md b/docs/api/cli.md new file mode 100644 index 0000000..57eabf4 --- /dev/null +++ b/docs/api/cli.md @@ -0,0 +1,5 @@ +# CLI + +::: optimhc.cli + options: + members: true diff --git a/docs/api/core.md b/docs/api/core.md new file mode 100644 index 0000000..744a49c --- /dev/null +++ b/docs/api/core.md @@ -0,0 +1,37 @@ +# Core + +## Config + +::: optimhc.core.config + options: + members: true + +## Pipeline + +::: optimhc.core.pipeline + options: + members: true + +## Feature Generation + +::: optimhc.core.feature_generation + options: + members: true + +## PSM Container + +::: optimhc.psm_container + options: + members: true + +## Logging + +::: optimhc.core.logging_helper + options: + members: true + +## Utilities + +::: optimhc.utils + options: + members: true diff --git a/docs/api/features.md b/docs/api/features.md new file mode 100644 index 0000000..99f852a --- /dev/null +++ b/docs/api/features.md @@ -0,0 +1,55 @@ +# Feature Generators + +## Base Class + +::: optimhc.feature_generator.base_feature_generator + options: + members: true + +## Basic + +::: optimhc.feature_generator.basic + options: + members: true + +## Spectral Similarity + +::: optimhc.feature_generator.spectral_similarity + options: + members: true + +## DeepLC + +::: optimhc.feature_generator.DeepLC + options: + members: true + +## Overlapping Peptide + +::: optimhc.feature_generator.overlapping_peptide + options: + members: true + +## PWM + +::: optimhc.feature_generator.PWM + options: + members: true + +## MHCflurry + +::: optimhc.feature_generator.mhcflurry + options: + members: true + +## NetMHCpan + +::: optimhc.feature_generator.netMHCpan + options: + members: true + +## NetMHCIIpan + +::: optimhc.feature_generator.netMHCIIpan + options: + members: true diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 0000000..92d04bf --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,14 @@ +# API Reference + +This section provides auto-generated documentation for all OptiMHC modules and classes. + +## Modules + +| Module | Description | +|---|---| +| [Core](core.md) | Configuration, pipeline orchestration, PSM container, and logging | +| [I/O](io.md) | Input parsers for PepXML, PIN, and mzML formats | +| [Feature Generators](features.md) | Base class and all feature implementations | +| [Rescoring](rescore.md) | Mokapot integration and machine learning models | +| [Visualization](visualization.md) | Plotting functions for results and diagnostics | +| [CLI](cli.md) | Command-line interface | diff --git a/docs/api/io.md b/docs/api/io.md new file mode 100644 index 0000000..c8ab726 --- /dev/null +++ b/docs/api/io.md @@ -0,0 +1,25 @@ +# I/O + +## Parser Package + +::: optimhc.parser + options: + members: true + +## PepXML Parser + +::: optimhc.parser.pepxml + options: + members: true + +## PIN Parser + +::: optimhc.parser.pin + options: + members: true + +## mzML Parser + +::: optimhc.parser.mzml + options: + members: true diff --git a/docs/api/rescore.md b/docs/api/rescore.md new file mode 100644 index 0000000..6fe2ca8 --- /dev/null +++ b/docs/api/rescore.md @@ -0,0 +1,13 @@ +# Rescoring + +## Mokapot Integration + +::: optimhc.rescore.mokapot + options: + members: true + +## Models + +::: optimhc.rescore.model + options: + members: true diff --git a/docs/api/visualization.md b/docs/api/visualization.md new file mode 100644 index 0000000..8d68d29 --- /dev/null +++ b/docs/api/visualization.md @@ -0,0 +1,23 @@ +# Visualization + +::: optimhc.visualization + options: + members: true + +## Q-value Plots + +::: optimhc.visualization.plot_roc + options: + members: true + +## Feature Plots + +::: optimhc.visualization.plot_features + options: + members: true + +## Target/Decoy Distribution + +::: optimhc.visualization.plot_tdc_distribution + options: + members: true diff --git a/docs/development/index.md b/docs/development/index.md new file mode 100644 index 0000000..cfe6391 --- /dev/null +++ b/docs/development/index.md @@ -0,0 +1,59 @@ +# Development + +This page covers how to set up a development environment for contributing to OptiMHC. + +## Clone the Repository + +```bash +git clone https://github.com/5h4ng/OptiMHC.git +cd OptiMHC +``` + +## Install Dependencies + +OptiMHC uses [uv](https://docs.astral.sh/uv/) for dependency management (recommended): + +```bash +uv sync --locked --group dev +``` + +Alternatively, using pip: + +```bash +pip install -e ".[gui]" +pip install pytest ruff pre-commit +``` + +## Running Tests + +```bash +uv run pytest # All tests +uv run pytest tests/test_psm_container.py # Single file +uv run pytest tests/ -k "test_config" # Filter by name +``` + +## Linting and Formatting + +OptiMHC uses [Ruff](https://docs.astral.sh/ruff/) for linting and formatting: + +```bash +uv run ruff check . # Lint +uv run ruff format . # Format +uv run ruff format --check . # Check without modifying +``` + +Configuration: `line-length = 99`, rules `["E", "F", "I"]` (pycodestyle errors, pyflakes, isort). `E501` (line too long) is ignored. Ruff excludes `docs/`, `examples/`, and `optimhc/gui/`. + +## Pre-commit Hooks + +```bash +uv run pre-commit install # Install hooks +uv run pre-commit run --all-files # Run all hooks manually +``` + +## Building Documentation + +```bash +mkdocs serve # Local preview at http://127.0.0.1:8000 +mkdocs build # Static build to site/ +``` diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md new file mode 100644 index 0000000..50611e4 --- /dev/null +++ b/docs/getting-started/installation.md @@ -0,0 +1,77 @@ +# Installation + +## Requirements + +- Python >= 3.10, < 3.13 + +## Install from Source + +```bash +git clone https://github.com/5h4ng/OptiMHC.git +cd OptiMHC +pip install -e . +``` + +Or using **uv** (recommended for development): + +```bash +git clone https://github.com/5h4ng/OptiMHC.git +cd OptiMHC +uv sync --locked +``` + +!!! note "PyPI" + PyPI distribution is not yet available. For now, install from source as shown above. + +## NetMHCpan / NetMHCIIpan Setup + +Most OptiMHC dependencies are installed automatically via pip. The only exceptions are **NetMHCpan** and **NetMHCIIpan**, which are standalone executables that must be downloaded and installed manually. + +### 1. Download + +- [NetMHCpan 4.1](https://services.healthtech.dtu.dk/services/NetMHCpan-4.1/) (MHC Class I) +- [NetMHCIIpan 4.3](https://services.healthtech.dtu.dk/services/NetMHCIIpan-4.3/) (MHC Class II) + +Both require a license from DTU Health Tech (free for academic use). + +### 2. Install and Add to PATH + +After downloading and extracting, follow the installation instructions provided by DTU. Then make sure the executables are accessible from your shell: + +=== "Linux / macOS" + + ```bash + # Add to your ~/.bashrc or ~/.zshrc: + export PATH="/path/to/netMHCpan-4.1:$PATH" + export PATH="/path/to/netMHCIIpan-4.3:$PATH" + ``` + +=== "Verify" + + ```bash + which netMHCpan # Should print the path to the executable + which netMHCIIpan # Should print the path to the executable + ``` + +OptiMHC calls these tools via the [mhctools](https://github.com/openvax/mhctools) library, which invokes `netMHCpan` and `netMHCIIpan` as command-line programs. If they are not on your `PATH`, the prediction step will fail with a "command not found" error. + +### 3. Test the Installation + +```bash +netMHCpan -v # Should print the version number +netMHCIIpan -v # Should print the version number +``` + +!!! tip + If you do not need MHC binding predictions, you can skip this step entirely and use OptiMHC with other features (Basic, SpectralSimilarity, DeepLC, PWM, OverlappingPeptide, MHCflurry) — all of which are installed via pip. + +## Verify OptiMHC Installation + +```bash +optimhc --help +``` + +You should see the available commands: `pipeline`, `experiment`, and `gui`. + +!!! note "GUI" + The Streamlit GUI is currently under development. diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md new file mode 100644 index 0000000..0012c47 --- /dev/null +++ b/docs/getting-started/quickstart.md @@ -0,0 +1,78 @@ +# Quick Start + +This guide walks you through running your first OptiMHC rescoring pipeline. + +## 1. Prepare a Configuration File + +Create a YAML file (e.g., `my_config.yaml`) that describes your input data and desired features: + +```yaml +experimentName: my_first_run +inputType: pepxml +inputFile: + - path/to/your/search_results.pep.xml +decoyPrefix: DECOY_ +outputDir: ./results + +allele: + - HLA-A*02:01 + +featureGenerator: + - name: Basic + - name: OverlappingPeptide + params: + minOverlapLength: 7 + minLength: 7 + maxLength: 20 + - name: PWM + params: + class: I + +rescore: + testFDR: 0.01 + model: Percolator +``` + +!!! tip + Start with lightweight features like **Basic**, **OverlappingPeptide**, and **PWM** — they require no external setup. You can add more features (SpectralSimilarity, DeepLC, MHCflurry, NetMHCpan) later. + +## 2. Run the Pipeline + +```bash +optimhc pipeline --config my_config.yaml +``` + +Or pass options directly on the command line: + +```bash +optimhc pipeline \ + --inputType pepxml \ + --inputFile path/to/search_results.pep.xml \ + --outputDir ./results \ + --allele HLA-A*02:01 \ + --model Percolator \ + --testFDR 0.01 +``` + +## 3. Inspect the Output + +After a successful run, the output directory will contain: + +``` +results/ +├── my_first_run.mokapot.psms.txt # Rescored PSMs with q-values +├── my_first_run.mokapot.peptides.txt # Peptide-level results +├── my_first_run.pin # PIN file with all features +├── models/ # Saved rescoring model(s) +└── figures/ + ├── qvalues.png # Q-value curves + ├── feature_importance.png # Feature importance bar chart + ├── feature_correlation.png # Feature correlation heatmap + └── target_decoy_histogram.png # Target vs decoy distributions +``` + +## What's Next? + +- See [Examples](../tutorial/examples.md) for complete Class I, Class II, and experiment-mode configurations +- Read [Pipeline Workflow](../tutorial/workflow.md) for a detailed explanation of each pipeline step +- Explore [Features](../tutorial/features/index.md) to understand what each feature computes diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..73ce52c --- /dev/null +++ b/docs/index.md @@ -0,0 +1,32 @@ +# OptiMHC + +**An optimum rescoring pipeline for immunopeptidomics data that significantly enhances peptide identification performance.** + +OptiMHC integrates multiple rescoring features with machine learning-based rescoring to maximize the number of confidently identified peptides from mass spectrometry experiments. + +## How It Works + +``` +Input files (PepXML / PIN) + → Parsing & feature extraction + → PsmContainer (central data structure) + → Feature generation (Basic, Spectral, RT, MHC binding, PWM, Overlap, …) + → Machine learning rescoring (Percolator / XGBoost / RandomForest) + → Visualization & output +``` + +1. **Parse** search engine results from PepXML or PIN format into a unified `PsmContainer`. +2. **Generate features** using a configurable set of features — each adds new scoring dimensions to the PSM data. +3. **Rescore** PSMs with machine learning models (Percolator SVM, XGBoost, or RandomForest) trained via mokapot to separate targets from decoys at a controlled FDR. +4. **Visualize** results with q-value curves, feature importance plots, and target/decoy distributions. + +## Getting Started + +- [Installation](getting-started/installation.md) — set up OptiMHC on your system +- [Quick Start](getting-started/quickstart.md) — run your first rescoring pipeline in minutes + +## Learn More + +- [Tutorial](tutorial/index.md) — examples, pipeline walkthrough, and feature explanations +- [API Reference](api/index.md) — detailed module and class documentation +- [Development](development/index.md) — set up a development environment diff --git a/docs/javascripts/mathjax.js b/docs/javascripts/mathjax.js new file mode 100644 index 0000000..7e48906 --- /dev/null +++ b/docs/javascripts/mathjax.js @@ -0,0 +1,19 @@ +window.MathJax = { + tex: { + inlineMath: [["\\(", "\\)"]], + displayMath: [["\\[", "\\]"]], + processEscapes: true, + processEnvironments: true + }, + options: { + ignoreHtmlClass: ".*|", + processHtmlClass: "arithmatex" + } +}; + +document$.subscribe(() => { + MathJax.startup.output.clearCache() + MathJax.typesetClear() + MathJax.texReset() + MathJax.typesetPromise() +}) diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 747ffb7..0000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=source -set BUILDDIR=build - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -if "%1" == "" goto help - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/requirements.txt b/docs/requirements.txt index 5d8368e..9314de1 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,2 @@ -sphinx -sphinx_rtd_theme -sphinx-autodoc-typehints -sphinxcontrib-napoleon -nbsphinx \ No newline at end of file +mkdocs-material +mkdocstrings[python] diff --git a/docs/source/_build/html/.buildinfo b/docs/source/_build/html/.buildinfo deleted file mode 100644 index 03b0beb..0000000 --- a/docs/source/_build/html/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 9ac75ebf1fb41cfdfe1a9024563055cd -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/source/_build/html/.doctrees/api.doctree b/docs/source/_build/html/.doctrees/api.doctree deleted file mode 100644 index 935e7ba..0000000 Binary files a/docs/source/_build/html/.doctrees/api.doctree and /dev/null differ diff --git a/docs/source/_build/html/.doctrees/environment.pickle b/docs/source/_build/html/.doctrees/environment.pickle deleted file mode 100644 index e09bf0e..0000000 Binary files a/docs/source/_build/html/.doctrees/environment.pickle and /dev/null differ diff --git a/docs/source/_build/html/.doctrees/examples.doctree b/docs/source/_build/html/.doctrees/examples.doctree deleted file mode 100644 index c7a9b6e..0000000 Binary files a/docs/source/_build/html/.doctrees/examples.doctree and /dev/null differ diff --git a/docs/source/_build/html/.doctrees/index.doctree b/docs/source/_build/html/.doctrees/index.doctree deleted file mode 100644 index 9b1950e..0000000 Binary files a/docs/source/_build/html/.doctrees/index.doctree and /dev/null differ diff --git a/docs/source/_build/html/.doctrees/installation.doctree b/docs/source/_build/html/.doctrees/installation.doctree deleted file mode 100644 index 568b97a..0000000 Binary files a/docs/source/_build/html/.doctrees/installation.doctree and /dev/null differ diff --git a/docs/source/_build/html/.doctrees/usage.doctree b/docs/source/_build/html/.doctrees/usage.doctree deleted file mode 100644 index 6a14d0a..0000000 Binary files a/docs/source/_build/html/.doctrees/usage.doctree and /dev/null differ diff --git a/docs/source/_build/html/_modules/index.html b/docs/source/_build/html/_modules/index.html deleted file mode 100644 index fc3014c..0000000 --- a/docs/source/_build/html/_modules/index.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - - - Overview: module code — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- - -
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/cli.html b/docs/source/_build/html/_modules/optimhc/cli.html deleted file mode 100644 index c23668a..0000000 --- a/docs/source/_build/html/_modules/optimhc/cli.html +++ /dev/null @@ -1,276 +0,0 @@ - - - - - - - - optimhc.cli — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for optimhc.cli

-import os
-import sys
-import logging
-import click
-import yaml
-import json
-from optimhc.core import Pipeline
-from optimhc.core.config import Config
-
-logging.basicConfig(
-    level=logging.INFO,
-    format="%(asctime)s %(levelname)s %(name)s: %(message)s",
-    handlers=[logging.StreamHandler()]
-)
-
-logger = logging.getLogger(__name__)
-
-@click.group()
-def cli():
-    """
-    optiMHC - A high-performance rescoring pipeline for immunopeptidomics data.
-    """
-    pass
-
-
-[docs] -def parse_cli_config(**kwargs): - # Remove None values and build a config dict - return {k: v for k, v in kwargs.items() if v is not None and v != ()}
- - -@cli.command() -@click.option( - "--config", - type=click.Path(exists=True), - help="Path to YAML configuration file", -) -@click.option( - "--inputType", - type=click.Choice(["pepxml", "pin"]), - help="Type of input file", -) -@click.option( - "--inputFile", - type=click.Path(exists=True), - multiple=True, - help="Path(s) to input PSM file(s). Can be specified multiple times for multiple files.", -) -@click.option( - "--decoyPrefix", - type=str, - help="Prefix used to identify decoy sequences", -) -@click.option( - "--outputDir", - type=click.Path(), - help="Output directory", -) -@click.option( - "--visualization/--no-visualization", - is_flag=True, - default=None, - help="Enable/disable visualization", -) -@click.option( - "--numProcesses", - type=int, - help="Number of parallel processes", -) -@click.option( - "--allele", - type=str, - multiple=True, - help="Allele(s) for which predictions will be computed", -) -@click.option( - "--featureGenerator", - type=str, - multiple=True, - help="Feature generator configuration in JSON format", -) -@click.option( - "--logLevel", - type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR"]), - help="Logging level", -) -@click.option( - "--testFDR", - type=float, - help="FDR threshold for testing", -) -@click.option( - "--model", - type=click.Choice(["Percolator", "XGBoost", "RandomForest"]), - help="Model to use for rescoring", -) -def pipeline( - config, - inputtype, - inputfile, - decoyprefix, - outputdir, - visualization, - numprocesses, - allele, - featuregenerator, - loglevel, - testfdr, - model, -): - """Run the optiMHC pipeline with the specified configuration.""" - # Load configuration - if config: - pipeline_config = Config(config) - else: - pipeline_config = Config() - - # Override with command-line parameters - if inputtype: - pipeline_config["inputType"] = inputtype - if inputfile: - pipeline_config["inputFile"] = list(inputfile) - if decoyprefix: - pipeline_config["decoyPrefix"] = decoyprefix - if outputdir: - pipeline_config["outputDir"] = outputdir - if visualization is not None: - pipeline_config["visualization"] = visualization - if numprocesses: - pipeline_config["numProcess"] = numprocesses - if allele: - pipeline_config["allele"] = list(allele) - if loglevel: - pipeline_config["logLevel"] = loglevel - if featuregenerator: - feature_generators = [] - for fg in featuregenerator: - try: - fg_config = json.loads(fg) - feature_generators.append(fg_config) - except json.JSONDecodeError as e: - raise click.BadParameter(f"Invalid JSON format for feature generator: {e}") - pipeline_config["featureGenerator"] = feature_generators - if testfdr: - pipeline_config["rescore"]["testFDR"] = testfdr - if model: - pipeline_config["rescore"]["model"] = model - - # Run pipeline - pipeline_config.validate() - pipeline = Pipeline(pipeline_config) - pipeline.run() - -@cli.command() -@click.option( - "--config", - type=click.Path(exists=True), - required=True, - help="Path to YAML configuration file", -) -def experiment(config): - """Run multiple experiments with different feature combinations.""" - # Load configuration - pipeline_config = Config(config) - - # Run experiments - pipeline = Pipeline(pipeline_config) - pipeline.run_experiments() - -if __name__ == "__main__": - cli() -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/core/logging_helper.html b/docs/source/_build/html/_modules/optimhc/core/logging_helper.html deleted file mode 100644 index f980cd6..0000000 --- a/docs/source/_build/html/_modules/optimhc/core/logging_helper.html +++ /dev/null @@ -1,224 +0,0 @@ - - - - - - - - optimhc.core.logging_helper — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for optimhc.core.logging_helper

-import logging
-
-
-[docs] -def setup_loggers(log_file=None, log_level="INFO"): - """ - Create or update all loggers so that each logger has a StreamHandler and optionally a FileHandler. - This ensures all log messages are displayed in the console and optionally saved to a file. - - Parameters - ---------- - log_file : str, optional - Path to the log file. If None, no file logging is set up. - log_level : str, optional - Logging level (DEBUG, INFO, WARNING, ERROR). Default is "INFO". - """ - # Disable mhctools logging, avoid the warning message when multiprocessing - for logger_name in ["mhctools", "mhctools.base_commandline_predictor", - "mhctools.netmhc", "mhctools.netmhciipan"]: - logger = logging.getLogger(logger_name) - logger.disabled = True - logger.propagate = False - logger.setLevel(logging.CRITICAL) - - loggers = [logging.getLogger(name) for name in logging.root.manager.loggerDict] - level = getattr(logging, log_level.upper(), logging.INFO) - - #debug_logging() - - for lg in loggers: - if lg.name.startswith("mhctools"): - continue - - lg.disabled = False - has_stream_handler = any( - isinstance(handler, logging.StreamHandler) for handler in lg.handlers - ) - if not has_stream_handler: - console_handler = logging.StreamHandler() - console_handler.setLevel(level) - formatter = logging.Formatter( - "%(asctime)s - %(name)s - %(levelname)s - %(message)s" - ) - console_handler.setFormatter(formatter) - lg.addHandler(console_handler) - - if log_file: - has_file_handler = any( - isinstance(handler, logging.FileHandler) for handler in lg.handlers - ) - if not has_file_handler: - file_handler = logging.FileHandler(log_file, mode="a") - file_handler.setLevel(level) - formatter = logging.Formatter( - "%(asctime)s - %(name)s - %(levelname)s - %(message)s" - ) - file_handler.setFormatter(formatter) - lg.addHandler(file_handler) - - lg.propagate = False - lg.setLevel(level) - - if lg.name.startswith("optimhc"): - lg.disabled = False - - root_logger = logging.getLogger() - root_logger.disabled = False - root_logger.setLevel(level)
- - - -
-[docs] -def debug_logging(): - """ - Print debugging information for all loggers that start with 'optimhc' and - the root logger. This helps verify that logger configurations are set properly. - """ - print("\n=== Debugging Loggers ===\n") - loggers = [ - logging.getLogger(name) for name in logging.root.manager.loggerDict.keys() - ] - for lg in loggers: - if lg.name.startswith("optimhc"): - print(f"Logger Name: {lg.name}") - print( - f" - Effective Level: {logging.getLevelName(lg.getEffectiveLevel())}" - ) - print( - f" - Explicit Level: {logging.getLevelName(lg.level)} (default: NOTSET)" - ) - print(f" - Propagate: {lg.propagate}") - print(f" - Disabled: {lg.disabled}") - - if lg.handlers: - for handler in lg.handlers: - print(f" Handler: {type(handler).__name__}") - print(f" - Level: {logging.getLevelName(handler.level)}") - print(f" - Formatter: {handler.formatter}") - if isinstance(handler, logging.FileHandler): - print(f" - Log File: {handler.baseFilename}") - print(f" - Stream: {getattr(handler, 'stream', None)}") - else: - print(f" No handlers attached to the logger.") - print("") - - root_logger = logging.getLogger() - print(f"Root Logger:") - print(f" - Level: {logging.getLevelName(root_logger.level)}") - print(f" - Handlers: {len(root_logger.handlers)}") - for handler in root_logger.handlers: - print(f" Handler: {type(handler).__name__}") - print(f" - Level: {logging.getLevelName(handler.level)}") - print(f" - Formatter: {handler.formatter}") - if isinstance(handler, logging.FileHandler): - print(f" - Log File: {handler.baseFilename}") - print(f" - Stream: {getattr(handler, 'stream', None)}") - print("\n=== End of Logger Debugging ===\n")
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/core/pipeline.html b/docs/source/_build/html/_modules/optimhc/core/pipeline.html deleted file mode 100644 index f69a5cc..0000000 --- a/docs/source/_build/html/_modules/optimhc/core/pipeline.html +++ /dev/null @@ -1,545 +0,0 @@ - - - - - - - - optimhc.core.pipeline — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for optimhc.core.pipeline

-"""
-pipeline.py
-
-Implements the main optiMHC pipeline for immunopeptidomics rescoring, including input parsing,
-feature generation, rescoring, result saving, and visualization. Supports both single-run and
-experiment modes.
-"""
-import os
-import logging
-import gc
-import pandas as pd
-from multiprocessing import Process
-from mokapot.model import PercolatorModel
-
-from optimhc.parser import read_pin, read_pepxml
-from optimhc.rescore import mokapot
-from optimhc.rescore.model import XGBoostPercolatorModel, RandomForestPercolatorModel
-from optimhc.visualization import (
-    plot_feature_importance,
-    visualize_target_decoy_features,
-    visualize_feature_correlation,
-    plot_qvalues,
-)
-from optimhc.core.config import load_config, Config
-from optimhc.core.logging_helper import setup_loggers
-from optimhc.core.feature_generation import generate_features
-
-logger = logging.getLogger(__name__)
-
-
-[docs] -class Pipeline: - """ - Main pipeline class for optiMHC, encapsulating the full data processing workflow. - - This class orchestrates input parsing, feature generation, rescoring, result saving, and visualization. - It supports both single-run and experiment modes (multiple feature/model combinations). - - Parameters - ---------- - config : str, dict, or Config - Path to YAML config, dict, or Config object. - - Examples - -------- - >>> from optimhc.core import Pipeline - >>> pipeline = Pipeline(config) - >>> pipeline.run() - """ -
-[docs] - def __init__(self, config): - """ - Initialize the pipeline with a configuration file, dict, or Config object. - - Parameters - ---------- - config : str, dict, or Config - Path to YAML config, dict, or Config object. - """ - logger.debug(f"config: {config}") - if isinstance(config, Config): - self.config = config - else: - self.config = Config(config) - self.config.validate() - self.experiment = self.config.get("experimentName", "optimhc_experiment") - self.output_dir = os.path.join(self.config["outputDir"], self.experiment) - os.makedirs(self.output_dir, exist_ok=True) - setup_loggers(os.path.join(self.output_dir, "log"), self.config.get("logLevel", "INFO")) - - self.visualization_enabled = self.config.get("visualization", True) - self.save_models = self.config.get("saveModels", True) - self.test_fdr = self.config.get("rescore", {}).get("testFDR", 0.01) - self.model_type = self.config.get("rescore", {}).get("model", "Percolator") - self.n_jobs = self.config.get("rescore", {}).get("numJobs", 1)
- - -
-[docs] - def read_input(self): - """ - Read input PSMs based on configuration. - - Returns - ------- - PsmContainer - Object containing loaded PSMs. - - Raises - ------ - ValueError - If input type is unsupported. - Exception - If file reading fails. - """ - input_type = self.config["inputType"] - input_files = self.config["inputFile"] - if not isinstance(input_files, list): - input_files = [input_files] - - try: - if input_type == "pepxml": - psms = read_pepxml(input_files, decoy_prefix=self.config["decoyPrefix"]) - elif input_type == "pin": - psms = read_pin(input_files) - else: - raise ValueError(f"Unsupported input type: {input_type}") - return psms - except Exception as e: - logger.error(f"Failed to read input files: {e}") - raise
- - -
-[docs] - def _generate_features(self, psms): - """ - Generate features for PSMs using the configured feature generators. - - Parameters - ---------- - psms : PsmContainer - PSM container object. - - Returns - ------- - PsmContainer - PSM container with generated features. - """ - generate_features(psms, self.config) - return psms
- - -
-[docs] - def rescore(self, psms, model_type=None, n_jobs=None, test_fdr=None, rescoring_features=None): - """ - Perform rescoring on the PSMs using the specified or configured model. - - Parameters - ---------- - psms : PsmContainer - PSM container object. - model_type : str, optional - Model type ('XGBoost', 'RandomForest', 'Percolator'). - n_jobs : int, optional - Number of parallel jobs. - test_fdr : float, optional - FDR threshold. - rescoring_features : list, optional - List of features to use for rescoring. - - Returns - ------- - results : mokapot.Results - Rescoring results. - models : list - Trained models. - - Notes - ----- - Rescoring logic is adapted from mokapot (https://mokapot.readthedocs.io/) - """ - test_fdr = test_fdr if test_fdr is not None else self.test_fdr - model_type = model_type if model_type is not None else self.model_type - n_jobs = n_jobs if n_jobs is not None else self.n_jobs - - if model_type == "XGBoost": - model = XGBoostPercolatorModel(n_jobs=n_jobs) - elif model_type == "RandomForest": - model = RandomForestPercolatorModel(n_jobs=n_jobs) - elif model_type == "Percolator": - model = PercolatorModel(n_jobs=n_jobs) - else: - model = PercolatorModel(n_jobs=n_jobs) - - kwargs = {} - if rescoring_features is not None: - kwargs["rescoring_features"] = rescoring_features - - results, models = mokapot.rescore( - psms, model=model, test_fdr=test_fdr, **kwargs - ) - return results, models
- - -
-[docs] - def save_results(self, psms, results, models, output_dir=None, file_root="optimhc"): - """ - Save rescoring results, PSM data, and trained models to disk. - - Parameters - ---------- - psms : PsmContainer - PSM container object. - results : mokapot.Results - Rescoring results. - models : list - Trained models. - output_dir : str, optional - Output directory. - file_root : str, optional - Root name for output files. - """ - output_dir = output_dir if output_dir is not None else self.output_dir - - results.to_txt(dest_dir=output_dir, file_root=file_root, decoys=True) - psms.write_pin(os.path.join(output_dir, f"{file_root}.pin")) - - if self.save_models: - model_dir = os.path.join(output_dir, "models") - os.makedirs(model_dir, exist_ok=True) - logger.info(f"Saving models to {model_dir}") - for i, model in enumerate(models): - model.save(os.path.join(model_dir, f"{file_root}.model{i}"))
- - -
-[docs] - def visualize_results(self, psms, results, models, output_dir=None, sources=None): - """ - Generate and save visualizations for the analysis results. - - Parameters - ---------- - psms : PsmContainer - PSM container object. - results : mokapot.Results - Rescoring results. - models : list - Trained models. - output_dir : str, optional - Output directory. - sources : list, optional - Feature sources to include in visualizations. - """ - if not self.visualization_enabled: - logger.info("Visualization is disabled. Skipping...") - return - - output_dir = output_dir if output_dir is not None else self.output_dir - fig_dir = os.path.join(output_dir, "figures") - os.makedirs(fig_dir, exist_ok=True) - - plot_qvalues( - results, - save_path=os.path.join(fig_dir, "qvalues.png"), - threshold=0.05, - ) - - if sources: - rescoring_features = { - k: v for k, v in psms.rescoring_features.items() if k in sources - } - else: - rescoring_features = psms.rescoring_features - - plot_feature_importance( - models, - rescoring_features, - save_path=os.path.join(fig_dir, "feature_importance.png"), - ) - visualize_feature_correlation( - psms, - save_path=os.path.join(fig_dir, "feature_correlation.png"), - ) - visualize_target_decoy_features( - psms, - num_cols=4, - save_path=os.path.join(fig_dir, "target_decoy_histogram.png"), - )
- - -
-[docs] - def _run_single_experiment(self, psms, exp_config, exp_name, exp_dir): - """ - Run a single experiment with the specified configuration. - - Parameters - ---------- - psms : PsmContainer - PSM container object. - exp_config : dict - Experiment-specific configuration. - exp_name : str - Name of the experiment. - exp_dir : str - Output directory for the experiment. - - Returns - ------- - bool - True if experiment succeeded, False otherwise. - """ - try: - os.makedirs(exp_dir, exist_ok=True) - - source = exp_config.get("source", None) - model_type = exp_config.get("model", self.model_type) - n_jobs = exp_config.get("numJobs", self.n_jobs) - - logger.info(f"Running experiment '{exp_name}' with sources: {source}") - - # Generate list of features based on the provided sources - features = [] - if source: - for s in source: - features.extend(psms.rescoring_features.get(s, [])) - logger.info(f"Features used in experiment '{exp_name}': {features}") - - results, models = self.rescore( - psms, - model_type=model_type, - n_jobs=n_jobs, - test_fdr=self.test_fdr, - rescoring_features=features, - ) - - self.save_results( - psms, results, models, output_dir=exp_dir, file_root=exp_name - ) - - fig_dir = os.path.join(exp_dir, "figures") - - plot_qvalues( - results, - save_path=os.path.join(fig_dir, "qvalues.png"), - threshold=0.05, - ) - - plot_feature_importance( - models, - rescoring_features={ - k: v for k, v in psms.rescoring_features.items() if k in source - }, - save_path=os.path.join(fig_dir, "feature_importance.png"), - ) - - return True - - except Exception as e: - logger.error(f"Experiment '{exp_name}' failed: {e}") - return False - - finally: - # Explicit resource release to free up memory after each experiment - try: - del results - del models - except Exception: - pass - gc.collect()
- - -
-[docs] - def run(self): - """ - Run the complete optiMHC pipeline (single run mode). - - This method executes the full workflow: input parsing, feature generation, rescoring, saving, and visualization. - - Returns - ------- - psms : PsmContainer - PSM container object. - results : mokapot.Results - Rescoring results. - models : list - Trained models. - """ - logger.info("Starting analysis pipeline") - - psms = self.read_input() - psms = self._generate_features(psms) - results, models = self.rescore(psms) - self.save_results(psms, results, models) - self.visualize_results(psms, results, models) - - logger.info(f"Analysis pipeline completed, results saved to {self.output_dir}") - return psms, results, models
- - -
-[docs] - def run_experiments(self): - """ - Run experiments with different feature/model combinations using multiprocessing. - - Each experiment is executed in its own process for complete resource isolation. - The experiment configurations must be provided in the config under the 'experiments' key. - - Returns - ------- - None - """ - logger.info("Starting experiment mode with multiple feature combinations") - - psms = self.read_input() - psms = self._generate_features(psms) - - # Save the generated pin file for reference - pin_path = os.path.join(self.output_dir, f"optimhc.{self.experiment}.pin") - psms.write_pin(pin_path) - fig_summary_dir = os.path.join(self.output_dir, "figures") - os.makedirs(fig_summary_dir, exist_ok=True) - visualize_feature_correlation( - psms, - save_path=os.path.join(fig_summary_dir, "feature_correlation.png"), - ) - # visualize_target_decoy_features( - # psms, - # num_cols=4, - # save_path=os.path.join(fig_summary_dir, 'target_decoy_histogram.png'), - # ) - - experiment_configs = self.config.get("experiments", []) - processes = [] - for i, exp_config in enumerate(experiment_configs): - exp_name = exp_config.get("name", f"Experiment_{i + 1}") - exp_dir = os.path.join(self.output_dir, exp_name) - - logger.info(f"Starting experiment '{exp_name}' in a separate process") - p = Process( - target=self._run_single_experiment, - args=(psms, exp_config, exp_name, exp_dir), - ) - p.start() - processes.append(p) - - # Wait for all experiment processes to finish - for p in processes: - p.join() - - logger.info("All experiments completed")
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/feature_generator/DeepLC.html b/docs/source/_build/html/_modules/optimhc/feature_generator/DeepLC.html deleted file mode 100644 index e74f660..0000000 --- a/docs/source/_build/html/_modules/optimhc/feature_generator/DeepLC.html +++ /dev/null @@ -1,600 +0,0 @@ - - - - - - - - optimhc.feature_generator.DeepLC — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for optimhc.feature_generator.DeepLC

-# feature_generator/DeepLC.py
-# TODO: Use koina for prediction
-
-import logging
-from deeplc import DeepLC
-from optimhc.psm_container import PsmContainer
-from typing import List, Union, Optional, Dict
-from optimhc import utils
-import pandas as pd
-import numpy as np
-from optimhc.feature_generator.base_feature_generator import BaseFeatureGenerator
-
-logger = logging.getLogger(__name__)
-logging.getLogger("deeplc.feat_extractor").setLevel(logging.CRITICAL)
-logging.getLogger("deeplc.feat_extractor").disabled = True
-
-
-
-[docs] -class DeepLCFeatureGenerator(BaseFeatureGenerator): - """ - Generate DeepLC-based features for rescoring. - - This generator uses DeepLC to predict retention times and calculates various - features based on the differences between predicted and observed retention times. - - Parameters - ---------- - psms : PsmContainer - PSMs to generate features for. - calibration_criteria_column : str - Column name in the PSMs DataFrame to use for DeepLC calibration. - lower_score_is_better : bool, optional - Whether a lower PSM score denotes a better matching PSM. Default is False. - calibration_set_size : int or float, optional - Amount of best PSMs to use for DeepLC calibration. If this value is lower - than the number of available PSMs, all PSMs will be used. Default is 0.15. - processes : int, optional - Number of processes to use in DeepLC. Default is 1. - model_path : str, optional - Path to the DeepLC model. If None, the default model will be used. - remove_pre_nxt_aa : bool, optional - Whether to remove the first and last amino acids from the peptide sequence. - Default is True. - mod_dict : dict, optional - Dictionary of modifications to be used for DeepLC. If None, no modifications - will be used. - - Notes - ----- - DeepLC retraining is on by default. Add ``deeplc_retrain: False`` as a keyword - argument to disable retraining. - - The generated features include: - - observed_retention_time: Original retention time from the data - - predicted_retention_time: DeepLC predicted retention time - - retention_time_diff: Difference between predicted and observed times - - abs_retention_time_diff: Absolute difference between predicted and observed times - - retention_time_ratio: Ratio of min(pred,obs) to max(pred,obs) - """ - -
-[docs] - def __init__( - self, - psms: PsmContainer, - calibration_criteria_column: str, - lower_score_is_better: bool = False, - calibration_set_size: Union[int, float, None] = None, - processes: int = 1, - model_path: Optional[str] = None, - remove_pre_nxt_aa: bool = True, - mod_dict: Optional[Dict[str, str]] = None, - *args, - **kwargs, - ): - """ - Generate DeepLC-based features for rescoring. - - DeepLC retraining is on by default. Add ``deeplc_retrain: False`` as a keyword argument to - disable retraining. - - Parameters: - psms: PsmContainer - PSMs to generate features for. - calibration_criteria_column: str - Column name in the PSMs DataFrame to use for DeepLC calibration. - lower_score_is_better - Whether a lower PSM score denotes a better matching PSM. Default: False - calibration_set_size: int or float - Amount of best PSMs to use for DeepLC calibration. If this value is lower - than the number of available PSMs, all PSMs will be used. (default: 0.15) - processes: {int, None} - Number of processes to use in DeepLC. Defaults to 1. - model_path: str - Path to the DeepLC model. If None, the default model will be used. - remove_pre_nxt_aa: bool - Whether to remove the first and last amino acids from the peptide sequence. - Default: True - mod_dict: dict - Dictionary of modifications to be used for DeepLC. If None, no modifications will be used. - *args: list - Additional positional arguments are passed to DeepLC. - kwargs: dict - Additional keyword arguments are passed to DeepLC. - """ - self.psms = psms - self.lower_score_is_better = lower_score_is_better - self.calibration_criteria_column = calibration_criteria_column - self.calibration_set_size = calibration_set_size - self.processes = processes - self.model_path = model_path - self.remove_pre_nxt_aa = remove_pre_nxt_aa - self.mod_dict = mod_dict - self.deeplc_df = self._get_deeplc_df() - self.DeepLC = DeepLC - self._raw_predictions = None - if model_path is not None: - self.deeplc_predictor = self.DeepLC( - n_jobs=self.processes, - path_model=model_path, - ) - else: - self.deeplc_predictor = self.DeepLC(n_jobs=self.processes) - logger.info( - f"Initialized DeepLCFeatureGenerator with {len(self.psms)} PSMs." - f" Calibration criteria: {self.calibration_criteria_column}." - f" Lower score is better: {self.lower_score_is_better}." - f" Calibration set size: {self.calibration_set_size}." - f" Processes: {self.processes}." - f" Model path: {self.model_path}." - )
- - - @property - def feature_columns(self) -> List[str]: - """ - Return the list of generated feature column names. - - Returns - ------- - List[str] - List of feature column names: - - observed_retention_time - - predicted_retention_time - - retention_time_diff - - abs_retention_time_diff - - retention_time_ratio - """ - return [ - "observed_retention_time", - "predicted_retention_time", - "retention_time_diff", - "abs_retention_time_diff", - "retention_time_ratio", - ] - - @property - def id_column(self) -> List[str]: - """ - Return the list of input columns required for the feature generator. - - Returns - ------- - List[str] - List of input columns required for feature generation. - Currently returns an empty list as the required columns are - handled internally by the PsmContainer. - """ - return [""] - -
-[docs] - def _get_deeplc_df(self): - """ - Extract the format required by DeepLC, while retaining necessary original information. - - Returns - ------- - pd.DataFrame - DataFrame with the required DeepLC format and original information: - - original_seq: Original peptide sequence - - label: Target/decoy label - - seq: Cleaned peptide sequence - - modifications: Unimod format modifications - - tr: Retention time - - score: Calibration criteria score - - Raises - ------ - ValueError - If retention time column is not found in the PSMs DataFrame. - - Notes - ----- - This method prepares the data in the format required by DeepLC, - including cleaning peptide sequences and converting modifications - to Unimod format. - """ - df_deeplc = pd.DataFrame() - df_psm = self.psms.psms - df_deeplc["original_seq"] = df_psm[self.psms.peptide_column] - df_deeplc["label"] = df_psm[self.psms.label_column] - - if self.remove_pre_nxt_aa: - df_deeplc["seq"] = df_deeplc["original_seq"].apply( - utils.remove_pre_and_nxt_aa - ) - else: - df_deeplc["seq"] = df_deeplc["original_seq"] - - # Apply extract_unimod_from_peptidoform once and store both results. - if self.mod_dict is None: - logger.warning("No mod_dict provided. Removing modifications.") - df_deeplc["seq"] = df_deeplc["seq"].apply( - lambda x: utils.remove_modifications(x) - ) - df_deeplc["modifications"] = "" - else: - extracted_results = df_deeplc["seq"].apply( - lambda x: utils.extract_unimod_from_peptidoform( - x, mod_dict=self.mod_dict - ) - ) - df_deeplc["seq"] = extracted_results.apply(lambda x: x[0]) - df_deeplc["modifications"] = extracted_results.apply(lambda x: x[1]) - - if self.psms.retention_time_column is None: - raise ValueError("DeepLC requires retention time values.") - - df_deeplc["tr"] = df_psm[self.psms.retention_time_column] - df_deeplc["score"] = df_psm[self.calibration_criteria_column] - - logger.debug("DeepLC input DataFrame:") - logger.debug(df_deeplc) - - return df_deeplc
- - -
-[docs] - def generate_features(self) -> pd.DataFrame: - """ - Generate DeepLC features for the provided PSMs. - - Returns - ------- - pd.DataFrame - DataFrame containing the PSMs with added DeepLC features: - - original_seq: Original peptide sequence - - observed_retention_time: Original retention time - - predicted_retention_time: DeepLC predicted retention time - - retention_time_diff: Difference between predicted and observed times - - abs_retention_time_diff: Absolute difference between predicted and observed times - - retention_time_ratio: Ratio of min(pred,obs) to max(pred,obs) - - Notes - ----- - This method: - 1. Prepares data in DeepLC format - 2. Calibrates DeepLC if calibration set is specified - 3. Predicts retention times - 4. Calculates various retention time-based features - 5. Handles missing values by imputing with median values - """ - logger.info("Generating DeepLC features.") - - # Extract DeepLC input DataFrame - self.deeplc_df = self._get_deeplc_df() - - # Calibrate DeepLC predictor - if self.calibration_set_size: - calibration_df = self._get_calibration_psms(self.deeplc_df) - logger.debug(f"Calibrating DeepLC with {len(calibration_df)} PSMs.") - self.deeplc_predictor.calibrate_preds( - seq_df=calibration_df[["seq", "tr", "modifications"]] - ) - - # Predict retention times - logger.info("Predicting retention times using DeepLC.") - predictions = self.deeplc_predictor.make_preds( - seq_df=self.deeplc_df[["seq", "tr", "modifications"]] - ) - - self._raw_predictions = pd.DataFrame( - { - "peptide": self.deeplc_df["seq"], - "predicted_rt": predictions, - "observed_rt": self.deeplc_df["tr"], - "modifications": self.deeplc_df["modifications"], - } - ) - - # Calculate retention time differences - rt_diffs = predictions - self.deeplc_df["tr"] - self.deeplc_df["predicted_retention_time"] = predictions - self.deeplc_df["retention_time_diff"] = rt_diffs - - result_df = pd.DataFrame() - result_df["original_seq"] = self.deeplc_df["original_seq"] - result_df["observed_retention_time"] = self.deeplc_df["tr"] - result_df["predicted_retention_time"] = self.deeplc_df[ - "predicted_retention_time" - ] - result_df["retention_time_diff"] = self.deeplc_df["retention_time_diff"] - result_df["abs_retention_time_diff"] = self.deeplc_df[ - "retention_time_diff" - ].abs() - - # Adopt from 'DeepRescore2': RTR = min(pred, obs) / max(pred, obs) - result_df["retention_time_ratio"] = np.minimum( - result_df["predicted_retention_time"], result_df["observed_retention_time"] - ) / np.maximum( - result_df["predicted_retention_time"], result_df["observed_retention_time"] - ) - - for col in self.feature_columns: - nan_rows = result_df[result_df[col].isna()] - if not nan_rows.empty: - logger.warning( - f"Column {col} contains NaN values. Rows with NaN values:\n{nan_rows}" - ) - median_value = result_df[col].median() - result_df[col].fillna(median_value, inplace=True) - result_df[col] = result_df[col].astype(float) - - return result_df
- - -
-[docs] - def _get_calibration_psms(self, deeplc_df: pd.DataFrame) -> pd.DataFrame: - """ - Get the best scoring PSMs for calibration based on the calibration criteria. - - Parameters - ---------- - deeplc_df : pd.DataFrame - DataFrame containing DeepLC input data. - - Returns - ------- - pd.DataFrame - DataFrame of PSMs selected for calibration, containing only target PSMs. - - Raises - ------ - ValueError - If calibration_set_size is a float not between 0 and 1. - TypeError - If calibration_set_size is neither int nor float. - - Notes - ----- - This method: - 1. Sorts PSMs based on calibration criteria - 2. Selects top N PSMs based on calibration_set_size - 3. Filters to keep only target PSMs - """ - logger.debug("Selecting PSMs for calibration.") - - # Sort PSMs based on calibration criteria - sorted_psms = deeplc_df.sort_values( - by="score", ascending=self.lower_score_is_better - ) - - # Select calibration set - if isinstance(self.calibration_set_size, float): - if not 0 < self.calibration_set_size <= 1: - logger.error("calibration_set_size as float must be between 0 and 1.") - raise ValueError( - "If `calibration_set_size` is a float, it must be between 0 and 1." - ) - n_cal = int(len(sorted_psms) * self.calibration_set_size) - elif isinstance(self.calibration_set_size, int): - n_cal = self.calibration_set_size - if n_cal > len(sorted_psms): - logger.warning( - f"Requested calibration_set_size ({n_cal}) exceeds number of PSMs ({len(sorted_psms)}). Using all PSMs for calibration." - ) - n_cal = len(sorted_psms) - else: - logger.error("calibration_set_size must be either int or float.") - raise TypeError( - "Expected int or float for `calibration_set_size`. " - f"Got {type(self.calibration_set_size)} instead." - ) - - calibration_psms = sorted_psms.head(n_cal) - logger.debug(f"Selected {n_cal} PSMs for calibration.") - calibration_psms = calibration_psms[calibration_psms["label"] == True] - logger.debug(f"Selected {len(calibration_psms)} target PSMs for calibration.") - return calibration_psms
- - -
-[docs] - def get_full_data(self) -> pd.DataFrame: - """ - Get the full DeepLC DataFrame. - - Returns - ------- - pd.DataFrame - DataFrame containing the DeepLC input data with all columns: - - original_seq: Original peptide sequence - - label: Target/decoy label - - seq: Cleaned peptide sequence - - modifications: Unimod format modifications - - tr: Retention time - - score: Calibration criteria score - - predicted_retention_time: DeepLC predicted retention time - - retention_time_diff: Difference between predicted and observed times - """ - return self.deeplc_df
- - - @property - def raw_predictions(self) -> pd.DataFrame: - """ - Get the raw predictions DataFrame. - - Returns - ------- - pd.DataFrame - DataFrame containing the raw predictions: - - peptide: Cleaned peptide sequence - - predicted_rt: DeepLC predicted retention time - - observed_rt: Original retention time - - modifications: Unimod format modifications - - Notes - ----- - If predictions haven't been generated yet, this will trigger - feature generation automatically. - """ - if self._raw_predictions is None: - self.generate_features() - return self._raw_predictions - -
-[docs] - def get_raw_predictions(self) -> pd.DataFrame: - """ - Get the raw predictions DataFrame. - - Returns - ------- - pd.DataFrame - DataFrame containing the raw predictions: - - peptide: Cleaned peptide sequence - - predicted_rt: DeepLC predicted retention time - - observed_rt: Original retention time - - modifications: Unimod format modifications - - Notes - ----- - This is a convenience method that returns the same data as the - raw_predictions property. - """ - return self.raw_predictions
- - -
-[docs] - def save_raw_predictions(self, file_path: str, **kwargs) -> None: - """ - Save the raw prediction results to a file. - - Parameters - ---------- - file_path : str - Path to save the file. - **kwargs : dict - Additional parameters passed to pandas.DataFrame.to_csv. - If 'index' is not specified, it defaults to False. - - Notes - ----- - This method saves the raw predictions DataFrame to a CSV file. - The DataFrame includes: - - peptide: Cleaned peptide sequence - - predicted_rt: DeepLC predicted retention time - - observed_rt: Original retention time - - modifications: Unimod format modifications - """ - if "index" not in kwargs: - kwargs["index"] = False - if self.raw_predictions is not None: - self.raw_predictions.to_csv(file_path, **kwargs) - logger.info(f"Raw predictions saved to {file_path}") - else: - logger.warning("Raw predictions have not been generated yet.")
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/feature_generator/PWM.html b/docs/source/_build/html/_modules/optimhc/feature_generator/PWM.html deleted file mode 100644 index 896fd76..0000000 --- a/docs/source/_build/html/_modules/optimhc/feature_generator/PWM.html +++ /dev/null @@ -1,875 +0,0 @@ - - - - - - - - optimhc.feature_generator.PWM — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for optimhc.feature_generator.PWM

-# feature_generator/PWM.py
-
-import os
-import pandas as pd
-import numpy as np
-from typing import Optional, Dict, Union, List, Tuple
-from optimhc.feature_generator.base_feature_generator import BaseFeatureGenerator
-import logging
-from optimhc import utils
-
-logger = logging.getLogger(__name__)
-
-# n_flank_pwm and c_flank_pwm are used for MHC class II PWM calculation.
-# The original PPM is recorded in data/PWMs/II/n_flank_ppm and data/PWMs/II/c_flank_ppm,
-# which is converted to PWM by taking the background frequency as 0.05.
-
-c_flank_pwm_data = {
-    "A": [0.891194, 0.599125, 0.567353],
-    "C": [-3.952777, -4.345220, -4.612584],
-    "D": [0.291173, 0.550133, 0.325690],
-    "E": [0.687212, 0.662834, 0.834717],
-    "F": [-1.250652, -0.784627, -1.139232],
-    "G": [0.509354, -0.368370, 0.919885],
-    "H": [-0.808229, -0.836628, -0.591508],
-    "I": [-0.046196, -0.034123, -1.564360],
-    "K": [0.452471, 0.665617, 1.086677],
-    "L": [0.522681, 0.382607, 0.208291],
-    "M": [-2.131278, -2.144693, -2.008653],
-    "N": [-0.208044, -0.699085, 0.022668],
-    "P": [0.417673, 1.179052, -0.018850],
-    "Q": [-0.033656, -0.051822, 0.274208],
-    "R": [0.535173, 0.397917, 0.924583],
-    "S": [0.542669, 0.255087, 0.360228],
-    "T": [-0.359617, -0.322741, -0.028565],
-    "V": [0.507861, 0.582748, -0.584754],
-    "W": [-3.432776, -3.024952, -3.391619],
-    "Y": [-1.144700, -0.725718, -1.221789],
-    # Add a pseudo-entry for 'X' with 0 contribution
-    "X": [0.0, 0.0, 0.0],
-}
-
-c_flank_pwm = pd.DataFrame.from_dict(
-    c_flank_pwm_data, orient="index", columns=["Pos1", "Pos2", "Pos3"]
-)
-
-n_flank_pwm_data = {
-    "A": [0.672938, 0.494511, 0.290216],
-    "C": [-5.464582, -5.140732, -5.112201],
-    "D": [0.856850, 0.732683, 0.398964],
-    "E": [0.692225, 0.660452, 0.746641],
-    "F": [-1.024461, -1.529751, -0.687119],
-    "G": [0.873872, 0.746332, 0.630604],
-    "H": [-1.386627, -1.212169, -1.011943],
-    "I": [0.138351, -0.461093, 0.095419],
-    "K": [0.801095, 0.639492, 0.847272],
-    "L": [0.562420, -0.162599, 0.201511],
-    "M": [-2.230132, -2.754557, -2.397489],
-    "N": [-0.198452, -0.099572, -0.214853],
-    "P": [-1.491966, 1.405721, 0.532710],
-    "Q": [-0.622442, -0.151155, -0.006518],
-    "R": [0.216375, 0.217991, 0.545768],
-    "S": [0.623057, 0.416164, 0.236042],
-    "T": [0.160517, 0.011151, -0.111494],
-    "V": [0.523780, 0.239183, 0.330202],
-    "W": [-3.276340, -4.050898, -2.959115],
-    "Y": [-1.060520, -1.689795, -0.674071],
-    # Add a pseudo-entry for 'X' with 0 contribution
-    "X": [0.0, 0.0, 0.0],
-}
-
-n_flank_pwm = pd.DataFrame.from_dict(
-    n_flank_pwm_data, orient="index", columns=["Pos1", "Pos2", "Pos3"]
-)
-
-
-
-[docs] -class PWMFeatureGenerator(BaseFeatureGenerator): - """ - Generates PWM (Position Weight Matrix) features for peptides based on specified MHC alleles. - - This generator calculates PWM scores for each peptide against the provided MHC class I or II allele PWMs. - - Parameters - ---------- - peptides : list of str - Series of peptide sequences. - alleles : list of str - List of MHC allele names (e.g., ['HLA-A01:01', 'HLA-B07:02']). - anchors : int, optional - Number of anchor positions to consider for MHC class I. Default is 2. - mhc_class : str, optional - MHC class, either 'I' or 'II'. Default is 'I'. - pwm_path : str or os.PathLike, optional - Custom path to PWM files. Defaults to '../../data/PWMs'. - remove_pre_nxt_aa : bool, optional - Whether to include the previous and next amino acids in peptides. - If True, remove them. Default is False. - remove_modification : bool, optional - Whether to include modifications in peptides. - If True, remove them. Default is True. - - Attributes - ---------- - peptides : pd.Series - Series of peptide sequences. - alleles : list of str - List of MHC allele names. - mhc_class : str - MHC class ('I' or 'II'). - pwm_path : str or os.PathLike - Path to PWM files. - pwms : dict - Dictionary of PWMs for each allele and mer length. - anchors : int - Number of anchor positions for MHC class I. - remove_pre_nxt_aa : bool - Whether to remove pre/post neighboring amino acids. - remove_modification : bool - Whether to remove modifications. - - Notes - ----- - For MHC class I: - - Generates 'PWM_Score_{allele}' and optionally 'Anchor_Score_{allele}' columns. - For MHC class II: - - Generates 'PWM_Score_{allele}' (core 9-mer), - - 'N_Flank_PWM_Score_{allele}', - - 'C_Flank_PWM_Score_{allele}' columns. - """ - - CLASS_II_CORE_LENGTH = 9 - DEFAULT_PWM_PATH = os.path.join(os.path.dirname(__file__), "..", "PWMs") - -
-[docs] - def __init__( - self, - peptides: List[str], - alleles: List[str], - anchors: int = 2, - mhc_class: str = "I", - pwm_path: Optional[Union[str, os.PathLike]] = None, - remove_pre_nxt_aa: bool = False, - remove_modification: bool = True, - *args, - **kwargs, - ): - """ - Initializes the PWMFeatureGenerator. - - Parameters: - peptides (List[str]): Series of peptide sequences. - alleles (List[str]): List of MHC allele names (e.g., ['HLA-A01:01', 'HLA-B07:02']). - mhc_class (str): MHC class, either 'I' or 'II'. Default is 'I'. - pwm_path (Optional[Union[str, os.PathLike]]): Custom path to PWM files. Defaults to '../../data/PWMs'. - remove_pre_nxt_aa (bool): Whether to include the previous and next amino acids in peptides. - If True, remove them. Default is False. - remove_modification (bool): Whether to include modifications in peptides. - If True, remove them. Default is True. - """ - self.peptides = pd.Series(peptides) - self.alleles = alleles - self.mhc_class = mhc_class.upper() - if self.mhc_class not in {"I", "II"}: - raise ValueError("MHC class must be 'I' or 'II'.") - self.pwm_path = pwm_path if pwm_path else PWMFeatureGenerator.DEFAULT_PWM_PATH - logger.info(f"PWM path: {self.pwm_path}") - self.pwms: Dict[str, Dict[int, pd.DataFrame]] = ( - self._load_pwms() - ) # Dict[allele, Dict[mer, pd.DataFrame]] - - for allele, pwms in self.pwms.items(): - for mer, pwm in pwms.items(): - logger.debug( - f"Loaded PWM for allele {allele}, length {mer}: {pwm.shape[1]} positions" - ) - logger.debug(pwm) - self.anchors = anchors - if mhc_class == "I" and anchors > 0: - logger.info("Number of anchors: {}".format(self.anchors)) - self.remove_pre_nxt_aa = remove_pre_nxt_aa - logger.info( - "Remove pre and next amino acids: {}".format(self.remove_pre_nxt_aa) - ) - self.remove_modification = remove_modification - logger.info("Remove modifications: {}".format(self.remove_modification)) - logger.info( - f"Initialized PWMFeatureGenerator with {len(peptides)} peptides and alleles: {alleles}" - )
- - - @property - def id_column(self) -> List[str]: - """ - Get a list of input columns required for the feature generator. - - Returns - ------- - list of str - List of column names required for feature generation. - """ - return ["Peptide"] - -
-[docs] - def _extract_trailing_numbers(self, text: str) -> str: - """ - Extract the trailing numbers from a string. - - Parameters - ---------- - text : str - Input string to extract numbers from. - - Returns - ------- - str or None - The trailing numbers if found, None otherwise. - - Examples - -------- - >>> _extract_trailing_numbers('ABC123') - '123' - >>> _extract_trailing_numbers('ABC') - None - >>> _extract_trailing_numbers('L123') - '123' - """ - import re - - match = re.search(r"\d+$", text) - return match.group() if match else None
- - -
-[docs] - def _default_allele_pwm_files(self) -> Dict[str, Dict[int, str]]: - """ - Construct default PWM file paths for each allele based on MHC class. - - Returns - ------- - dict of str to dict of int to str - Dictionary mapping alleles to their PWM files for each mer length. - Format: {allele: {mer_length: file_path}} - - Notes - ----- - For MHC class I: - - Allele directory format: HLA-A01:01 -> HLA-A01_01 - For MHC class II: - - Allele directory format: DRB10101 -> DRB1_0101 - - Fixed core length of 9 - """ - class_path = os.path.join(self.pwm_path, self.mhc_class) - pwm_files = {} - for allele in self.alleles: - pwm_files[allele] = {} - if self.mhc_class == "I": - allele_dir = allele.replace("*", "").replace(":", "_") - elif self.mhc_class == "II": - allele_dir = allele.replace("*", "").replace(":", "") - if len(allele_dir) == 8: - # DRB10101 -> DRB1_0101 - allele_dir = f"{allele_dir[:4]}_{allele_dir[4:]}" - allele_dir_path = os.path.join(class_path, allele_dir) - pwm_files_list = utils.list_all_files_in_directory(allele_dir_path) - logger.info( - f"Searching for PWM files for allele {allele} in {allele_dir_path}" - ) - logger.debug(f"Found PWM files for allele {allele}: {pwm_files_list}") - if self.mhc_class == "I": - for pwm_file in pwm_files_list: - # assume the trailing numbers in the file name indicate the mer length - mer = int(self._extract_trailing_numbers(str(pwm_file))) - if mer is not None: - pwm_files[allele][mer] = pwm_file - else: - logger.error( - f"Mer length not found in PWM file name: {pwm_file}. Assuming the tailing number is the mer length." - ) - raise ValueError( - f"Mer length not found in PWM file name: {pwm_file}" - ) - elif self.mhc_class == "II": - # class II PWMs are fixed length: 9, which indicates the core length - if len(pwm_files_list) == 1: - pwm_files[allele][PWMFeatureGenerator.CLASS_II_CORE_LENGTH] = ( - pwm_files_list[0] - ) - else: - logger.error( - f"Expected 1 PWM file for allele {allele}, found {len(pwm_files_list)}: {pwm_files_list}" - ) - raise ValueError( - f"Expected 1 PWM file for allele {allele}, found {len(pwm_files_list)}: {pwm_files_list}" - ) - - logger.debug(f"Default PWM file paths set for alleles: {self.alleles}") - return pwm_files
- - -
-[docs] - def _most_conserved_postions(self, pwm: pd.DataFrame, n: int = 2) -> List[int]: - """ - Find the n most conserved positions in the PWM. - - Parameters - ---------- - pwm : pd.DataFrame - Position Weight Matrix to analyze. - n : int, optional - Number of positions to return. Default is 2. - - Returns - ------- - list of int - Indices of the n most conserved positions. - - Raises - ------ - ValueError - If n exceeds the PWM length. - - Notes - ----- - In our study, we only use the anchor score for class I MHC. - Conservation is measured using Shannon entropy. - """ - if n > pwm.shape[1]: - raise ValueError( - f"Number of positions to return ({n}) exceeds the PWM length ({pwm.shape[1]})." - ) - pfm = pwm.apply(lambda x: 2**x) - entropy = -1 * (pfm * np.log2(pfm)).sum(axis=0) - return entropy.nsmallest(n).index.tolist()
- - -
-[docs] - def _load_pwms(self) -> Dict[str, Dict[int, pd.DataFrame]]: - """ - Load PWMs for each allele from the constructed file paths. - - Returns - ------- - dict of str to dict of int to pd.DataFrame - Dictionary of PWMs for each allele and mer length. - Format: {allele: {mer_length: pwm_dataframe}} - - Raises - ------ - FileNotFoundError - If a PWM file is not found. - Exception - If there is an error loading a PWM file. - - Notes - ----- - PWM files are expected to be space-delimited text files with amino acids as row indices - and positions as column indices. - """ - pwms = {} - allele_pwm_files = self._default_allele_pwm_files() - for allele, mer_files in allele_pwm_files.items(): - pwms[allele] = {} - for mer, file_path in mer_files.items(): - if not os.path.exists(file_path): - logger.error(f"PWM file not found: {file_path}") - raise FileNotFoundError(f"PWM file not found: {file_path}") - try: - pwm = pd.read_csv( - file_path, sep='\s+', header=None, index_col=0 - ) - pwm.columns = [f"{pos+1}" for pos in range(pwm.shape[1])] - pwms[allele][mer] = pwm - logger.debug( - f"Loaded PWM for allele {allele}, length {mer} from {file_path}" - ) - except Exception as e: - logger.error(f"Error loading PWM file {file_path}: {e}") - raise e - return pwms
- - -
-[docs] - def _cal_PWM_score_I(self, peptide: str, allele: str) -> Optional[float]: - """ - Calculate PWM scores for MHC class I. - - Parameters - ---------- - peptide : str - The peptide sequence to score. - allele : str - The MHC allele to score against. - - Returns - ------- - float or None - PWM score for the peptide against the allele's PWM, or None if out of range. - - Notes - ----- - If peptide length is out of range, returns None. - """ - peptide_len = len(peptide) - min_mer = min(self.pwms[allele].keys()) - max_mer = max(self.pwms[allele].keys()) - - if peptide_len < min_mer or peptide_len > max_mer: - return pd.NA - else: - pwm = self.pwms[allele][peptide_len] - try: - score = sum(pwm.loc[aa, str(i + 1)] for i, aa in enumerate(peptide)) - except KeyError as e: - logger.error( - f"Residue '{e}' not found in PWM for allele={allele}, " - f"peptide={peptide}, length={peptide_len}" - ) - raise ValueError(f"Residue '{e}' not found in PWM.") - return score
- - -
-[docs] - def _cal_PWM_score_II( - self, peptide: str, allele: str - ) -> Tuple[Optional[float], Optional[float], Optional[float]]: - """ - Calculate PWM scores for MHC class II using a sliding 9-mer window. - - Parameters - ---------- - peptide : str - The peptide sequence to score. - allele : str - The MHC allele to score against. - - Returns - ------- - tuple of (float or None, float or None, float or None) - A tuple containing: - - core_score : float or None - Score for the best 9-mer core. - - n_flank_score : float or None - Score for the N-terminal flanking region. - - c_flank_score : float or None - Score for the C-terminal flanking region. - Returns (None, None, None) if peptide has length < 9. - - Notes - ----- - The method: - 1. Slides over all possible 9-mer windows to find the highest core PWM score. - 2. Once the best core is found, extracts up to 3 AA on each flank (N-flank and C-flank). - 3. If the flank has fewer than 3 residues, pads with 'X'. - 4. Scores each flank with n_flank_pwm and c_flank_pwm. - """ - core_len = PWMFeatureGenerator.CLASS_II_CORE_LENGTH - pwm = self.pwms[allele][core_len] - - if len(peptide) < core_len: - # Return NaNs for the scores if peptide too short - return (pd.NA, pd.NA, pd.NA) - - best_score = None - best_core = None - best_core_start_idx = 0 - - # Slide over possible 9-mer windows - for start_idx in range(len(peptide) - core_len + 1): - subpeptide_9 = peptide[start_idx : start_idx + core_len] - try: - tmp_score = sum( - float(pwm.loc[aa, str(i + 1)]) for i, aa in enumerate(subpeptide_9) - ) - except KeyError as e: - logger.error( - f"Residue '{e}' not found in PWM for allele={allele}, " - f"peptide={peptide} (subpep={subpeptide_9})." - ) - raise ValueError(f"Residue '{e}' not found in PWM.") - - if (best_score is None) or (tmp_score > best_score): - best_score = tmp_score - best_core = subpeptide_9 - best_core_start_idx = start_idx - - logger.debug( - f"Peptide: {peptide}, best core: {best_core}, " - f"start_idx: {best_core_start_idx}, score: {best_score}" - ) - - # Compute N-flank and C-flank (up to 3 residues) - n_flank_start = max(0, best_core_start_idx - 3) - n_flank_end = best_core_start_idx - c_flank_start = best_core_start_idx + core_len - c_flank_end = min(len(peptide), c_flank_start + 3) - - n_flank_seq = peptide[n_flank_start:n_flank_end] - c_flank_seq = peptide[c_flank_start:c_flank_end] - - # Pad with 'X' if flank < 3 residues - n_flank_seq = (("X" * (3 - len(n_flank_seq))) + n_flank_seq)[ - -3: - ] # ensure length = 3 - c_flank_seq = (c_flank_seq + ("X" * (3 - len(c_flank_seq))))[ - :3 - ] # ensure length = 3 - - n_flank_score = 0.0 - c_flank_score = 0.0 - for i, aa in enumerate(n_flank_seq): - try: - n_flank_score += n_flank_pwm.loc[aa, f"Pos{i+1}"] - except KeyError: - # 'X' or unknown -> 0 - pass - - for i, aa in enumerate(c_flank_seq): - try: - c_flank_score += c_flank_pwm.loc[aa, f"Pos{i+1}"] - except KeyError: - # 'X' or unknown -> 0 - pass - - logger.debug( - f"n_flank: {n_flank_seq}, c_flank: {c_flank_seq}, " - f"n_flank_score: {n_flank_score}, c_flank_score: {c_flank_score}" - ) - - return (best_score, n_flank_score, c_flank_score)
- - -
-[docs] - def _cal_PWM_score( - self, peptide: str, allele: str - ) -> Union[float, Tuple[float, float, float]]: - """ - Calculates PWM scores for a single peptide across all applicable mer lengths for a given allele. - - For MHC class I: - Returns a single float (or pd.NA). - For MHC class II: - Returns a tuple of (core_score, n_flank_score, c_flank_score) or (pd.NA, pd.NA, pd.NA). - """ - if self.mhc_class == "I": - return self._cal_PWM_score_I(peptide, allele) - else: - return self._cal_PWM_score_II(peptide, allele)
- - -
-[docs] - def _cal_anchor_score( - self, peptide: str, allele: str, anchor_dict: Dict[int, List[int]] - ) -> Optional[float]: - """ - Calculate anchor score for a single peptide across all applicable mer lengths for a given allele. - - Parameters - ---------- - peptide : str - The peptide sequence to score. - allele : str - The MHC allele to score against. - anchor_dict : dict of int to list of int - Dictionary containing the most conserved positions for each mer length. - - Returns - ------- - float or None - Anchor score for the peptide against the allele's PWM, or None if out of range. - - Notes - ----- - Only implemented for MHC class I. For MHC class II, returns None. - """ - peptide_len = len(peptide) - if self.mhc_class == "I": - min_mer = min(self.pwms[allele].keys()) - max_mer = max(self.pwms[allele].keys()) - if peptide_len < min_mer or peptide_len > max_mer: - return pd.NA - else: - pwm = self.pwms[allele].get(peptide_len) - score = 0 - anchors = anchor_dict[peptide_len] - try: - for anchor in anchors: - anchor = int(anchor) - score += pwm.loc[peptide[anchor - 1], str(anchor)] - except KeyError as e: - logger.error( - f"Position '{e}' not found in PWM for allele {allele} and peptide {peptide} with length {peptide_len}." - ) - raise ValueError(f"Position '{e}' not found in PWM.") - return score - elif self.mhc_class == "II": - logger.warning("Anchor score calculation not implemented for MHC class II.") - return None
- - -
-[docs] - def set_pwms(self, pwms: Dict[str, Dict[int, pd.DataFrame]]): - """ - Set PWMs directly, allowing for custom PWMs to be provided. - - Parameters - ---------- - pwms : dict of str to dict of int to pd.DataFrame - Dictionary of PWMs for each allele and mer length. - Format: {allele: {mer_length: pwm_dataframe}} - """ - self.pwms = pwms - logger.info(f"Set custom PWMs for alleles: {list(pwms.keys())}")
- - -
-[docs] - def generate_features(self) -> pd.DataFrame: - """ - Generate PWM features for all peptides across specified alleles. - - Returns - ------- - pd.DataFrame - DataFrame containing generated features: - For MHC class I: - - 'PWM_Score_{allele}' and optionally 'Anchor_Score_{allele}' columns. - For MHC class II: - - 'PWM_Score_{allele}' (core 9-mer), - - 'N_Flank_PWM_Score_{allele}', - - 'C_Flank_PWM_Score_{allele}' columns. - - Notes - ----- - Missing values are imputed with the median value for each feature. - """ - features_df = pd.DataFrame(self.peptides, columns=["Peptide"]) - features_df["clean_peptide"] = features_df["Peptide"] - if self.remove_pre_nxt_aa: - features_df["clean_peptide"] = features_df["Peptide"].apply( - utils.remove_pre_and_nxt_aa - ) - if self.remove_modification: - features_df["clean_peptide"] = features_df["clean_peptide"].apply( - utils.remove_modifications - ) - - # Convert nonstandard amino acids: U -> C - features_df["clean_peptide"] = features_df["clean_peptide"].apply( - lambda x: x.replace("U", "C") - ) - - for allele in self.alleles: - logger.info( - f"Generating PWM scores for allele: {allele}, total peptides: {len(features_df)}" - ) - - if self.mhc_class == "I": - # Class I returns a single score - features_df[f"PWM_Score_{allele}"] = features_df["clean_peptide"].apply( - lambda peptide: self._cal_PWM_score(peptide, allele) - ) - na_count = features_df[f"PWM_Score_{allele}"].isna().sum() - logger.info( - f"Missing PWM scores for {na_count} peptides. Using median for imputation." - ) - features_df.fillna( - { - f"PWM_Score_{allele}": features_df[ - f"PWM_Score_{allele}" - ].median() - }, - inplace=True, - ) - - if self.anchors != 0: - logger.info( - f"Generating anchor scores for allele: {allele}, total peptides: {len(features_df)}" - ) - anchor_dict = {} - min_mer = min(self.pwms[allele].keys()) - max_mer = max(self.pwms[allele].keys()) - for mer_len in range(min_mer, max_mer + 1): - anchor_dict[mer_len] = self._most_conserved_postions( - self.pwms[allele][mer_len], self.anchors - ) - logger.info( - f"Most conserved positions for allele {allele}: {anchor_dict}" - ) - features_df[f"Anchor_Score_{allele}"] = features_df[ - "clean_peptide" - ].apply( - lambda peptide: self._cal_anchor_score( - peptide, allele, anchor_dict - ) - ) - na_count = features_df[f"Anchor_Score_{allele}"].isna().sum() - logger.info( - f"Missing anchor scores for {na_count} peptides. Using median for imputation." - ) - features_df.fillna( - { - f"Anchor_Score_{allele}": features_df[ - f"Anchor_Score_{allele}" - ].median() - }, - inplace=True, - ) - - else: - # Class II returns (core_score, n_flank_score, c_flank_score) - features_df[ - [ - f"PWM_Score_{allele}", - f"N_Flank_PWM_Score_{allele}", - f"C_Flank_PWM_Score_{allele}", - ] - ] = features_df["clean_peptide"].apply( - lambda pep: pd.Series(self._cal_PWM_score(pep, allele)) - ) - - # Impute missing (NaN) with medians for each new column - for col in [ - f"PWM_Score_{allele}", - f"N_Flank_PWM_Score_{allele}", - f"C_Flank_PWM_Score_{allele}", - ]: - na_count = features_df[col].isna().sum() - logger.info( - f"Missing {col} for {na_count} peptides. Using median for imputation." - ) - median_val = features_df[col].median() - features_df.loc[:, col] = features_df[col].fillna(median_val) - features_df[col] = features_df[col].infer_objects(copy=False) - - features_df.drop(columns=["clean_peptide"], inplace=True) - - return features_df
- - - @property - def feature_columns(self) -> List[str]: - """ - Returns a list of feature names generated by the feature generator. - """ - if self.mhc_class == "I": - feature_columns = [f"PWM_Score_{allele}" for allele in self.alleles] - if self.anchors != 0: - anchor_columns = [f"Anchor_Score_{allele}" for allele in self.alleles] - feature_columns.extend(anchor_columns) - return feature_columns - else: - # Class II - feature_columns = [] - for allele in self.alleles: - feature_columns.append(f"PWM_Score_{allele}") - feature_columns.append(f"N_Flank_PWM_Score_{allele}") - feature_columns.append(f"C_Flank_PWM_Score_{allele}") - return feature_columns
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/feature_generator/base_feature_generator.html b/docs/source/_build/html/_modules/optimhc/feature_generator/base_feature_generator.html deleted file mode 100644 index 9fc8702..0000000 --- a/docs/source/_build/html/_modules/optimhc/feature_generator/base_feature_generator.html +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - - optimhc.feature_generator.base_feature_generator — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for optimhc.feature_generator.base_feature_generator

-# feature_generators/base_feature_generator.py
-
-from abc import ABC, abstractmethod
-import pandas as pd
-from typing import List
-
-
-
-[docs] -class BaseFeatureGenerator(ABC): - """ - Abstract base class for all feature generators in the rescoring pipeline. - """ - - @property - @abstractmethod - def feature_columns(self) -> List[str]: - """ - Returns a list of feature names generated by the feature generator. - """ - pass - - @property - @abstractmethod - def id_column(self) -> List[str]: - """ - Returns the column or columns used as key or keys to merge features with PSMs. - """ - pass - -
-[docs] - @abstractmethod - def generate_features(self) -> pd.DataFrame: - """ - Generates features. - """ - pass
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/feature_generator/basic.html b/docs/source/_build/html/_modules/optimhc/feature_generator/basic.html deleted file mode 100644 index 09f0ca6..0000000 --- a/docs/source/_build/html/_modules/optimhc/feature_generator/basic.html +++ /dev/null @@ -1,289 +0,0 @@ - - - - - - - - optimhc.feature_generator.basic — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for optimhc.feature_generator.basic

-# feature_generator/basic.py
-
-from optimhc.feature_generator.base_feature_generator import BaseFeatureGenerator
-import pandas as pd
-from typing import List
-import logging
-from optimhc import utils
-from scipy.stats import entropy  # Import entropy from scipy
-
-logger = logging.getLogger(__name__)
-
-
-
-[docs] -class BasicFeatureGenerator(BaseFeatureGenerator): - """ - Feature generator that generates basic features from peptide sequences. - - This generator calculates features such as peptide length, proportion of unique amino acids, - Shannon entropy of amino acid distribution, difference between peptide length and average peptide length, - and count of unique amino acids. - - Parameters - ---------- - peptides : List[str] - List of peptide sequences to generate features for. - remove_pre_nxt_aa : bool, optional - Whether to remove the amino acids adjacent to the peptide. - If True, removes them. Default is True. - remove_modification : bool, optional - Whether to remove modifications in the peptide sequences. - If True, removes them. Default is True. - - Notes - ----- - The generated features include: - - length_diff_from_avg: Difference between peptide length and average length - - abs_length_diff_from_avg: Absolute difference between peptide length and average length - - unique_aa_count: Number of unique amino acids in the peptide - - unique_aa_proportion: Proportion of unique amino acids in the peptide - - shannon_entropy: Shannon entropy of amino acid distribution - """ - - def __init__( - self, - peptides: List[str], - remove_pre_nxt_aa: bool = True, - remove_modification: bool = True, - *args, - **kwargs, - ): - super().__init__(*args, **kwargs) - self.peptides = peptides - self.remove_pre_nxt_aa = remove_pre_nxt_aa - self.remove_modification = remove_modification - self.avg_length = None - logger.info(f"Initialized BasicFeatureGenerator with {len(peptides)} peptides.") - - @property - def feature_columns(self) -> List[str]: - """Return the list of generated feature column names.""" - return [ - #'peptide_length', - "length_diff_from_avg", - "abs_length_diff_from_avg", - "unique_aa_count", - "unique_aa_proportion", - "shannon_entropy", - ] - - @property - def id_column(self) -> List[str]: - """ - Return the list of input columns required for feature generation. - - Returns - ------- - List[str] - List of input column names required for feature generation. - Currently only requires 'Peptide' column. - """ - return ["Peptide"] - -
-[docs] - def _preprocess_peptide(self, peptide: str) -> str: - """ - Preprocess peptide sequence by removing adjacent amino acids and modifications. - - Parameters: - peptide (str): Original peptide sequence. - - Returns: - str: Preprocessed peptide sequence. - """ - if self.remove_pre_nxt_aa: - peptide = utils.remove_pre_and_nxt_aa(peptide) - if self.remove_modification: - peptide = utils.remove_modifications(peptide) - return peptide
- - -
-[docs] - def _shannon_entropy(self, sequence: str) -> float: - """ - Calculate the Shannon entropy of a peptide sequence. - - Parameters: - sequence (str): Peptide sequence. - - Returns: - float: Shannon entropy value. - """ - if len(sequence) == 0: - return 0.0 - # Calculate frequency of each unique amino acid - bases = list(set(sequence)) - freq_list = [sequence.count(base) / len(sequence) for base in bases] - return entropy(freq_list, base=2)
- - -
-[docs] - def generate_features(self) -> pd.DataFrame: - """ - Generate basic features for the provided peptides. - - Returns - ------- - pd.DataFrame - DataFrame containing peptides and their computed features: - - length_diff_from_avg: Difference from average peptide length - - abs_length_diff_from_avg: Absolute difference from average length - - unique_aa_count: Number of unique amino acids - - unique_aa_proportion: Proportion of unique amino acids - - shannon_entropy: Shannon entropy of amino acid distribution - - Raises - ------ - ValueError - If NaN values are found in the generated features. - - Notes - ----- - All features are converted to float type before returning. - The method calculates average peptide length across all peptides - and uses it as a reference for length-based features. - """ - logger.info("Generating basic features.") - peptides_df = pd.DataFrame(self.peptides, columns=["Peptide"]) - peptides_df["clean_peptide"] = peptides_df["Peptide"].apply( - self._preprocess_peptide - ) - peptides_df["peptide_length"] = peptides_df["clean_peptide"].apply(len) - self.avg_length = peptides_df["peptide_length"].mean() - peptides_df["length_diff_from_avg"] = ( - peptides_df["peptide_length"] - self.avg_length - ) - peptides_df["abs_length_diff_from_avg"] = peptides_df[ - "length_diff_from_avg" - ].abs() - peptides_df["unique_aa_count"] = peptides_df["clean_peptide"].apply( - lambda x: len(set(x)) - ) - peptides_df["unique_aa_proportion"] = ( - peptides_df["unique_aa_count"] / peptides_df["peptide_length"] - ) - peptides_df["shannon_entropy"] = peptides_df["clean_peptide"].apply( - self._shannon_entropy - ) - features_df = peptides_df[["Peptide"] + self.feature_columns] - # Fix SettingWithCopyWarning: make an explicit copy before assignment - features_df = features_df.copy() - for col in self.feature_columns: - features_df[col] = features_df[col].astype(float) - if features_df.isna().sum().sum() > 0: - logger.error("NaN values found in the generated features.") - raise ValueError("NaN values found in the generated features.") - - logger.info(f"Generated basic features for {len(features_df)} peptides.") - return features_df
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/feature_generator/mhcflurry.html b/docs/source/_build/html/_modules/optimhc/feature_generator/mhcflurry.html deleted file mode 100644 index 46f7c40..0000000 --- a/docs/source/_build/html/_modules/optimhc/feature_generator/mhcflurry.html +++ /dev/null @@ -1,484 +0,0 @@ - - - - - - - - optimhc.feature_generator.mhcflurry — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for optimhc.feature_generator.mhcflurry

-from optimhc.feature_generator.base_feature_generator import BaseFeatureGenerator
-import pandas as pd
-from mhcflurry import Class1PresentationPredictor
-from typing import List, Dict
-from optimhc import utils
-import logging
-from tqdm import tqdm
-from typing import Optional, List, Dict, Union
-
-logger = logging.getLogger(__name__)
-
-
-
-[docs] -class MHCflurryFeatureGenerator(BaseFeatureGenerator): - """ - Generate MHCflurry features for peptides based on specified MHC class I alleles. - - This generator calculates MHCflurry presentation scores for each peptide against - the provided MHC class I alleles. - - Parameters - ---------- - peptides : List[str] - List of peptide sequences. - alleles : List[str] - List of MHC allele names (e.g., ['HLA-A01:01', 'HLA-B07:02']). - remove_pre_nxt_aa : bool, optional - Whether to include the previous and next amino acids in peptides. - If True, remove them. Default is True. - remove_modification : bool, optional - Whether to include modifications in peptides. - If True, remove them. Default is True. - - Notes - ----- - The generated features include: - - mhcflurry_affinity: Binding affinity score - - mhcflurry_processing_score: Processing score - - mhcflurry_presentation_score: Presentation score - - mhcflurry_presentation_percentile: Presentation percentile - """ - - MIN_PEPTIDE_LENGTH = 8 - MAX_PEPTIDE_LENGTH = 15 - - def __init__( - self, - peptides: List[str], - alleles: List[str], - remove_pre_nxt_aa: bool = False, - remove_modification: bool = True, - *args, - **kwargs, - ): - self.peptides = peptides - self.alleles = alleles - self.remove_pre_nxt_aa = remove_pre_nxt_aa - self.remove_modification = remove_modification - self.predictor = Class1PresentationPredictor.load() - self.predictions = None - self._raw_predictions = None - logger.info( - f"Initialized MHCflurryFeatureGenerator with {len(peptides)} peptides and alleles: {alleles}" - ) - - @property - def feature_columns(self) -> List[str]: - """ - Return the list of generated feature column names. - - Returns - ------- - List[str] - List of feature column names: - - mhcflurry_affinity - - mhcflurry_processing_score - - mhcflurry_presentation_score - - mhcflurry_presentation_percentile - """ - return [ - "mhcflurry_affinity", - "mhcflurry_processing_score", - "mhcflurry_presentation_score", - "mhcflurry_presentation_percentile", - ] - - @property - def id_column(self) -> List[str]: - """ - Return the list of input columns required for the feature generator. - - Returns - ------- - List[str] - List of input column names. - """ - return ["Peptide"] - -
-[docs] - def _preprocess_peptides(self, peptide: str) -> str: - """ - Preprocess peptide sequence by removing flanking amino acids and modifications. - - Parameters - ---------- - peptide : str - Original peptide sequence. - - Returns - ------- - str - Preprocessed peptide sequence. - """ - if self.remove_pre_nxt_aa: - peptide = utils.remove_pre_and_nxt_aa(peptide) - if self.remove_modification: - peptide = utils.remove_modifications(peptide) - # U -> C - peptide = peptide.replace("U", "C") - return peptide
- - -
-[docs] - def _predict(self) -> pd.DataFrame: - """ - Run MHCflurry predictions using the `predict` method and cache the result. - - Returns - ------- - pd.DataFrame - DataFrame containing the prediction results, with the best prediction - for each sequence. - - Notes - ----- - This method: - 1. Preprocesses peptides - 2. Filters peptides by length (8-15 amino acids) - 3. Runs MHCflurry predictions - 4. Merges results with original peptides - """ - if self.predictions is None: - logger.info("Running MHCflurry predictions.") - self.predictions = pd.DataFrame(self.peptides, columns=["Peptide"]) - self.predictions["clean_peptide"] = self.predictions["Peptide"].apply( - self._preprocess_peptides - ) - # filter out peptides with length between 8 and 15, which are the only lengths supported by MHCflurry - peptides_to_predict = self.predictions[ - self.predictions["clean_peptide"].apply( - lambda x: MHCflurryFeatureGenerator.MIN_PEPTIDE_LENGTH - <= len(x) - <= MHCflurryFeatureGenerator.MAX_PEPTIDE_LENGTH - ) - ] - logger.info( - f"Predicting MHCflurry scores for {len(peptides_to_predict)} peptides. The missing peptides will be filled with median values." - ) - mhcflurry_results = self.predictor.predict( - peptides=peptides_to_predict["clean_peptide"].unique().tolist(), - alleles=self.alleles, - verbose=0, - ) - self._raw_predictions = mhcflurry_results.copy() - self.predictions = self.predictions.merge( - mhcflurry_results, - left_on="clean_peptide", - right_on="peptide", - how="left", - ) - self.predictions.drop(columns=["clean_peptide", "peptide"], inplace=True) - else: - logger.info("MHCflurry predictions already exist. Skipping prediction.") - return self.predictions
- - - @property - def raw_predictions(self) -> pd.DataFrame: - """ - Return the raw predictions DataFrame. - - Returns - ------- - pd.DataFrame - DataFrame containing the raw predictions: - - peptide: Cleaned peptide sequence - - allele: MHC allele - - affinity: Binding affinity - - processing_score: Processing score - - presentation_score: Presentation score - - presentation_percentile: Presentation percentile - """ - if self._raw_predictions is None: - self._predict() - return self._raw_predictions - -
-[docs] - def get_raw_predictions(self) -> pd.DataFrame: - """ - Get the raw prediction results DataFrame from MHCflurry. - - Returns - ------- - pd.DataFrame - Raw prediction results DataFrame containing: - - peptide: Cleaned peptide sequence - - allele: MHC allele - - affinity: Binding affinity - - processing_score: Processing score - - presentation_score: Presentation score - - presentation_percentile: Presentation percentile - """ - return self.raw_predictions
- - -
-[docs] - def save_raw_predictions(self, file_path: str, **kwargs) -> None: - """ - Save the raw prediction results to a file. - - Parameters - ---------- - file_path : str - Path to save the file. - **kwargs : dict - Additional parameters passed to pandas.DataFrame.to_csv. - If 'index' is not specified, it defaults to False. - - Notes - ----- - This method saves the raw predictions DataFrame to a CSV file. - The DataFrame includes: - - peptide: Cleaned peptide sequence - - allele: MHC allele - - affinity: Binding affinity - - processing_score: Processing score - - presentation_score: Presentation score - - presentation_percentile: Presentation percentile - """ - if "index" not in kwargs: - kwargs["index"] = False - if self.raw_predictions is not None: - self.raw_predictions.to_csv(file_path, **kwargs) - logger.info(f"Raw prediction results saved to: {file_path}") - else: - logger.warning("No raw prediction results available to save.")
- - -
-[docs] - def generate_features(self) -> pd.DataFrame: - """ - Generate MHCflurry features for the provided peptides and alleles. - - Returns - ------- - pd.DataFrame - DataFrame containing the peptides and their predicted MHCflurry features: - - Peptide: Original peptide sequence - - mhcflurry_affinity: Binding affinity - - mhcflurry_processing_score: Processing score - - mhcflurry_presentation_score: Presentation score - - mhcflurry_presentation_percentile: Presentation percentile - - Notes - ----- - This method: - 1. Runs MHCflurry predictions - 2. Renames columns to include 'mhcflurry_' prefix - 3. Fills missing values with median values - 4. Returns the final feature DataFrame - """ - self._predict() - features_df = self.predictions.copy() - features_df.rename( - columns={ - "affinity": "mhcflurry_affinity", - "processing_score": "mhcflurry_processing_score", - "presentation_score": "mhcflurry_presentation_score", - "presentation_percentile": "mhcflurry_presentation_percentile", - }, - inplace=True, - ) - na_count = features_df["mhcflurry_affinity"].isna().sum() - features_df.fillna( - value={ - "mhcflurry_affinity": features_df["mhcflurry_affinity"].median(), - "mhcflurry_processing_score": features_df[ - "mhcflurry_processing_score" - ].median(), - "mhcflurry_presentation_score": features_df[ - "mhcflurry_presentation_score" - ].median(), - "mhcflurry_presentation_percentile": features_df[ - "mhcflurry_presentation_percentile" - ].median(), - }, - inplace=True, - ) - logger.info(f"Generated MHCflurry features for {len(features_df)} peptides.") - features_df = features_df[ - [ - "Peptide", - "mhcflurry_affinity", - "mhcflurry_processing_score", - "mhcflurry_presentation_score", - "mhcflurry_presentation_percentile", - ] - ] - if features_df.isna().sum().sum() > 0: - logger.warning("NaN values found in the generated features.") - return features_df[ - [ - "Peptide", - "mhcflurry_affinity", - "mhcflurry_processing_score", - "mhcflurry_presentation_score", - "mhcflurry_presentation_percentile", - ] - ]
- - -
-[docs] - def get_best_allele(self) -> pd.DataFrame: - """ - Get the best allele for each peptide. - - Returns - ------- - pd.DataFrame - DataFrame containing the best alleles for the peptides: - - Peptide: Original peptide sequence - - mhcflurry_best_allele: Best binding allele - - Notes - ----- - The best allele is determined by the lowest presentation percentile rank. - """ - best_allele_df = self.predictions[["Peptide", "best_allele"]] - best_allele_df.rename( - columns={"best_allele": "mhcflurry_best_allele"}, inplace=True - ) - - logger.info( - f"Generated best allele information for {len(best_allele_df)} peptides." - ) - - return best_allele_df
- - -
-[docs] - def predictions_to_dataframe(self) -> pd.DataFrame: - """ - Convert the predictions to a DataFrame. - - Returns - ------- - pd.DataFrame - DataFrame containing the predictions. - - Raises - ------ - ValueError - If no predictions are available. - """ - if self.predictions is None: - raise ValueError( - "No predictions available. Please run 'generate_features' first." - ) - return self.predictions
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/feature_generator/netMHCIIpan.html b/docs/source/_build/html/_modules/optimhc/feature_generator/netMHCIIpan.html deleted file mode 100644 index ca50792..0000000 --- a/docs/source/_build/html/_modules/optimhc/feature_generator/netMHCIIpan.html +++ /dev/null @@ -1,798 +0,0 @@ - - - - - - - - optimhc.feature_generator.netMHCIIpan — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for optimhc.feature_generator.netMHCIIpan

-# feature_generator/netMHCIIpan.py
-
-# TODO: set 'BA' and 'EL' as optional parameters for the user to choose the prediction method.
-
-from optimhc.feature_generator.base_feature_generator import BaseFeatureGenerator
-import pandas as pd
-from mhctools import NetMHCIIpan43_BA, NetMHCIIpan43_EL
-from typing import List, Dict, Optional
-from optimhc import utils
-import logging
-from multiprocessing import Pool, cpu_count
-from functools import partial
-from tqdm import tqdm
-
-logger = logging.getLogger(__name__)
-
-
-
-[docs] -def _predict_peptide_chunk_class2( - peptides_chunk: List[str], alleles: List[str] -) -> pd.DataFrame: - """ - Use NetMHCIIpan43_BA to predict a batch of peptides (MHC Class II). - - Parameters: - peptides_chunk (List[str]): A batch of peptide sequences to predict. - alleles (List[str]): List of MHC Class II alleles, e.g., ['DRB1_0101', 'DRB1_0102']. - - Returns: - pd.DataFrame: A DataFrame containing prediction results. - """ - predictor = NetMHCIIpan43_BA(alleles=alleles) - results = predictor.predict_peptides(peptides_chunk) - return results.to_dataframe()
- - - -
-[docs] -class NetMHCIIpanFeatureGenerator(BaseFeatureGenerator): - """ - Generate NetMHCIIpan features for given peptides based on specified MHC Class II alleles. - - This feature generator uses the NetMHCIIpan43_BA interface to predict MHC Class II binding - for each peptide and returns scores and features based on the specified parameters. - - Parameters - ---------- - peptides : List[str] - List of peptide sequences. - alleles : List[str] - List of MHC Class II alleles, e.g., ['DRB1_0101', 'DRB1_0102']. - mode : str, optional - Feature generation mode. Options: - - 'best': Return only the best result for each peptide across all alleles. - - 'all': Return prediction results for each peptide across all alleles (with allele-specific column suffixes). - Default is 'best'. - remove_pre_nxt_aa : bool, optional - Whether to remove the amino acids flanking the peptide (e.g., removing X-AA/AA-X forms). - Default is True. - remove_modification : bool, optional - Whether to remove modification information from peptides, e.g., (Phospho). - Default is True. - n_processes : int, optional - Number of processes to use. Default is 1 (no multiprocessing). - show_progress : bool, optional - Whether to display a progress bar. Default is False. - - Notes - ----- - The generated features include: - - netmhciipan_score: Raw binding score - - netmhciipan_affinity: Binding affinity in nM - - netmhciipan_percentile_rank: Percentile rank of the binding score - """ - - MIN_PEPTIDE_LENGTH = 9 # Minimum peptide length for MHC Class II is usually 9 - MAX_PEPTIDE_LENGTH = 50 # Can be adjusted based on use case - CHUNKSIZE = 500 - - def __init__( - self, - peptides: List[str], - alleles: List[str], - mode: str = "best", - remove_pre_nxt_aa: bool = True, - remove_modification: bool = True, - n_processes: int = 1, - show_progress: bool = False, - *args, - **kwargs, - ): - if mode not in ["best", "all"]: - raise ValueError("Mode must be one of 'best' or 'all'.") - - self.peptides = peptides - self.alleles = alleles - self.mode = mode - if len(alleles) == 1: - self.mode = "best" - logger.info("Only one allele provided. Switching to 'best' mode.") - - self.remove_pre_nxt_aa = remove_pre_nxt_aa - self.remove_modification = remove_modification - self.n_processes = min(n_processes, cpu_count()) - self.show_progress = show_progress - self.predictor = NetMHCIIpan43_BA(alleles=self.alleles) - self.predictions = None - self._raw_predictions = None - - logger.info( - f"Initialized NetMHCIIpanFeatureGenerator with {len(peptides)} peptides, " - f"alleles={alleles}, mode={self.mode}, " - f"n_processes={self.n_processes}, show_progress={self.show_progress}" - ) - - @property - def feature_columns(self) -> List[str]: - """ - Return the list of generated feature column names, determined by the mode. - Only includes numerical features, excluding any string features like allele names. - - Returns - ------- - List[str] - List of feature column names: - - For 'all' mode: netmhciipan_score_{allele}, netmhciipan_affinity_{allele}, - netmhciipan_percentile_rank_{allele} for each allele - - For both modes: netmhciipan_best_score, netmhciipan_best_affinity, - netmhciipan_best_percentile_rank - """ - columns = [] - if self.mode == "all": - allele_specific = [] - for allele in self.alleles: - allele_specific.extend( - [ - f"netmhciipan_score_{allele}", - f"netmhciipan_affinity_{allele}", - f"netmhciipan_percentile_rank_{allele}", - ] - ) - columns.extend(allele_specific) - - # Both 'best' and 'all' modes include best allele numerical information - columns.extend( - [ - "netmhciipan_best_score", - "netmhciipan_best_affinity", - "netmhciipan_best_percentile_rank", - ] - ) - return columns - - @property - def id_column(self) -> List[str]: - """ - Return the list of input columns required for the feature generator. - - Returns - ------- - List[str] - List of input column names. - """ - return ["Peptide"] - -
-[docs] - def _preprocess_peptides(self, peptide: str) -> str: - """ - Preprocess the input peptide by removing flanking amino acids, - modifications, and replacing non-standard amino acids. - - Parameters - ---------- - peptide : str - The original peptide sequence. - - Returns - ------- - str - The preprocessed peptide sequence. - """ - if self.remove_pre_nxt_aa: - peptide = utils.remove_pre_and_nxt_aa(peptide) - if self.remove_modification: - peptide = utils.remove_modifications(peptide) - # Replace any non-standard amino acids if necessary - peptide = peptide.replace("U", "C") - return peptide
- - -
-[docs] - def _predict_multiprocessing(self, peptides_to_predict: List[str]) -> pd.DataFrame: - """ - Run NetMHCIIpan predictions using multiprocessing. - - Parameters - ---------- - peptides_to_predict : List[str] - List of peptides to predict. - - Returns - ------- - pd.DataFrame - DataFrame containing the prediction results: - - peptide: Peptide sequence - - allele: MHC allele - - score: Raw binding score - - affinity: Binding affinity in nM - - percentile_rank: Percentile rank - - Notes - ----- - This method: - 1. Splits peptides into chunks - 2. Processes chunks in parallel - 3. Combines results into a single DataFrame - """ - logger.info("Running NetMHCIIpan predictions with multiprocessing.") - chunksize = min( - NetMHCIIpanFeatureGenerator.CHUNKSIZE, - max(1, len(peptides_to_predict) // self.n_processes), - ) - func = partial(_predict_peptide_chunk_class2, alleles=self.alleles) - - with Pool(processes=self.n_processes) as pool: - if self.show_progress: - results = list( - tqdm( - pool.imap( - func, - [ - peptides_to_predict[i : i + chunksize] - for i in range(0, len(peptides_to_predict), chunksize) - ], - ), - total=(len(peptides_to_predict) + chunksize - 1) // chunksize, - desc="Predicting NetMHCIIpan", - ) - ) - else: - results = pool.map( - func, - [ - peptides_to_predict[i : i + chunksize] - for i in range(0, len(peptides_to_predict), chunksize) - ], - ) - - netmhciipan_results = pd.concat(results, ignore_index=True) - # Save the raw prediction results - self._raw_predictions = netmhciipan_results.copy() - logger.info( - f"Completed multiprocessing predictions for {len(peptides_to_predict)} peptides." - ) - return netmhciipan_results
- - -
-[docs] - def _predict(self) -> pd.DataFrame: - """ - Run NetMHCIIpan predictions and cache the result. - - Returns - ------- - pd.DataFrame - DataFrame containing the prediction results: - - Peptide: Original peptide sequence - - peptide: Cleaned peptide sequence - - allele: MHC allele - - score: Raw binding score - - affinity: Binding affinity in nM - - percentile_rank: Percentile rank - - Notes - ----- - This method: - 1. Preprocesses peptides - 2. Filters peptides by length (9-30 amino acids) - 3. Runs predictions (with or without multiprocessing) - 4. Merges results with original peptides - """ - if self.predictions is not None: - logger.info("NetMHCIIpan predictions already exist. Skipping prediction.") - return self.predictions - - logger.info("Starting NetMHCIIpan predictions.") - self.predictions = pd.DataFrame(self.peptides, columns=["Peptide"]) - self.predictions["clean_peptide"] = self.predictions["Peptide"].apply( - self._preprocess_peptides - ) - - # Filter peptides that meet the length requirements - peptides_to_predict = ( - self.predictions[ - self.predictions["clean_peptide"].apply( - lambda x: NetMHCIIpanFeatureGenerator.MIN_PEPTIDE_LENGTH - <= len(x) - <= NetMHCIIpanFeatureGenerator.MAX_PEPTIDE_LENGTH - ) - ]["clean_peptide"] - .unique() - .tolist() - ) - - logger.info( - f"Found {len(peptides_to_predict)} unique peptides meeting the length requirements." - ) - - if self.n_processes > 1: - netmhciipan_results = self._predict_multiprocessing(peptides_to_predict) - else: - netmhciipan_results = self.predictor.predict_peptides( - peptides_to_predict - ).to_dataframe() - # If not using multiprocessing, save raw prediction results here - self._raw_predictions = netmhciipan_results.copy() - - logger.info( - f"Predicted NetMHCIIpan results for {len(netmhciipan_results)} peptides." - ) - - self.predictions = self.predictions.merge( - netmhciipan_results, left_on="clean_peptide", right_on="peptide", how="left" - ) - self.predictions.drop(columns=["clean_peptide"], inplace=True) - - logger.info( - f"Completed NetMHCIIpan predictions for {len(peptides_to_predict)} peptides." - ) - return self.predictions
- - -
-[docs] - def generate_features(self) -> pd.DataFrame: - """ - Generate the final feature table with NetMHCIIpan features for each peptide. - - Returns - ------- - pd.DataFrame - DataFrame containing peptides and their predicted features: - - Peptide: Original peptide sequence - - For 'all' mode: netmhciipan_score_{allele}, netmhciipan_affinity_{allele}, - netmhciipan_percentile_rank_{allele} for each allele - - For both modes: netmhciipan_best_score, netmhciipan_best_affinity, - netmhciipan_best_percentile_rank - - Notes - ----- - The features generated depend on the mode: - - 'best': Only the best allele information for each peptide - - 'all': All allele predictions plus best allele information - - Missing values are handled consistently by filling with median values - for numeric columns. - """ - predictions_df = self._predict() - - features_df = pd.DataFrame({"Peptide": self.peptides}) - - # Generate allele-specific features if mode is 'all', otherwise generate best allele features - if self.mode == "all": - features_df = self._generate_all_allele_features( - predictions_df, features_df - ) - features_df = self._generate_best_allele_features(predictions_df, features_df) - - features_df = self._fill_missing_values(features_df) - - selected_columns = ["Peptide"] + self.feature_columns - logger.info(f"Final selected feature columns: {selected_columns}") - features_df = features_df[selected_columns] - - if features_df.isna().sum().sum() > 0: - logger.warning( - "NaN values still exist in the generated features after filling with median/mode values." - ) - - return features_df
- - -
-[docs] - def _generate_all_allele_features( - self, predictions_df: pd.DataFrame, features_df: pd.DataFrame - ) -> pd.DataFrame: - """ - Generate features for all alleles. - - Parameters - ---------- - predictions_df : pd.DataFrame - The predictions DataFrame. - features_df : pd.DataFrame - The features DataFrame to update. - - Returns - ------- - pd.DataFrame - Updated features DataFrame with all allele features: - - Peptide: Original peptide sequence - - netmhciipan_score_{allele}: Raw binding score for each allele - - netmhciipan_affinity_{allele}: Binding affinity for each allele - - netmhciipan_percentile_rank_{allele}: Percentile rank for each allele - """ - logger.info("Generating features for all alleles.") - - for allele in self.alleles: - logger.info(f"Adding scores for allele {allele}.") - allele_df = predictions_df[predictions_df["allele"] == allele].copy() - - if allele_df.empty: - logger.warning( - f"No prediction results found for allele {allele}. Filling with NaN." - ) - allele_features = pd.DataFrame( - { - "Peptide": self.peptides, - f"netmhciipan_score_{allele}": [pd.NA] * len(self.peptides), - f"netmhciipan_affinity_{allele}": [pd.NA] * len(self.peptides), - f"netmhciipan_percentile_rank_{allele}": [pd.NA] - * len(self.peptides), - } - ) - else: - allele_df = allele_df.rename( - columns={ - "score": f"netmhciipan_score_{allele}", - "affinity": f"netmhciipan_affinity_{allele}", - "percentile_rank": f"netmhciipan_percentile_rank_{allele}", - } - ) - - allele_features = allele_df[ - [ - "Peptide", - f"netmhciipan_score_{allele}", - f"netmhciipan_affinity_{allele}", - f"netmhciipan_percentile_rank_{allele}", - ] - ] - - features_df = features_df.merge(allele_features, on="Peptide", how="left") - - logger.info("Added scores for all alleles.") - return features_df
- - -
-[docs] - def _generate_best_allele_features( - self, predictions_df: pd.DataFrame, features_df: pd.DataFrame - ) -> pd.DataFrame: - """ - Generate features for the best allele. - - Parameters - ---------- - predictions_df : pd.DataFrame - The predictions DataFrame. - features_df : pd.DataFrame - The features DataFrame to update. - - Returns - ------- - pd.DataFrame - Updated features DataFrame with best allele features: - - Peptide: Original peptide sequence - - netmhciipan_best_allele: Best binding allele - - netmhciipan_best_score: Best binding score - - netmhciipan_best_affinity: Best binding affinity - - netmhciipan_best_percentile_rank: Best percentile rank - - Notes - ----- - The best allele is determined by the lowest percentile rank. - """ - logger.info("Generating features for best allele.") - - valid_predictions = predictions_df.dropna(subset=["percentile_rank"]) - - if valid_predictions.empty: - logger.warning("No valid predictions available to select the best allele.") - best_allele_features = pd.DataFrame( - { - "Peptide": self.peptides, - "netmhciipan_best_allele": ["Unknown"] * len(self.peptides), - "netmhciipan_best_score": [pd.NA] * len(self.peptides), - "netmhciipan_best_affinity": [pd.NA] * len(self.peptides), - "netmhciipan_best_percentile_rank": [pd.NA] * len(self.peptides), - } - ) - else: - # Find the index of the minimum percentile rank for each peptide - idx = valid_predictions.groupby("Peptide")["percentile_rank"].idxmin() - - best_allele_features = valid_predictions.loc[idx].rename( - columns={ - "allele": "netmhciipan_best_allele", - "score": "netmhciipan_best_score", - "affinity": "netmhciipan_best_affinity", - "percentile_rank": "netmhciipan_best_percentile_rank", - } - ) - - best_allele_features = best_allele_features[ - [ - "Peptide", - "netmhciipan_best_allele", - "netmhciipan_best_score", - "netmhciipan_best_affinity", - "netmhciipan_best_percentile_rank", - ] - ] - - # Handle missing peptides - missing_peptides = set(self.peptides) - set(best_allele_features["Peptide"]) - - if missing_peptides: - logger.warning( - f"Found {len(missing_peptides)} peptides with no best allele prediction." - ) - missing_features = pd.DataFrame( - { - "Peptide": list(missing_peptides), - "netmhciipan_best_allele": ["Unknown"] * len(missing_peptides), - "netmhciipan_best_score": [pd.NA] * len(missing_peptides), - "netmhciipan_best_affinity": [pd.NA] * len(missing_peptides), - "netmhciipan_best_percentile_rank": [pd.NA] - * len(missing_peptides), - } - ) - best_allele_features = pd.concat( - [best_allele_features, missing_features], ignore_index=True - ) - - features_df = features_df.merge(best_allele_features, on="Peptide", how="left") - logger.info("Added best allele information.") - - return features_df
- - -
-[docs] - def _fill_missing_values(self, features_df: pd.DataFrame) -> pd.DataFrame: - """ - Fill missing values in the features DataFrame. - - Parameters - ---------- - features_df : pd.DataFrame - The features DataFrame to update. - - Returns - ------- - pd.DataFrame - Updated features DataFrame with filled missing values. - - Notes - ----- - This method: - 1. Fills best allele string values with 'Unknown' - 2. Fills numeric values with median for all allele features - 3. Fills numeric values for best allele features with median - """ - logger.info("Filling missing values in the features DataFrame.") - - # Fill best allele string values - if "netmhciipan_best_allele" in features_df.columns: - features_df["netmhciipan_best_allele"].fillna("Unknown", inplace=True) - - # Fill numeric values with median for all allele features - if self.mode == "all": - for allele in self.alleles: - for metric in ["score", "affinity", "percentile_rank"]: - col = f"netmhciipan_{metric}_{allele}" - if col in features_df.columns: - median_value = features_df[col].median() - features_df[col].fillna(median_value, inplace=True) - - # Fill numeric values for best allele features - for metric in ["best_score", "best_affinity", "best_percentile_rank"]: - col = f"netmhciipan_{metric}" - if col in features_df.columns and features_df[col].isna().any(): - median_value = features_df[col].median() - # If all values are NA, median will be NA, so use 0 instead - median_value = 0 if pd.isna(median_value) else median_value - features_df[col].fillna(median_value, inplace=True) - - logger.info("Filled missing values in the features DataFrame.") - return features_df
- - -
-[docs] - def predictions_to_dataframe(self) -> pd.DataFrame: - """ - Convert the predictions to a DataFrame. - - Returns - ------- - pd.DataFrame - DataFrame containing the predictions. - - Raises - ------ - ValueError - If no predictions are available. - """ - if self.predictions is None: - raise ValueError( - "No predictions available. Please run 'generate_features' first." - ) - return self.predictions
- - - @property - def raw_predictions(self) -> pd.DataFrame: - """ - Return the raw prediction results from NetMHCIIpan. - - Returns - ------- - pd.DataFrame - Raw prediction results DataFrame containing: - - peptide: Cleaned peptide sequence - - allele: MHC allele - - score: Raw binding score - - affinity: Binding affinity in nM - - percentile_rank: Percentile rank - """ - if self._raw_predictions is None: - self._predict() - return self._raw_predictions - -
-[docs] - def get_raw_predictions(self) -> pd.DataFrame: - """ - Get the raw prediction results DataFrame from NetMHCIIpan. - - Returns - ------- - pd.DataFrame - Raw prediction results DataFrame containing: - - peptide: Cleaned peptide sequence - - allele: MHC allele - - score: Raw binding score - - affinity: Binding affinity in nM - - percentile_rank: Percentile rank - """ - return self.raw_predictions
- - -
-[docs] - def save_raw_predictions(self, file_path: str, **kwargs) -> None: - """ - Save the raw prediction results to a file. - - Parameters - ---------- - file_path : str - Path to save the file. - **kwargs : dict - Additional parameters passed to pandas.DataFrame.to_csv. - If 'index' is not specified, it defaults to False. - - Notes - ----- - This method saves the raw predictions DataFrame to a CSV file. - The DataFrame includes: - - peptide: Cleaned peptide sequence - - allele: MHC allele - - score: Raw binding score - - affinity: Binding affinity in nM - - percentile_rank: Percentile rank - """ - if "index" not in kwargs: - kwargs["index"] = False - if self.raw_predictions is not None: - self.raw_predictions.to_csv(file_path, **kwargs) - logger.info(f"Raw prediction results saved to: {file_path}") - else: - logger.warning("No raw prediction results available to save.")
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/feature_generator/netMHCpan.html b/docs/source/_build/html/_modules/optimhc/feature_generator/netMHCpan.html deleted file mode 100644 index 6a439f2..0000000 --- a/docs/source/_build/html/_modules/optimhc/feature_generator/netMHCpan.html +++ /dev/null @@ -1,825 +0,0 @@ - - - - - - - - optimhc.feature_generator.netMHCpan — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for optimhc.feature_generator.netMHCpan

-# feature_generators/netmhcpan_feature_generator.py
-
-# TODO: Except 'best' mode, the other modes seems to be not working properly. We need to investigate this issue.
-
-from .base_feature_generator import BaseFeatureGenerator
-import pandas as pd
-from mhctools import NetMHCpan41
-from typing import List, Dict, Optional
-from optimhc import utils
-import logging
-from multiprocessing import Pool, cpu_count
-from functools import partial
-from tqdm import tqdm
-
-logger = logging.getLogger(__name__)
-
-# Helper function for multiprocessing
-
-[docs] -def _predict_peptide_chunk( - peptides_chunk: List[str], alleles: List[str] -) -> pd.DataFrame: - """ - Predict NetMHCpan scores for a chunk of peptides. - - Parameters - ---------- - peptides_chunk : List[str] - List of peptide sequences. - alleles : List[str] - List of MHC allele names. - - Returns - ------- - pd.DataFrame - DataFrame containing predictions: - - peptide: Peptide sequence - - allele: MHC allele - - score: Raw binding score - - affinity: Binding affinity in nM - - percentile_rank: Percentile rank - """ - predictor = NetMHCpan41(alleles=alleles) - results = predictor.predict_peptides(peptides_chunk) - return results.to_dataframe()
- - - -
-[docs] -class NetMHCpanFeatureGenerator(BaseFeatureGenerator): - """ - Generate NetMHCpan features for peptides based on specified MHC class I alleles. - - This generator calculates NetMHCpan binding predictions for each peptide against - the provided MHC class I alleles. - - Parameters - ---------- - peptides : List[str] - List of peptide sequences. - alleles : List[str] - List of MHC allele names (e.g., ['HLA-A*02:01', 'HLA-B*07:02']). - mode : str, optional - Mode of feature generation. Options: - - 'best': Return only the best allele information for each peptide. - - 'all': Return predictions for all alleles with allele-specific suffixes plus best allele info. - Default is 'best'. - remove_pre_nxt_aa : bool, optional - Whether to include the previous and next amino acids in peptides. - If True, remove them. Default is True. - remove_modification : bool, optional - Whether to include modifications in peptides. - If True, remove them. Default is True. - n_processes : int, optional - Number of processes to use for multiprocessing. - Default is 1 (no multiprocessing). - show_progress : bool, optional - Whether to display a progress bar. Default is False. - - Notes - ----- - The generated features include: - - netmhcpan_score: Raw binding score - - netmhcpan_affinity: Binding affinity in nM - - netmhcpan_percentile_rank: Percentile rank of the binding score - """ - - MIN_PEPTIDE_LENGTH = 8 - MAX_PEPTIDE_LENGTH = 30 - CHUNKSIZE = 250 - - def __init__( - self, - peptides: List[str], - alleles: List[str], - mode: str = "best", - remove_pre_nxt_aa: bool = False, - remove_modification: bool = True, - n_processes: int = 1, - show_progress: bool = False, - *args, - **kwargs, - ): - if mode not in ["best", "all"]: - raise ValueError("Mode must be one of 'best' or 'all'.") - - self.peptides = peptides - self.alleles = alleles - self.mode = mode - if len(alleles) == 1: - self.mode = "best" - logger.info("Only one allele provided. Switching to 'best' mode.") - self.remove_pre_nxt_aa = remove_pre_nxt_aa - self.remove_modification = remove_modification - self.n_processes = min(n_processes, cpu_count()) - self.show_progress = show_progress - self.predictor = NetMHCpan41(alleles=self.alleles) - self.predictions = None - self._raw_predictions = None - logger.info( - f"Initialized NetMHCpanFeatureGenerator with {len(peptides)} peptides, alleles: {alleles}, mode: {mode}, n_processes: {self.n_processes}, show_progress: {self.show_progress}" - ) - - @property - def feature_columns(self) -> List[str]: - """ - Return the list of generated feature column names, determined by the mode. - Only includes numerical features, excluding any string features like allele names. - - Returns - ------- - List[str] - List of feature column names: - - For 'all' mode: netmhcpan_score_{allele}, netmhcpan_affinity_{allele}, - netmhcpan_percentile_rank_{allele} for each allele - - For both modes: netmhcpan_best_score, netmhcpan_best_affinity, - netmhcpan_best_percentile_rank - """ - columns = [] - if self.mode == "all": - allele_specific = [] - for allele in self.alleles: - allele_specific.extend( - [ - f"netmhcpan_score_{allele}", - f"netmhcpan_affinity_{allele}", - f"netmhcpan_percentile_rank_{allele}", - ] - ) - columns.extend(allele_specific) - - # Both 'best' and 'all' modes include best allele numerical information - columns.extend( - [ - "netmhcpan_best_score", - "netmhcpan_best_affinity", - "netmhcpan_best_percentile_rank", - ] - ) - return columns - - @property - def id_column(self) -> List[str]: - """ - Return the list of input columns required for the feature generator. - - Returns - ------- - List[str] - List of input column names. - """ - return ["Peptide"] - -
-[docs] - def _preprocess_peptides(self, peptide: str) -> str: - """ - Preprocess the input peptide by removing flanking amino acids, - modifications, and replacing non-standard amino acids. - - Parameters - ---------- - peptide : str - The original peptide sequence. - - Returns - ------- - str - The preprocessed peptide sequence. - """ - if self.remove_pre_nxt_aa: - peptide = utils.remove_pre_and_nxt_aa(peptide) - if self.remove_modification: - peptide = utils.remove_modifications(peptide) - # Replace any non-standard amino acids if necessary - peptide = peptide.replace("U", "C") - return peptide
- - -
-[docs] - def _predict_multiprocessing(self, peptides_to_predict: List[str]) -> pd.DataFrame: - """ - Run NetMHCpan predictions using multiprocessing. - - Parameters - ---------- - peptides_to_predict : List[str] - List of peptides to predict. - - Returns - ------- - pd.DataFrame - DataFrame containing the prediction results: - - peptide: Peptide sequence - - allele: MHC allele - - score: Raw binding score - - affinity: Binding affinity in nM - - percentile_rank: Percentile rank - - Notes - ----- - This method: - 1. Splits peptides into chunks - 2. Processes chunks in parallel - 3. Combines results into a single DataFrame - """ - logger.info("Running NetMHCpan predictions with multiprocessing.") - chunksize = min( - NetMHCpanFeatureGenerator.CHUNKSIZE, - max(1, len(peptides_to_predict) // self.n_processes), - ) - func = partial(_predict_peptide_chunk, alleles=self.alleles) - - with Pool(processes=self.n_processes) as pool: - if self.show_progress: - results = list( - tqdm( - pool.imap( - func, - [ - peptides_to_predict[i : i + chunksize] - for i in range(0, len(peptides_to_predict), chunksize) - ], - ), - total=(len(peptides_to_predict) + chunksize - 1) // chunksize, - desc="Predicting NetMHCpan", - ) - ) - else: - results = pool.map( - func, - [ - peptides_to_predict[i : i + chunksize] - for i in range(0, len(peptides_to_predict), chunksize) - ], - ) - - netmhcpan_results = pd.concat(results, ignore_index=True) - # Save the raw prediction results - self._raw_predictions = netmhcpan_results.copy() - logger.info( - f"Completed multiprocessing predictions for {len(peptides_to_predict)} peptides." - ) - return netmhcpan_results
- - -
-[docs] - def _predict(self) -> pd.DataFrame: - """ - Run NetMHCpan predictions and cache the result. - - Returns - ------- - pd.DataFrame - DataFrame containing the prediction results: - - Peptide: Original peptide sequence - - peptide: Cleaned peptide sequence - - allele: MHC allele - - score: Raw binding score - - affinity: Binding affinity in nM - - percentile_rank: Percentile rank - - Notes - ----- - This method: - 1. Preprocesses peptides - 2. Filters peptides by length (8-30 amino acids) - 3. Runs predictions (with or without multiprocessing) - 4. Merges results with original peptides - """ - if self.predictions is not None: - logger.info("NetMHCpan predictions already exist. Skipping prediction.") - return self.predictions - - logger.info("Starting NetMHCpan predictions.") - self.predictions = pd.DataFrame(self.peptides, columns=["Peptide"]) - self.predictions["clean_peptide"] = self.predictions["Peptide"].apply( - self._preprocess_peptides - ) - - # Filter peptides that meet the length requirements - peptides_to_predict = ( - self.predictions[ - self.predictions["clean_peptide"].apply( - lambda x: NetMHCpanFeatureGenerator.MIN_PEPTIDE_LENGTH - <= len(x) - <= NetMHCpanFeatureGenerator.MAX_PEPTIDE_LENGTH - ) - ]["clean_peptide"] - .unique() - .tolist() - ) - - logger.info( - f"Found {len(peptides_to_predict)} unique peptides meeting the length requirements." - ) - - if self.n_processes > 1: - netmhcpan_results = self._predict_multiprocessing(peptides_to_predict) - else: - netmhcpan_results = self.predictor.predict_peptides( - peptides_to_predict - ).to_dataframe() - # If not using multiprocessing, save raw prediction results here - self._raw_predictions = netmhcpan_results.copy() - - logger.info( - f"Predicted NetMHCpan results for {len(netmhcpan_results)} peptides." - ) - - self.predictions = self.predictions.merge( - netmhcpan_results, left_on="clean_peptide", right_on="peptide", how="left" - ) - self.predictions.drop(columns=["clean_peptide"], inplace=True) - - logger.info( - f"Completed NetMHCpan predictions for {len(peptides_to_predict)} peptides." - ) - return self.predictions
- - - @property - def raw_predictions(self) -> pd.DataFrame: - """ - Return the raw prediction results from NetMHCpan. - - Returns - ------- - pd.DataFrame - Raw prediction results DataFrame containing: - - peptide: Cleaned peptide sequence - - allele: MHC allele - - score: Raw binding score - - affinity: Binding affinity in nM - - percentile_rank: Percentile rank - """ - if self._raw_predictions is None: - self._predict() - return self._raw_predictions - -
-[docs] - def get_raw_predictions(self) -> pd.DataFrame: - """ - Get the raw prediction results DataFrame from NetMHCpan. - - Returns - ------- - pd.DataFrame - Raw prediction results DataFrame containing: - - peptide: Cleaned peptide sequence - - allele: MHC allele - - score: Raw binding score - - affinity: Binding affinity in nM - - percentile_rank: Percentile rank - """ - return self.raw_predictions
- - -
-[docs] - def save_raw_predictions(self, file_path: str, **kwargs) -> None: - """ - Save the raw prediction results to a file. - - Parameters - ---------- - file_path : str - Path to save the file. - **kwargs : dict - Additional parameters passed to pandas.DataFrame.to_csv. - If 'index' is not specified, it defaults to False. - - Notes - ----- - This method saves the raw predictions DataFrame to a CSV file. - The DataFrame includes: - - peptide: Cleaned peptide sequence - - allele: MHC allele - - score: Raw binding score - - affinity: Binding affinity in nM - - percentile_rank: Percentile rank - """ - if "index" not in kwargs: - kwargs["index"] = False - if self.raw_predictions is not None: - self.raw_predictions.to_csv(file_path, **kwargs) - logger.info(f"Raw prediction results saved to: {file_path}") - else: - logger.warning("No raw prediction results available to save.")
- - -
-[docs] - def generate_features(self) -> pd.DataFrame: - """ - Generate the final feature table with NetMHCpan features for each peptide. - - Returns - ------- - pd.DataFrame - DataFrame containing peptides and their predicted features: - - Peptide: Original peptide sequence - - For 'all' mode: netmhcpan_score_{allele}, netmhcpan_affinity_{allele}, - netmhcpan_percentile_rank_{allele} for each allele - - For both modes: netmhcpan_best_score, netmhcpan_best_affinity, - netmhcpan_best_percentile_rank - - Notes - ----- - The features generated depend on the mode: - - 'best': Only the best allele information for each peptide - - 'all': All allele predictions plus best allele information - - Missing values are handled consistently by filling with median values - for numeric columns. - """ - predictions_df = self._predict() - - features_df = pd.DataFrame({"Peptide": self.peptides}) - - # Generate allele-specific features if mode is 'all', otherwise generate best allele features - if self.mode == "all": - features_df = self._generate_all_allele_features( - predictions_df, features_df - ) - features_df = self._generate_best_allele_features(predictions_df, features_df) - - features_df = self._fill_missing_values(features_df) - - selected_columns = ["Peptide"] + self.feature_columns - logger.info(f"Final selected feature columns: {selected_columns}") - features_df = features_df[selected_columns] - - if features_df.isna().sum().sum() > 0: - logger.warning( - "NaN values still exist in the generated features after filling with median/mode values." - ) - - return features_df
- - -
-[docs] - def _generate_all_allele_features( - self, predictions_df: pd.DataFrame, features_df: pd.DataFrame - ) -> pd.DataFrame: - """ - Generate features for all alleles. - - Parameters - ---------- - predictions_df : pd.DataFrame - The predictions DataFrame. - features_df : pd.DataFrame - The features DataFrame to update. - - Returns - ------- - pd.DataFrame - Updated features DataFrame with all allele features: - - Peptide: Original peptide sequence - - netmhcpan_score_{allele}: Raw binding score for each allele - - netmhcpan_affinity_{allele}: Binding affinity for each allele - - netmhcpan_percentile_rank_{allele}: Percentile rank for each allele - """ - logger.info("Generating features for all alleles.") - - for allele in self.alleles: - logger.info(f"Adding scores for allele {allele}.") - allele_df = predictions_df[predictions_df["allele"] == allele].copy() - - if allele_df.empty: - logger.warning( - f"No prediction results found for allele {allele}. Filling with NaN." - ) - allele_features = pd.DataFrame( - { - "Peptide": self.peptides, - f"netmhcpan_score_{allele}": [pd.NA] * len(self.peptides), - f"netmhcpan_affinity_{allele}": [pd.NA] * len(self.peptides), - f"netmhcpan_percentile_rank_{allele}": [pd.NA] - * len(self.peptides), - } - ) - else: - allele_df = allele_df.rename( - columns={ - "score": f"netmhcpan_score_{allele}", - "affinity": f"netmhcpan_affinity_{allele}", - "percentile_rank": f"netmhcpan_percentile_rank_{allele}", - } - ) - - allele_features = allele_df[ - [ - "Peptide", - f"netmhcpan_score_{allele}", - f"netmhcpan_affinity_{allele}", - f"netmhcpan_percentile_rank_{allele}", - ] - ] - - features_df = features_df.merge(allele_features, on="Peptide", how="left") - - logger.info("Added scores for all alleles.") - return features_df
- - -
-[docs] - def _generate_best_allele_features( - self, predictions_df: pd.DataFrame, features_df: pd.DataFrame - ) -> pd.DataFrame: - """ - Generate features for the best allele. - - Parameters - ---------- - predictions_df : pd.DataFrame - The predictions DataFrame. - features_df : pd.DataFrame - The features DataFrame to update. - - Returns - ------- - pd.DataFrame - Updated features DataFrame with best allele features: - - Peptide: Original peptide sequence - - netmhcpan_best_allele: Best binding allele - - netmhcpan_best_score: Best binding score - - netmhcpan_best_affinity: Best binding affinity - - netmhcpan_best_percentile_rank: Best percentile rank - - Notes - ----- - The best allele is determined by the lowest percentile rank. - """ - logger.info("Generating features for best allele.") - - valid_predictions = predictions_df.dropna(subset=["percentile_rank"]) - - if valid_predictions.empty: - logger.warning("No valid predictions available to select the best allele.") - best_allele_features = pd.DataFrame( - { - "Peptide": self.peptides, - "netmhcpan_best_allele": ["Unknown"] * len(self.peptides), - "netmhcpan_best_score": [pd.NA] * len(self.peptides), - "netmhcpan_best_affinity": [pd.NA] * len(self.peptides), - "netmhcpan_best_percentile_rank": [pd.NA] * len(self.peptides), - } - ) - else: - # Find the index of the minimum percentile rank for each peptide - idx = valid_predictions.groupby("Peptide")["percentile_rank"].idxmin() - - best_allele_features = valid_predictions.loc[idx].rename( - columns={ - "allele": "netmhcpan_best_allele", - "score": "netmhcpan_best_score", - "affinity": "netmhcpan_best_affinity", - "percentile_rank": "netmhcpan_best_percentile_rank", - } - ) - - best_allele_features = best_allele_features[ - [ - "Peptide", - "netmhcpan_best_allele", - "netmhcpan_best_score", - "netmhcpan_best_affinity", - "netmhcpan_best_percentile_rank", - ] - ] - - # Handle missing peptides - missing_peptides = set(self.peptides) - set(best_allele_features["Peptide"]) - - if missing_peptides: - logger.warning( - f"Found {len(missing_peptides)} peptides with no best allele prediction." - ) - missing_features = pd.DataFrame( - { - "Peptide": list(missing_peptides), - "netmhcpan_best_allele": ["Unknown"] * len(missing_peptides), - "netmhcpan_best_score": [pd.NA] * len(missing_peptides), - "netmhcpan_best_affinity": [pd.NA] * len(missing_peptides), - "netmhcpan_best_percentile_rank": [pd.NA] - * len(missing_peptides), - } - ) - best_allele_features = pd.concat( - [best_allele_features, missing_features], ignore_index=True - ) - - features_df = features_df.merge(best_allele_features, on="Peptide", how="left") - logger.info("Added best allele information.") - - return features_df
- - -
-[docs] - def _fill_missing_values(self, features_df: pd.DataFrame) -> pd.DataFrame: - """ - Fill missing values in the features DataFrame. - - Parameters - ---------- - features_df : pd.DataFrame - The features DataFrame to update. - - Returns - ------- - pd.DataFrame - Updated features DataFrame with filled missing values. - - Notes - ----- - This method: - 1. Fills best allele string values with 'Unknown' - 2. Fills numeric values with median for all allele features - 3. Fills numeric values for best allele features with median - """ - logger.info("Filling missing values in the features DataFrame.") - - # Fill best allele string values - if "netmhcpan_best_allele" in features_df.columns: - features_df["netmhcpan_best_allele"].fillna("Unknown", inplace=True) - - # Fill numeric values with median for all allele features - if self.mode == "all": - for allele in self.alleles: - for metric in ["score", "affinity", "percentile_rank"]: - col = f"netmhcpan_{metric}_{allele}" - if col in features_df.columns: - median_value = features_df[col].median() - features_df[col].fillna(median_value, inplace=True) - - # Fill numeric values for best allele features - for metric in ["best_score", "best_affinity", "best_percentile_rank"]: - col = f"netmhcpan_{metric}" - if col in features_df.columns and features_df[col].isna().any(): - median_value = features_df[col].median() - # If all values are NA, median will be NA, so use 0 instead - median_value = 0 if pd.isna(median_value) else median_value - features_df[col].fillna(median_value, inplace=True) - - logger.info("Filled missing values in the features DataFrame.") - return features_df
- - -
-[docs] - def predictions_to_dataframe(self) -> pd.DataFrame: - """ - Convert the predictions to a DataFrame. - - Returns - ------- - pd.DataFrame - DataFrame containing the predictions. - - Raises - ------ - ValueError - If no predictions are available. - """ - if self.predictions is None: - raise ValueError( - "No predictions available. Please run 'generate_features' first." - ) - return self.predictions
-
- - - # def get_best_allele(self) -> pd.DataFrame: - # """ - # Return the best allele (with the lowest percentile rank) for each peptide across all alleles. - - # Returns: - # pd.DataFrame: DataFrame containing the best allele information. - # """ - # logger.info("Getting best allele information.") - # predictions_df = self._predict() - - # features_df = pd.DataFrame({'Peptide': self.peptides}) - # best_features_df = self._generate_best_allele_features(predictions_df, features_df) - # best_columns = ['Peptide', 'netmhcpan_best_allele', 'netmhcpan_best_score', - # 'netmhcpan_best_affinity', 'netmhcpan_best_percentile_rank'] - # best_allele_df = best_features_df[best_columns] - # best_allele_df = self._fill_missing_values(best_allele_df) - - # logger.info(f"Generated best allele information for {len(best_allele_df)} peptides.") - # return best_allele_df -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/feature_generator/overlapping_peptide.html b/docs/source/_build/html/_modules/optimhc/feature_generator/overlapping_peptide.html deleted file mode 100644 index a71f870..0000000 --- a/docs/source/_build/html/_modules/optimhc/feature_generator/overlapping_peptide.html +++ /dev/null @@ -1,1067 +0,0 @@ - - - - - - - - optimhc.feature_generator.overlapping_peptide — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for optimhc.feature_generator.overlapping_peptide

-# feature_generator/overlapping_peptide.py
-
-import logging
-import pandas as pd
-import numpy as np
-import networkx as nx
-from collections import defaultdict
-from typing import List, Dict, Union, Tuple
-from scipy.stats import entropy
-from tqdm import tqdm
-from optimhc import utils
-from optimhc.feature_generator.base_feature_generator import BaseFeatureGenerator
-from optimhc.psm_container import PsmContainer
-
-logger = logging.getLogger(__name__)
-
-
-
-[docs] -class OverlappingPeptideFeatureGenerator(BaseFeatureGenerator): - """ - Generates features based on peptide sequence overlaps using the Overlap-Layout-Consensus (OLC) algorithm. - - This generator constructs an overlap graph of peptides, removes transitive edges, simplifies the graph to contigs, - and computes features such as the number of overlaps, log-transformed overlap counts, overlap ranks, and contig lengths. - It also filters out peptides with low entropy or outlier lengths before processing. - Additionally, it records detailed information about brother peptides and contigs, accessible via the get_all_data method. - - Parameters - ---------- - peptides : list of str - List of peptide sequences. - min_overlap_length : int, optional - Minimum required overlap length for peptides to be considered overlapping. Default is 6. - min_length : int, optional - Minimum peptide length to include in processing. Default is 7. - max_length : int, optional - Maximum peptide length to include in processing. Default is 60. - min_entropy : float, optional - Minimum Shannon entropy for peptides to include in processing. Default is 0. - fill_missing : str, optional - Method to fill missing values for filtered peptides. Options are 'median' or 'zero'. Default is 'median'. - remove_pre_nxt_aa : bool, optional - Whether to remove the preceding and following amino acids from peptides. Default is False. - remove_modification : bool, optional - Whether to remove modifications from peptides. Default is True. - - Attributes - ---------- - original_peptides : list of str - Original list of peptide sequences. - min_overlap_length : int - Minimum required overlap length. - min_length : int - Minimum peptide length. - max_length : int - Maximum peptide length. - min_entropy : float - Minimum Shannon entropy. - fill_missing : str - Method to fill missing values. - remove_pre_nxt_aa : bool - Whether to remove preceding and following amino acids. - remove_modification : bool - Whether to remove modifications. - filtered_peptides : list of str - List of peptides after filtering. - filtered_indices : list of int - Indices of filtered peptides. - peptide_to_index : dict of str to int - Mapping of peptides to their indices. - overlap_data : pd.DataFrame - DataFrame containing overlap data. - peptide_to_contig : dict of str to int - Mapping of peptides to their contig indices. - assembled_contigs : list of dict - List of assembled contigs. - full_data : pd.DataFrame - Full data including brother peptides and contig information. - _overlap_graph : nx.DiGraph - Overlap graph. - _simplified_graph : nx.DiGraph - Simplified graph with transitive edges removed. - - Notes - ----- - Key Data Structures: - 1. contigs: List[List[str]] - - Represents non-branching paths in the overlap graph - - Each inner list contains peptide sequences that form a continuous chain - - Example: [['PEPTIDE1', 'PEPTIDE2'], ['PEPTIDE3']] - - 2. assembled_contigs: List[Dict] - - Contains the assembled sequences and their constituent peptides - - Each dictionary has two keys: - 'sequence': The merged/assembled sequence of overlapping peptides - 'peptides': List of peptides that were used to build this contig - - Example: [ - { - 'sequence': 'LONGPEPTIDESEQUENCE', - 'peptides': ['LONGPEP', 'PEPTIDE', 'SEQUENCE'] - }, - { - 'sequence': 'SINGLEPEPTIDE', - 'peptides': ['SINGLEPEPTIDE'] - } - ] - - 3. peptide_to_contig: Dict[str, int] - - Maps each peptide to its contig index in assembled_contigs - - Key: peptide sequence - - Value: index of the contig containing this peptide - - Example: { - 'LONGPEP': 0, - 'PEPTIDE': 0, - 'SEQUENCE': 0, - 'SINGLEPEPTIDE': 1 - } - - 4. overlap_graph (_overlap_graph): nx.DiGraph - - Directed graph representing all possible overlaps between peptides - - Nodes: peptide sequences - - Edges: overlaps between peptides - - Edge weights: length of overlap - - 5. simplified_graph (_simplified_graph): nx.DiGraph - - Simplified version of overlap_graph with transitive edges removed - - Used for final contig assembly - - More efficient representation of essential overlaps - """ - - def __init__( - self, - peptides: List[str], - min_overlap_length: int = 6, - min_length: int = 7, - max_length: int = 60, - min_entropy: float = 0, - fill_missing: str = "median", # 'median' or 'zero' - remove_pre_nxt_aa: bool = False, - remove_modification: bool = True, - *args, - **kwargs, - ): - self.original_peptides = peptides - self.min_overlap_length = min_overlap_length - self.min_length = min_length - self.max_length = max_length - self.min_entropy = min_entropy - self.fill_missing = fill_missing.lower() - self.remove_pre_nxt_aa = remove_pre_nxt_aa - self.remove_modification = remove_modification - self.filtered_peptides = [] - self.filtered_indices = [] - self.peptide_to_index = {} - self.overlap_data = None - self.peptide_to_contig = {} - self.assembled_contigs = [] - self.full_data = None - self._overlap_graph = None - self._simplified_graph = None - logger.info( - f"Initialized OverlappingPeptideFeatureGenerator with {len(peptides)} peptides and minimum overlap length: {min_overlap_length}" - ) - logger.info( - f"remove_pre_nxt_aa: {remove_pre_nxt_aa}, remove_modification: {remove_modification}" - ) - logger.info( - f"Peptide filtering parameters - min_length: {min_length}, max_length: {max_length}, min_entropy: {min_entropy}" - ) - - @property - def id_column(self) -> List[str]: - """ - Returns a list of input columns required for the feature generator. - - Returns: - List[str]: List of input columns. - """ - return ["Peptide"] - - @property - def feature_columns(self) -> List[str]: - """Returns the feature column names.""" - """ - return ['contig_member_count', 'log_contig_member_count', 'contig_member_rank', 'log_contig_member_rank', - 'contig_seq_length_diff', 'contig_length', 'contig_extension_ratio'] - """ - return [ - "contig_member_count", - "contig_extension_ratio", - "contig_member_rank", - "contig_length", - ] - - @property - def overlap_graph(self) -> nx.DiGraph: - """Returns the overlap graph.""" - return self._overlap_graph - - @property - def simplified_graph(self) -> nx.DiGraph: - """Returns the layout graph.""" - return self._simplified_graph - - @property - def contigs(self) -> List[Dict]: - """Returns the assembled contigs.""" - return self.assembled_contigs - -
-[docs] - def _shannon_entropy(self, sequence: str) -> float: - """ - Calculate the Shannon entropy of a peptide sequence. - - Parameters: - sequence (str): Peptide sequence. - - Returns: - float: Shannon entropy value. - """ - bases = list(set(sequence)) - freq_list = [sequence.count(base) / len(sequence) for base in bases] - return entropy(freq_list, base=2)
- - - def _preprocess_peptides(self, peptide: str) -> str: - if self.remove_pre_nxt_aa: - peptide = utils.remove_pre_and_nxt_aa(peptide) - if self.remove_modification: - peptide = utils.remove_modifications(peptide) - # U -> C - peptide = peptide.replace("U", "C") - return peptide - -
-[docs] - def _filter_peptides(self, peptides: List[str]) -> List[str]: - """ - Filter out peptides based on length and entropy. - - Parameters: - peptides (List[str]): List of peptide sequences. - - Returns: - List[str]: Filtered list of peptide sequences. - """ - filtered_peptides = [] - for peptide in peptides: - if len(peptide) < self.min_length or len(peptide) > self.max_length: - continue - entropy_val = self._shannon_entropy(peptide) - if entropy_val < self.min_entropy: - continue - filtered_peptides.append(peptide) - logger.info( - f"Filtered out {len(peptides) - len(filtered_peptides)} peptides based on length and entropy." - ) - logger.info(f"Remaining peptides: {len(filtered_peptides)}") - return filtered_peptides
- - -
-[docs] - def _construct_prefix_index( - self, peptides: List[str], min_overlap_length: int - ) -> Dict[str, List[int]]: - """ - Construct an index of prefixes for all peptides. - - Parameters: - peptides (List[str]): List of peptide sequences. - - Returns: - Dict[str, List[int]]: Dictionary mapping prefixes to list of peptide indices. - """ - prefix_index = defaultdict(list) - for idx, seq in enumerate(peptides): - seq_len = len(seq) - # Store prefixes of length from min_overlap_length up to seq_len - for i in range(min_overlap_length, seq_len + 1): - prefix = seq[:i] - prefix_index[prefix].append(idx) - return prefix_index
- - -
-[docs] - def _build_overlap_graph( - self, peptides: List[str], prefix_index: Dict[str, List[int]] - ) -> nx.DiGraph: - """ - Build the overlap graph from the list of peptides. - - Parameters: - peptides (List[str]): List of peptide sequences. - prefix_index (Dict[str, List[int]]): Index of prefixes. - - Returns: - nx.DiGraph: Overlap graph. - """ - G = nx.DiGraph() - for idx, peptide in enumerate(peptides): - seq_len = len(peptide) - G.add_node(peptide) - # Iterate over possible suffix lengths - for i in range(self.min_overlap_length, seq_len): - suffix = peptide[-i:] - if suffix in prefix_index: - for matching_idx in prefix_index[suffix]: - if matching_idx != idx: - matching_peptide = peptides[matching_idx] - overlap_length = len(suffix) - # Ensure the overlap is the maximal possible - existing_weight = ( - G[peptide][matching_peptide]["weight"] - if G.has_edge(peptide, matching_peptide) - else 0 - ) - if overlap_length > existing_weight: - G.add_edge( - peptide, matching_peptide, weight=overlap_length - ) - # Handle the case where the entire peptide matches the prefix of another peptide - suffix = peptide # Full peptide as suffix - if suffix in prefix_index: - for matching_idx in prefix_index[suffix]: - if matching_idx != idx: - matching_peptide = peptides[matching_idx] - G.add_edge(peptide, matching_peptide, weight=seq_len) - return G
- - -
-[docs] - def _remove_transitive_edges(self, G: nx.DiGraph) -> nx.DiGraph: - """ - Remove transitive edges from the overlap graph G. - - Parameters: - G (nx.DiGraph): Overlap graph. - - Returns: - nx.DiGraph: Simplified graph with transitive edges removed. - """ - logger.info("Removing transitive edges from the overlap graph.") - - ## if A->B->C, A->C, then remove A->C - to_remove = [] - for u in G: - for v in G.successors(u): - for w in G.successors(v): - if G.has_edge(u, w): - to_remove.append((u, w)) - G.remove_edges_from(to_remove) - - to_remove = [] - for u in G: - for v in G.successors(u): - for w in G.successors(v): - for x in G.successors(w): - if G.has_edge(u, x): - to_remove.append((u, x)) - G.remove_edges_from(to_remove) - - return G
- - -
-[docs] - def _simplify_graph_to_contigs(self, G: nx.DiGraph) -> List[List[str]]: - """ - Simplify the graph by finding non-branching paths (contigs). - - Parameters: - G (nx.DiGraph): Overlap graph. - - Returns: - List[List[str]]: List of contigs, each contig is a list of peptide sequences. - """ - logger.info("Simplifying graph to contigs (non-branching paths).") - contigs = [] - visited = set() - - for node in G.nodes(): - # Check if node is a potential starting point of a contig - if G.in_degree(node) != 1 or G.out_degree(node) != 1: - if node not in visited: - contig = [node] - visited.add(node) - # Extend contig forward - current_node = node - while G.out_degree(current_node) == 1: - successor = next(G.successors(current_node)) - if G.in_degree(successor) == 1 and successor not in visited: - contig.append(successor) - visited.add(successor) - current_node = successor - else: - break - contigs.append(contig) - - logger.info(f"Found {len(contigs)} contigs.") - return contigs
- - -
-[docs] - def _assemble_contigs(self, contigs: List[List[str]], G: nx.DiGraph) -> List[Dict]: - """ - Assemble sequences for each contig. - - Parameters: - contigs (List[List[str]]): List of contigs. - - Returns: - List[Dict]: List of assembled contigs with sequence and peptides. - """ - logger.info("Assembling contigs from peptides.") - assembled_contigs = [] - for contig in contigs: - if len(contig) == 1: - # Single node contig - assembled_seq = contig[0] - else: - assembled_seq = contig[0] - for i in range(1, len(contig)): - # Get the overlap length between consecutive peptides - overlap_length = G[contig[i - 1]][contig[i]]["weight"] - # Append non-overlapping part of the next peptide - assembled_seq += contig[i][overlap_length:] - assembled_contigs.append({"sequence": assembled_seq, "peptides": contig}) - return assembled_contigs
- - -
-[docs] - def _map_peptides_to_contigs(self, assembled_contigs: List[Dict]): - """ - Map each peptide to its contig. - - Parameters: - assembled_contigs (List[Dict]): List of assembled contigs. - """ - logger.info("Mapping peptides to contigs.") - peptide_to_contig = {} - for idx, contig in enumerate(assembled_contigs): - peptides_in_contig = contig["peptides"] - for peptide in peptides_in_contig: - peptide_to_contig[peptide] = idx - return peptide_to_contig
- - -
-[docs] - def _remove_redundant_peptides( - self, peptides: List[str] - ) -> Tuple[List[str], Dict[str, str]]: - """ - Remove peptides that are fully contained in other peptides. - - Parameters: - peptides (List[str]): List of peptide sequences. - - Returns: - accepted_peptides (List[str]): List of accepted peptides not redundant (largest container peptides). - redundant_mapping (Dict[str, str]): Mapping of redundant peptide to its largest container peptide. - """ - sorted_peptides = sorted(peptides, key=len, reverse=True) - min_pep_len = len(sorted_peptides[-1]) - peptides_set = set(sorted_peptides) - accepted_set = set() - redundant_mapping = {} - for pep in sorted_peptides: - if pep not in peptides_set: - continue - if pep in accepted_set: - continue - accepted_set.add(pep) - pep_len = len(pep) - for L in range(min_pep_len, pep_len + 1): - for i in range(0, pep_len - L + 1): - sub_pep = pep[i : i + L] - if sub_pep == pep: - continue - if sub_pep in peptides_set: - redundant_mapping[sub_pep] = pep - peptides_set.remove(sub_pep) - - logger.info(f"Remove {len(redundant_mapping)} redundant peptides.") - logger.info(f"Accepted {len(accepted_set)} non-redundant peptides.") - return list(accepted_set), redundant_mapping
- - -
-[docs] - def _build_full_contig_map(self, peptides: List[str]) -> Dict[int, List[str]]: - """ - Build a mapping from contig index to list of peptides (both accepted and redundant). - - Parameters: - peptides (List[str]): Original list of peptides. - - Returns: - Dict[int, List[str]]: Mapping from contig index to list of peptides. - """ - full_contig_map = defaultdict(list) - for pep in peptides: - contig_idx = self.peptide_to_contig.get(pep, None) - if contig_idx is not None: - full_contig_map[contig_idx].append(pep) - return full_contig_map
- - -
-[docs] - def _calculate_overlap_contig_features(self, peptides: List[str]) -> pd.DataFrame: - """ - Calculate overlap and contig related features for peptides. - - This method computes the overlap graph, simplifies it to contigs, assembles contigs, - and generates features for each peptide. It also handles redundant peptides by mapping them - to their container peptides. - - Parameters: - peptides (List[str]): List of peptide sequences. - - Returns: - pd.DataFrame: DataFrame containing the computed features. - """ - accepted_peptides, redundant_mapping = self._remove_redundant_peptides(peptides) - - logger.info("Constructing prefix index...") - prefix_index = self._construct_prefix_index( - accepted_peptides, self.min_overlap_length - ) - - logger.info("Building overlap graph...") - self._overlap_graph = self._build_overlap_graph(accepted_peptides, prefix_index) - - logger.info( - f"Overlap graph has {self._overlap_graph.number_of_nodes()} nodes and {self._overlap_graph.number_of_edges()} edges." - ) - self._simplified_graph = self._remove_transitive_edges(self._overlap_graph) - - logger.info( - f"Simplified graph has {self._simplified_graph.number_of_nodes()} nodes and {self._simplified_graph.number_of_edges()} edges." - ) - contigs = self._simplify_graph_to_contigs(self._simplified_graph) - - logger.info(f"Found {len(contigs)} contigs.") - self.assembled_contigs = self._assemble_contigs(contigs, self._simplified_graph) - self.peptide_to_contig = self._map_peptides_to_contigs(self.assembled_contigs) - - # Map redundant peptides to their container peptides - for redundant_peptide, container_peptide in redundant_mapping.items(): - if container_peptide not in self.peptide_to_contig: - logger.debug( - f"Container peptide {container_peptide} not found in contigs." - ) - logger.debug( - f"This may occur if the container peptide is a branching node in the overlap graph." - ) - logger.debug(f"Assigning {container_peptide} to its own contig.") - - new_contig_idx = len(self.assembled_contigs) - new_contig = { - "sequence": container_peptide, - "peptides": [container_peptide], - "full_contig_peptides": [container_peptide], - } - self.assembled_contigs.append(new_contig) - self.peptide_to_contig[container_peptide] = new_contig_idx - - self.peptide_to_contig[redundant_peptide] = self.peptide_to_contig[ - container_peptide - ] - - # Build a mapping from contig index to list of peptides (both accepted and redundant) - full_contig_map = self._build_full_contig_map(peptides) - - for contig_index, full_peptides in full_contig_map.items(): - self.assembled_contigs[contig_index]["full_contig_peptides"] = full_peptides - - # Compute features for each peptide in the filtered list - feature_list = [] - for pep in peptides: - contig_idx = self.peptide_to_contig.get(pep, None) - if contig_idx is not None: - full_count = len(full_contig_map[contig_idx]) - contig_member_count = full_count - contig_length = len(self.assembled_contigs[contig_idx]["sequence"]) - else: - contig_member_count = 0 - contig_length = len(pep) - feature_list.append( - { - "clean_peptide": pep, - "contig_member_count": contig_member_count, - "contig_length": contig_length, - } - ) - - features_df = pd.DataFrame(feature_list) - features_df["log_contig_member_count"] = features_df[ - "contig_member_count" - ].apply(lambda x: np.log(x + 1e-6)) - features_df["contig_member_rank"] = features_df["contig_member_count"].rank( - method="min", ascending=False - ) - features_df["log_contig_member_rank"] = features_df["contig_member_rank"].apply( - lambda x: np.log(x + 1e-6) - ) - features_df["contig_seq_length_diff"] = features_df[ - "contig_length" - ] - features_df["clean_peptide"].apply(len) - features_df["contig_extension_ratio"] = features_df[ - "contig_seq_length_diff" - ] / features_df["clean_peptide"].apply(len) - - return features_df
- - -
-[docs] - def _integrate_overlap_features(self) -> pd.DataFrame: - """ - Compute the features for each peptide, ensuring output aligns with input. - - This method preprocesses and filters peptides, calculates overlap and contig features, - maps them back to the original peptides, and fills missing values. - - Returns: - pd.DataFrame: DataFrame containing features for each peptide. - """ - if self.overlap_data is None: - # 1. Preprocess and filter peptides - self.overlap_data = pd.DataFrame( - self.original_peptides, columns=["Peptide"] - ) - self.overlap_data["clean_peptide"] = self.overlap_data["Peptide"].apply( - self._preprocess_peptides - ) - self.filtered_peptides = self._filter_peptides( - self.overlap_data["clean_peptide"].unique().tolist() - ) - - # 2. Compute overlaps and features for filtered peptides - features_df = self._calculate_overlap_contig_features( - self.filtered_peptides - ) - - # 3. Map features back to the original peptides - logger.info("Mapping features back to original peptides.") - self.overlap_data = self.overlap_data.merge( - features_df, on="clean_peptide", how="left" - ) - - # 4. Handle missing features for peptides that were filtered out - missing_counts = self.overlap_data["contig_member_count"].isna().sum() - logger.info( - f"Number of peptides with missing features (filtered out): {missing_counts}" - ) - if self.fill_missing == "median": - logger.info("Filling missing values with median.") - median_values = { - "contig_member_count": self.overlap_data[ - "contig_member_count" - ].median(), - "log_contig_member_count": self.overlap_data[ - "log_contig_member_count" - ].median(), - "contig_member_rank": self.overlap_data[ - "contig_member_rank" - ].median(), - "log_contig_member_rank": self.overlap_data[ - "log_contig_member_rank" - ].median(), - "contig_length": self.overlap_data["contig_length"].median(), - "contig_seq_length_diff": self.overlap_data[ - "contig_seq_length_diff" - ].median(), - "contig_extension_ratio": self.overlap_data[ - "contig_extension_ratio" - ].median(), - } - self.overlap_data.fillna(value=median_values, inplace=True) - elif self.fill_missing == "zero": - logger.info("Filling missing values with zero.") - self.overlap_data.fillna(value=0, inplace=True) - else: - logger.warning( - f"Invalid fill_missing option '{self.fill_missing}'. Defaulting to zero." - ) - self.overlap_data.fillna(value=0, inplace=True) - logger.info("Feature computation completed.") - else: - logger.info("Features have already been computed. Skipping recomputation.") - return self.overlap_data
- - -
-[docs] - def generate_features(self) -> pd.DataFrame: - """ - Generates features for peptide overlaps, including the count of overlapping peptides, contig length, - and log-transformed counts and ranks. - - Returns: - pd.DataFrame: DataFrame containing the features. - """ - features_df = self._integrate_overlap_features() - features_df = features_df[["Peptide"] + self.feature_columns] - logger.info(f"Generated overlap features for {len(features_df)} peptides.") - return features_df
- - -
-[docs] - def get_full_data(self) -> pd.DataFrame: - """ - Returns the full data including brother peptides and contig information for each peptide. - In the output, the lists of contig peptides and brother peptides include redundant peptides, - so that their counts match the corresponding peptide and contig_member_count. - - Returns: - pd.DataFrame: DataFrame containing peptides and their brother peptides and contigs. - """ - self._integrate_overlap_features() - if self.full_data is not None: - logger.info("Full data has already been computed. Returning cached data.") - return self.full_data - data_list = [] - - for peptide in tqdm(self.filtered_peptides): - contig_idx = self.peptide_to_contig.get(peptide, None) - if contig_idx is not None: - contig_info = self.assembled_contigs[contig_idx] - # Use full contig peptides (including redundant ones) if available - full_peptides = contig_info.get( - "full_contig_peptides", contig_info["peptides"] - ) - brother_peptides = [p for p in full_peptides if p != peptide] - data_list.append( - { - "clean_peptide": peptide, - "BrotherPeptides": brother_peptides, - "ContigSequence": contig_info["sequence"], - "ContigPeptides": full_peptides, - } - ) - - full_data_df = pd.DataFrame(data_list) - self.full_data = self.overlap_data.merge( - full_data_df, on="clean_peptide", how="left" - ) - return self.full_data
-
- - - -''' -# TODO: test - -def assign_brother_aggregated_feature( - psms: PsmContainer, - feature_columns: Union[str, List[str]], - overlapping_source: str, - source_name: str = 'OverlappingGroupFeatures' -) -> None: - """ - Assign aggregated features based on brother peptides to the PSMs. - - For PSMs with the same ContigSequence (brother peptides), compute the mean of specified features - and assign these aggregated features back to each PSM in the group. - If a PSM does not have a ContigSequence (no brothers), its new features will be set to the original values. - - Metadata in the PSM container: - { - "source_name": { - "metadata_field_1": "value1", - "metadata_field_2": "value2" - } - } - - Parameters: - psms (PsmContainer): PSM container containing the peptides and features. - feature_columns (Union[str, List[str]]): Name of the feature column(s) to aggregate. - overlapping_source (str): Source name of the overlapping peptide features. - source_name (str): Name of the new feature source. - - Returns: - None - """ - if isinstance(feature_columns, str): - feature_columns = [feature_columns] - psms_df = psms.psms - - if psms.metadata_column is None: - raise ValueError("The PSMs do not contain metadata.") - metadata = psms_df[psms.metadata_column] - print(metadata) - - - def get_overlapping_data(x): - try: - return x.get(overlapping_source, {}) - except AttributeError: - logger.error(f"Metadata for PSM {x} is not a dictionary.") - return {} - - def get_contig_sequence(x): - try: - return x.get('ContigSequence', None) - except AttributeError: - logger.error(f"Invalid metadata for PSM {x}.") - return None - - overlapping_data = metadata.apply(get_overlapping_data) - contig_sequences = overlapping_data.apply(get_contig_sequence) - print(overlapping_data) - print(contig_sequences) - - psms_df['ContigSequence'] = contig_sequences - - for feature in feature_columns: - if feature not in psms_df.columns: - raise ValueError(f"Feature column '{feature}' not found in PSMs.") - - grouped_mean = psms_df.groupby('ContigSequence')[feature_columns].mean().reset_index() - #grouped_sum = psms_df.groupby('ContigSequence')[feature_columns].sum().reset_index() - - """ - grouped = grouped_mean.merge(grouped_sum, - on='ContigSequence', - suffixes=('_brother_mean', '_brother_sum')) - """ - psms_with_agg = psms_df.merge(grouped_mean, - on='ContigSequence', - how='left', - suffixes=('', '_brother_mean')) - - - # use the original feature values if the aggregated values are missing - for feature in feature_columns: - mean_feature = feature + '_brother_mean' - sum_feature = feature + '_brother_sum' - psms_with_agg[mean_feature].fillna(psms_with_agg[feature], inplace=True) - psms_with_agg[sum_feature].fillna(psms_with_agg[feature], inplace=True) - - - agg_feature_columns = [] - for feature in feature_columns: - mean_feature = feature + '_brother_mean' - sum_feature = feature + '_brother_sum' - agg_feature_columns.append(mean_feature) - agg_feature_columns.append(sum_feature) - - new_features_df = psms_with_agg[agg_feature_columns] - new_features_df.columns = agg_feature_columns - - psms.add_features_by_index(new_features_df, source=source_name) - - -''' - - -
-[docs] -def assign_brother_aggregated_feature( - psms: PsmContainer, - feature_columns: Union[str, List[str]], - overlapping_source: str, - source_name: str = "OverlappingGroupFeatures", -) -> None: - """ - Assign aggregated features based on brother peptides to the PSMs. - - For PSMs with the same ContigSequence (brother peptides), compute the mean of specified features - and assign these aggregated features back to each PSM in the group. Additionally, compute - the sum as mean * (contig_member_count + 1). If a PSM does not have a ContigSequence (no brothers), - its new features will be set to the original values. - - Parameters: - psms (PsmContainer): PSM container containing the peptides and features. - feature_columns (Union[str, List[str]]): Name of the feature column(s) to aggregate. - overlapping_source (str): Source name of the overlapping peptide features. - source_name (str): Name of the new feature source. - - Returns: - None - """ - if isinstance(feature_columns, str): - feature_columns = [feature_columns] - psms_df = psms.psms - - if psms.metadata_column is None: - raise ValueError("The PSMs do not contain metadata.") - metadata = psms_df[psms.metadata_column] - - def get_overlapping_data(x): - if isinstance(x, dict): - return x.get(overlapping_source, {}) - else: - logger.warning(f"Invalid metadata entry: {x}") - return {} - - overlapping_data = metadata.apply(get_overlapping_data) - - def get_contig_sequence(x): - if isinstance(x, dict): - return x.get("ContigSequence", None) - else: - logger.warning(f"Invalid overlapping data entry: {x}") - return None - - contig_sequences = overlapping_data.apply(get_contig_sequence) - - psms_df["ContigSequence"] = contig_sequences - - if "contig_member_count" not in psms_df.columns: - raise ValueError("'contig_member_count' column not found in PSMs.") - - missing_features = [ - feature for feature in feature_columns if feature not in psms_df.columns - ] - if missing_features: - raise ValueError(f"Feature columns not found in PSMs: {missing_features}") - - grouped_mean = ( - psms_df.groupby("ContigSequence")[feature_columns].mean().reset_index() - ) - grouped_mean = grouped_mean.rename( - columns={feature: f"{feature}_contig_avg" for feature in feature_columns} - ) - - psms_with_agg = psms_df.merge(grouped_mean, on="ContigSequence", how="left") - - for feature in feature_columns: - mean_feature = f"{feature}_contig_avg" - sum_feature = f"{feature}_contig_sum" - psms_with_agg["contig_member_count"] = psms_with_agg[ - "contig_member_count" - ].fillna(0) - psms_with_agg[sum_feature] = psms_with_agg[mean_feature] * ( - psms_with_agg["contig_member_count"] - ) - psms_with_agg[sum_feature].fillna(psms_with_agg[feature], inplace=True) - - for feature in feature_columns: - mean_feature = f"{feature}_contig_avg" - psms_with_agg[mean_feature].fillna(psms_with_agg[feature], inplace=True) - - agg_feature_columns = [f"{feature}_contig_avg" for feature in feature_columns] + [ - f"{feature}_contig_sum" for feature in feature_columns - ] - - new_features_df = psms_with_agg[agg_feature_columns] - - psms.add_features_by_index(features_df=new_features_df, source=source_name)
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/feature_generator/spectra_similarity.html b/docs/source/_build/html/_modules/optimhc/feature_generator/spectra_similarity.html deleted file mode 100644 index 4e11b99..0000000 --- a/docs/source/_build/html/_modules/optimhc/feature_generator/spectra_similarity.html +++ /dev/null @@ -1,1355 +0,0 @@ - - - - - - - - optimhc.feature_generator.spectra_similarity — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for optimhc.feature_generator.spectra_similarity

-# feature_generator/spectra_similarity.py
-
-import logging
-import pandas as pd
-import numpy as np
-from typing import List, Dict, Tuple, Union, Optional
-import os
-from scipy.stats import pearsonr, spearmanr
-from scipy.spatial.distance import cosine
-from optimhc.feature_generator.base_feature_generator import BaseFeatureGenerator
-from optimhc import utils
-from optimhc.parser import extract_mzml_data
-from koinapy import Koina
-
-logger = logging.getLogger(__name__)
-
-
-
-[docs] -class SpectraSimilarityFeatureGenerator(BaseFeatureGenerator): - """ - Feature generator for calculating similarity between experimental and predicted spectra. - - This class works through the following steps: - 1. Extract experimental spectral data from mzML files - 2. Use Koina for theoretical spectra prediction - 3. Align experimental and predicted spectra - 4. Calculate similarity metrics as features - - Parameters: - peptides (List[str]): List of peptide sequences - charges (List[int]): List of charge states - scan_ids (List[int]): List of scan IDs - mz_file_paths (List[str]): List of mzML file paths - model_type (str): Prediction model type, either "HCD" or "CID" - collision_energies (List[float]): List of collision energies, required when model_type is "HCD" - remove_pre_nxt_aa (bool): Whether to remove preceding and next amino acids, default is True - remove_modification (bool): Whether to remove modifications, default is True - url (str): Koina server URL, default is "koina.wilhelmlab.org:443" - top_n (int): Number of top peaks to use for alignment, default is 12 - tolerance_ppm (float): Mass tolerance for alignment in ppm, default is 20 - """ - - def __init__( - self, - spectrum_ids: List[str], - peptides: List[str], - charges: List[int], - scan_ids: List[int], - mz_file_paths: List[str], - model_type: str, - collision_energies: List[float] = None, - instruments: List[str] = None, - fragmentation_types: List[str] = None, - remove_pre_nxt_aa: bool = False, - mod_dict: Optional[Dict[str, str]] = None, - url: str = "koina.wilhelmlab.org:443", - top_n: int = 36, - tolerance_ppm: float = 20, - ): - self.spectrum_ids = spectrum_ids - self.peptides = peptides - self.charges = charges - self.scan_ids = scan_ids - self.mz_file_paths = mz_file_paths - self.model_type = model_type - self.collision_energies = collision_energies - self.instruments = instruments - self.fragmentation_types = fragmentation_types - self.remove_pre_nxt_aa = remove_pre_nxt_aa - self.mod_dict = mod_dict - self.url = url - self.top_n = top_n - self.tolerance_ppm = tolerance_ppm - self.results = None - self._raw_predictions = None - - logger.info( - f"Initializing SpectraSimilarityFeatureGenerator with {len(peptides)} PSMs" - ) - logger.info(f"Using model: {self.model_type}") - - self.df = pd.DataFrame( - { - "spectrum_id": self.spectrum_ids, - "scan": self.scan_ids, - "peptide": self.peptides, - "charge": self.charges, - "mz_file_path": self.mz_file_paths, - } - ) - - self.df["processed_peptide"] = self.df["peptide"].apply( - self._preprocess_peptide - ) - logger.info( - f"Recevied {len(self.df)} PSMs for spectral similarity feature generation" - ) - - @property - def id_column(self) -> List[str]: - """ - Returns a list of input columns required for the feature generator. - """ - return ["spectrum_id", "peptide", "charge"] - - @property - def feature_columns(sself) -> List[str]: - """ - Returns a list of feature columns generated by the feature generator. - """ - return [ - "spectral_angle_similarity", - "cosine_similarity", - "pearson_correlation", - "spearman_correlation", - "mean_squared_error", - "unweighted_entropy_similarity", - "predicted_seen_nonzero", - "predicted_not_seen", - ] - -
-[docs] - def input_df(self) -> pd.DataFrame: - """ - Return the generated features as a DataFrame. - - Returns: - pd.DataFrame: DataFrame containing the generated features - """ - return self.df
- - -
-[docs] - def _preprocess_peptide(self, peptide: str) -> str: - """ - Preprocess peptide sequence. - - As Prosit does not support non-standard amino acid 'U', we replace it with 'C'. - - Parameters - ---------- - peptide : str - Original peptide sequence. - - Returns - ------- - str - Processed peptide sequence. - - Notes - ----- - This is nonsense when it comes to spectral prediction, but we need to keep it - for compatibility with Koina. In the future, this should be prohibited at the input level. - """ - processed_peptide = peptide - - if self.remove_pre_nxt_aa: - processed_peptide = utils.remove_pre_and_nxt_aa(processed_peptide) - - if self.mod_dict is None: - processed_peptide = utils.remove_modifications(processed_peptide) - else: - for mod, replacement in self.mod_dict.items(): - processed_peptide = processed_peptide.replace(mod, replacement) - - # Replace non-standard amino acid 'U' with 'C'. - # This is nosense when it comes to spectral prediction. - # But we need to keep it for compatibility with Koina. - # In the future, this should be prohibited at the input level. - if "U" in utils.remove_modifications(processed_peptide): - logger.warning( - f"Peptide sequence contains non-standard amino acid 'U': {processed_peptide}. Replacing with 'C'." - ) - # Replace 'U' with 'C' - processed_peptide = processed_peptide.replace("U", "C") - - return processed_peptide
- - -
-[docs] - def _extract_experimental_spectra(self) -> pd.DataFrame: - """ - Extract experimental spectral data from mzML files. - - Returns - ------- - pd.DataFrame - DataFrame containing experimental spectral data. - - Notes - ----- - The method groups scan IDs by file path for efficiency and extracts - spectral data from each mzML file. The resulting DataFrame contains - m/z values, intensities, and associated metadata for each spectrum. - """ - logger.info("Extracting experimental spectral data...") - - # Group scan IDs by file path for efficiency - file_to_scans = {} - for i, row in self.df.iterrows(): - scan_id = row["scan"] - file_path = row["mz_file_path"] - if file_path not in file_to_scans: - file_to_scans[file_path] = [] - file_to_scans[file_path].append(scan_id) - - exp_spectra_dfs = [] - for file_path, scan_ids in file_to_scans.items(): - logger.info(f"Extracting {len(scan_ids)} scans from {file_path}") - spectra_df = extract_mzml_data(file_path, scan_ids) - spectra_df["mz_file_path"] = file_path - exp_spectra_dfs.append(spectra_df) - - # Merge spectral data from all files - if exp_spectra_dfs: - exp_spectra_df = pd.concat(exp_spectra_dfs, ignore_index=True) - logger.info( - f"Successfully extracted {len(exp_spectra_df)} experimental spectra" - ) - return exp_spectra_df - else: - logger.warning("No experimental spectral data found") - return pd.DataFrame()
- - -
-[docs] - def _predict_theoretical_spectra( - self, processed_peptides: List[str], charges: List[int] - ) -> pd.DataFrame: - """ - Use Koina to predict theoretical spectra. - - Parameters - ---------- - processed_peptides : list of str - List of preprocessed peptide sequences. - charges : list of int - List of charge states. - - Returns - ------- - pd.DataFrame - DataFrame containing predicted spectral data. - - Raises - ------ - Exception - If there is an error during Koina prediction. - - Notes - ----- - The method uses Koina to predict theoretical spectra for each peptide. - For AlphaPeptDeep_ms2_generic model, inputs are split into batches - grouped by peptide length for prediction. - """ - logger.info(f"Predicting theoretical spectra using {self.model_type}...") - - inputs = pd.DataFrame() - inputs["peptide_sequences"] = np.array(processed_peptides) - inputs["precursor_charges"] = np.array(charges) - - if self.collision_energies is not None: - inputs["collision_energies"] = np.array(self.collision_energies) - - if self.instruments is not None: - inputs["instrument_types"] = np.array(self.instruments) - - if self.fragmentation_types is not None: - inputs["fragmentation_types"] = np.array(self.fragmentation_types) - - model = Koina(self.model_type, self.url) - - try: - predictions = model.predict(inputs) - except Exception as e: - logger.error(f"Error during Koina prediction: {e}") - logger.error("Koina prediction failed. Please check:") - logger.error("- Input parameters compatibility") - logger.error("- Supported modifications") - logger.error("- Peptide length limits") - logger.error(f"Details at: https://koina.proteomicsdb.org/") - - if self.model_type == "AlphaPeptDeep_ms2_generic": - logger.info( - "Splitting inputs into batches grouped by peptide length for prediction..." - ) - import re - - # Compute peptide length by removing modifications in square brackets. - inputs["peptide_length"] = inputs["peptide_sequences"].apply( - lambda x: len(re.sub(r"\[.*?\]", "", x)) - ) - - predictions_batches = [] - # Group the DataFrame by calculated peptide length. Each group is submitted as one batch. - for peptide_length, group in inputs.groupby("peptide_length"): - logger.info( - f"Processing {len(group)} entries for peptide length {peptide_length}." - ) - try: - batch_predictions = model.predict(group) - predictions_batches.append(batch_predictions) - except Exception as batch_error: - logger.error( - f"Error during Koina prediction for batch with peptide length {peptide_length}: {batch_error}" - ) - logger.error(f"Batch data:\n{group}") - raise - predictions = pd.concat(predictions_batches, ignore_index=True) - else: - raise ValueError(f"Unsupported model type: {self.model_type}") - - # Save the raw prediction results - self._raw_predictions = predictions.copy() - - # Convert prediction results to a suitable format - pred_df = predictions.copy() - pred_df.rename( - columns={ - "peptide_sequences": "processed_peptide", - "precursor_charges": "charge", - "intensities": "pred_intensity", - "mz": "pred_mz", - }, - inplace=True, - ) - - # Group by peptide and charge, convert predicted mz and intensity to lists - grouped_df = ( - pred_df.groupby(["processed_peptide", "charge"]) - .agg({"pred_intensity": list, "pred_mz": list, "annotation": list}) - .reset_index() - ) - - logger.info(f"Successfully predicted {len(grouped_df)} theoretical spectra") - return grouped_df
- - - @property - def raw_predictions(self) -> pd.DataFrame: - """ - Returns the raw prediction results from Koina. - - Returns: - pd.DataFrame: Raw prediction results DataFrame - """ - if self._raw_predictions is None: - if self.results is None: - self.results = self._generate_features() - return self._raw_predictions - -
-[docs] - def get_raw_predictions(self) -> pd.DataFrame: - """ - Get the raw prediction results DataFrame from Koina. - - Returns: - pd.DataFrame: Raw prediction results DataFrame - """ - return self.raw_predictions
- - -
-[docs] - def save_raw_predictions(self, file_path: str, **kwargs) -> None: - """ - Save the raw prediction results to a file. - - Parameters: - file_path (str): Path to save the file - **kwargs: Other parameters passed to pandas.DataFrame.to_csv - """ - if "index" not in kwargs: - kwargs["index"] = False - if self.raw_predictions is not None: - self.raw_predictions.to_csv(file_path, **kwargs) - logger.info(f"Raw prediction results saved to: {file_path}") - else: - logger.warning("No raw prediction results available to save.")
- - -
-[docs] - def _sort_spectrum_by_mz( - self, - mz: List[float], - intensity: List[float], - annotation: Optional[List[str]] = None, - ) -> Tuple[np.ndarray, np.ndarray, Optional[np.ndarray]]: - """ - Sort spectrum (m/z, intensity, and optionally annotation) by m/z values. - - Parameters - ---------- - mz : list of float - m/z values. - intensity : list of float - Intensity values. - annotation : list of str, optional - Fragment annotations. - - Returns - ------- - tuple of (np.ndarray, np.ndarray, np.ndarray or None) - Sorted m/z, intensity, and annotation arrays. - If annotation is None, the third element will be None. - """ - mz_array = np.array(mz) - intensity_array = np.array(intensity) - sorted_indices = np.argsort(mz_array) - sorted_mz = mz_array[sorted_indices] - sorted_intensity = intensity_array[sorted_indices] - - sorted_annotation = None - if annotation is not None: - annotation_array = np.array(annotation) - sorted_annotation = annotation_array[sorted_indices] - - return sorted_mz, sorted_intensity, sorted_annotation
- - -
-[docs] - def _align_spectra_all_peaks( - self, - exp_mz: List[float], - exp_intensity: List[float], - pred_mz: List[float], - pred_intensity: List[float], - pred_annotation: Optional[List[str]] = None, - use_ppm: bool = True, - ) -> Tuple[np.ndarray, np.ndarray, List[Tuple], Dict]: - """ - Align experimental and predicted spectra. - - Parameters: - exp_mz (List[float]): Experimental m/z values - exp_intensity (List[float]): Experimental intensity values - pred_mz (List[float]): Predicted m/z values - pred_intensity (List[float]): Predicted intensity values - pred_annotation (Optional[List[str]]): Predicted fragment annotations - use_ppm (bool): Whether to use ppm tolerance or Da tolerance - - Returns: - Tuple[np.ndarray, np.ndarray, List[Tuple], Dict]: - - Aligned experimental intensity vector - - Predicted intensity vector - - Matching index pairs - - Additional info including original sorted arrays - """ - # Sort both experimental and predicted spectra by m/z - exp_mz_sorted, exp_intensity_sorted, _ = self._sort_spectrum_by_mz( - exp_mz, exp_intensity - ) - - if pred_annotation is not None: - pred_mz_sorted, pred_intensity_sorted, pred_annotation_sorted = ( - self._sort_spectrum_by_mz(pred_mz, pred_intensity, pred_annotation) - ) - else: - pred_mz_sorted, pred_intensity_sorted, pred_annotation_sorted = ( - self._sort_spectrum_by_mz(pred_mz, pred_intensity) - ) - aligned_exp_intensity = np.zeros(len(pred_mz_sorted)) - aligned_pred_intensity = pred_intensity_sorted.copy() - matched_indices = [] - - start_pos = 0 - - for i, pred_peak_mz in enumerate(pred_mz_sorted): - if use_ppm: - fragment_min = pred_peak_mz * (1 - self.tolerance_ppm / 1e6) - fragment_max = pred_peak_mz * (1 + self.tolerance_ppm / 1e6) - else: - # If using Da tolerance, use a fixed window (typically ~0.05 Da) - tolerance = 0.05 # Default value - fragment_min = pred_peak_mz - tolerance - fragment_max = pred_peak_mz + tolerance - - matched_int = 0 - matched_exp_idx = None - past_start = 0 - - while start_pos + past_start < len(exp_mz_sorted): - exp_peak_mz = exp_mz_sorted[start_pos + past_start] - if exp_peak_mz < fragment_min: - start_pos += 1 - elif exp_peak_mz <= fragment_max: - exp_peak_int = exp_intensity_sorted[start_pos + past_start] - if exp_peak_int > matched_int: - matched_int = exp_peak_int - matched_exp_idx = start_pos + past_start - past_start += 1 - else: - break - - aligned_exp_intensity[i] = matched_int - - # Record matching index pairs (pred_idx, exp_idx) - pred_idx = i - exp_idx = matched_exp_idx - matched_indices.append((pred_idx, exp_idx)) - - additional_info = { - "exp_mz_sorted": exp_mz_sorted, - "exp_intensity_sorted": exp_intensity_sorted, - "pred_mz_sorted": pred_mz_sorted, - "pred_intensity_sorted": pred_intensity_sorted, - "pred_annotation_sorted": pred_annotation_sorted, - } - - return ( - aligned_exp_intensity, - aligned_pred_intensity, - matched_indices, - additional_info, - )
- - -
-[docs] - def _get_top_peaks_vectors( - self, - aligned_exp_intensity: np.ndarray, - aligned_pred_intensity: np.ndarray, - matched_indices: List[Tuple], - top_n: int, - ) -> Tuple[np.ndarray, np.ndarray, List[Tuple]]: - """ - Extract top N peaks based on predicted intensity for similarity calculation - - Parameters: - aligned_exp_intensity (np.ndarray): Aligned experimental intensity vector - aligned_pred_intensity (np.ndarray): Aligned predicted intensity vector - matched_indices (List[Tuple]): Matching index pairs (pred_idx, exp_idx) - top_n (int): Number of top peaks to extract - - Returns: - Tuple[np.ndarray, np.ndarray, List[Tuple]]: - - Top N experimental intensity vector - - Top N predicted intensity vector - - Top N matching index pairs - """ - # Get indices of top N peaks by predicted intensity - num_peaks = min(top_n, len(aligned_pred_intensity)) - top_pred_indices = np.argsort(-aligned_pred_intensity)[:num_peaks] - - # Extract top N peaks - top_exp_intensity = np.zeros(num_peaks) - top_pred_intensity = np.zeros(num_peaks) - top_matched_indices = [] - - for i, orig_idx in enumerate(top_pred_indices): - top_exp_intensity[i] = aligned_exp_intensity[orig_idx] - top_pred_intensity[i] = aligned_pred_intensity[orig_idx] - top_matched_indices.append(matched_indices[orig_idx]) - - return top_exp_intensity, top_pred_intensity, top_matched_indices
- - -
-[docs] - def _align_spectra( - self, - exp_mz: List[float], - exp_intensity: List[float], - pred_mz: List[float], - pred_intensity: List[float], - pred_annotation: Optional[List[str]] = None, - ) -> Tuple[np.ndarray, np.ndarray, List[Tuple]]: - """ - Align experimental and predicted spectra. - - This is a wrapper around _align_spectra_all_peaks and _get_top_peaks_vectors - to maintain backward compatibility while using the improved algorithm. - - Parameters - ---------- - exp_mz : list of float - Experimental m/z values. - exp_intensity : list of float - Experimental intensity values. - pred_mz : list of float - Predicted m/z values. - pred_intensity : list of float - Predicted intensity values. - pred_annotation : list of str, optional - Predicted fragment annotations. - - Returns - ------- - tuple of (np.ndarray, np.ndarray, list of tuple) - - Aligned experimental intensity vector - - Predicted intensity vector - - Matching index pairs (for top N peaks) - - Notes - ----- - The method first aligns all peaks using _align_spectra_all_peaks, then - extracts the top N peaks using _get_top_peaks_vectors for compatibility - with existing code. - """ - # First align all peaks - all_exp_intensity, all_pred_intensity, all_matched_indices, additional_info = ( - self._align_spectra_all_peaks( - exp_mz, - exp_intensity, - pred_mz, - pred_intensity, - pred_annotation, - use_ppm=True, - ) - ) - - # Then get top N peaks for compatibility with existing code - top_exp_intensity, top_pred_intensity, top_matched_indices = ( - self._get_top_peaks_vectors( - all_exp_intensity, all_pred_intensity, all_matched_indices, self.top_n - ) - ) - - return top_exp_intensity, top_pred_intensity, top_matched_indices
- - -
-[docs] - def _normalize_vector_l2(self, vector: np.ndarray) -> np.ndarray: - """ - Normalize a vector using L2 normalization (unit vector). - - Parameters - ---------- - vector : np.ndarray - Input vector to normalize. - - Returns - ------- - np.ndarray - L2-normalized vector. - - Notes - ----- - If the input vector has zero norm, the original vector is returned unchanged. - """ - norm = np.linalg.norm(vector) # Calculate the L2 norm (Euclidean norm) - if norm == 0: - return vector - return vector / norm
- - -
-[docs] - def _normalize_vector_sum(self, vector: np.ndarray) -> np.ndarray: - """ - Perform sum normalization (probability normalization) on a vector. - - Parameters - ---------- - vector : np.ndarray - Input vector. - - Returns - ------- - np.ndarray - Sum normalized vector (sum to 1). - - Notes - ----- - If the input vector has zero sum, the original vector is returned unchanged. - """ - total = np.sum(vector) - if total > 0: - return vector / total - return vector
- - -
-[docs] - def _calculate_spectral_angle_similarity( - self, exp_vector: np.ndarray, pred_vector: np.ndarray - ) -> float: - """ - Calculate the spectral angle between experimental and predicted vectors. - - Normalize the angle to [0, 1] where 1 is the best similarity. - - Parameters - ---------- - exp_vector : np.ndarray - Experimental intensity vector. - pred_vector : np.ndarray - Predicted intensity vector. - - Returns - ------- - float - Spectral angle similarity (0-1, higher is better). - - Notes - ----- - The spectral angle is calculated as: SA = 1 - (2 * angle / π), - where angle is the angle between the normalized vectors in radians. - """ - exp_norm = self._normalize_vector_l2(exp_vector) - pred_norm = self._normalize_vector_l2(pred_vector) - dot_product = np.sum(exp_norm * pred_norm) - - # Clamp dot product to [-1, 1] to avoid numerical issues - dot_product = np.clip(dot_product, -1.0, 1.0) - angle = np.arccos(dot_product) # Angle in radians - - # Return similarity score: SA = 1 - (2 * angle / π) - return 1 - (2 * angle / np.pi)
- - -
-[docs] - def _calculate_cosine_similarity( - self, exp_vector: np.ndarray, pred_vector: np.ndarray - ) -> float: - """ - Calculate the cosine similarity between experimental and predicted vectors. - - Parameters - ---------- - exp_vector : np.ndarray - Experimental intensity vector. - pred_vector : np.ndarray - Predicted intensity vector. - - Returns - ------- - float - Cosine similarity (0-1, higher is better). - - Notes - ----- - The cosine similarity is calculated as 1 - cosine_distance. - If either vector has zero sum, returns 0.0. - """ - if np.sum(exp_vector) == 0 or np.sum(pred_vector) == 0: - return 0.0 - - # Normalize vectors using L2 normalization - exp_norm = self._normalize_vector_l2(exp_vector) - pred_norm = self._normalize_vector_l2(pred_vector) - - # Use 1 - cosine distance = cosine similarity - return 1.0 - cosine(exp_norm, pred_norm)
- - -
-[docs] - def _calculate_spearman_correlation( - self, exp_vector: np.ndarray, pred_vector: np.ndarray - ) -> float: - """ - Calculate Spearman correlation coefficient between experimental and predicted vectors. - - Parameters - ---------- - exp_vector : np.ndarray - Experimental intensity vector. - pred_vector : np.ndarray - Predicted intensity vector. - - Returns - ------- - float - Spearman correlation coefficient (-1 to 1, higher is better). - - Notes - ----- - If either vector has no variation (std = 0), returns 0.0. - """ - # Handle vectors with no variation - if np.std(exp_vector) == 0 or np.std(pred_vector) == 0: - return 0.0 - - try: - r, _ = spearmanr(exp_vector, pred_vector) - return r - except: - return
- - -
-[docs] - def _calculate_pearson_correlation( - self, exp_vector: np.ndarray, pred_vector: np.ndarray - ) -> float: - """ - Calculate Pearson correlation coefficient between experimental and predicted vectors. - - Parameters - ---------- - exp_vector : np.ndarray - Experimental intensity vector. - pred_vector : np.ndarray - Predicted intensity vector. - - Returns - ------- - float - Pearson correlation coefficient (-1 to 1, higher is better). - - Notes - ----- - If either vector has no variation (std = 0), returns 0.0. - """ - # Handle vectors with no variation - if np.std(exp_vector) == 0 or np.std(pred_vector) == 0: - return 0.0 - - try: - r, _ = pearsonr(exp_vector, pred_vector) - return r - except: - return 0.0
- - -
-[docs] - def _calculate_mean_squared_error( - self, exp_vector: np.ndarray, pred_vector: np.ndarray - ) -> float: - """ - Calculate mean squared error between experimental and predicted vectors. - - Parameters - ---------- - exp_vector : np.ndarray - Experimental intensity vector. - pred_vector : np.ndarray - Predicted intensity vector. - - Returns - ------- - float - Mean squared error (lower is better). - - Notes - ----- - The vectors are normalized using L2 normalization before calculating - the mean squared error for fair comparison. - """ - # Normalize vectors for fair comparison using L2 normalization - exp_norm = self._normalize_vector_l2(exp_vector) - pred_norm = self._normalize_vector_l2(pred_vector) - - return np.mean((exp_norm - pred_norm) ** 2)
- - -
-[docs] - def _calculate_entropy(self, vector: np.ndarray) -> float: - """ - Calculate Shannon entropy of a vector that has already been sum-1 normalized. - - Parameters - ---------- - vector : np.ndarray - Input vector, which has already been sum-1 normalized. - - Returns - ------- - float - Shannon entropy. - - Notes - ----- - Only non-zero probabilities are considered for entropy calculation. - If all probabilities are zero, returns 0.0. - """ - # Only consider non-zero probabilities for entropy calculation - mask = vector > 0 - if not np.any(mask): - return 0.0 - - prob_vector = vector[mask] - return -np.sum(prob_vector * np.log(prob_vector))
- - -
-[docs] - def _calculate_unweighted_entropy_similarity( - self, exp_vector: np.ndarray, pred_vector: np.ndarray - ) -> float: - """ - Calculate unweighted spectral entropy between experimental and predicted vectors. - - Parameters - ---------- - exp_vector : np.ndarray - Experimental intensity vector. - pred_vector : np.ndarray - Predicted intensity vector. - - Returns - ------- - float - Spectral entropy similarity. - - Notes - ----- - Based on the method described in https://www.nature.com/articles/s41592-021-01331-z. - The spectral entropy is calculated using the formula: - 1 - (2*S_PM - S_P - S_M)/ln(4), where S_PM is the entropy of the mixed - distribution, and S_P and S_M are the entropies of the individual distributions. - """ - # Sum-to-1 normalization - sum_normalized_exp_vector = self._normalize_vector_sum(exp_vector) - sum_normalized_pred_vector = self._normalize_vector_sum(pred_vector) - s_exp = self._calculate_entropy(sum_normalized_exp_vector) - s_pred = self._calculate_entropy(sum_normalized_pred_vector) - s_mixed = self._calculate_entropy( - 0.5 * (sum_normalized_exp_vector + sum_normalized_pred_vector) - ) - - # Calculate spectral entropy using formula: 1 - (2*S_PM - S_P - S_M)/ln(4) - unweighted_entropy_similarity = 1.0 - (2 * s_mixed - s_exp - s_pred) / np.log(4) - - return unweighted_entropy_similarity
- - - # TODO: Assign weight to each peak based on the entropy of a spectrum. - # The original paper uses 3 as a entropy cutoff to assign more weight to the low intensity peaks. - # But the cutoff value is determined by a small-molecular dataset rather than a proteomics dataset. - # Thus, we need to design our own heuristic algorithm to calculate a similar feature for proteomics dataset. - # We can refer to the practice of MSBooster. - # url: https://github.com/Nesvilab/MSBooster/blame/master/src/main/java/features/spectra/SpectrumComparison.java#L803 - - # def _calculate_entropy_similarity(self, exp_vector: np.ndarray, pred_vector: np.ndarray) -> float: - -
-[docs] - def _calculate_predicted_counts( - self, exp_vector: np.ndarray, pred_vector: np.ndarray - ) -> Tuple[int, int]: - """ - Calculate counts of predicted peaks seen/not seen in experimental spectrum. - - Parameters - ---------- - exp_vector : np.ndarray - Experimental intensity vector. - pred_vector : np.ndarray - Predicted intensity vector. - - Returns - ------- - tuple of (int, int) - - predicted_seen_nonzero: Number of predicted peaks that are also present in experimental spectrum - - predicted_not_seen: Number of predicted peaks that are not present in experimental spectrum - """ - predicted_seen_nonzero = np.sum((pred_vector > 0) & (exp_vector > 0)) - predicted_not_seen = np.sum((pred_vector > 0) & (exp_vector == 0)) - - return predicted_seen_nonzero, predicted_not_seen
- - -
-[docs] - def _calculate_bray_curtis_similarity( - self, exp_vector: np.ndarray, pred_vector: np.ndarray - ) -> float: - """ - Calculate Bray-Curtis similarity between experimental and predicted vectors - - Parameters: - exp_vector (np.ndarray): Experimental intensity vector - pred_vector (np.ndarray): Predicted intensity vector - - Returns: - float: Bray-Curtis similarity (0-1, higher is better) - """ - # Bray-Curtis dissimilarity: sum(|exp - pred|) / sum(exp + pred) - numerator = np.sum(np.abs(exp_vector - pred_vector)) - denominator = np.sum(exp_vector + pred_vector) - - if denominator == 0: - return 0.0 - - # Convert dissimilarity to similarity: 1 - dissimilarity - return 1.0 - (numerator / denominator)
- - -
-[docs] - def _calculate_similarity_features( - self, exp_vector: np.ndarray, pred_vector: np.ndarray - ) -> Dict[str, float]: - """ - Calculate all similarity features between experimental and predicted vectors. - - Parameters - ---------- - exp_vector : np.ndarray - Experimental intensity vector. - pred_vector : np.ndarray - Predicted intensity vector. - - Returns - ------- - dict of str to float - Dictionary of similarity features, including: - - spectral_angle_similarity: Spectral angle similarity (0-1) - - cosine_similarity: Cosine similarity (0-1) - - pearson_correlation: Pearson correlation (-1 to 1) - - spearman_correlation: Spearman correlation (-1 to 1) - - mean_squared_error: Mean squared error - - unweighted_entropy_similarity: Spectral entropy similarity - - predicted_seen_nonzero: Number of predicted peaks seen in experimental spectrum - - predicted_not_seen: Number of predicted peaks not seen in experimental spectrum - - bray_curtis_similarity: Bray-Curtis similarity (0-1) - """ - spectral_angle_similarity = self._calculate_spectral_angle_similarity( - exp_vector, pred_vector - ) - cosine_similarity = self._calculate_cosine_similarity(exp_vector, pred_vector) - pearson_correlation = self._calculate_pearson_correlation( - exp_vector, pred_vector - ) - spearman_correlation = self._calculate_spearman_correlation( - exp_vector, pred_vector - ) - mean_squared_error = self._calculate_mean_squared_error(exp_vector, pred_vector) - unweighted_entropy_similarity = self._calculate_unweighted_entropy_similarity( - exp_vector, pred_vector - ) - predicted_seen_nonzero, predicted_not_seen = self._calculate_predicted_counts( - exp_vector, pred_vector - ) - bray_curtis_similarity = self._calculate_bray_curtis_similarity( - exp_vector, pred_vector - ) - - return { - "spectral_angle_similarity": spectral_angle_similarity, - "cosine_similarity": cosine_similarity, - "pearson_correlation": pearson_correlation, - "spearman_correlation": spearman_correlation, - "mean_squared_error": mean_squared_error, - "unweighted_entropy_similarity": unweighted_entropy_similarity, - "predicted_seen_nonzero": predicted_seen_nonzero, - "predicted_not_seen": predicted_not_seen, - "bray_curtis_similarity": bray_curtis_similarity, - }
- - -
-[docs] - def _generate_features(self) -> pd.DataFrame: - """ - Generate spectral similarity features - - Returns: - pd.DataFrame: DataFrame containing generated features - """ - psm_df = self.df.copy() - pred_spectra_df = self._predict_theoretical_spectra( - processed_peptides=psm_df["processed_peptide"].tolist(), - charges=psm_df["charge"].tolist(), - ) - - exp_spectra_df = self._extract_experimental_spectra() - - if not exp_spectra_df.empty: - psm_df = pd.merge( - psm_df, - exp_spectra_df, - on=["scan", "mz_file_path", "charge"], - how="inner", - validate="m:1", - ) - else: - logger.error( - "Could not extract experimental spectral data, cannot continue processing" - ) - return pd.DataFrame() - - if len(psm_df) != len(self.df): - logger.warning("Some PSMs were not found in experimental spectral data") - - psm_df = pd.merge( - psm_df, pred_spectra_df, on=["processed_peptide", "charge"], how="inner" - ) - results = [] - - logger.info( - "Matching experimental and predicted spectra... This may take a while." - ) - for _, row in psm_df.iterrows(): - exp_mz = row["mz"] - exp_intensity = row["intensity"] - pred_mz = row["pred_mz"] - pred_intensity = row["pred_intensity"] - pred_annotation = row["annotation"] if "annotation" in row else None - - # Align all peaks - ( - all_exp_intensity, - all_pred_intensity, - all_matched_indices, - additional_info, - ) = self._align_spectra_all_peaks( - exp_mz, - exp_intensity, - pred_mz, - pred_intensity, - pred_annotation, - use_ppm=True, - ) - - # Extract top N peaks for similarity calculations - top_exp_intensity, top_pred_intensity, top_matched_indices = ( - self._get_top_peaks_vectors( - all_exp_intensity, - all_pred_intensity, - all_matched_indices, - self.top_n, - ) - ) - - similarity_features = self._calculate_similarity_features( - top_exp_intensity, top_pred_intensity - ) - result = { - "exp_vector": all_exp_intensity.tolist(), - "pred_vector": all_pred_intensity.tolist(), - "matched_indices": all_matched_indices, - "exp_top_vector": top_exp_intensity.tolist(), - "pred_top_vector": top_pred_intensity.tolist(), - "top_matched_indices": top_matched_indices, - **similarity_features, - "exp_mz_sorted": additional_info["exp_mz_sorted"].tolist(), - "exp_intensity_sorted": additional_info[ - "exp_intensity_sorted" - ].tolist(), - "pred_mz_sorted": additional_info["pred_mz_sorted"].tolist(), - "pred_intensity_sorted": additional_info[ - "pred_intensity_sorted" - ].tolist(), - } - - # Add annotations if available - if additional_info["pred_annotation_sorted"] is not None: - result["pred_annotation_sorted"] = additional_info[ - "pred_annotation_sorted" - ].tolist() - - results.append(result) - - results_df = pd.DataFrame(results) - psm_df = pd.concat( - [psm_df.reset_index(drop=True), results_df.reset_index(drop=True)], axis=1 - ) - - result_columns = [ - "mz_file_path", - "spectrum_id", - "scan", - "peptide", - "charge", - "processed_peptide", - "mz", - "intensity", - "pred_mz", - "pred_intensity", - "annotation", - "exp_mz_sorted", - "exp_intensity_sorted", - "pred_mz_sorted", - "pred_intensity_sorted", - ( - "pred_annotation_sorted" - if "pred_annotation_sorted" in psm_df.columns - else None - ), - "exp_vector", - "pred_vector", - "matched_indices", - "exp_top_vector", - "pred_top_vector", - "top_matched_indices", - "spectral_angle_similarity", - "cosine_similarity", - "pearson_correlation", - "spearman_correlation", - "mean_squared_error", - "unweighted_entropy_similarity", - "predicted_seen_nonzero", - "predicted_not_seen", - ] - - result_columns = [col for col in result_columns if col is not None] - result_df = psm_df[result_columns] - - logger.info( - f"Successfully generated spectral similarity features for {len(result_df)} PSMs" - ) - return result_df
- - -
-[docs] - def generate_features(self) -> pd.DataFrame: - """ - Public interface for generating spectral similarity features. - - Returns - ------- - pd.DataFrame - DataFrame containing the generated features. - - Notes - ----- - This method is a wrapper around _generate_features that ensures - the results are cached and only computed once. - """ - if self.results is None: - self.results = self._generate_features() - return self.results[self.id_column + self.feature_columns]
- - -
-[docs] - def get_full_data(self) -> pd.DataFrame: - """ - Return the full DataFrame with all columns. - - Returns - ------- - pd.DataFrame - Full DataFrame with all columns. - - Notes - ----- - This method returns the complete DataFrame including all intermediate - results and raw data used in feature generation. - """ - return self.results
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/parser/mzml.html b/docs/source/_build/html/_modules/optimhc/parser/mzml.html deleted file mode 100644 index 3a727b6..0000000 --- a/docs/source/_build/html/_modules/optimhc/parser/mzml.html +++ /dev/null @@ -1,219 +0,0 @@ - - - - - - - - optimhc.parser.mzml — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for optimhc.parser.mzml

-import logging
-import numpy as np
-import pandas as pd
-from pyteomics import mzml
-
-logger = logging.getLogger(__name__)
-
-
-
-[docs] -def extract_mzml_data(mzml_filename, scan_ids=None): - """ - Extract scan data from an mzML file. - - Parameters - ---------- - mzml_filename : str - The path to the mzML file. - scan_ids : list[int] or None, optional - A list of scan IDs to extract. If None, extracts all scans. - - Returns - ------- - pd.DataFrame - A DataFrame containing the extracted scan data with columns: - - source: The source file name - - scan: The scan ID - - mz: The m/z values array - - intensity: The intensity values array - - charge: The charge state - - retention_time: The retention time - - Notes - ----- - This function: - 1. Reads the mzML file using pyteomics - 2. Extracts scan data including retention time, charge state, m/z values, and intensities - 3. Filters scans based on provided scan IDs if specified - 4. Returns a DataFrame with the extracted data - """ - filename = mzml_filename.split("/")[-1].replace(".mzML", "") - logger.info(f"Extracting scans from {mzml_filename}") - - scan_ids = set(scan_ids) if scan_ids is not None else None - - ( - extracted_scan_ids, - mzml_filenames, - intensities, - mz_values, - charges, - retention_times, - ) = ([], [], [], [], [], []) - - try: - with mzml.read(mzml_filename) as reader: - for spectrum in reader: - try: - scan_id = int(spectrum["id"].split("scan=")[-1]) - - if scan_ids is not None and scan_id not in scan_ids: - continue - - mz_array = np.array(spectrum.get("m/z array", [])) - intensity_array = np.array(spectrum.get("intensity array", [])) - - charge = None - try: - charge = int( - spectrum["precursorList"]["precursor"][0][ - "selectedIonList" - ]["selectedIon"][0]["charge state"] - ) - except (KeyError, ValueError, IndexError): - pass - - retention_time = None - try: - retention_time = float( - spectrum["scanList"]["scan"][0]["scan start time"] - ) - except (KeyError, ValueError, IndexError): - pass - - extracted_scan_ids.append(scan_id) - mzml_filenames.append(filename) - intensities.append(intensity_array) - mz_values.append(mz_array) - charges.append(charge) - retention_times.append(retention_time) - - except Exception as e: - logger.warning(f"Skipping scan {scan_id} due to error: {e}") - - except Exception as e: - logger.error(f"Failed to parse mzML file {mzml_filename}: {e}") - raise RuntimeError(f"Error processing mzML file {mzml_filename}: {e}") - - data_dict = { - "source": mzml_filenames, - "scan": extracted_scan_ids, - "mz": mz_values, - "intensity": intensities, - "charge": charges, - "retention_time": retention_times, - } - - scans_df = pd.DataFrame(data_dict) - scans_df = scans_df.drop_duplicates(subset=["source", "scan"]) - - logger.info(f"Successfully extracted {len(scans_df)} scans from {mzml_filename}") - - return scans_df
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/parser/pepxml.html b/docs/source/_build/html/_modules/optimhc/parser/pepxml.html deleted file mode 100644 index 1c6ae41..0000000 --- a/docs/source/_build/html/_modules/optimhc/parser/pepxml.html +++ /dev/null @@ -1,489 +0,0 @@ - - - - - - - - optimhc.parser.pepxml — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for optimhc.parser.pepxml

-import logging
-import pandas as pd
-import itertools
-import numpy as np
-from functools import partial
-from lxml import etree
-from optimhc.psm_container import PsmContainer
-
-logger = logging.getLogger(__name__)
-
-
-
-[docs] -def read_pepxml(pepxml_files, decoy_prefix="DECOY_"): - """ - Read PSMs from a list of PepXML files. - - Parameters - ---------- - pepxml_files : Union[str, List[str]] - The file path to the PepXML file or a list of file paths. - decoy_prefix : str, optional - The prefix used to indicate a decoy protein in the description lines - of the FASTA file. Default is "DECOY_". - - Returns - ------- - PsmContainer - A PsmContainer object containing the PSM data. - - Raises - ------ - ValueError - If the PepXML files were generated by Percolator or PeptideProphet. - - Notes - ----- - This function: - 1. Reads and parses PepXML files - 2. Calculates mass difference features - 3. Processes matched ions and complementary ions - 4. Creates charge columns - 5. Log-transforms p-values - 6. Returns a PsmContainer with the processed data - """ - proton = 1.00727646677 - if isinstance(pepxml_files, str): - pepxml_files = [pepxml_files] - psms = pd.concat([_parse_pepxml(f, decoy_prefix) for f in pepxml_files]) - - # Check that these PSMs are not from Percolator or PeptideProphet: - illegal_cols = { - "Percolator q-Value", - "Percolator PEP", - "Percolator SVMScore", - } - - if illegal_cols.intersection(set(psms.columns)): - raise ValueError( - "The PepXML files appear to have generated by Percolator or " - "PeptideProphet; hence, they should not be analyzed with mokapot." - ) - - # For open modification searches: - psms["mass_diff"] = psms["exp_mass"] - psms["calc_mass"] - # Calculate massdiff features - exp_mz = psms["exp_mass"] / psms["charge"] + proton - calc_mz = psms["calc_mass"] / psms["charge"] + proton - psms["abs_mz_diff"] = (exp_mz - calc_mz).abs() - - # Calculate matched ions and complementary ions - if "num_matched_ions" in psms.columns and "tot_num_ions" in psms.columns: - if (psms["tot_num_ions"] != 0).all(): - psms["matched_ions_ratio"] = psms["num_matched_ions"] / psms["tot_num_ions"] - - # Log number of candidates: - if "num_matched_peptides" in psms.columns: - psms["num_matched_peptides"] = np.log10(psms["num_matched_peptides"]) - - # Create charge columns: - psms = pd.concat([psms, pd.get_dummies(psms["charge"], prefix="charge")], axis=1) - - # psms = psms.drop("charge", axis=1) - # -log10 p-values - nonfeat_cols = [ - "ms_data_file", - "scan", - "spectrum", - "label", - "calc_mass", - "peptide", - "proteins", - "charge", - "retention_time", - ] - - feat_cols = [c for c in psms.columns if c not in nonfeat_cols] - psms = psms.apply(_log_features, features=feat_cols) - rescoring_features = {"Original": feat_cols} - - return PsmContainer( - psms=psms, - label_column="label", - scan_column="scan", - spectrum_column="spectrum", - ms_data_file_column="ms_data_file", - peptide_column="peptide", - protein_column="proteins", - charge_column="charge", - rescoring_features=rescoring_features, - hit_rank_column="rank", - retention_time_column="retention_time", - )
- - - -""" -This code is adapted from 'Mokapot' -Source: https://github.com/wfondrie/mokapot -License: Apache License 2.0 -""" - - -
-[docs] -def _parse_pepxml(pepxml_file, decoy_prefix): - """ - Parse the PSMs of a PepXML into a DataFrame. - - Parameters - ---------- - pepxml_file : str - The PepXML file to parse. - decoy_prefix : str - The prefix used to indicate a decoy protein in the description lines - of the FASTA file. - - Returns - ------- - pandas.DataFrame - A DataFrame containing the information about each PSM. - - Raises - ------ - ValueError - If the file is not a PepXML file or is malformed. - """ - logger.info("Reading %s...", pepxml_file) - parser = etree.iterparse(str(pepxml_file), tag="{*}msms_run_summary") - parse_fun = partial(_parse_msms_run, decoy_prefix=decoy_prefix) - spectra = map(parse_fun, parser) - try: - psms = itertools.chain.from_iterable(spectra) - df = pd.DataFrame.from_records(itertools.chain.from_iterable(psms)) - df["ms_data_file"] = df["ms_data_file"].astype("category") - except etree.XMLSyntaxError: - raise ValueError(f"{pepxml_file} is not a PepXML file or is malformed.") - return df
- - - -
-[docs] -def _parse_msms_run(msms_run, decoy_prefix): - """ - Parse a single MS/MS run. - - Parameters - ---------- - msms_run : tuple of anything, lxml.etree.Element - The second element of the tuple should be the XML element for a single - msms_run. The first is not used, but is necessary for compatibility - with using :code:`map()`. - decoy_prefix : str - The prefix used to indicate a decoy protein in the description lines - of the FASTA file. - - Yields - ------ - dict - A dictionary describing all of the PSMs in a run. - """ - msms_run = msms_run[1] - ms_data_file = msms_run.get("base_name") - run_ext = msms_run.get("raw_data") - if not ms_data_file.endswith(run_ext): - ms_data_file += run_ext - - run_info = {"ms_data_file": ms_data_file} - for spectrum in msms_run.iter("{*}spectrum_query"): - yield _parse_spectrum(spectrum, run_info, decoy_prefix)
- - - -
-[docs] -def _parse_spectrum(spectrum, run_info, decoy_prefix): - """ - Parse the PSMs for a single mass spectrum. - - Parameters - ---------- - spectrum : lxml.etree.Element - The XML element for a single spectrum. - run_info : dict - The parsed run data. - decoy_prefix : str - The prefix used to indicate a decoy protein in the description lines - of the FASTA file. - - Yields - ------ - dict - A dictionary describing all of the PSMs for a spectrum. - """ - spec_info = run_info.copy() - spec_info["spectrum"] = str(spectrum.get("spectrum")) - spec_info["scan"] = int(spectrum.get("end_scan")) - spec_info["charge"] = int(spectrum.get("assumed_charge")) - spec_info["retention_time"] = float(spectrum.get("retention_time_sec")) - spec_info["exp_mass"] = float(spectrum.get("precursor_neutral_mass")) - for psms in spectrum.iter("{*}search_result"): - for psm in psms.iter("{*}search_hit"): - yield _parse_psm(psm, spec_info, decoy_prefix=decoy_prefix)
- - - -
-[docs] -def _parse_psm(psm_info, spec_info, decoy_prefix): - """ - Parse a single PSM. - - Parameters - ---------- - psm_info : lxml.etree.Element - The XML element containing information about the PSM. - spec_info : dict - The parsed spectrum data. - decoy_prefix : str - The prefix used to indicate a decoy protein in the description lines - of the FASTA file. - - Returns - ------- - dict - A dictionary containing parsed data about the PSM. - """ - psm = spec_info.copy() - psm["calc_mass"] = float(psm_info.get("calc_neutral_pep_mass")) - psm["peptide"] = psm_info.get("peptide") - psm["proteins"] = [psm_info.get("protein").split(" ")[0]] - psm["label"] = not psm["proteins"][0].startswith(decoy_prefix) - psm["rank"] = int(psm_info.get("hit_rank")) - - # Begin features: - try: - psm["missed_cleavages"] = int(psm_info.get("num_missed_cleavages")) - except TypeError: - pass - - try: - psm["ntt"] = int(psm_info.get("num_tol_term")) - except TypeError: - pass - - try: - psm["num_matched_peptides"] = int(psm_info.get("num_matched_peptides")) - except TypeError: - pass - - try: - psm["num_matched_ions"] = int(psm_info.get("num_matched_ions")) - except TypeError: - pass - - try: - psm["tot_num_ions"] = int(psm_info.get("tot_num_ions")) - except TypeError: - pass - - queries = [ - "{*}modification_info", - "{*}search_score", - "{*}alternative_protein", - ] - for element in psm_info.iter(*queries): - if "modification_info" in element.tag: - offset = 0 - mod_pep = psm["peptide"] - for mod in element.iter("{*}mod_aminoacid_mass"): - idx = offset + int(mod.get("position")) - mass = mod.get("mass") - mod_pep = mod_pep[:idx] + "[" + mass + "]" + mod_pep[idx:] - offset += 2 + len(mass) - - psm["peptide"] = mod_pep - - elif "alternative_protein" in element.tag: - psm["proteins"].append(element.get("protein").split(" ")[0]) - if not psm["label"]: - psm["label"] = not psm["proteins"][-1].startswith(decoy_prefix) - - else: - psm[element.get("name")] = element.get("value") - - psm["proteins"] = "\t".join(psm["proteins"]) - return psm
- - - -
-[docs] -def _log_features(col, features): - """ - Log-transform columns that are p-values or E-values. - - Parameters - ---------- - col : pandas.Series - A column of the dataset. - features : list of str - The features of the dataset. Only feature columns will be considered - for transformation. - - Returns - ------- - pandas.Series - The log-transformed values of the column if the feature was determined - to be a p-value. - - Notes - ----- - This function: - 1. Detects columns written in scientific notation and log them - 2. Uses a simple heuristic to find p-value / E-value features - 3. Only transforms if values span >4 orders of magnitude - 4. Preserves precision for scientific notation values - """ - if col.name not in features: - return col - elif col.dtype == "bool": - return col.astype(float) - - col = col.astype(str).str.lower() - - # Detect columns written in scientific notation and log them: - # This is specifically needed to preserve precision. - if col.str.contains("e").any() and (col.astype(float) > 0).all(): - split = col.str.split("e", expand=True) - root = split.loc[:, 0] - root = root.astype(float) - power = split.loc[:, 1] - power[pd.isna(power)] = "0" - power = power.astype(int) - - zero_idx = root == 0 - root[zero_idx] = 1 - power[zero_idx] = power[~zero_idx].min() - diff = power.max() - power.min() - if abs(diff) >= 4: - logger.info(" - log-transformed the '%s' feature.", col.name) - return np.log10(root) + power - else: - return col.astype(float) - - col = col.astype(float) - - # A simple heuristic to find p-value / E-value features: - # Non-negative: - if col.min() >= 0: - # Make sure this isn't a binary column: - if not np.array_equal(col.values, col.values.astype(bool)): - # Only log if values span >4 orders of magnitude, - # excluding values that are exactly zero: - zero_idx = col == 0 - col_min = col[~zero_idx].min() - if col.max() / col_min >= 10000: - col[~zero_idx] = np.log10(col[~zero_idx]) - col[zero_idx] = col[~zero_idx].min() - 1 - logger.info(" - log-transformed the '%s' feature.", col.name) - - return col
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/parser/pin.html b/docs/source/_build/html/_modules/optimhc/parser/pin.html deleted file mode 100644 index 1fb2927..0000000 --- a/docs/source/_build/html/_modules/optimhc/parser/pin.html +++ /dev/null @@ -1,276 +0,0 @@ - - - - - - - - optimhc.parser.pin — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for optimhc.parser.pin

-import logging
-from typing import List, Optional, Tuple, Union
-import pandas as pd
-from optimhc.psm_container import PsmContainer
-
-logger = logging.getLogger(__name__)
-
-
-
-[docs] -def read_pin( - pin_files: Union[str, List[str], pd.DataFrame], - retention_time_column: Optional[str] = None, -) -> PsmContainer: - """ - Read PSMs from a Percolator INput (PIN) file. - - Parameters - ---------- - pin_files : Union[str, List[str], pd.DataFrame] - The file path to the PIN file, a list of file paths, or a DataFrame - containing PIN data. - retention_time_column : Optional[str], optional - The column containing the retention time. If None, no retention time - will be included. - - Returns - ------- - PsmContainer - A PsmContainer object containing the PSM data. - - Notes - ----- - This function: - 1. Reads PIN file(s) into a DataFrame - 2. Identifies required columns (case-insensitive) - 3. Processes scan IDs and hit ranks - 4. Converts data types appropriately - 5. Creates a PsmContainer with the processed data - """ - logger.info("Reading PIN file(s) into PsmContainer.") - if isinstance(pin_files, str): - pin_files = [pin_files] - - pin_df = pd.concat([_read_single_pin_as_df(pin_file) for pin_file in pin_files]) - logger.info(f"Read {len(pin_df)} PSMs from {len(pin_files)} PIN files.") - logger.debug(pin_df.head()) - logger.debug(pin_df.columns) - logger.debug(pin_df.iloc[0]) - - def find_required_columns(col: str, columns: List[str]) -> str: - """ - Case-insensitive search for a column in the DataFrame. - Returns the matching column name with original casing. - """ - col_lower = col.lower() - column_map = {c.lower(): c for c in columns} - if col_lower not in column_map: - raise ValueError( - f"Column '{col}' not found in PSM data (case-insensitive)." - ) - return column_map[col_lower] - - # non-feature columns (case-insensitive search) - label = find_required_columns("Label", pin_df.columns) - scan = find_required_columns("ScanNr", pin_df.columns) - specid = find_required_columns("SpecId", pin_df.columns) - peptide = find_required_columns("Peptide", pin_df.columns) - protein = find_required_columns("Proteins", pin_df.columns) - - # Comet: P2PI20160713_pilling_C1RA2_BB72_P1_31_3_1 - # MSFragger: P2PI20160713_pilling_C1RA2_BB72_P1.3104.3104.2_1 - - def parse_specid(specid: str) -> Tuple[str, int]: - if "_" in specid: - parts = specid.rsplit("_", 1) - if len(parts) != 2: - raise ValueError(f"SpecId format invalid: {specid}") - unique_id = parts[0] - hit_rank = int(parts[1]) - return unique_id, hit_rank - else: - return specid, 1 - - hit_rank = "rank" - if "rank" in [c.lower() for c in pin_df.columns]: - pass - else: - pin_df[specid], pin_df["rank"] = zip(*pin_df[specid].apply(parse_specid)) - - retention_time_column = ( - find_required_columns(retention_time_column, pin_df.columns) - if retention_time_column - else None - ) - - # feature columns: columns that are not non-feature columns - non_feature_columns = [label, scan, specid, peptide, protein] - feature_columns = [col for col in pin_df.columns if col not in non_feature_columns] - - logger.info( - f"Columns: label={label}, scan={scan}, specid={specid}, peptide={peptide}, \ - protein={protein}, hit_rank={hit_rank}, retention_time={retention_time_column}, features={feature_columns}" - ) - - pin_df[scan] = pin_df[scan].astype(str) - pin_df[specid] = pin_df[specid].astype(str) - pin_df[peptide] = pin_df[peptide].astype(str) - pin_df[protein] = pin_df[protein].astype(str) - pin_df[hit_rank] = pin_df[hit_rank].astype(float).astype(int) - if retention_time_column: - pin_df[retention_time_column] = pin_df[retention_time_column].astype(float) - for col in feature_columns: - pin_df[col] = pin_df[col].astype(float) - - # label = 1 for target, -1 for decoy. Convert to Boolean. - pin_df[label] = pin_df[label] == "1" - rescoring_features = {"Original": feature_columns} - - return PsmContainer( - psms=pin_df, - label_column=label, - scan_column=scan, - spectrum_column=specid, - ms_data_file_column=None, - peptide_column=peptide, - protein_column=protein, - rescoring_features=rescoring_features, - hit_rank_column=hit_rank, - retention_time_column=retention_time_column, - )
- - - -
-[docs] -def _read_single_pin_as_df(pin_file: str) -> pd.DataFrame: - """ - Read a single PIN file into a DataFrame. - - Parameters - ---------- - pin_file : str - The file path to the PIN file. - - Returns - ------- - pd.DataFrame - A DataFrame containing the PSM data. - - Notes - ----- - This function: - 1. Reads the PIN file header - 2. Processes the proteins column as a tab-separated list - 3. Creates a DataFrame with the processed data - """ - logger.info(f"Reading PIN file: {pin_file}") - with open(pin_file, "r") as f: - header = f.readline().strip().split("\t") - header_len = len(header) - data = [] - for line in f: - parts = line.strip().split("\t") - proteins_column_num = len(parts) - header_len + 1 - proteins = "\t".join(parts[-proteins_column_num:]) - data.append(parts[: len(parts) - proteins_column_num] + [proteins]) - df = pd.DataFrame(data, columns=header) - logger.debug(f"Header: {header}") - return df
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/psm_container.html b/docs/source/_build/html/_modules/optimhc/psm_container.html deleted file mode 100644 index 576b8a1..0000000 --- a/docs/source/_build/html/_modules/optimhc/psm_container.html +++ /dev/null @@ -1,936 +0,0 @@ - - - - - - - - optimhc.psm_container — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for optimhc.psm_container

-# psm_container.py
-
-import logging
-from typing import List, Optional, Union, Dict
-import pandas as pd
-import numpy as np
-
-logger = logging.getLogger(__name__)
-
-
-
-[docs] -class PsmContainer: - """ - A container for managing peptide-spectrum matches (PSMs) in immunopeptidomics rescoring pipelines. - - Parameters - ---------- - psms : pd.DataFrame - DataFrame containing the PSM data. - label_column : str - Column containing the label (True for target, False for decoy). - scan_column : str - Column containing the scan number. - spectrum_column : str - Column containing the spectrum identifier. - ms_data_file_column : str - Column containing the MS data file that the PSM originated from. - peptide_column : str - Column containing the peptide sequence. - protein_column : str - Column containing the protein accessions. - rescoring_features : dict of str to list of str - Dictionary of feature columns for rescoring. - hit_rank_column : str, optional - Column containing the hit rank. - charge_column : str, optional - Column containing the charge state. - retention_time_column : str, optional - Column containing the retention time. - metadata_column : str, optional - Column containing metadata. - - Attributes - ---------- - psms : pd.DataFrame - Copy of the DataFrame containing the PSM data. - target_psms : pd.DataFrame - DataFrame containing only target PSMs (label = True). - decoy_psms : pd.DataFrame - DataFrame containing only decoy PSMs (label = False). - peptides : list of str - List containing all peptides from the PSM data. - columns : list of str - List of column names in the PSM DataFrame. - rescoring_features : dict of str to list of str - Dictionary of rescoring feature columns in the PSM DataFrame. - """ - - def __init__( - self, - psms: pd.DataFrame, - label_column: str, - scan_column: str, - spectrum_column: str, - ms_data_file_column: str, - peptide_column: str, - protein_column: str, - rescoring_features: Dict[str, List[str]], - hit_rank_column: Optional[str] = None, - charge_column: Optional[int] = None, - retention_time_column: Optional[str] = None, - metadata_column: Optional[str] = None, - ): - self._psms = psms.copy() - self._psms.reset_index(drop=True, inplace=True) - self.label_column = label_column - self.scan_column = scan_column - self.spectrum_column = spectrum_column - self.ms_data_file_column = ms_data_file_column - self.peptide_column = peptide_column - self.protein_column = protein_column - self.hit_rank_column = hit_rank_column - self.retention_time_column = retention_time_column - self.metadata_column = metadata_column - self.rescoring_features = rescoring_features - self.charge_column = charge_column - # rescore result column - self.rescore_result_column = None - - # check if the columns are in the dataframe - def check_column(col): - if col and col not in psms.columns: - raise ValueError(f"Column '{col}' not found in PSM data.") - - check_column(label_column) - check_column(scan_column) - check_column(spectrum_column) - check_column(ms_data_file_column) - check_column(peptide_column) - check_column(protein_column) - check_column(hit_rank_column) - check_column(retention_time_column) - check_column(charge_column) - - if psms[label_column].nunique() == 1 and psms[label_column].iloc[0] == True: - raise ValueError("All PSMs are labeled as target. No decoy PSMs found.") - elif psms[label_column].nunique() == 1 and psms[label_column].iloc[0] == False: - raise ValueError("All PSMs are labeled as decoy. No target PSMs found.") - - def check_metadata_column(col): - # check the type is Dict[str, Dict[str, str]] - if col and col not in psms.columns: - raise ValueError(f"Column '{col}' not found in PSM data.") - if not all(isinstance(x, dict) for x in self._psms[col]): - raise ValueError(f"Column '{col}' must contain dictionaries.") - - if metadata_column: - check_metadata_column(metadata_column) - - def check_rescoring_features(features: Dict[str, List[str]]): - for key, cols in features.items(): - for col in cols: - if col not in psms.columns: - raise ValueError( - f"Column '{col}' not found in PSM data for feature '{key}'." - ) - - check_rescoring_features(rescoring_features) - - # check if the number of decoy psms is not 0 - if len(self.decoy_psms) == 0: - logger.error("No decoy PSMs found. Please check the decoy prefix.") - raise ValueError("No decoy PSMs found.") - - logger.info("PsmContainer initialized with %d PSM entries.", len(self._psms)) - if self.ms_data_file_column: - logger.info( - "PSMs originated from %d MS data file(S).", - len(self._psms[ms_data_file_column].unique()), - ) - logger.info("target psms: %d", len(self.target_psms)) - logger.info("decoy psms: %d", len(self.decoy_psms)) - logger.info("unique peptides: %d", len(np.unique(self.peptides))) - logger.info("rescoing features: %s", rescoring_features) - - - @property - def psms(self) -> pd.DataFrame: - """ - Get a copy of the PSM DataFrame to prevent external modification. - - Returns - ------- - pd.DataFrame - A copy of the PSM DataFrame. - """ - # TODO: return in a specific column order - return self._psms.copy() - -
-[docs] - def __len__(self) -> int: - """ - Get the number of PSMs in the container. - - Returns - ------- - int - Number of PSMs. - """ - return len(self._psms)
- - - @property - def target_psms(self) -> pd.DataFrame: - """ - Get a DataFrame containing only target PSMs. - - Returns - ------- - pd.DataFrame - DataFrame with only target PSMs (label = True). - """ - return self._psms[self._psms[self.label_column] == True].copy() - - @property - def decoy_psms(self) -> pd.DataFrame: - """ - Get a DataFrame containing only decoy PSMs. - - Returns - ------- - pd.DataFrame - DataFrame with only decoy PSMs (label = False). - """ - return self._psms[self._psms[self.label_column] == False].copy() - - @property - def columns(self) -> List[str]: - """ - Get the column names of the PSM DataFrame. - - Returns - ------- - list of str - List of column names. - """ - return list(self._psms.columns) - - @property - def feature_columns(self) -> List[str]: - """ - Get a list of all feature columns in the PSM DataFrame. - - Returns - ------- - list of str - List of feature column names. - """ - return [col for cols in self.rescoring_features.values() for col in cols] - - @property - def feature_sources(self) -> List[str]: - """ - Get a list of all feature sources in the PSM DataFrame. - - Returns - ------- - list of str - List of feature source names. - """ - return list(self.rescoring_features.keys()) - - @property - def peptides(self) -> List[str]: - """ - Get the peptide sequences from the PSM data. - - Returns - ------- - list of str - List of peptide sequences. - """ - return self._psms[self.peptide_column].tolist() - - @property - def ms_data_files(self) -> List[str]: - """ - Get the MS data files from the PSM data. - - Returns - ------- - list of str - List of MS data file names. - """ - return self._psms[self.ms_data_file_column].tolist() - - @property - def scan_ids(self) -> List[int]: - """ - Get the scan numbers from the PSM data. - - Returns - ------- - list of int - List of scan numbers. - """ - return self._psms[self.scan_column].tolist() - - @property - def charges(self) -> List[int]: - """ - Get the charge states from the PSM data. - - Returns - ------- - list of int - List of charge states. - """ - return self._psms[self.charge_column].tolist() - - @property - def metadata(self) -> pd.Series: - """ - Get the metadata from the PSM data. - - Returns - ------- - pd.Series - Series containing metadata for each PSM. - """ - return self._psms[self.metadata_column].copy() - - @property - def spectrum_ids(self) -> List[str]: - """ - Get the spectrum identifiers from the PSM data. - - Returns - ------- - list of str - List of spectrum identifiers. - """ - return self._psms[self.spectrum_column].tolist() - - @property - def identifier_columns(self) -> List[str]: - """ - Get the columns that uniquely identify each PSM. - - Returns - ------- - list of str - List of identifier column names. - """ - return [ - self.scan_column, - self.spectrum_column, - self.peptide_column, - self.protein_column, - ] - -
-[docs] - def copy(self) -> "PsmContainer": - """ - Return a deep copy of the PsmContainer object. - - Returns - ------- - PsmContainer - A deep copy of the current PsmContainer. - """ - import copy - - return copy.deepcopy(self)
- - -
-[docs] - def __repr__(self) -> str: - """ - Return a string representation of the PsmContainer. - - Returns - ------- - str - String summary of the PsmContainer. - """ - return ( - f"PsmContainer with {len(self)} PSMs\n" - f"\t - Target PSMs: {len(self.target_psms)}\n" - f"\t - Decoy PSMs: {len(self.decoy_psms)}\n" - f"\t - Unique Peptides: {len(np.unique(self.peptides))}\n" - f"\t - Unique Spectra: {len(self._psms[self.spectrum_column].unique())}\n" - f"\t - Rescoring Features: {self.rescoring_features}\n" - )
- - -
-[docs] - def drop_features(self, features: List[str]) -> None: - """ - Drop specified features from the PSM DataFrame. - - Parameters - ---------- - features : list of str - List of feature column names to drop. - - Raises - ------ - ValueError - If any of the features do not exist in the DataFrame. - """ - missing_features = [f for f in features if f not in self._psms.columns] - if missing_features: - raise ValueError(f"Features not found in PSM data: {missing_features}") - - self._psms.drop(columns=features, inplace=True) - # Create a list of sources to update - sources_to_update = [] - for source, cols in self.rescoring_features.items(): - self.rescoring_features[source] = [ - col for col in cols if col not in features - ] - if not self.rescoring_features[source]: - sources_to_update.append(source) - - logger.info( - f"Sources to be removed: {sources_to_update}. Because all the features are removed." - ) - # Remove sources with no features left - for source in sources_to_update: - del self.rescoring_features[source]
- - -
-[docs] - def drop_source(self, source: str) -> None: - """ - Drop all features associated with a specific source from the PSM DataFrame. - - Parameters - ---------- - source : str - Name of the source to drop. - - Raises - ------ - ValueError - If the source does not exist in the rescoring features. - """ - if source not in self.rescoring_features: - raise ValueError(f"Source '{source}' not found in rescoring features.") - self.drop_features(self.rescoring_features[source])
- - -
-[docs] - def add_metadata( - self, - metadata_df: pd.DataFrame, - psms_key: Union[str, List[str]], - metadata_key: Union[str, List[str]], - source, - ) -> None: - """ - Merge new metadata into the PSM DataFrame based on specified columns. - Metadata from the specified source is stored as a nested dictionary inside the metadata column. - - Parameters - ---------- - metadata_df : pd.DataFrame - DataFrame containing new metadata to add. - psms_key : str or list of str - Column name(s) in the PSM data to merge on. - metadata_key : str or list of str - Column name(s) in the metadata data to merge on. - source : str - Name of the source of the new metadata. - """ - if self.metadata_column is None: - logger.info("No existing metadata column. Creating new metadata column.") - self.metadata_column = "metadata" - self._psms["metadata"] = [{} for _ in range(len(self._psms))] - - metadata_cols = [col for col in metadata_df.columns if col not in metadata_key] - merged_df = self.psms.merge( - metadata_df, left_on=psms_key, right_on=metadata_key, how="left" - ) - if source in self._psms["metadata"]: - logger.warning(f"{source} already exists in metadata. Overwriting.") - for col in metadata_cols: - merged_df["metadata"] = merged_df.apply( - lambda row: { - **row["metadata"], - source: ( - {col: row[col]} - if source not in row["metadata"] - else {**row["metadata"][source], col: row[col]} - ), - }, - axis=1, - ) - - self._psms["metadata"] = merged_df["metadata"]
- - -
-[docs] - def get_top_hits(self, n: int = 1): - """ - Get the top n hits based on the hit rank column. - If the hit rank column is not specified, returns the original PSMs. - - Parameters - ---------- - n : int, optional - The number of top hits to return. Default is 1. - - Returns - ------- - PsmContainer - A new PsmContainer object containing the top n hits. - """ - if self.hit_rank_column is None: - logger.warning("Rank column not specified. Return the original PSMs.") - return self.copy() - - psms = self.copy() - psms._psms = psms._psms[psms._psms[self.hit_rank_column] <= n] - return psms
- - -
-[docs] - def add_features( - self, - features_df: pd.DataFrame, - psms_key: Union[str, List[str]], - feature_key: Union[str, List[str]], - source: str, - suffix: Optional[str] = None, - ) -> None: - """Merge new features into the PSM DataFrame based on specified columns. - - This method performs a left join between the PSM data and feature data, - ensuring that all PSMs are preserved while adding new features. It handles - column name conflicts through optional suffixing and maintains feature source - tracking. - - Parameters - ---------- - features_df : pd.DataFrame - DataFrame containing new features to add. - psms_key : str or list of str - Column name(s) in the PSM data to merge on. - feature_key : str or list of str - Column name(s) in the features data to merge on. - source : str - Name of the source of the new features (e.g., 'deeplc', 'netmhc'). - suffix : str, optional - Suffix to add to the new columns if there's a name conflict. - Required when new feature columns have the same names as existing columns. - For example, if adding features from different sources (e.g., 'score' from - DeepLC and NetMHC), use suffixes like '_deeplc' or '_netmhc' to distinguish them. - - Returns - ------- - None - - Raises - ------ - ValueError - If duplicate columns exist without suffix. - If merging features changes the number of PSMs. - - Notes - ----- - The method follows these steps: - 1. Validates input and prepares merge keys - 2. Checks for column name conflicts - 3. Manages feature source: if the source already exists, it will be overwritten - 4. Performs left join merge - 5. Verifies data integrity - - Suffix Usage - ----------- - The suffix parameter is used to handle column name conflicts: - - When adding features from different sources that might have the same column names - - When you want to keep both the original and new features with the same name - - When you need to track the source of features in the column names - - If suffix is not provided and there are duplicate column names: - - The method will raise a ValueError - - You must either provide a suffix or rename the columns before adding - - Examples - -------- - >>> container = PsmContainer(...) - >>> # Adding features without suffix (no conflicts) - >>> features_df1 = pd.DataFrame({ - ... 'scan': [1, 2, 3], - ... 'feature1': [0.1, 0.2, 0.3], - ... 'feature2': [0.4, 0.5, 0.6] - ... }) - >>> container.add_features( - ... features_df1, - ... psms_key='scan', - ... feature_key='scan', - ... source='source1' - ... ) - >>> # Adding features with suffix (handling conflicts) - >>> features_df2 = pd.DataFrame({ - ... 'scan': [1, 2, 3], - ... 'score': [0.8, 0.9, 0.7], # This would conflict with existing 'score' - ... 'feature3': [0.7, 0.8, 0.9] - ... }) - >>> container.add_features( - ... features_df2, - ... psms_key='scan', - ... feature_key='scan', - ... source='source2', - ... suffix='_new' # 'score' becomes 'score_new' - ... ) - """ - if isinstance(psms_key, str): - psms_key = [psms_key] - - if isinstance(feature_key, str): - feature_key = [feature_key] - - new_feature_cols = [ - col for col in features_df.columns if col not in feature_key - ] - - for cols in new_feature_cols: - if cols in self._psms.columns: - logger.warning(f"Column '{cols}' already exists in PSM data.") - if suffix is None: - logger.warning("No suffix provided. Using default suffix ") - raise ValueError("Duplicate columns exist. No suffix provided.") - else: - logger.warning( - f"Suffix '{suffix}' provided. Using suffix '{suffix}'." - ) - logger.info(f"Adding {len(new_feature_cols)} new features from {source}.") - - if not new_feature_cols: - logger.warning("No new features to add. Check the feature key and PSMs key.") - logger.warning(f"Feature key: {feature_key}; PSMs key: {psms_key}") - - if source in self.rescoring_features: - logger.warning( - f"{source} already exists in rescoring features. Overwriting." - ) - self.drop_source(source) - - # TODO: reluctant logic - if suffix is None: - suffixes = ("", "") - else: - suffixes = ("", suffix) - - self.rescoring_features[source] = [ - col + suffixes[1] for col in new_feature_cols - ] - features_df = features_df.rename( - columns={col: col + suffixes[1] for col in new_feature_cols} - ) - original_len = len(self._psms) - # avoid merge the right key to the psms - self._psms = self._psms.merge( - features_df, left_on=psms_key, right_on=feature_key, how="left" - ) - - if feature_key != psms_key: - cols_to_drop = [ - col - for col in feature_key - if col not in psms_key and col in self._psms.columns - ] - if cols_to_drop: - logger.debug( - f"Dropping columns from feature_key not in psms_key: {cols_to_drop}" - ) - self._psms.drop(columns=cols_to_drop, inplace=True) - - if len(self._psms) != original_len: - raise ValueError( - "Merging features resulted in a change in the number of PSMs. Check for duplicate keys." - )
- - -
-[docs] - def add_features_by_index( - self, features_df: pd.DataFrame, source: str, suffix: Optional[str] = None - ) -> None: - """ - Merge new features into the PSM DataFrame based on the DataFrame index. - - Parameters - ---------- - features_df : pd.DataFrame - DataFrame containing new features to add. - source : str - Name of the source of the new features. - suffix : str, optional - Suffix to add to the new columns if there's a name conflict. - """ - new_feature_cols = [col for col in features_df.columns] - for col in new_feature_cols: - if col in self._psms.columns: - logger.warning(f"Column '{col}' already exists in PSM data.") - if suffix is None: - logger.warning("No suffix provided. Using default suffix.") - raise ValueError("Duplicate columns exist. No suffix provided.") - else: - logger.warning( - f"Suffix '{suffix}' provided. Using suffix '{suffix}'." - ) - - logger.info( - f"Adding {len(new_feature_cols)} new features from {source} by index." - ) - - if not new_feature_cols: - logger.warning("No new features to add.") - raise ValueError("No new features to add.") - - if source in self.rescoring_features: - logger.warning( - f"{source} already exists in rescoring features. Overwriting." - ) - self.drop_source(source) - - if suffix is None: - suffixes = ("", "") - else: - suffixes = ("", suffix) - - self.rescoring_features[source] = [ - col + suffixes[1] for col in new_feature_cols - ] - features_df.rename( - columns={col: col + suffixes[1] for col in new_feature_cols}, inplace=True - ) - original_len = len(self._psms) - self._psms = self._psms.merge( - features_df, - left_index=True, - right_index=True, - how="left", # Perform a left join to preserve all original PSM data - ) - - # Ensure that the merge did not change the number of rows in the PSM DataFrame - if len(self._psms) != original_len: - raise ValueError( - "Merging features resulted in a change in the number of PSMs. Check for duplicate indices." - )
- - -
-[docs] - def add_results( - self, - results_df: pd.DataFrame, - psms_key: Union[str, List[str]], - result_key: Union[str, List[str]], - ) -> None: - """ - Add results of rescore engine to the PSM DataFrame based on specified columns. - - Parameters - ---------- - results_df : pd.DataFrame - DataFrame containing new results to add. - psms_key : str or list of str - Column name(s) in the PSM data to merge on. - result_key : str or list of str - Column name(s) in the results data to merge on. - """ - if self.rescore_result_column is not None: - logger.warning("Rescore result column already exists. Overwriting.") - - if set(self._psms.columns) & set(results_df.columns): - raise ValueError( - "Duplicate columns exist. Please rename the columns in the results data." - ) - - self.rescore_result_column = result_key - self._psms = self._psms.merge( - results_df, - left_on=psms_key, - right_on=result_key, - how="left", - validate="one_to_one", - ) - self._psms.drop(columns=result_key, inplace=True) - logger.info(f"Added rescore results to PSM data.")
- - -
-[docs] - def write_pin(self, output_file: str, source: List[str] = None) -> None: - """ - Write the PSM data to a Percolator input (PIN) file. - - Percolator accepts input in a simple tab-delimited format where each row contains features associated with a single PSM: - id <tab> label <tab> scannr <tab> feature1 <tab> ... <tab> featureN <tab> peptide <tab> proteinId1 <tab> .. <tab> proteinIdM - With header: - SpecID <tab> Label <tab> ScanNr <tab> Feature1 <tab> ... <tab> FeatureN <tab> Peptide <tab> Proteins - - Parameters - ---------- - output_file : str - The path to the output PIN file. - source : list of str, optional - List of feature sources to include. If None, include all sources. - - Returns - ------- - pd.DataFrame - The DataFrame written to the PIN file. - """ - df = self._psms.copy() - # Check if the label column is str - # Case1: label column is str - if df[self.label_column].dtype == "str": - df["PercolatorLabel"] = df[self.label_column].map({"True": 1, "False": -1}) - # Case2: label column is bool - elif df[self.label_column].dtype == "bool": - df["PercolatorLabel"] = df[self.label_column].map({True: 1, False: -1}) - else: - # try to convert to bool - logger.warning("Label column is not str or bool. Converting to bool.") - df["PercolatorLabel"] = ( - df[self.label_column].astype(bool).map({True: 1, False: -1}) - ) - logger.info("Writing PIN file to %s", output_file) - - feature_cols = [] - if source is None: - for _, cols in self.rescoring_features.items(): - feature_cols.extend(cols) - else: - for s in source: - if s not in self.rescoring_features: - raise ValueError(f"Source '{s}' not found in rescoring features.") - feature_cols.extend(self.rescoring_features[s]) - - pin_df = pd.DataFrame() - pin_df["SpecID"] = df[self.spectrum_column] - pin_df["Label"] = df["PercolatorLabel"] - pin_df["ScanNr"] = df[self.scan_column] - for col in feature_cols: - pin_df[col] = df[col] - - pin_df["Peptide"] = df[self.peptide_column] - pin_df["Proteins"] = df[self.protein_column].apply( - lambda x: ";".join(x) if isinstance(x, (list, tuple)) else x - ) - pin_df.to_csv(output_file, sep="\t", index=False) - logger.info("PIN file written to %s", output_file) - - return pin_df
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/utils.html b/docs/source/_build/html/_modules/optimhc/utils.html deleted file mode 100644 index 03ef12c..0000000 --- a/docs/source/_build/html/_modules/optimhc/utils.html +++ /dev/null @@ -1,381 +0,0 @@ - - - - - - - - optimhc.utils — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for optimhc.utils

-# utils.py
-
-import pandas as pd
-import numpy as np
-from pathlib import Path
-from typing import List, Optional, Dict, Union
-from logging import getLogger
-
-logger = getLogger(__name__)
-
-
-
-[docs] -def convert_pfm_to_pwm(pfm_filename, pseudocount=0.8, background_freqs=None): - """ - Convert a Position Frequency Matrix (PFM) file to a Position Weight Matrix (PWM). - - Parameters - ---------- - pfm_filename : str - The file path to the PFM file. - pseudocount : float, optional - The pseudocount to add to the PFM to avoid zero probabilities. Default is 0.8. - background_freqs : dict, optional - Dictionary containing the background frequencies for each amino acid. - If None, uses 1/20 for all. - - Returns - ------- - pd.DataFrame - DataFrame representation of the PWM. - - Notes - ----- - The conversion process involves: - 1. Adding pseudocounts to the PFM - 2. Converting to Position Probability Matrix (PPM) - 3. Converting to PWM using log2(PPM/background_freqs) - """ - # Default background frequencies if not provided - if background_freqs is None: - background_freqs = 1 / 20 - - pfm = pd.read_csv(pfm_filename, sep="\t", header=None, index_col=0) - pfm.drop(pfm.columns[-1], axis=1, inplace=True) # Drop any extraneous columns - pfm_pseudo = pfm + pseudocount - ppm = pfm_pseudo.div(pfm_pseudo.sum(axis=0), axis=1) - pwm = np.log2(ppm.div(list(background_freqs.values()))) - - return pwm
- - - -
-[docs] -def remove_pre_and_nxt_aa(peptide: str) -> str: - """ - Remove the pre and next amino acids from a peptide sequence. - - Parameters - ---------- - peptide : str - The peptide sequence with flanking amino acids. - Example: '.AANDAGYFNDEMAPIEVKTK.' - - Returns - ------- - str - The peptide sequence with flanking amino acids removed. - Example: 'AANDAGYFNDEMAPIEVKTK' - - Notes - ----- - This function removes any amino acids before the first '.' and after the last '.' - in the peptide sequence. - """ - import re - - return re.sub(r"^[^.]*\.|\.[^.]*$", "", peptide)
- - - -
-[docs] -def remove_modifications(peptide: str, keep_modification=None) -> str: - """ - Remove modifications from a peptide sequence, with an option to keep specific modifications. - - Parameters - ---------- - peptide : str - The peptide sequence with modifications in brackets. - Example: 'AANDAGYFNDEM[15.9949]APIEVK[42.0106]TK' - keep_modification : str or list, optional - The modification(s) to keep. If provided, only these modifications will be - preserved in the output sequence. Default is None. - - Returns - ------- - str - The peptide sequence with modifications removed or kept. - Example: 'AANDAGYFNDEMAPIEVKTK' (if keep_modification is None) - Example: 'AANDAGYFNDEM[15.9949]APIEVKTK' (if keep_modification=['15.9949']) - - Notes - ----- - Modifications are specified in square brackets after the amino acid. - If keep_modification is provided, only those specific modifications will be - preserved in the output sequence. - """ - import re - - if keep_modification is None: - return re.sub(r"\[.*?\]", "", peptide) - else: - if not isinstance(keep_modification, list): - keep_modification = [keep_modification] - - def replace_mod(match): - mod = match.group(0) - if any(keep in mod for keep in keep_modification): - return mod - return "" - - return re.sub(r"\[.*?\]", replace_mod, peptide)
- - - -
-[docs] -def preprocess_peptide(peptide: str) -> str: - """ - Preprocess the peptide sequence by removing flanking regions and modifications. - - Parameters - ---------- - peptide : str - Original peptide sequence with possible flanking regions and modifications. - Example: '.AANDAGYFNDEM[15.9949]APIEVK[42.0106]TK.' - - Returns - ------- - str - Cleaned peptide sequence without flanking regions and modifications. - Example: 'AANDAGYFNDEMAPIEVKTK' - - Notes - ----- - This function performs two operations in sequence: - 1. Removes flanking amino acids using remove_pre_and_nxt_aa - 2. Removes all modifications using remove_modifications - """ - peptide = remove_pre_and_nxt_aa(peptide) - peptide = remove_modifications(peptide) - return peptide
- - - -
-[docs] -def list_all_files_in_directory(directory_path: str) -> List[str]: - """ - Retrieve all files in the specified directory and return a list of file paths. - - Parameters - ---------- - directory_path : str - The path to the directory to search in. - Example: '/path/to/directory' - - Returns - ------- - list of str - List of absolute file paths found in the directory and its subdirectories. - Example: ['/path/to/directory/file1.txt', '/path/to/directory/subdir/file2.txt'] - - Notes - ----- - This function recursively searches through all subdirectories and returns - absolute paths for all files found. - """ - path = Path(directory_path) - file_list = [str(file) for file in path.rglob("*") if file.is_file()] - return file_list
- - - -
-[docs] -def extract_unimod_from_peptidoform(peptide: str, mod_dict: dict) -> tuple: - """ - Convert a modified peptide sequence into DeepLC format. - - Parameters - ---------- - peptide : str - The input peptide sequence with modifications in brackets. - Example: 'AANDAGYFNDEM[15.9949]APIEVK[42.0106]TK' - mod_dict : dict - Dictionary mapping modification names (in peptide) to corresponding Unimod names. - Example: {'15.9949': 'Oxidation', '42.0106': 'Acetyl'} - - Returns - ------- - tuple - (seq, modifications): - seq : str - The unmodified peptide sequence. - modifications : str - String of modifications formatted as `position|UnimodName`, separated by pipes `|`. - """ - clean_sequence = "" - modifications = [] - current_position = 0 - i = 0 - while i < len(peptide): - if peptide[i] == "[": - end = peptide.find("]", i) - if end == -1: - raise ValueError( - f"Invalid modification format in {peptide}: missing closing bracket." - ) - mod_name = peptide[i + 1 : end] - if mod_name not in mod_dict: - raise ValueError( - f"Modification '{mod_name}' not found in the dictionary." - ) - modifications.append(f"{current_position}|{mod_dict[mod_name]}") - i = end + 1 - else: - clean_sequence += peptide[i] - current_position += 1 - i += 1 - - modification_str = "|".join(modifications) - logger.debug(f"Original peptide: {peptide}") - logger.debug(f"Output clean_sequence: {clean_sequence}") - logger.debug(f"Output modifications: {modification_str}") - return clean_sequence, modification_str
- - - -
-[docs] -def convert_to_unimod_format(peptide: str, mod_dict: dict) -> str: - """ - Convert a modified peptide sequence into Unimod format. - - Parameters - ---------- - peptide : str - The input peptide sequence with modifications in brackets. - Example: 'AANDAGYFNDEM[15.9949]APIEVK[42.0106]TK' - mod_dict : dict - Dictionary mapping modification names (in peptide) to corresponding Unimod names. - Example: {'15.9949': 'UNIMOD:4', '42.0106': 'UNIMOD:1'} - - Returns - ------- - str - The peptide sequence formatted for Unimod. - Example: 'AANDAGYFNDEM[UNIMOD:4]APIEVK[UNIMOD:1]TK' - - Notes - ----- - This function replaces the modification names in brackets with their - corresponding Unimod identifiers while preserving the peptide sequence - structure. - """ - res = peptide - for key, value in mod_dict.items(): - res = res.replace(f"[{key}]", f"[{value}]") - logger.debug(f"Original peptide: {peptide}") - logger.debug(f"Output peptide: {res}") - return res
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/visualization/plot_features.html b/docs/source/_build/html/_modules/optimhc/visualization/plot_features.html deleted file mode 100644 index 094d07b..0000000 --- a/docs/source/_build/html/_modules/optimhc/visualization/plot_features.html +++ /dev/null @@ -1,317 +0,0 @@ - - - - - - - - optimhc.visualization.plot_features — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for optimhc.visualization.plot_features

-import numpy as np
-import matplotlib.pyplot as plt
-import logging
-from itertools import cycle
-from matplotlib.patches import Patch
-from optimhc.psm_container import PsmContainer
-import seaborn as sns
-from optimhc.visualization.save_or_show_plot import save_or_show_plot
-
-logger = logging.getLogger(__name__)
-
-
-
-[docs] -def plot_feature_importance( - models, rescoring_features, save_path=None, sort=False, error=False, **kwargs -): - """ - Unified function to plot average feature importance across multiple models. - - This function supports: - - Linear models (e.g., Linear SVR) which provide an 'estimator' attribute with a 'coef_'. - The absolute value of the coefficients is used for importance, and hatch patterns are applied - to differentiate between positive and negative coefficients. - - XGBoost models which provide a 'feature_importances_' attribute. Since these values are - always positive, no hatch patterns are applied. - - Parameters - ---------- - models : list - A list of model objects. - For linear models, each model should have an 'estimator' with 'coef_'. - For XGBoost models, each model should have a 'feature_importances_' attribute. - rescoring_features : dict - A dictionary where keys are sources and values are lists of features. - save_path : str, optional - If provided, saves the plot to the specified path. - sort : bool, optional - If True, sorts the features by their importance in descending order. - Default is False. - error : bool, optional - If True, adds error bars to the plot. Default is False. - **kwargs : dict - Additional plotting parameters such as 'figsize' and 'dpi'. - - Notes - ----- - The function automatically detects the model type based on the presence of the corresponding attribute. - For linear models, it uses hatch patterns to differentiate between positive and negative coefficients. - For XGBoost models, it uses solid bars since the importances are always positive. - """ - # Determine the model type based on the first model in the list. - if hasattr(models[0].estimator, "coef_"): - model_type = "linear" - elif hasattr(models[0].estimator, "feature_importances_"): - model_type = "xgb" - else: - raise ValueError( - "Model type not recognized. Model must have 'estimator.coef_' for linear models or " - "'estimator.feature_importances_' for XGBoost models." - ) - - if model_type == "linear": - feature_importances = [] - for model in models: - coefficients = model.estimator.coef_ - feature_importances.append(np.abs(coefficients).mean(axis=0)) - logger.debug(f"Model coefficients shape: {coefficients.shape}") - - average_feature_importance = np.mean(feature_importances, axis=0) - std_feature_importance = np.std(feature_importances, axis=0) - feature_signs = np.mean( - [model.estimator.coef_.mean(axis=0) for model in models], axis=0 - ) - - elif model_type == "xgb": - feature_importances = [] - for model in models: - # Use the XGBoost feature importances directly as they are always positive - imp = model.estimator.feature_importances_ - feature_importances.append(imp) - logger.debug(f"Model feature importances shape: {imp.shape}") - - average_feature_importance = np.mean(feature_importances, axis=0) - std_feature_importance = np.std(feature_importances, axis=0) - feature_signs = np.ones_like(average_feature_importance) - - logger.debug( - f"Total rescoring features: {len(sum(rescoring_features.values(), []))}" - ) - logger.debug( - f"Average feature importance length: {len(average_feature_importance)}" - ) - logger.debug(f"Features: {sum(rescoring_features.values(), [])}") - - figsize = kwargs.get("figsize", (15, 10)) - dpi = kwargs.get("dpi", 300) - - all_features = [] - all_importances = [] - all_errors = [] - all_colors = [] - all_hatches = [] # Hatch patterns will be applied only for linear models. - - color_cycle = cycle(plt.cm.tab10.colors) - - for source, features in rescoring_features.items(): - color = next(color_cycle) - indices = [ - i - for i, name in enumerate(sum(rescoring_features.values(), [])) - if name in features - ] - source_importances = average_feature_importance[indices] - source_std = std_feature_importance[indices] - - if model_type == "linear": - source_signs = feature_signs[indices] - - if sort: - sorted_indices = np.argsort(-source_importances) - else: - sorted_indices = np.arange(len(features)) - - sorted_features = [features[i] for i in sorted_indices] - sorted_importances = source_importances[sorted_indices] - sorted_std = source_std[sorted_indices] - - all_features.extend(sorted_features) - all_importances.extend(sorted_importances) - all_errors.extend(sorted_std) - all_colors.extend([color] * len(sorted_features)) - - if model_type == "linear": - # For linear models, use hatch patterns to differentiate positive and negative coefficients. - # An empty hatch ('') for positive and '\\' for negative coefficients. - sorted_signs = source_signs[sorted_indices] - all_hatches.extend(["" if sign >= 0 else "\\\\" for sign in sorted_signs]) - else: - all_hatches.extend([""] * len(sorted_features)) - - fig, ax = plt.subplots(figsize=figsize, dpi=dpi) - if error: - bars = ax.barh( - all_features, all_importances, xerr=all_errors, color=all_colors, capsize=5 - ) - else: - bars = ax.barh(all_features, all_importances, color=all_colors) - - if model_type == "linear": - for bar, hatch in zip(bars, all_hatches): - bar.set_hatch(hatch) - legend_hatches = [ - Patch(facecolor="white", edgecolor="black", hatch="", label="Positive"), - Patch(facecolor="white", edgecolor="black", hatch="\\\\", label="Negative"), - ] - legend_colors = [ - Patch(facecolor=color, edgecolor="black", label=source) - for color, source in zip(plt.cm.tab10.colors, rescoring_features.keys()) - ] - ax.legend(handles=legend_hatches + legend_colors, loc="best") - else: - legend_colors = [ - Patch(facecolor=color, edgecolor="black", label=source) - for color, source in zip(plt.cm.tab10.colors, rescoring_features.keys()) - ] - ax.legend(handles=legend_colors, loc="best") - - ax.set_xlabel("Average Feature Importance") - ax.set_ylabel("Feature") - - save_or_show_plot(save_path, logger)
- - - -
-[docs] -def visualize_feature_correlation(psms: PsmContainer, save_path=None, **kwargs): - """ - Visualize the correlation between features in a DataFrame using a heatmap. - - Parameters - ---------- - psms : PsmContainer - A PsmContainer object containing the features to visualize. - save_path : str, optional - The file path to save the plot. If not provided, the plot is displayed. - **kwargs : dict - Additional plotting parameters such as `figsize` and `dpi`, etc. - - Notes - ----- - This function: - 1. Extracts all rescoring features from the PsmContainer - 2. Calculates the correlation matrix between features - 3. Creates a heatmap visualization of the correlations - 4. Uses a coolwarm colormap to show positive and negative correlations - """ - figsize = kwargs.get("figsize", (40, 36)) - dpi = kwargs.get("dpi", 300) - - rescoring_features = [ - item for sublist in psms.rescoring_features.values() for item in sublist - ] - fig, ax = plt.subplots(figsize=figsize, dpi=dpi) - corr = psms.psms[rescoring_features].corr() - # sns.heatmap(corr, annot=True, fmt=".2f", cmap='coolwarm', ax=ax) - sns.heatmap(corr, cmap="coolwarm", ax=ax) - ax.set_title("Feature Correlation Heatmap") - - save_or_show_plot(save_path, logger)
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/visualization/plot_roc.html b/docs/source/_build/html/_modules/optimhc/visualization/plot_roc.html deleted file mode 100644 index 0bbe968..0000000 --- a/docs/source/_build/html/_modules/optimhc/visualization/plot_roc.html +++ /dev/null @@ -1,194 +0,0 @@ - - - - - - - - optimhc.visualization.plot_roc — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for optimhc.visualization.plot_roc

-import os
-import matplotlib.pyplot as plt
-from optimhc.visualization.save_or_show_plot import save_or_show_plot
-import logging
-
-logger = logging.getLogger(__name__)
-
-
-
-[docs] -def plot_qvalues( - results, - save_path=None, - dpi=300, - figsize=(15, 10), - threshold=0.05, - colors=None, - **kwargs, -): - """ - Plot q-values for the given results. - - Parameters - ---------- - results : object or list - A list of results objects or a single result object. - Each result object should have a method `plot_qvalues`. - save_path : str, optional - If provided, saves the plot to the specified path. - dpi : int, optional - The resolution of the plot. Default is 300. - figsize : tuple, optional - The size of the figure. Default is (15, 10). - threshold : float, optional - The q-value threshold for plotting. Default is 0.05. - colors : list, optional - A list of colors for the plots. If not provided, uses default colors. - **kwargs : dict - Additional plotting parameters. - - Returns - ------- - None - The function displays or saves the plot. - - Notes - ----- - This function: - 1. Creates a figure with two subplots for PSMs and peptides - 2. Plots q-values for each result with different colors - 3. Adds legends and titles to each subplot - 4. Saves or displays the plot based on save_path - """ - if not isinstance(results, list): - results = [results] - - if colors is None: - colors = [ - "#1f77b4", - "#ff7f0e", - "#2ca02c", - "#d62728", - "#9467bd", - "#8c564b", - "#e377c2", - "#7f7f7f", - "#bcbd22", - "#17becf", - ] - - fig, axs = plt.subplots(1, 2, figsize=figsize, dpi=dpi) - - for i, result in enumerate(results): - for ax, level in zip(axs, ["psms", "peptides"]): - result.plot_qvalues( - level=level, - c=colors[i % len(colors)], - ax=ax, - threshold=threshold, - label=f"Result {i+1}" if len(results) > 1 else "Results", - linewidth=1, - **kwargs, - ) - ax.legend(frameon=False) - ax.set_title(f"{level}") - - plt.tight_layout() - return save_or_show_plot(save_path, logger)
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/visualization/plot_tdc_distribution.html b/docs/source/_build/html/_modules/optimhc/visualization/plot_tdc_distribution.html deleted file mode 100644 index 7640244..0000000 --- a/docs/source/_build/html/_modules/optimhc/visualization/plot_tdc_distribution.html +++ /dev/null @@ -1,205 +0,0 @@ - - - - - - - - optimhc.visualization.plot_tdc_distribution — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for optimhc.visualization.plot_tdc_distribution

-import matplotlib.pyplot as plt
-import seaborn as sns
-import numpy as np
-import logging
-from optimhc.psm_container import PsmContainer
-from optimhc.visualization.save_or_show_plot import save_or_show_plot
-
-logger = logging.getLogger(__name__)
-
-
-
-[docs] -def visualize_target_decoy_features( - psms: PsmContainer, num_cols=5, save_path=None, **kwargs -): - """ - Visualize the distribution of features in a DataFrame using kernel density estimation plots. - - Parameters - ---------- - psms : PsmContainer - A PsmContainer object containing the features to visualize. - num_cols : int, optional - The number of columns in the plot grid. Default is 5. - save_path : str, optional - The file path to save the plot. If not provided, the plot is displayed. - **kwargs : dict - Additional plotting parameters such as `figsize` and `dpi`, etc. - - Notes - ----- - This function: - 1. Extracts rescoring features from the PsmContainer - 2. Filters out features with only one unique value - 3. Creates a grid of plots showing the distribution of each feature - 4. Separates target and decoy PSMs in each plot - 5. Uses kernel density estimation to show the distribution shape - """ - rescoring_features = [ - item - for sublist in psms.rescoring_features.values() - for item in sublist - if item != psms.hit_rank_column - ] - - # drop features that only have one value - rescoring_features = [ - feature - for feature in rescoring_features - if len(psms.psms[feature].unique()) > 1 - ] - - num_features = len(rescoring_features) - num_rows = (num_features + num_cols - 1) // num_cols - - figsize = kwargs.get("figsize", (15, num_rows * 15 / num_cols)) - dpi = kwargs.get("dpi", 300) - - fig, axes = plt.subplots(num_rows, num_cols, figsize=figsize, dpi=dpi) - axes = axes.flatten() - - psms_top_hits = psms.psms[psms.psms[psms.hit_rank_column] == 1].copy() - num_true_hits = len(psms_top_hits[psms_top_hits[psms.label_column] == True]) - num_decoys = len(psms_top_hits[psms_top_hits[psms.label_column] == False]) - logger.debug(f"Number of true hits: {num_true_hits}") - logger.debug(f"Number of decoys: {num_decoys}") - psms_top_hits[psms.label_column] = psms_top_hits[psms.label_column].map( - {True: "Target", False: "Decoy"} - ) - - for i, feature in enumerate(rescoring_features): - try: - ax = axes[i] - sns.histplot( - data=psms_top_hits, - x=feature, - hue=psms.label_column, - ax=ax, - bins="auto", - common_bins=True, - multiple="dodge", - fill=True, - alpha=0.3, - stat="frequency", - kde=True, - linewidth=0, - ) - ax.set_title(feature) - ax.set_xlabel("") - ax.set_ylabel("") - - except Exception as e: - logger.error(f"Error plotting feature {feature}: {e}") - ax.set_visible(False) - - for j in range(i + 1, len(axes)): - fig.delaxes(axes[j]) - - save_or_show_plot(save_path, logger)
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_modules/optimhc/visualization/save_or_show_plot.html b/docs/source/_build/html/_modules/optimhc/visualization/save_or_show_plot.html deleted file mode 100644 index 02900cc..0000000 --- a/docs/source/_build/html/_modules/optimhc/visualization/save_or_show_plot.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - optimhc.visualization.save_or_show_plot — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for optimhc.visualization.save_or_show_plot

-import os
-import matplotlib.pyplot as plt
-
-
-
-[docs] -def save_or_show_plot(save_path, logger, tight_layout=True): - if tight_layout: - plt.tight_layout() - if save_path: - os.makedirs(os.path.dirname(save_path), exist_ok=True) - plt.savefig(save_path, bbox_inches="tight") - logger.info(f"Plot saved to {save_path}") - else: - plt.show()
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/_sources/api.rst.txt b/docs/source/_build/html/_sources/api.rst.txt deleted file mode 100644 index 004ab07..0000000 --- a/docs/source/_build/html/_sources/api.rst.txt +++ /dev/null @@ -1,141 +0,0 @@ -API Reference -============ - -Core Modules ------------- - -.. automodule:: optimhc.core - :members: - :undoc-members: - :show-inheritance: - -Logging -------- - -.. automodule:: optimhc.core.logging_helper - :members: - :undoc-members: - :show-inheritance: - -CLI Interface ------------- - -.. automodule:: optimhc.cli - :members: - :undoc-members: - :show-inheritance: - - -PSM Container ------------- - -.. automodule:: optimhc.psm_container - :members: - :undoc-members: - :show-inheritance: - -Utilities --------- - -.. automodule:: optimhc.utils - :members: - :undoc-members: - :show-inheritance: - -Parser Modules -------------- - -.. automodule:: optimhc.parser - :members: - :undoc-members: - :show-inheritance: - -Parser Submodules -~~~~~~~~~~~~~~~ - -.. automodule:: optimhc.parser.pin - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.parser.pepxml - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.parser.mzml - :members: - :undoc-members: - :show-inheritance: - -Feature Generator ---------------- - -.. automodule:: optimhc.feature_generator - :members: - :undoc-members: - :show-inheritance: - -Feature Generator Submodules -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: optimhc.feature_generator.base_feature_generator - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.basic - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.DeepLC - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.mhcflurry - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.netMHCpan - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.netMHCIIpan - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.overlapping_peptide - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.PWM - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.spectra_similarity - :members: - :undoc-members: - :show-inheritance: - -Rescore Modules --------------- - -.. automodule:: optimhc.rescore - :members: - :undoc-members: - :show-inheritance: - -Visualization ------------- - -.. automodule:: optimhc.visualization - :members: - :undoc-members: - :show-inheritance: \ No newline at end of file diff --git a/docs/source/_build/html/_sources/examples.rst.txt b/docs/source/_build/html/_sources/examples.rst.txt deleted file mode 100644 index 4b7c683..0000000 --- a/docs/source/_build/html/_sources/examples.rst.txt +++ /dev/null @@ -1,4 +0,0 @@ -Examples -======== - -Example notebooks and code will be added here. \ No newline at end of file diff --git a/docs/source/_build/html/_sources/index.rst.txt b/docs/source/_build/html/_sources/index.rst.txt deleted file mode 100644 index 1116cb6..0000000 --- a/docs/source/_build/html/_sources/index.rst.txt +++ /dev/null @@ -1,32 +0,0 @@ -Welcome to optiMHC's documentation! -================================ - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - installation - usage - api - examples - -Introduction ------------- - -optiMHC is a high-performance rescoring pipeline for immunopeptidomics data to significantly enhance peptide identification performance. - -Features --------- - -* High-performance peptide identification -* Advanced rescoring algorithms -* Comprehensive documentation -* Easy to use API - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/docs/source/_build/html/_sources/installation.rst.txt b/docs/source/_build/html/_sources/installation.rst.txt deleted file mode 100644 index 97fab37..0000000 --- a/docs/source/_build/html/_sources/installation.rst.txt +++ /dev/null @@ -1,20 +0,0 @@ -Installation -============ - -Requirements ------------- - -* Python 3.9 or higher -* pip or conda - - -Development Installation ----------------------- - -To install the development version: - -.. code-block:: bash - - git clone https://github.com/yourusername/optimhc.git - cd optimhc - pip install -e . \ No newline at end of file diff --git a/docs/source/_build/html/_sources/usage.rst.txt b/docs/source/_build/html/_sources/usage.rst.txt deleted file mode 100644 index d0c08a9..0000000 --- a/docs/source/_build/html/_sources/usage.rst.txt +++ /dev/null @@ -1,4 +0,0 @@ -Usage -===== - -Usage instructions will be added here. \ No newline at end of file diff --git a/docs/source/_build/html/_static/_sphinx_javascript_frameworks_compat.js b/docs/source/_build/html/_static/_sphinx_javascript_frameworks_compat.js deleted file mode 100644 index 8141580..0000000 --- a/docs/source/_build/html/_static/_sphinx_javascript_frameworks_compat.js +++ /dev/null @@ -1,123 +0,0 @@ -/* Compatability shim for jQuery and underscores.js. - * - * Copyright Sphinx contributors - * Released under the two clause BSD licence - */ - -/** - * small helper function to urldecode strings - * - * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL - */ -jQuery.urldecode = function(x) { - if (!x) { - return x - } - return decodeURIComponent(x.replace(/\+/g, ' ')); -}; - -/** - * small helper function to urlencode strings - */ -jQuery.urlencode = encodeURIComponent; - -/** - * This function returns the parsed url parameters of the - * current request. Multiple values per key are supported, - * it will always return arrays of strings for the value parts. - */ -jQuery.getQueryParameters = function(s) { - if (typeof s === 'undefined') - s = document.location.search; - var parts = s.substr(s.indexOf('?') + 1).split('&'); - var result = {}; - for (var i = 0; i < parts.length; i++) { - var tmp = parts[i].split('=', 2); - var key = jQuery.urldecode(tmp[0]); - var value = jQuery.urldecode(tmp[1]); - if (key in result) - result[key].push(value); - else - result[key] = [value]; - } - return result; -}; - -/** - * highlight a given string on a jquery object by wrapping it in - * span elements with the given class name. - */ -jQuery.fn.highlightText = function(text, className) { - function highlight(node, addItems) { - if (node.nodeType === 3) { - var val = node.nodeValue; - var pos = val.toLowerCase().indexOf(text); - if (pos >= 0 && - !jQuery(node.parentNode).hasClass(className) && - !jQuery(node.parentNode).hasClass("nohighlight")) { - var span; - var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.className = className; - } - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - if (isInSVG) { - var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - var bbox = node.parentElement.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute('class', className); - addItems.push({ - "parent": node.parentNode, - "target": rect}); - } - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this, addItems); - }); - } - } - var addItems = []; - var result = this.each(function() { - highlight(this, addItems); - }); - for (var i = 0; i < addItems.length; ++i) { - jQuery(addItems[i].parent).before(addItems[i].target); - } - return result; -}; - -/* - * backward compatibility for jQuery.browser - * This will be supported until firefox bug is fixed. - */ -if (!jQuery.browser) { - jQuery.uaMatch = function(ua) { - ua = ua.toLowerCase(); - - var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || - /(webkit)[ \/]([\w.]+)/.exec(ua) || - /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || - /(msie) ([\w.]+)/.exec(ua) || - ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || - []; - - return { - browser: match[ 1 ] || "", - version: match[ 2 ] || "0" - }; - }; - jQuery.browser = {}; - jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; -} diff --git a/docs/source/_build/html/_static/basic.css b/docs/source/_build/html/_static/basic.css deleted file mode 100644 index f316efc..0000000 --- a/docs/source/_build/html/_static/basic.css +++ /dev/null @@ -1,925 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -div.section::after { - display: block; - content: ''; - clear: left; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; - word-wrap: break-word; - overflow-wrap : break-word; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox form.search { - overflow: hidden; -} - -div.sphinxsidebar #searchbox input[type="text"] { - float: left; - width: 80%; - padding: 0.25em; - box-sizing: border-box; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - float: left; - width: 20%; - border-left: none; - padding: 0.25em; - box-sizing: border-box; -} - - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li p.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; - margin-left: auto; - margin-right: auto; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable ul { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; -} - -table.indextable > tbody > tr > td > ul { - padding-left: 0em; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- domain module index --------------------------------------------------- */ - -table.modindextable td { - padding: 2px; - border-collapse: collapse; -} - -/* -- general body styles --------------------------------------------------- */ - -div.body { - min-width: 360px; - max-width: 800px; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - -moz-hyphens: auto; - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -a.headerlink { - visibility: hidden; -} - -a:visited { - color: #551A8B; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink, -caption:hover > a.headerlink, -p.caption:hover > a.headerlink, -div.code-block-caption:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, figure.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, figure.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, figure.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -img.align-default, figure.align-default, .figure.align-default { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-default { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar, -aside.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px; - background-color: #ffe; - width: 40%; - float: right; - clear: right; - overflow-x: auto; -} - -p.sidebar-title { - font-weight: bold; -} - -nav.contents, -aside.topic, -div.admonition, div.topic, blockquote { - clear: left; -} - -/* -- topics ---------------------------------------------------------------- */ - -nav.contents, -aside.topic, -div.topic { - border: 1px solid #ccc; - padding: 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- content of sidebars/topics/admonitions -------------------------------- */ - -div.sidebar > :last-child, -aside.sidebar > :last-child, -nav.contents > :last-child, -aside.topic > :last-child, -div.topic > :last-child, -div.admonition > :last-child { - margin-bottom: 0; -} - -div.sidebar::after, -aside.sidebar::after, -nav.contents::after, -aside.topic::after, -div.topic::after, -div.admonition::after, -blockquote::after { - display: block; - content: ''; - clear: both; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - margin-top: 10px; - margin-bottom: 10px; - border: 0; - border-collapse: collapse; -} - -table.align-center { - margin-left: auto; - margin-right: auto; -} - -table.align-default { - margin-left: auto; - margin-right: auto; -} - -table caption span.caption-number { - font-style: italic; -} - -table caption span.caption-text { -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -th > :first-child, -td > :first-child { - margin-top: 0px; -} - -th > :last-child, -td > :last-child { - margin-bottom: 0px; -} - -/* -- figures --------------------------------------------------------------- */ - -div.figure, figure { - margin: 0.5em; - padding: 0.5em; -} - -div.figure p.caption, figcaption { - padding: 0.3em; -} - -div.figure p.caption span.caption-number, -figcaption span.caption-number { - font-style: italic; -} - -div.figure p.caption span.caption-text, -figcaption span.caption-text { -} - -/* -- field list styles ----------------------------------------------------- */ - -table.field-list td, table.field-list th { - border: 0 !important; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -/* -- hlist styles ---------------------------------------------------------- */ - -table.hlist { - margin: 1em 0; -} - -table.hlist td { - vertical-align: top; -} - -/* -- object description styles --------------------------------------------- */ - -.sig { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; -} - -.sig-name, code.descname { - background-color: transparent; - font-weight: bold; -} - -.sig-name { - font-size: 1.1em; -} - -code.descname { - font-size: 1.2em; -} - -.sig-prename, code.descclassname { - background-color: transparent; -} - -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - -.sig-param.n { - font-style: italic; -} - -/* C++ specific styling */ - -.sig-inline.c-texpr, -.sig-inline.cpp-texpr { - font-family: unset; -} - -.sig.c .k, .sig.c .kt, -.sig.cpp .k, .sig.cpp .kt { - color: #0033B3; -} - -.sig.c .m, -.sig.cpp .m { - color: #1750EB; -} - -.sig.c .s, .sig.c .sc, -.sig.cpp .s, .sig.cpp .sc { - color: #067D17; -} - - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -:not(li) > ol > li:first-child > :first-child, -:not(li) > ul > li:first-child > :first-child { - margin-top: 0px; -} - -:not(li) > ol > li:last-child > :last-child, -:not(li) > ul > li:last-child > :last-child { - margin-bottom: 0px; -} - -ol.simple ol p, -ol.simple ul p, -ul.simple ol p, -ul.simple ul p { - margin-top: 0; -} - -ol.simple > li:not(:first-child) > p, -ul.simple > li:not(:first-child) > p { - margin-top: 0; -} - -ol.simple p, -ul.simple p { - margin-bottom: 0; -} - -aside.footnote > span, -div.citation > span { - float: left; -} -aside.footnote > span:last-of-type, -div.citation > span:last-of-type { - padding-right: 0.5em; -} -aside.footnote > p { - margin-left: 2em; -} -div.citation > p { - margin-left: 4em; -} -aside.footnote > p:last-of-type, -div.citation > p:last-of-type { - margin-bottom: 0em; -} -aside.footnote > p:last-of-type:after, -div.citation > p:last-of-type:after { - content: ""; - clear: both; -} - -dl.field-list { - display: grid; - grid-template-columns: fit-content(30%) auto; -} - -dl.field-list > dt { - font-weight: bold; - word-break: break-word; - padding-left: 0.5em; - padding-right: 5px; -} - -dl.field-list > dd { - padding-left: 0.5em; - margin-top: 0em; - margin-left: 0em; - margin-bottom: 0em; -} - -dl { - margin-bottom: 15px; -} - -dd > :first-child { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -.sig dd { - margin-top: 0px; - margin-bottom: 0px; -} - -.sig dl { - margin-top: 0px; - margin-bottom: 0px; -} - -dl > dd:last-child, -dl > dd:last-child > :last-child { - margin-bottom: 0; -} - -dt:target, span.highlighted { - background-color: #fbe54e; -} - -rect.highlighted { - fill: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -.classifier:before { - font-style: normal; - margin: 0 0.5em; - content: ":"; - display: inline-block; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -.translated { - background-color: rgba(207, 255, 207, 0.2) -} - -.untranslated { - background-color: rgba(255, 207, 207, 0.2) -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -pre, div[class*="highlight-"] { - clear: both; -} - -span.pre { - -moz-hyphens: none; - -ms-hyphens: none; - -webkit-hyphens: none; - hyphens: none; - white-space: nowrap; -} - -div[class*="highlight-"] { - margin: 1em 0; -} - -td.linenos pre { - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - display: block; -} - -table.highlighttable tbody { - display: block; -} - -table.highlighttable tr { - display: flex; -} - -table.highlighttable td { - margin: 0; - padding: 0; -} - -table.highlighttable td.linenos { - padding-right: 0.5em; -} - -table.highlighttable td.code { - flex: 1; - overflow: hidden; -} - -.highlight .hll { - display: block; -} - -div.highlight pre, -table.highlighttable pre { - margin: 0; -} - -div.code-block-caption + div { - margin-top: 0; -} - -div.code-block-caption { - margin-top: 1em; - padding: 2px 5px; - font-size: small; -} - -div.code-block-caption code { - background-color: transparent; -} - -table.highlighttable td.linenos, -span.linenos, -div.highlight span.gp { /* gp: Generic.Prompt */ - user-select: none; - -webkit-user-select: text; /* Safari fallback only */ - -webkit-user-select: none; /* Chrome/Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+ */ -} - -div.code-block-caption span.caption-number { - padding: 0.1em 0.3em; - font-style: italic; -} - -div.code-block-caption span.caption-text { -} - -div.literal-block-wrapper { - margin: 1em 0; -} - -code.xref, a code { - background-color: transparent; - font-weight: bold; -} - -h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -span.eqno a.headerlink { - position: absolute; - z-index: 1; -} - -div.math:hover a.headerlink { - visibility: visible; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/docs/source/_build/html/_static/css/badge_only.css b/docs/source/_build/html/_static/css/badge_only.css deleted file mode 100644 index 88ba55b..0000000 --- a/docs/source/_build/html/_static/css/badge_only.css +++ /dev/null @@ -1 +0,0 @@ -.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions .rst-other-versions .rtd-current-item{font-weight:700}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}#flyout-search-form{padding:6px} \ No newline at end of file diff --git a/docs/source/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff b/docs/source/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff deleted file mode 100644 index 6cb6000..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff and /dev/null differ diff --git a/docs/source/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 b/docs/source/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 deleted file mode 100644 index 7059e23..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 and /dev/null differ diff --git a/docs/source/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff b/docs/source/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff deleted file mode 100644 index f815f63..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff and /dev/null differ diff --git a/docs/source/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 b/docs/source/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 deleted file mode 100644 index f2c76e5..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 and /dev/null differ diff --git a/docs/source/_build/html/_static/css/fonts/fontawesome-webfont.eot b/docs/source/_build/html/_static/css/fonts/fontawesome-webfont.eot deleted file mode 100644 index e9f60ca..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/docs/source/_build/html/_static/css/fonts/fontawesome-webfont.svg b/docs/source/_build/html/_static/css/fonts/fontawesome-webfont.svg deleted file mode 100644 index 855c845..0000000 --- a/docs/source/_build/html/_static/css/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,2671 +0,0 @@ - - - - -Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 - By ,,, -Copyright Dave Gandy 2016. All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/source/_build/html/_static/css/fonts/fontawesome-webfont.ttf b/docs/source/_build/html/_static/css/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 35acda2..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/docs/source/_build/html/_static/css/fonts/fontawesome-webfont.woff b/docs/source/_build/html/_static/css/fonts/fontawesome-webfont.woff deleted file mode 100644 index 400014a..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/docs/source/_build/html/_static/css/fonts/fontawesome-webfont.woff2 b/docs/source/_build/html/_static/css/fonts/fontawesome-webfont.woff2 deleted file mode 100644 index 4d13fc6..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/fontawesome-webfont.woff2 and /dev/null differ diff --git a/docs/source/_build/html/_static/css/fonts/lato-bold-italic.woff b/docs/source/_build/html/_static/css/fonts/lato-bold-italic.woff deleted file mode 100644 index 88ad05b..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/lato-bold-italic.woff and /dev/null differ diff --git a/docs/source/_build/html/_static/css/fonts/lato-bold-italic.woff2 b/docs/source/_build/html/_static/css/fonts/lato-bold-italic.woff2 deleted file mode 100644 index c4e3d80..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/lato-bold-italic.woff2 and /dev/null differ diff --git a/docs/source/_build/html/_static/css/fonts/lato-bold.woff b/docs/source/_build/html/_static/css/fonts/lato-bold.woff deleted file mode 100644 index c6dff51..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/lato-bold.woff and /dev/null differ diff --git a/docs/source/_build/html/_static/css/fonts/lato-bold.woff2 b/docs/source/_build/html/_static/css/fonts/lato-bold.woff2 deleted file mode 100644 index bb19504..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/lato-bold.woff2 and /dev/null differ diff --git a/docs/source/_build/html/_static/css/fonts/lato-normal-italic.woff b/docs/source/_build/html/_static/css/fonts/lato-normal-italic.woff deleted file mode 100644 index 76114bc..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/lato-normal-italic.woff and /dev/null differ diff --git a/docs/source/_build/html/_static/css/fonts/lato-normal-italic.woff2 b/docs/source/_build/html/_static/css/fonts/lato-normal-italic.woff2 deleted file mode 100644 index 3404f37..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/lato-normal-italic.woff2 and /dev/null differ diff --git a/docs/source/_build/html/_static/css/fonts/lato-normal.woff b/docs/source/_build/html/_static/css/fonts/lato-normal.woff deleted file mode 100644 index ae1307f..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/lato-normal.woff and /dev/null differ diff --git a/docs/source/_build/html/_static/css/fonts/lato-normal.woff2 b/docs/source/_build/html/_static/css/fonts/lato-normal.woff2 deleted file mode 100644 index 3bf9843..0000000 Binary files a/docs/source/_build/html/_static/css/fonts/lato-normal.woff2 and /dev/null differ diff --git a/docs/source/_build/html/_static/css/theme.css b/docs/source/_build/html/_static/css/theme.css deleted file mode 100644 index 0f14f10..0000000 --- a/docs/source/_build/html/_static/css/theme.css +++ /dev/null @@ -1,4 +0,0 @@ -html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search .wy-dropdown>aactive,.wy-side-nav-search .wy-dropdown>afocus,.wy-side-nav-search>a:hover,.wy-side-nav-search>aactive,.wy-side-nav-search>afocus{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon,.wy-side-nav-search>a.icon{display:block}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.switch-menus{position:relative;display:block;margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-side-nav-search>div.switch-menus>div.language-switch,.wy-side-nav-search>div.switch-menus>div.version-switch{display:inline-block;padding:.2em}.wy-side-nav-search>div.switch-menus>div.language-switch select,.wy-side-nav-search>div.switch-menus>div.version-switch select{display:inline-block;margin-right:-2rem;padding-right:2rem;max-width:240px;text-align-last:center;background:none;border:none;border-radius:0;box-shadow:none;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-size:1em;font-weight:400;color:hsla(0,0%,100%,.3);cursor:pointer;appearance:none;-webkit-appearance:none;-moz-appearance:none}.wy-side-nav-search>div.switch-menus>div.language-switch select:active,.wy-side-nav-search>div.switch-menus>div.language-switch select:focus,.wy-side-nav-search>div.switch-menus>div.language-switch select:hover,.wy-side-nav-search>div.switch-menus>div.version-switch select:active,.wy-side-nav-search>div.switch-menus>div.version-switch select:focus,.wy-side-nav-search>div.switch-menus>div.version-switch select:hover{background:hsla(0,0%,100%,.1);color:hsla(0,0%,100%,.5)}.wy-side-nav-search>div.switch-menus>div.language-switch select option,.wy-side-nav-search>div.switch-menus>div.version-switch select option{color:#000}.wy-side-nav-search>div.switch-menus>div.language-switch:has(>select):after,.wy-side-nav-search>div.switch-menus>div.version-switch:has(>select):after{display:inline-block;width:1.5em;height:100%;padding:.1em;content:"\f0d7";font-size:1em;line-height:1.2em;font-family:FontAwesome;text-align:center;pointer-events:none;box-sizing:border-box}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions .rst-other-versions .rtd-current-item{font-weight:700}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}#flyout-search-form{padding:6px}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/docs/source/_build/html/_static/doctools.js b/docs/source/_build/html/_static/doctools.js deleted file mode 100644 index 4d67807..0000000 --- a/docs/source/_build/html/_static/doctools.js +++ /dev/null @@ -1,156 +0,0 @@ -/* - * doctools.js - * ~~~~~~~~~~~ - * - * Base JavaScript utilities for all Sphinx HTML documentation. - * - * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ -"use strict"; - -const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ - "TEXTAREA", - "INPUT", - "SELECT", - "BUTTON", -]); - -const _ready = (callback) => { - if (document.readyState !== "loading") { - callback(); - } else { - document.addEventListener("DOMContentLoaded", callback); - } -}; - -/** - * Small JavaScript module for the documentation. - */ -const Documentation = { - init: () => { - Documentation.initDomainIndexTable(); - Documentation.initOnKeyListeners(); - }, - - /** - * i18n support - */ - TRANSLATIONS: {}, - PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), - LOCALE: "unknown", - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext: (string) => { - const translated = Documentation.TRANSLATIONS[string]; - switch (typeof translated) { - case "undefined": - return string; // no translation - case "string": - return translated; // translation exists - default: - return translated[0]; // (singular, plural) translation tuple exists - } - }, - - ngettext: (singular, plural, n) => { - const translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated !== "undefined") - return translated[Documentation.PLURAL_EXPR(n)]; - return n === 1 ? singular : plural; - }, - - addTranslations: (catalog) => { - Object.assign(Documentation.TRANSLATIONS, catalog.messages); - Documentation.PLURAL_EXPR = new Function( - "n", - `return (${catalog.plural_expr})` - ); - Documentation.LOCALE = catalog.locale; - }, - - /** - * helper function to focus on search bar - */ - focusSearchBar: () => { - document.querySelectorAll("input[name=q]")[0]?.focus(); - }, - - /** - * Initialise the domain index toggle buttons - */ - initDomainIndexTable: () => { - const toggler = (el) => { - const idNumber = el.id.substr(7); - const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); - if (el.src.substr(-9) === "minus.png") { - el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; - toggledRows.forEach((el) => (el.style.display = "none")); - } else { - el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; - toggledRows.forEach((el) => (el.style.display = "")); - } - }; - - const togglerElements = document.querySelectorAll("img.toggler"); - togglerElements.forEach((el) => - el.addEventListener("click", (event) => toggler(event.currentTarget)) - ); - togglerElements.forEach((el) => (el.style.display = "")); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); - }, - - initOnKeyListeners: () => { - // only install a listener if it is really needed - if ( - !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && - !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS - ) - return; - - document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; - // bail with special keys - if (event.altKey || event.ctrlKey || event.metaKey) return; - - if (!event.shiftKey) { - switch (event.key) { - case "ArrowLeft": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const prevLink = document.querySelector('link[rel="prev"]'); - if (prevLink && prevLink.href) { - window.location.href = prevLink.href; - event.preventDefault(); - } - break; - case "ArrowRight": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const nextLink = document.querySelector('link[rel="next"]'); - if (nextLink && nextLink.href) { - window.location.href = nextLink.href; - event.preventDefault(); - } - break; - } - } - - // some keyboard layouts may need Shift to get / - switch (event.key) { - case "/": - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; - Documentation.focusSearchBar(); - event.preventDefault(); - } - }); - }, -}; - -// quick alias for translations -const _ = Documentation.gettext; - -_ready(Documentation.init); diff --git a/docs/source/_build/html/_static/documentation_options.js b/docs/source/_build/html/_static/documentation_options.js deleted file mode 100644 index 13d90ff..0000000 --- a/docs/source/_build/html/_static/documentation_options.js +++ /dev/null @@ -1,13 +0,0 @@ -const DOCUMENTATION_OPTIONS = { - VERSION: '0.1.0', - LANGUAGE: 'en', - COLLAPSE_INDEX: false, - BUILDER: 'html', - FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false, - SHOW_SEARCH_SUMMARY: true, - ENABLE_SEARCH_SHORTCUTS: true, -}; \ No newline at end of file diff --git a/docs/source/_build/html/_static/file.png b/docs/source/_build/html/_static/file.png deleted file mode 100644 index a858a41..0000000 Binary files a/docs/source/_build/html/_static/file.png and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-bold.eot b/docs/source/_build/html/_static/fonts/Lato/lato-bold.eot deleted file mode 100644 index 3361183..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-bold.eot and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-bold.ttf b/docs/source/_build/html/_static/fonts/Lato/lato-bold.ttf deleted file mode 100644 index 29f691d..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-bold.ttf and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-bold.woff b/docs/source/_build/html/_static/fonts/Lato/lato-bold.woff deleted file mode 100644 index c6dff51..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-bold.woff and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-bold.woff2 b/docs/source/_build/html/_static/fonts/Lato/lato-bold.woff2 deleted file mode 100644 index bb19504..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-bold.woff2 and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-bolditalic.eot b/docs/source/_build/html/_static/fonts/Lato/lato-bolditalic.eot deleted file mode 100644 index 3d41549..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-bolditalic.eot and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-bolditalic.ttf b/docs/source/_build/html/_static/fonts/Lato/lato-bolditalic.ttf deleted file mode 100644 index f402040..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-bolditalic.ttf and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-bolditalic.woff b/docs/source/_build/html/_static/fonts/Lato/lato-bolditalic.woff deleted file mode 100644 index 88ad05b..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-bolditalic.woff and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-bolditalic.woff2 b/docs/source/_build/html/_static/fonts/Lato/lato-bolditalic.woff2 deleted file mode 100644 index c4e3d80..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-bolditalic.woff2 and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-italic.eot b/docs/source/_build/html/_static/fonts/Lato/lato-italic.eot deleted file mode 100644 index 3f82642..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-italic.eot and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-italic.ttf b/docs/source/_build/html/_static/fonts/Lato/lato-italic.ttf deleted file mode 100644 index b4bfc9b..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-italic.ttf and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-italic.woff b/docs/source/_build/html/_static/fonts/Lato/lato-italic.woff deleted file mode 100644 index 76114bc..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-italic.woff and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-italic.woff2 b/docs/source/_build/html/_static/fonts/Lato/lato-italic.woff2 deleted file mode 100644 index 3404f37..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-italic.woff2 and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-regular.eot b/docs/source/_build/html/_static/fonts/Lato/lato-regular.eot deleted file mode 100644 index 11e3f2a..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-regular.eot and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-regular.ttf b/docs/source/_build/html/_static/fonts/Lato/lato-regular.ttf deleted file mode 100644 index 74decd9..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-regular.ttf and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-regular.woff b/docs/source/_build/html/_static/fonts/Lato/lato-regular.woff deleted file mode 100644 index ae1307f..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-regular.woff and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/Lato/lato-regular.woff2 b/docs/source/_build/html/_static/fonts/Lato/lato-regular.woff2 deleted file mode 100644 index 3bf9843..0000000 Binary files a/docs/source/_build/html/_static/fonts/Lato/lato-regular.woff2 and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot b/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot deleted file mode 100644 index 79dc8ef..0000000 Binary files a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf b/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf deleted file mode 100644 index df5d1df..0000000 Binary files a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff b/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff deleted file mode 100644 index 6cb6000..0000000 Binary files a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 b/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 deleted file mode 100644 index 7059e23..0000000 Binary files a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot b/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot deleted file mode 100644 index 2f7ca78..0000000 Binary files a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf b/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf deleted file mode 100644 index eb52a79..0000000 Binary files a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff b/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff deleted file mode 100644 index f815f63..0000000 Binary files a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff and /dev/null differ diff --git a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 b/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 deleted file mode 100644 index f2c76e5..0000000 Binary files a/docs/source/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 and /dev/null differ diff --git a/docs/source/_build/html/_static/jquery.js b/docs/source/_build/html/_static/jquery.js deleted file mode 100644 index c4c6022..0000000 --- a/docs/source/_build/html/_static/jquery.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t a.language.name.localeCompare(b.language.name)); - - const languagesHTML = ` -
-
Languages
- ${languages - .map( - (translation) => ` -
- ${translation.language.code} -
- `, - ) - .join("\n")} -
- `; - return languagesHTML; - } - - function renderVersions(config) { - if (!config.versions.active.length) { - return ""; - } - const versionsHTML = ` -
-
Versions
- ${config.versions.active - .map( - (version) => ` -
- ${version.slug} -
- `, - ) - .join("\n")} -
- `; - return versionsHTML; - } - - function renderDownloads(config) { - if (!Object.keys(config.versions.current.downloads).length) { - return ""; - } - const downloadsNameDisplay = { - pdf: "PDF", - epub: "Epub", - htmlzip: "HTML", - }; - - const downloadsHTML = ` -
-
Downloads
- ${Object.entries(config.versions.current.downloads) - .map( - ([name, url]) => ` -
- ${downloadsNameDisplay[name]} -
- `, - ) - .join("\n")} -
- `; - return downloadsHTML; - } - - document.addEventListener("readthedocs-addons-data-ready", function (event) { - const config = event.detail.data(); - - const flyout = ` -
- - Read the Docs - v: ${config.versions.current.slug} - - -
-
- ${renderLanguages(config)} - ${renderVersions(config)} - ${renderDownloads(config)} -
-
On Read the Docs
-
- Project Home -
-
- Builds -
-
- Downloads -
-
-
-
Search
-
-
- -
-
-
-
- - Hosted by Read the Docs - -
-
- `; - - // Inject the generated flyout into the body HTML element. - document.body.insertAdjacentHTML("beforeend", flyout); - - // Trigger the Read the Docs Addons Search modal when clicking on the "Search docs" input from inside the flyout. - document - .querySelector("#flyout-search-form") - .addEventListener("focusin", () => { - const event = new CustomEvent("readthedocs-search-show"); - document.dispatchEvent(event); - }); - }) -} - -if (themeLanguageSelector || themeVersionSelector) { - function onSelectorSwitch(event) { - const option = event.target.selectedIndex; - const item = event.target.options[option]; - window.location.href = item.dataset.url; - } - - document.addEventListener("readthedocs-addons-data-ready", function (event) { - const config = event.detail.data(); - - const versionSwitch = document.querySelector( - "div.switch-menus > div.version-switch", - ); - if (themeVersionSelector) { - let versions = config.versions.active; - if (config.versions.current.hidden || config.versions.current.type === "external") { - versions.unshift(config.versions.current); - } - const versionSelect = ` - - `; - - versionSwitch.innerHTML = versionSelect; - versionSwitch.firstElementChild.addEventListener("change", onSelectorSwitch); - } - - const languageSwitch = document.querySelector( - "div.switch-menus > div.language-switch", - ); - - if (themeLanguageSelector) { - if (config.projects.translations.length) { - // Add the current language to the options on the selector - let languages = config.projects.translations.concat( - config.projects.current, - ); - languages = languages.sort((a, b) => - a.language.name.localeCompare(b.language.name), - ); - - const languageSelect = ` - - `; - - languageSwitch.innerHTML = languageSelect; - languageSwitch.firstElementChild.addEventListener("change", onSelectorSwitch); - } - else { - languageSwitch.remove(); - } - } - }); -} - -document.addEventListener("readthedocs-addons-data-ready", function (event) { - // Trigger the Read the Docs Addons Search modal when clicking on "Search docs" input from the topnav. - document - .querySelector("[role='search'] input") - .addEventListener("focusin", () => { - const event = new CustomEvent("readthedocs-search-show"); - document.dispatchEvent(event); - }); -}); \ No newline at end of file diff --git a/docs/source/_build/html/_static/language_data.js b/docs/source/_build/html/_static/language_data.js deleted file mode 100644 index 367b8ed..0000000 --- a/docs/source/_build/html/_static/language_data.js +++ /dev/null @@ -1,199 +0,0 @@ -/* - * language_data.js - * ~~~~~~~~~~~~~~~~ - * - * This script contains the language-specific data used by searchtools.js, - * namely the list of stopwords, stemmer, scorer and splitter. - * - * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; - - -/* Non-minified version is copied as a separate JS file, if available */ - -/** - * Porter Stemmer - */ -var Stemmer = function() { - - var step2list = { - ational: 'ate', - tional: 'tion', - enci: 'ence', - anci: 'ance', - izer: 'ize', - bli: 'ble', - alli: 'al', - entli: 'ent', - eli: 'e', - ousli: 'ous', - ization: 'ize', - ation: 'ate', - ator: 'ate', - alism: 'al', - iveness: 'ive', - fulness: 'ful', - ousness: 'ous', - aliti: 'al', - iviti: 'ive', - biliti: 'ble', - logi: 'log' - }; - - var step3list = { - icate: 'ic', - ative: '', - alize: 'al', - iciti: 'ic', - ical: 'ic', - ful: '', - ness: '' - }; - - var c = "[^aeiou]"; // consonant - var v = "[aeiouy]"; // vowel - var C = c + "[^aeiouy]*"; // consonant sequence - var V = v + "[aeiou]*"; // vowel sequence - - var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 - var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 - var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 - var s_v = "^(" + C + ")?" + v; // vowel in stem - - this.stemWord = function (w) { - var stem; - var suffix; - var firstch; - var origword = w; - - if (w.length < 3) - return w; - - var re; - var re2; - var re3; - var re4; - - firstch = w.substr(0,1); - if (firstch == "y") - w = firstch.toUpperCase() + w.substr(1); - - // Step 1a - re = /^(.+?)(ss|i)es$/; - re2 = /^(.+?)([^s])s$/; - - if (re.test(w)) - w = w.replace(re,"$1$2"); - else if (re2.test(w)) - w = w.replace(re2,"$1$2"); - - // Step 1b - re = /^(.+?)eed$/; - re2 = /^(.+?)(ed|ing)$/; - if (re.test(w)) { - var fp = re.exec(w); - re = new RegExp(mgr0); - if (re.test(fp[1])) { - re = /.$/; - w = w.replace(re,""); - } - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1]; - re2 = new RegExp(s_v); - if (re2.test(stem)) { - w = stem; - re2 = /(at|bl|iz)$/; - re3 = new RegExp("([^aeiouylsz])\\1$"); - re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re2.test(w)) - w = w + "e"; - else if (re3.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - else if (re4.test(w)) - w = w + "e"; - } - } - - // Step 1c - re = /^(.+?)y$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(s_v); - if (re.test(stem)) - w = stem + "i"; - } - - // Step 2 - re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step2list[suffix]; - } - - // Step 3 - re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step3list[suffix]; - } - - // Step 4 - re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; - re2 = /^(.+?)(s|t)(ion)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - if (re.test(stem)) - w = stem; - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1] + fp[2]; - re2 = new RegExp(mgr1); - if (re2.test(stem)) - w = stem; - } - - // Step 5 - re = /^(.+?)e$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - re2 = new RegExp(meq1); - re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) - w = stem; - } - re = /ll$/; - re2 = new RegExp(mgr1); - if (re.test(w) && re2.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - - // and turn initial Y back to y - if (firstch == "y") - w = firstch.toLowerCase() + w.substr(1); - return w; - } -} - diff --git a/docs/source/_build/html/_static/minus.png b/docs/source/_build/html/_static/minus.png deleted file mode 100644 index d96755f..0000000 Binary files a/docs/source/_build/html/_static/minus.png and /dev/null differ diff --git a/docs/source/_build/html/_static/nbsphinx-broken-thumbnail.svg b/docs/source/_build/html/_static/nbsphinx-broken-thumbnail.svg deleted file mode 100644 index 4919ca8..0000000 --- a/docs/source/_build/html/_static/nbsphinx-broken-thumbnail.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/docs/source/_build/html/_static/nbsphinx-code-cells.css b/docs/source/_build/html/_static/nbsphinx-code-cells.css deleted file mode 100644 index a3fb27c..0000000 --- a/docs/source/_build/html/_static/nbsphinx-code-cells.css +++ /dev/null @@ -1,259 +0,0 @@ -/* remove conflicting styling from Sphinx themes */ -div.nbinput.container div.prompt *, -div.nboutput.container div.prompt *, -div.nbinput.container div.input_area pre, -div.nboutput.container div.output_area pre, -div.nbinput.container div.input_area .highlight, -div.nboutput.container div.output_area .highlight { - border: none; - padding: 0; - margin: 0; - box-shadow: none; -} - -div.nbinput.container > div[class*=highlight], -div.nboutput.container > div[class*=highlight] { - margin: 0; -} - -div.nbinput.container div.prompt *, -div.nboutput.container div.prompt * { - background: none; -} - -div.nboutput.container div.output_area .highlight, -div.nboutput.container div.output_area pre { - background: unset; -} - -div.nboutput.container div.output_area div.highlight { - color: unset; /* override Pygments text color */ -} - -/* avoid gaps between output lines */ -div.nboutput.container div[class*=highlight] pre { - line-height: normal; -} - -/* input/output containers */ -div.nbinput.container, -div.nboutput.container { - display: -webkit-flex; - display: flex; - align-items: flex-start; - margin: 0; - width: 100%; -} -@media (max-width: 540px) { - div.nbinput.container, - div.nboutput.container { - flex-direction: column; - } -} - -/* input container */ -div.nbinput.container { - padding-top: 5px; -} - -/* last container */ -div.nblast.container { - padding-bottom: 5px; -} - -/* input prompt */ -div.nbinput.container div.prompt pre, -/* for sphinx_immaterial theme: */ -div.nbinput.container div.prompt pre > code { - color: #307FC1; -} - -/* output prompt */ -div.nboutput.container div.prompt pre, -/* for sphinx_immaterial theme: */ -div.nboutput.container div.prompt pre > code { - color: #BF5B3D; -} - -/* all prompts */ -div.nbinput.container div.prompt, -div.nboutput.container div.prompt { - width: 4.5ex; - padding-top: 5px; - position: relative; - user-select: none; -} - -div.nbinput.container div.prompt > div, -div.nboutput.container div.prompt > div { - position: absolute; - right: 0; - margin-right: 0.3ex; -} - -@media (max-width: 540px) { - div.nbinput.container div.prompt, - div.nboutput.container div.prompt { - width: unset; - text-align: left; - padding: 0.4em; - } - div.nboutput.container div.prompt.empty { - padding: 0; - } - - div.nbinput.container div.prompt > div, - div.nboutput.container div.prompt > div { - position: unset; - } -} - -/* disable scrollbars and line breaks on prompts */ -div.nbinput.container div.prompt pre, -div.nboutput.container div.prompt pre { - overflow: hidden; - white-space: pre; -} - -/* input/output area */ -div.nbinput.container div.input_area, -div.nboutput.container div.output_area { - -webkit-flex: 1; - flex: 1; - overflow: auto; -} -@media (max-width: 540px) { - div.nbinput.container div.input_area, - div.nboutput.container div.output_area { - width: 100%; - } -} - -/* input area */ -div.nbinput.container div.input_area { - border: 1px solid #e0e0e0; - border-radius: 2px; - /*background: #f5f5f5;*/ -} - -/* override MathJax center alignment in output cells */ -div.nboutput.container div[class*=MathJax] { - text-align: left !important; -} - -/* override sphinx.ext.imgmath center alignment in output cells */ -div.nboutput.container div.math p { - text-align: left; -} - -/* standard error */ -div.nboutput.container div.output_area.stderr { - background: #fdd; -} - -/* ANSI colors */ -.ansi-black-fg { color: #3E424D; } -.ansi-black-bg { background-color: #3E424D; } -.ansi-black-intense-fg { color: #282C36; } -.ansi-black-intense-bg { background-color: #282C36; } -.ansi-red-fg { color: #E75C58; } -.ansi-red-bg { background-color: #E75C58; } -.ansi-red-intense-fg { color: #B22B31; } -.ansi-red-intense-bg { background-color: #B22B31; } -.ansi-green-fg { color: #00A250; } -.ansi-green-bg { background-color: #00A250; } -.ansi-green-intense-fg { color: #007427; } -.ansi-green-intense-bg { background-color: #007427; } -.ansi-yellow-fg { color: #DDB62B; } -.ansi-yellow-bg { background-color: #DDB62B; } -.ansi-yellow-intense-fg { color: #B27D12; } -.ansi-yellow-intense-bg { background-color: #B27D12; } -.ansi-blue-fg { color: #208FFB; } -.ansi-blue-bg { background-color: #208FFB; } -.ansi-blue-intense-fg { color: #0065CA; } -.ansi-blue-intense-bg { background-color: #0065CA; } -.ansi-magenta-fg { color: #D160C4; } -.ansi-magenta-bg { background-color: #D160C4; } -.ansi-magenta-intense-fg { color: #A03196; } -.ansi-magenta-intense-bg { background-color: #A03196; } -.ansi-cyan-fg { color: #60C6C8; } -.ansi-cyan-bg { background-color: #60C6C8; } -.ansi-cyan-intense-fg { color: #258F8F; } -.ansi-cyan-intense-bg { background-color: #258F8F; } -.ansi-white-fg { color: #C5C1B4; } -.ansi-white-bg { background-color: #C5C1B4; } -.ansi-white-intense-fg { color: #A1A6B2; } -.ansi-white-intense-bg { background-color: #A1A6B2; } - -.ansi-default-inverse-fg { color: #FFFFFF; } -.ansi-default-inverse-bg { background-color: #000000; } - -.ansi-bold { font-weight: bold; } -.ansi-underline { text-decoration: underline; } - - -div.nbinput.container div.input_area div[class*=highlight] > pre, -div.nboutput.container div.output_area div[class*=highlight] > pre, -div.nboutput.container div.output_area div[class*=highlight].math, -div.nboutput.container div.output_area.rendered_html, -div.nboutput.container div.output_area > div.output_javascript, -div.nboutput.container div.output_area:not(.rendered_html) > img{ - padding: 5px; - margin: 0; -} - -/* fix copybtn overflow problem in chromium (needed for 'sphinx_copybutton') */ -div.nbinput.container div.input_area > div[class^='highlight'], -div.nboutput.container div.output_area > div[class^='highlight']{ - overflow-y: hidden; -} - -/* hide copy button on prompts for 'sphinx_copybutton' extension ... */ -.prompt .copybtn, -/* ... and 'sphinx_immaterial' theme */ -.prompt .md-clipboard.md-icon { - display: none; -} - -/* Some additional styling taken form the Jupyter notebook CSS */ -.jp-RenderedHTMLCommon table, -div.rendered_html table { - border: none; - border-collapse: collapse; - border-spacing: 0; - color: black; - font-size: 12px; - table-layout: fixed; -} -.jp-RenderedHTMLCommon thead, -div.rendered_html thead { - border-bottom: 1px solid black; - vertical-align: bottom; -} -.jp-RenderedHTMLCommon tr, -.jp-RenderedHTMLCommon th, -.jp-RenderedHTMLCommon td, -div.rendered_html tr, -div.rendered_html th, -div.rendered_html td { - text-align: right; - vertical-align: middle; - padding: 0.5em 0.5em; - line-height: normal; - white-space: normal; - max-width: none; - border: none; -} -.jp-RenderedHTMLCommon th, -div.rendered_html th { - font-weight: bold; -} -.jp-RenderedHTMLCommon tbody tr:nth-child(odd), -div.rendered_html tbody tr:nth-child(odd) { - background: #f5f5f5; -} -.jp-RenderedHTMLCommon tbody tr:hover, -div.rendered_html tbody tr:hover { - background: rgba(66, 165, 245, 0.2); -} - diff --git a/docs/source/_build/html/_static/nbsphinx-gallery.css b/docs/source/_build/html/_static/nbsphinx-gallery.css deleted file mode 100644 index 365c27a..0000000 --- a/docs/source/_build/html/_static/nbsphinx-gallery.css +++ /dev/null @@ -1,31 +0,0 @@ -.nbsphinx-gallery { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); - gap: 5px; - margin-top: 1em; - margin-bottom: 1em; -} - -.nbsphinx-gallery > a { - padding: 5px; - border: 1px dotted currentColor; - border-radius: 2px; - text-align: center; -} - -.nbsphinx-gallery > a:hover { - border-style: solid; -} - -.nbsphinx-gallery img { - max-width: 100%; - max-height: 100%; -} - -.nbsphinx-gallery > a > div:first-child { - display: flex; - align-items: start; - justify-content: center; - height: 120px; - margin-bottom: 5px; -} diff --git a/docs/source/_build/html/_static/nbsphinx-no-thumbnail.svg b/docs/source/_build/html/_static/nbsphinx-no-thumbnail.svg deleted file mode 100644 index 9dca758..0000000 --- a/docs/source/_build/html/_static/nbsphinx-no-thumbnail.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/docs/source/_build/html/_static/plus.png b/docs/source/_build/html/_static/plus.png deleted file mode 100644 index 7107cec..0000000 Binary files a/docs/source/_build/html/_static/plus.png and /dev/null differ diff --git a/docs/source/_build/html/_static/pygments.css b/docs/source/_build/html/_static/pygments.css deleted file mode 100644 index 84ab303..0000000 --- a/docs/source/_build/html/_static/pygments.css +++ /dev/null @@ -1,75 +0,0 @@ -pre { line-height: 125%; } -td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -.highlight .hll { background-color: #ffffcc } -.highlight { background: #f8f8f8; } -.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ -.highlight .err { border: 1px solid #FF0000 } /* Error */ -.highlight .k { color: #008000; font-weight: bold } /* Keyword */ -.highlight .o { color: #666666 } /* Operator */ -.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ -.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #9C6500 } /* Comment.Preproc */ -.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ -.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #A00000 } /* Generic.Deleted */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ -.highlight .gr { color: #E40000 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #008400 } /* Generic.Inserted */ -.highlight .go { color: #717171 } /* Generic.Output */ -.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #0044DD } /* Generic.Traceback */ -.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #008000 } /* Keyword.Pseudo */ -.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #B00040 } /* Keyword.Type */ -.highlight .m { color: #666666 } /* Literal.Number */ -.highlight .s { color: #BA2121 } /* Literal.String */ -.highlight .na { color: #687822 } /* Name.Attribute */ -.highlight .nb { color: #008000 } /* Name.Builtin */ -.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ -.highlight .no { color: #880000 } /* Name.Constant */ -.highlight .nd { color: #AA22FF } /* Name.Decorator */ -.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ -.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #0000FF } /* Name.Function */ -.highlight .nl { color: #767600 } /* Name.Label */ -.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #19177C } /* Name.Variable */ -.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mb { color: #666666 } /* Literal.Number.Bin */ -.highlight .mf { color: #666666 } /* Literal.Number.Float */ -.highlight .mh { color: #666666 } /* Literal.Number.Hex */ -.highlight .mi { color: #666666 } /* Literal.Number.Integer */ -.highlight .mo { color: #666666 } /* Literal.Number.Oct */ -.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ -.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ -.highlight .sc { color: #BA2121 } /* Literal.String.Char */ -.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ -.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ -.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ -.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ -.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ -.highlight .sx { color: #008000 } /* Literal.String.Other */ -.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ -.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ -.highlight .ss { color: #19177C } /* Literal.String.Symbol */ -.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ -.highlight .fm { color: #0000FF } /* Name.Function.Magic */ -.highlight .vc { color: #19177C } /* Name.Variable.Class */ -.highlight .vg { color: #19177C } /* Name.Variable.Global */ -.highlight .vi { color: #19177C } /* Name.Variable.Instance */ -.highlight .vm { color: #19177C } /* Name.Variable.Magic */ -.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/source/_build/html/_static/searchtools.js b/docs/source/_build/html/_static/searchtools.js deleted file mode 100644 index b08d58c..0000000 --- a/docs/source/_build/html/_static/searchtools.js +++ /dev/null @@ -1,620 +0,0 @@ -/* - * searchtools.js - * ~~~~~~~~~~~~~~~~ - * - * Sphinx JavaScript utilities for the full-text search. - * - * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ -"use strict"; - -/** - * Simple result scoring code. - */ -if (typeof Scorer === "undefined") { - var Scorer = { - // Implement the following function to further tweak the score for each result - // The function takes a result array [docname, title, anchor, descr, score, filename] - // and returns the new score. - /* - score: result => { - const [docname, title, anchor, descr, score, filename] = result - return score - }, - */ - - // query matches the full name of an object - objNameMatch: 11, - // or matches in the last dotted part of the object name - objPartialMatch: 6, - // Additive scores depending on the priority of the object - objPrio: { - 0: 15, // used to be importantResults - 1: 5, // used to be objectResults - 2: -5, // used to be unimportantResults - }, - // Used when the priority is not in the mapping. - objPrioDefault: 0, - - // query found in title - title: 15, - partialTitle: 7, - // query found in terms - term: 5, - partialTerm: 2, - }; -} - -const _removeChildren = (element) => { - while (element && element.lastChild) element.removeChild(element.lastChild); -}; - -/** - * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping - */ -const _escapeRegExp = (string) => - string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string - -const _displayItem = (item, searchTerms, highlightTerms) => { - const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; - const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; - const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; - const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; - const contentRoot = document.documentElement.dataset.content_root; - - const [docName, title, anchor, descr, score, _filename] = item; - - let listItem = document.createElement("li"); - let requestUrl; - let linkUrl; - if (docBuilder === "dirhtml") { - // dirhtml builder - let dirname = docName + "/"; - if (dirname.match(/\/index\/$/)) - dirname = dirname.substring(0, dirname.length - 6); - else if (dirname === "index/") dirname = ""; - requestUrl = contentRoot + dirname; - linkUrl = requestUrl; - } else { - // normal html builders - requestUrl = contentRoot + docName + docFileSuffix; - linkUrl = docName + docLinkSuffix; - } - let linkEl = listItem.appendChild(document.createElement("a")); - linkEl.href = linkUrl + anchor; - linkEl.dataset.score = score; - linkEl.innerHTML = title; - if (descr) { - listItem.appendChild(document.createElement("span")).innerHTML = - " (" + descr + ")"; - // highlight search terms in the description - if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js - highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); - } - else if (showSearchSummary) - fetch(requestUrl) - .then((responseData) => responseData.text()) - .then((data) => { - if (data) - listItem.appendChild( - Search.makeSearchSummary(data, searchTerms, anchor) - ); - // highlight search terms in the summary - if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js - highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); - }); - Search.output.appendChild(listItem); -}; -const _finishSearch = (resultCount) => { - Search.stopPulse(); - Search.title.innerText = _("Search Results"); - if (!resultCount) - Search.status.innerText = Documentation.gettext( - "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." - ); - else - Search.status.innerText = _( - "Search finished, found ${resultCount} page(s) matching the search query." - ).replace('${resultCount}', resultCount); -}; -const _displayNextItem = ( - results, - resultCount, - searchTerms, - highlightTerms, -) => { - // results left, load the summary and display it - // this is intended to be dynamic (don't sub resultsCount) - if (results.length) { - _displayItem(results.pop(), searchTerms, highlightTerms); - setTimeout( - () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), - 5 - ); - } - // search finished, update title and status message - else _finishSearch(resultCount); -}; -// Helper function used by query() to order search results. -// Each input is an array of [docname, title, anchor, descr, score, filename]. -// Order the results by score (in opposite order of appearance, since the -// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. -const _orderResultsByScoreThenName = (a, b) => { - const leftScore = a[4]; - const rightScore = b[4]; - if (leftScore === rightScore) { - // same score: sort alphabetically - const leftTitle = a[1].toLowerCase(); - const rightTitle = b[1].toLowerCase(); - if (leftTitle === rightTitle) return 0; - return leftTitle > rightTitle ? -1 : 1; // inverted is intentional - } - return leftScore > rightScore ? 1 : -1; -}; - -/** - * Default splitQuery function. Can be overridden in ``sphinx.search`` with a - * custom function per language. - * - * The regular expression works by splitting the string on consecutive characters - * that are not Unicode letters, numbers, underscores, or emoji characters. - * This is the same as ``\W+`` in Python, preserving the surrogate pair area. - */ -if (typeof splitQuery === "undefined") { - var splitQuery = (query) => query - .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) - .filter(term => term) // remove remaining empty strings -} - -/** - * Search Module - */ -const Search = { - _index: null, - _queued_query: null, - _pulse_status: -1, - - htmlToText: (htmlString, anchor) => { - const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); - for (const removalQuery of [".headerlink", "script", "style"]) { - htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() }); - } - if (anchor) { - const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`); - if (anchorContent) return anchorContent.textContent; - - console.warn( - `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.` - ); - } - - // if anchor not specified or not found, fall back to main content - const docContent = htmlElement.querySelector('[role="main"]'); - if (docContent) return docContent.textContent; - - console.warn( - "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template." - ); - return ""; - }, - - init: () => { - const query = new URLSearchParams(window.location.search).get("q"); - document - .querySelectorAll('input[name="q"]') - .forEach((el) => (el.value = query)); - if (query) Search.performSearch(query); - }, - - loadIndex: (url) => - (document.body.appendChild(document.createElement("script")).src = url), - - setIndex: (index) => { - Search._index = index; - if (Search._queued_query !== null) { - const query = Search._queued_query; - Search._queued_query = null; - Search.query(query); - } - }, - - hasIndex: () => Search._index !== null, - - deferQuery: (query) => (Search._queued_query = query), - - stopPulse: () => (Search._pulse_status = -1), - - startPulse: () => { - if (Search._pulse_status >= 0) return; - - const pulse = () => { - Search._pulse_status = (Search._pulse_status + 1) % 4; - Search.dots.innerText = ".".repeat(Search._pulse_status); - if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); - }; - pulse(); - }, - - /** - * perform a search for something (or wait until index is loaded) - */ - performSearch: (query) => { - // create the required interface elements - const searchText = document.createElement("h2"); - searchText.textContent = _("Searching"); - const searchSummary = document.createElement("p"); - searchSummary.classList.add("search-summary"); - searchSummary.innerText = ""; - const searchList = document.createElement("ul"); - searchList.classList.add("search"); - - const out = document.getElementById("search-results"); - Search.title = out.appendChild(searchText); - Search.dots = Search.title.appendChild(document.createElement("span")); - Search.status = out.appendChild(searchSummary); - Search.output = out.appendChild(searchList); - - const searchProgress = document.getElementById("search-progress"); - // Some themes don't use the search progress node - if (searchProgress) { - searchProgress.innerText = _("Preparing search..."); - } - Search.startPulse(); - - // index already loaded, the browser was quick! - if (Search.hasIndex()) Search.query(query); - else Search.deferQuery(query); - }, - - _parseQuery: (query) => { - // stem the search terms and add them to the correct list - const stemmer = new Stemmer(); - const searchTerms = new Set(); - const excludedTerms = new Set(); - const highlightTerms = new Set(); - const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); - splitQuery(query.trim()).forEach((queryTerm) => { - const queryTermLower = queryTerm.toLowerCase(); - - // maybe skip this "word" - // stopwords array is from language_data.js - if ( - stopwords.indexOf(queryTermLower) !== -1 || - queryTerm.match(/^\d+$/) - ) - return; - - // stem the word - let word = stemmer.stemWord(queryTermLower); - // select the correct list - if (word[0] === "-") excludedTerms.add(word.substr(1)); - else { - searchTerms.add(word); - highlightTerms.add(queryTermLower); - } - }); - - if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js - localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) - } - - // console.debug("SEARCH: searching for:"); - // console.info("required: ", [...searchTerms]); - // console.info("excluded: ", [...excludedTerms]); - - return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; - }, - - /** - * execute search (requires search index to be loaded) - */ - _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => { - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const titles = Search._index.titles; - const allTitles = Search._index.alltitles; - const indexEntries = Search._index.indexentries; - - // Collect multiple result groups to be sorted separately and then ordered. - // Each is an array of [docname, title, anchor, descr, score, filename]. - const normalResults = []; - const nonMainIndexResults = []; - - _removeChildren(document.getElementById("search-progress")); - - const queryLower = query.toLowerCase().trim(); - for (const [title, foundTitles] of Object.entries(allTitles)) { - if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { - for (const [file, id] of foundTitles) { - const score = Math.round(Scorer.title * queryLower.length / title.length); - const boost = titles[file] === title ? 1 : 0; // add a boost for document titles - normalResults.push([ - docNames[file], - titles[file] !== title ? `${titles[file]} > ${title}` : title, - id !== null ? "#" + id : "", - null, - score + boost, - filenames[file], - ]); - } - } - } - - // search for explicit entries in index directives - for (const [entry, foundEntries] of Object.entries(indexEntries)) { - if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { - for (const [file, id, isMain] of foundEntries) { - const score = Math.round(100 * queryLower.length / entry.length); - const result = [ - docNames[file], - titles[file], - id ? "#" + id : "", - null, - score, - filenames[file], - ]; - if (isMain) { - normalResults.push(result); - } else { - nonMainIndexResults.push(result); - } - } - } - } - - // lookup as object - objectTerms.forEach((term) => - normalResults.push(...Search.performObjectSearch(term, objectTerms)) - ); - - // lookup as search terms in fulltext - normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms)); - - // let the scorer override scores with a custom scoring function - if (Scorer.score) { - normalResults.forEach((item) => (item[4] = Scorer.score(item))); - nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); - } - - // Sort each group of results by score and then alphabetically by name. - normalResults.sort(_orderResultsByScoreThenName); - nonMainIndexResults.sort(_orderResultsByScoreThenName); - - // Combine the result groups in (reverse) order. - // Non-main index entries are typically arbitrary cross-references, - // so display them after other results. - let results = [...nonMainIndexResults, ...normalResults]; - - // remove duplicate search results - // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept - let seen = new Set(); - results = results.reverse().reduce((acc, result) => { - let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); - if (!seen.has(resultStr)) { - acc.push(result); - seen.add(resultStr); - } - return acc; - }, []); - - return results.reverse(); - }, - - query: (query) => { - const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query); - const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms); - - // for debugging - //Search.lastresults = results.slice(); // a copy - // console.info("search results:", Search.lastresults); - - // print the results - _displayNextItem(results, results.length, searchTerms, highlightTerms); - }, - - /** - * search for object names - */ - performObjectSearch: (object, objectTerms) => { - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const objects = Search._index.objects; - const objNames = Search._index.objnames; - const titles = Search._index.titles; - - const results = []; - - const objectSearchCallback = (prefix, match) => { - const name = match[4] - const fullname = (prefix ? prefix + "." : "") + name; - const fullnameLower = fullname.toLowerCase(); - if (fullnameLower.indexOf(object) < 0) return; - - let score = 0; - const parts = fullnameLower.split("."); - - // check for different match types: exact matches of full name or - // "last name" (i.e. last dotted part) - if (fullnameLower === object || parts.slice(-1)[0] === object) - score += Scorer.objNameMatch; - else if (parts.slice(-1)[0].indexOf(object) > -1) - score += Scorer.objPartialMatch; // matches in last name - - const objName = objNames[match[1]][2]; - const title = titles[match[0]]; - - // If more than one term searched for, we require other words to be - // found in the name/title/description - const otherTerms = new Set(objectTerms); - otherTerms.delete(object); - if (otherTerms.size > 0) { - const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); - if ( - [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) - ) - return; - } - - let anchor = match[3]; - if (anchor === "") anchor = fullname; - else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; - - const descr = objName + _(", in ") + title; - - // add custom score for some objects according to scorer - if (Scorer.objPrio.hasOwnProperty(match[2])) - score += Scorer.objPrio[match[2]]; - else score += Scorer.objPrioDefault; - - results.push([ - docNames[match[0]], - fullname, - "#" + anchor, - descr, - score, - filenames[match[0]], - ]); - }; - Object.keys(objects).forEach((prefix) => - objects[prefix].forEach((array) => - objectSearchCallback(prefix, array) - ) - ); - return results; - }, - - /** - * search for full-text terms in the index - */ - performTermsSearch: (searchTerms, excludedTerms) => { - // prepare search - const terms = Search._index.terms; - const titleTerms = Search._index.titleterms; - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const titles = Search._index.titles; - - const scoreMap = new Map(); - const fileMap = new Map(); - - // perform the search on the required terms - searchTerms.forEach((word) => { - const files = []; - const arr = [ - { files: terms[word], score: Scorer.term }, - { files: titleTerms[word], score: Scorer.title }, - ]; - // add support for partial matches - if (word.length > 2) { - const escapedWord = _escapeRegExp(word); - if (!terms.hasOwnProperty(word)) { - Object.keys(terms).forEach((term) => { - if (term.match(escapedWord)) - arr.push({ files: terms[term], score: Scorer.partialTerm }); - }); - } - if (!titleTerms.hasOwnProperty(word)) { - Object.keys(titleTerms).forEach((term) => { - if (term.match(escapedWord)) - arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); - }); - } - } - - // no match but word was a required one - if (arr.every((record) => record.files === undefined)) return; - - // found search word in contents - arr.forEach((record) => { - if (record.files === undefined) return; - - let recordFiles = record.files; - if (recordFiles.length === undefined) recordFiles = [recordFiles]; - files.push(...recordFiles); - - // set score for the word in each file - recordFiles.forEach((file) => { - if (!scoreMap.has(file)) scoreMap.set(file, {}); - scoreMap.get(file)[word] = record.score; - }); - }); - - // create the mapping - files.forEach((file) => { - if (!fileMap.has(file)) fileMap.set(file, [word]); - else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word); - }); - }); - - // now check if the files don't contain excluded terms - const results = []; - for (const [file, wordList] of fileMap) { - // check if all requirements are matched - - // as search terms with length < 3 are discarded - const filteredTermCount = [...searchTerms].filter( - (term) => term.length > 2 - ).length; - if ( - wordList.length !== searchTerms.size && - wordList.length !== filteredTermCount - ) - continue; - - // ensure that none of the excluded terms is in the search result - if ( - [...excludedTerms].some( - (term) => - terms[term] === file || - titleTerms[term] === file || - (terms[term] || []).includes(file) || - (titleTerms[term] || []).includes(file) - ) - ) - break; - - // select one (max) score for the file. - const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); - // add result to the result list - results.push([ - docNames[file], - titles[file], - "", - null, - score, - filenames[file], - ]); - } - return results; - }, - - /** - * helper function to return a node containing the - * search summary for a given text. keywords is a list - * of stemmed words. - */ - makeSearchSummary: (htmlText, keywords, anchor) => { - const text = Search.htmlToText(htmlText, anchor); - if (text === "") return null; - - const textLower = text.toLowerCase(); - const actualStartPosition = [...keywords] - .map((k) => textLower.indexOf(k.toLowerCase())) - .filter((i) => i > -1) - .slice(-1)[0]; - const startWithContext = Math.max(actualStartPosition - 120, 0); - - const top = startWithContext === 0 ? "" : "..."; - const tail = startWithContext + 240 < text.length ? "..." : ""; - - let summary = document.createElement("p"); - summary.classList.add("context"); - summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; - - return summary; - }, -}; - -_ready(Search.init); diff --git a/docs/source/_build/html/_static/sphinx_highlight.js b/docs/source/_build/html/_static/sphinx_highlight.js deleted file mode 100644 index 8a96c69..0000000 --- a/docs/source/_build/html/_static/sphinx_highlight.js +++ /dev/null @@ -1,154 +0,0 @@ -/* Highlighting utilities for Sphinx HTML documentation. */ -"use strict"; - -const SPHINX_HIGHLIGHT_ENABLED = true - -/** - * highlight a given string on a node by wrapping it in - * span elements with the given class name. - */ -const _highlight = (node, addItems, text, className) => { - if (node.nodeType === Node.TEXT_NODE) { - const val = node.nodeValue; - const parent = node.parentNode; - const pos = val.toLowerCase().indexOf(text); - if ( - pos >= 0 && - !parent.classList.contains(className) && - !parent.classList.contains("nohighlight") - ) { - let span; - - const closestNode = parent.closest("body, svg, foreignObject"); - const isInSVG = closestNode && closestNode.matches("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.classList.add(className); - } - - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - const rest = document.createTextNode(val.substr(pos + text.length)); - parent.insertBefore( - span, - parent.insertBefore( - rest, - node.nextSibling - ) - ); - node.nodeValue = val.substr(0, pos); - /* There may be more occurrences of search term in this node. So call this - * function recursively on the remaining fragment. - */ - _highlight(rest, addItems, text, className); - - if (isInSVG) { - const rect = document.createElementNS( - "http://www.w3.org/2000/svg", - "rect" - ); - const bbox = parent.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute("class", className); - addItems.push({ parent: parent, target: rect }); - } - } - } else if (node.matches && !node.matches("button, select, textarea")) { - node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); - } -}; -const _highlightText = (thisNode, text, className) => { - let addItems = []; - _highlight(thisNode, addItems, text, className); - addItems.forEach((obj) => - obj.parent.insertAdjacentElement("beforebegin", obj.target) - ); -}; - -/** - * Small JavaScript module for the documentation. - */ -const SphinxHighlight = { - - /** - * highlight the search words provided in localstorage in the text - */ - highlightSearchWords: () => { - if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight - - // get and clear terms from localstorage - const url = new URL(window.location); - const highlight = - localStorage.getItem("sphinx_highlight_terms") - || url.searchParams.get("highlight") - || ""; - localStorage.removeItem("sphinx_highlight_terms") - url.searchParams.delete("highlight"); - window.history.replaceState({}, "", url); - - // get individual terms from highlight string - const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); - if (terms.length === 0) return; // nothing to do - - // There should never be more than one element matching "div.body" - const divBody = document.querySelectorAll("div.body"); - const body = divBody.length ? divBody[0] : document.querySelector("body"); - window.setTimeout(() => { - terms.forEach((term) => _highlightText(body, term, "highlighted")); - }, 10); - - const searchBox = document.getElementById("searchbox"); - if (searchBox === null) return; - searchBox.appendChild( - document - .createRange() - .createContextualFragment( - '" - ) - ); - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords: () => { - document - .querySelectorAll("#searchbox .highlight-link") - .forEach((el) => el.remove()); - document - .querySelectorAll("span.highlighted") - .forEach((el) => el.classList.remove("highlighted")); - localStorage.removeItem("sphinx_highlight_terms") - }, - - initEscapeListener: () => { - // only install a listener if it is really needed - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; - - document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; - // bail with special keys - if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; - if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { - SphinxHighlight.hideSearchWords(); - event.preventDefault(); - } - }); - }, -}; - -_ready(() => { - /* Do not call highlightSearchWords() when we are on the search page. - * It will highlight words from the *previous* search query. - */ - if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); - SphinxHighlight.initEscapeListener(); -}); diff --git a/docs/source/_build/html/api.html b/docs/source/_build/html/api.html deleted file mode 100644 index a5af561..0000000 --- a/docs/source/_build/html/api.html +++ /dev/null @@ -1,4553 +0,0 @@ - - - - - - - - - API Reference — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- -
-

API Reference

-
-

Core Modules

-
-
-class optimhc.core.Pipeline(config)[source]
-

Bases: object

-

Main pipeline class for optiMHC, encapsulating the full data processing workflow.

-

This class orchestrates input parsing, feature generation, rescoring, result saving, and visualization. -It supports both single-run and experiment modes (multiple feature/model combinations).

-
-
Parameters:
-

config (str, dict, or Config) – Path to YAML config, dict, or Config object.

-
-
-
-

Examples

-
>>> from optimhc.core import Pipeline
->>> pipeline = Pipeline(config)
->>> pipeline.run()
-
-
-
-
-
-__init__(config)[source]
-

Initialize the pipeline with a configuration file, dict, or Config object.

-
-
Parameters:
-

config (str, dict, or Config) – Path to YAML config, dict, or Config object.

-
-
-
- -
-
-read_input()[source]
-

Read input PSMs based on configuration.

-
-
Returns:
-

Object containing loaded PSMs.

-
-
Return type:
-

PsmContainer

-
-
Raises:
-
    -
  • ValueError – If input type is unsupported.

  • -
  • Exception – If file reading fails.

  • -
-
-
-
- -
-
-_generate_features(psms)[source]
-

Generate features for PSMs using the configured feature generators.

-
-
Parameters:
-

psms (PsmContainer) – PSM container object.

-
-
Returns:
-

PSM container with generated features.

-
-
Return type:
-

PsmContainer

-
-
-
- -
-
-rescore(psms, model_type=None, n_jobs=None, test_fdr=None, rescoring_features=None)[source]
-

Perform rescoring on the PSMs using the specified or configured model.

-
-
Parameters:
-
    -
  • psms (PsmContainer) – PSM container object.

  • -
  • model_type (str, optional) – Model type (‘XGBoost’, ‘RandomForest’, ‘Percolator’).

  • -
  • n_jobs (int, optional) – Number of parallel jobs.

  • -
  • test_fdr (float, optional) – FDR threshold.

  • -
  • rescoring_features (list, optional) – List of features to use for rescoring.

  • -
-
-
Returns:
-

    -
  • results (mokapot.Results) – Rescoring results.

  • -
  • models (list) – Trained models.

  • -
-

-
-
-
-

Notes

-

Rescoring logic is adapted from mokapot (https://mokapot.readthedocs.io/)

-
-
- -
-
-save_results(psms, results, models, output_dir=None, file_root='optimhc')[source]
-

Save rescoring results, PSM data, and trained models to disk.

-
-
Parameters:
-
    -
  • psms (PsmContainer) – PSM container object.

  • -
  • results (mokapot.Results) – Rescoring results.

  • -
  • models (list) – Trained models.

  • -
  • output_dir (str, optional) – Output directory.

  • -
  • file_root (str, optional) – Root name for output files.

  • -
-
-
-
- -
-
-visualize_results(psms, results, models, output_dir=None, sources=None)[source]
-

Generate and save visualizations for the analysis results.

-
-
Parameters:
-
    -
  • psms (PsmContainer) – PSM container object.

  • -
  • results (mokapot.Results) – Rescoring results.

  • -
  • models (list) – Trained models.

  • -
  • output_dir (str, optional) – Output directory.

  • -
  • sources (list, optional) – Feature sources to include in visualizations.

  • -
-
-
-
- -
-
-_run_single_experiment(psms, exp_config, exp_name, exp_dir)[source]
-

Run a single experiment with the specified configuration.

-
-
Parameters:
-
    -
  • psms (PsmContainer) – PSM container object.

  • -
  • exp_config (dict) – Experiment-specific configuration.

  • -
  • exp_name (str) – Name of the experiment.

  • -
  • exp_dir (str) – Output directory for the experiment.

  • -
-
-
Returns:
-

True if experiment succeeded, False otherwise.

-
-
Return type:
-

bool

-
-
-
- -
-
-run()[source]
-

Run the complete optiMHC pipeline (single run mode).

-

This method executes the full workflow: input parsing, feature generation, rescoring, saving, and visualization.

-
-
Returns:
-

    -
  • psms (PsmContainer) – PSM container object.

  • -
  • results (mokapot.Results) – Rescoring results.

  • -
  • models (list) – Trained models.

  • -
-

-
-
-
- -
-
-run_experiments()[source]
-

Run experiments with different feature/model combinations using multiprocessing.

-

Each experiment is executed in its own process for complete resource isolation. -The experiment configurations must be provided in the config under the ‘experiments’ key.

-
-
Return type:
-

None

-
-
-
- -
- -
-
-

Logging

-
-
-optimhc.core.logging_helper.setup_loggers(log_file=None, log_level='INFO')[source]
-

Create or update all loggers so that each logger has a StreamHandler and optionally a FileHandler. -This ensures all log messages are displayed in the console and optionally saved to a file.

-
-
Parameters:
-
    -
  • log_file (str, optional) – Path to the log file. If None, no file logging is set up.

  • -
  • log_level (str, optional) – Logging level (DEBUG, INFO, WARNING, ERROR). Default is “INFO”.

  • -
-
-
-
- -
-
-optimhc.core.logging_helper.debug_logging()[source]
-

Print debugging information for all loggers that start with ‘optimhc’ and -the root logger. This helps verify that logger configurations are set properly.

-
- -
-
-

CLI Interface

-
-
-optimhc.cli.parse_cli_config(**kwargs)[source]
-
- -
-
-

PSM Container

-
-
-class optimhc.psm_container.PsmContainer(psms, label_column, scan_column, spectrum_column, ms_data_file_column, peptide_column, protein_column, rescoring_features, hit_rank_column=None, charge_column=None, retention_time_column=None, metadata_column=None)[source]
-

Bases: object

-

A container for managing peptide-spectrum matches (PSMs) in immunopeptidomics rescoring pipelines.

-
-
Parameters:
-
    -
  • psms (pd.DataFrame) – DataFrame containing the PSM data.

  • -
  • label_column (str) – Column containing the label (True for target, False for decoy).

  • -
  • scan_column (str) – Column containing the scan number.

  • -
  • spectrum_column (str) – Column containing the spectrum identifier.

  • -
  • ms_data_file_column (str) – Column containing the MS data file that the PSM originated from.

  • -
  • peptide_column (str) – Column containing the peptide sequence.

  • -
  • protein_column (str) – Column containing the protein accessions.

  • -
  • rescoring_features (dict of str to list of str) – Dictionary of feature columns for rescoring.

  • -
  • hit_rank_column (str, optional) – Column containing the hit rank.

  • -
  • charge_column (str, optional) – Column containing the charge state.

  • -
  • retention_time_column (str, optional) – Column containing the retention time.

  • -
  • metadata_column (str, optional) – Column containing metadata.

  • -
-
-
Variables:
-
    -
  • psms (pd.DataFrame) – Copy of the DataFrame containing the PSM data.

  • -
  • target_psms (pd.DataFrame) – DataFrame containing only target PSMs (label = True).

  • -
  • decoy_psms (pd.DataFrame) – DataFrame containing only decoy PSMs (label = False).

  • -
  • peptides (list of str) – List containing all peptides from the PSM data.

  • -
  • columns (list of str) – List of column names in the PSM DataFrame.

  • -
  • rescoring_features (dict of str to list of str) – Dictionary of rescoring feature columns in the PSM DataFrame.

  • -
-
-
-
-
-property psms: DataFrame
-

Get a copy of the PSM DataFrame to prevent external modification.

-
-
Returns:
-

A copy of the PSM DataFrame.

-
-
Return type:
-

pd.DataFrame

-
-
-
- -
-
-__len__()[source]
-

Get the number of PSMs in the container.

-
-
Returns:
-

Number of PSMs.

-
-
Return type:
-

int

-
-
-
- -
-
-property target_psms: DataFrame
-

Get a DataFrame containing only target PSMs.

-
-
Returns:
-

DataFrame with only target PSMs (label = True).

-
-
Return type:
-

pd.DataFrame

-
-
-
- -
-
-property decoy_psms: DataFrame
-

Get a DataFrame containing only decoy PSMs.

-
-
Returns:
-

DataFrame with only decoy PSMs (label = False).

-
-
Return type:
-

pd.DataFrame

-
-
-
- -
-
-property columns: List[str]
-

Get the column names of the PSM DataFrame.

-
-
Returns:
-

List of column names.

-
-
Return type:
-

list of str

-
-
-
- -
-
-property feature_columns: List[str]
-

Get a list of all feature columns in the PSM DataFrame.

-
-
Returns:
-

List of feature column names.

-
-
Return type:
-

list of str

-
-
-
- -
-
-property feature_sources: List[str]
-

Get a list of all feature sources in the PSM DataFrame.

-
-
Returns:
-

List of feature source names.

-
-
Return type:
-

list of str

-
-
-
- -
-
-property peptides: List[str]
-

Get the peptide sequences from the PSM data.

-
-
Returns:
-

List of peptide sequences.

-
-
Return type:
-

list of str

-
-
-
- -
-
-property ms_data_files: List[str]
-

Get the MS data files from the PSM data.

-
-
Returns:
-

List of MS data file names.

-
-
Return type:
-

list of str

-
-
-
- -
-
-property scan_ids: List[int]
-

Get the scan numbers from the PSM data.

-
-
Returns:
-

List of scan numbers.

-
-
Return type:
-

list of int

-
-
-
- -
-
-property charges: List[int]
-

Get the charge states from the PSM data.

-
-
Returns:
-

List of charge states.

-
-
Return type:
-

list of int

-
-
-
- -
-
-property metadata: Series
-

Get the metadata from the PSM data.

-
-
Returns:
-

Series containing metadata for each PSM.

-
-
Return type:
-

pd.Series

-
-
-
- -
-
-property spectrum_ids: List[str]
-

Get the spectrum identifiers from the PSM data.

-
-
Returns:
-

List of spectrum identifiers.

-
-
Return type:
-

list of str

-
-
-
- -
-
-property identifier_columns: List[str]
-

Get the columns that uniquely identify each PSM.

-
-
Returns:
-

List of identifier column names.

-
-
Return type:
-

list of str

-
-
-
- -
-
-copy()[source]
-

Return a deep copy of the PsmContainer object.

-
-
Returns:
-

A deep copy of the current PsmContainer.

-
-
Return type:
-

PsmContainer

-
-
-
- -
-
-__repr__()[source]
-

Return a string representation of the PsmContainer.

-
-
Returns:
-

String summary of the PsmContainer.

-
-
Return type:
-

str

-
-
-
- -
-
-drop_features(features)[source]
-

Drop specified features from the PSM DataFrame.

-
-
Parameters:
-

features (list of str) – List of feature column names to drop.

-
-
Raises:
-

ValueError – If any of the features do not exist in the DataFrame.

-
-
Return type:
-

None

-
-
-
- -
-
-drop_source(source)[source]
-

Drop all features associated with a specific source from the PSM DataFrame.

-
-
Parameters:
-

source (str) – Name of the source to drop.

-
-
Raises:
-

ValueError – If the source does not exist in the rescoring features.

-
-
Return type:
-

None

-
-
-
- -
-
-add_metadata(metadata_df, psms_key, metadata_key, source)[source]
-

Merge new metadata into the PSM DataFrame based on specified columns. -Metadata from the specified source is stored as a nested dictionary inside the metadata column.

-
-
Parameters:
-
    -
  • metadata_df (pd.DataFrame) – DataFrame containing new metadata to add.

  • -
  • psms_key (str or list of str) – Column name(s) in the PSM data to merge on.

  • -
  • metadata_key (str or list of str) – Column name(s) in the metadata data to merge on.

  • -
  • source (str) – Name of the source of the new metadata.

  • -
-
-
Return type:
-

None

-
-
-
- -
-
-get_top_hits(n=1)[source]
-

Get the top n hits based on the hit rank column. -If the hit rank column is not specified, returns the original PSMs.

-
-
Parameters:
-

n (int, optional) – The number of top hits to return. Default is 1.

-
-
Returns:
-

A new PsmContainer object containing the top n hits.

-
-
Return type:
-

PsmContainer

-
-
-
- -
-
-add_features(features_df, psms_key, feature_key, source, suffix=None)[source]
-

Merge new features into the PSM DataFrame based on specified columns.

-

This method performs a left join between the PSM data and feature data, -ensuring that all PSMs are preserved while adding new features. It handles -column name conflicts through optional suffixing and maintains feature source -tracking.

-
-
Parameters:
-
    -
  • features_df (pd.DataFrame) – DataFrame containing new features to add.

  • -
  • psms_key (str or list of str) – Column name(s) in the PSM data to merge on.

  • -
  • feature_key (str or list of str) – Column name(s) in the features data to merge on.

  • -
  • source (str) – Name of the source of the new features (e.g., ‘deeplc’, ‘netmhc’).

  • -
  • suffix (str, optional) – Suffix to add to the new columns if there’s a name conflict. -Required when new feature columns have the same names as existing columns. -For example, if adding features from different sources (e.g., ‘score’ from -DeepLC and NetMHC), use suffixes like ‘_deeplc’ or ‘_netmhc’ to distinguish them.

  • -
-
-
Return type:
-

None

-
-
Raises:
-

ValueError – If duplicate columns exist without suffix. - If merging features changes the number of PSMs.

-
-
-
-

Notes

-

The method follows these steps: -1. Validates input and prepares merge keys -2. Checks for column name conflicts -3. Manages feature source: if the source already exists, it will be overwritten -4. Performs left join merge -5. Verifies data integrity

-

The suffix parameter is used to handle column name conflicts: -- When adding features from different sources that might have the same column names -- When you want to keep both the original and new features with the same name -- When you need to track the source of features in the column names

-

If suffix is not provided and there are duplicate column names: -- The method will raise a ValueError -- You must either provide a suffix or rename the columns before adding

-
-
-

Examples

-
>>> container = PsmContainer(...)
->>> # Adding features without suffix (no conflicts)
->>> features_df1 = pd.DataFrame({
-...     'scan': [1, 2, 3],
-...     'feature1': [0.1, 0.2, 0.3],
-...     'feature2': [0.4, 0.5, 0.6]
-... })
->>> container.add_features(
-...     features_df1,
-...     psms_key='scan',
-...     feature_key='scan',
-...     source='source1'
-... )
->>> # Adding features with suffix (handling conflicts)
->>> features_df2 = pd.DataFrame({
-...     'scan': [1, 2, 3],
-...     'score': [0.8, 0.9, 0.7],  # This would conflict with existing 'score'
-...     'feature3': [0.7, 0.8, 0.9]
-... })
->>> container.add_features(
-...     features_df2,
-...     psms_key='scan',
-...     feature_key='scan',
-...     source='source2',
-...     suffix='_new'  # 'score' becomes 'score_new'
-... )
-
-
-
-
- -
-
-add_features_by_index(features_df, source, suffix=None)[source]
-

Merge new features into the PSM DataFrame based on the DataFrame index.

-
-
Parameters:
-
    -
  • features_df (pd.DataFrame) – DataFrame containing new features to add.

  • -
  • source (str) – Name of the source of the new features.

  • -
  • suffix (str, optional) – Suffix to add to the new columns if there’s a name conflict.

  • -
-
-
Return type:
-

None

-
-
-
- -
-
-add_results(results_df, psms_key, result_key)[source]
-

Add results of rescore engine to the PSM DataFrame based on specified columns.

-
-
Parameters:
-
    -
  • results_df (pd.DataFrame) – DataFrame containing new results to add.

  • -
  • psms_key (str or list of str) – Column name(s) in the PSM data to merge on.

  • -
  • result_key (str or list of str) – Column name(s) in the results data to merge on.

  • -
-
-
Return type:
-

None

-
-
-
- -
-
-write_pin(output_file, source=None)[source]
-

Write the PSM data to a Percolator input (PIN) file.

-
-
Percolator accepts input in a simple tab-delimited format where each row contains features associated with a single PSM:

id <tab> label <tab> scannr <tab> feature1 <tab> … <tab> featureN <tab> peptide <tab> proteinId1 <tab> .. <tab> proteinIdM

-
-
With header:

SpecID <tab> Label <tab> ScanNr <tab> Feature1 <tab> … <tab> FeatureN <tab> Peptide <tab> Proteins

-
-
-
-
Parameters:
-
    -
  • output_file (str) – The path to the output PIN file.

  • -
  • source (list of str, optional) – List of feature sources to include. If None, include all sources.

  • -
-
-
Returns:
-

The DataFrame written to the PIN file.

-
-
Return type:
-

pd.DataFrame

-
-
-
- -
- -
-
-

Utilities

-
-
-optimhc.utils.convert_pfm_to_pwm(pfm_filename, pseudocount=0.8, background_freqs=None)[source]
-

Convert a Position Frequency Matrix (PFM) file to a Position Weight Matrix (PWM).

-
-
Parameters:
-
    -
  • pfm_filename (str) – The file path to the PFM file.

  • -
  • pseudocount (float, optional) – The pseudocount to add to the PFM to avoid zero probabilities. Default is 0.8.

  • -
  • background_freqs (dict, optional) – Dictionary containing the background frequencies for each amino acid. -If None, uses 1/20 for all.

  • -
-
-
Returns:
-

DataFrame representation of the PWM.

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

The conversion process involves: -1. Adding pseudocounts to the PFM -2. Converting to Position Probability Matrix (PPM) -3. Converting to PWM using log2(PPM/background_freqs)

-
-
- -
-
-optimhc.utils.remove_pre_and_nxt_aa(peptide)[source]
-

Remove the pre and next amino acids from a peptide sequence.

-
-
Parameters:
-

peptide (str) – The peptide sequence with flanking amino acids. -Example: ‘.AANDAGYFNDEMAPIEVKTK.’

-
-
Returns:
-

The peptide sequence with flanking amino acids removed. -Example: ‘AANDAGYFNDEMAPIEVKTK’

-
-
Return type:
-

str

-
-
-
-

Notes

-

This function removes any amino acids before the first ‘.’ and after the last ‘.’ -in the peptide sequence.

-
-
- -
-
-optimhc.utils.remove_modifications(peptide, keep_modification=None)[source]
-

Remove modifications from a peptide sequence, with an option to keep specific modifications.

-
-
Parameters:
-
    -
  • peptide (str) – The peptide sequence with modifications in brackets. -Example: ‘AANDAGYFNDEM[15.9949]APIEVK[42.0106]TK’

  • -
  • keep_modification (str or list, optional) – The modification(s) to keep. If provided, only these modifications will be -preserved in the output sequence. Default is None.

  • -
-
-
Returns:
-

The peptide sequence with modifications removed or kept. -Example: ‘AANDAGYFNDEMAPIEVKTK’ (if keep_modification is None) -Example: ‘AANDAGYFNDEM[15.9949]APIEVKTK’ (if keep_modification=[‘15.9949’])

-
-
Return type:
-

str

-
-
-
-

Notes

-

Modifications are specified in square brackets after the amino acid. -If keep_modification is provided, only those specific modifications will be -preserved in the output sequence.

-
-
- -
-
-optimhc.utils.preprocess_peptide(peptide)[source]
-

Preprocess the peptide sequence by removing flanking regions and modifications.

-
-
Parameters:
-

peptide (str) – Original peptide sequence with possible flanking regions and modifications. -Example: ‘.AANDAGYFNDEM[15.9949]APIEVK[42.0106]TK.’

-
-
Returns:
-

Cleaned peptide sequence without flanking regions and modifications. -Example: ‘AANDAGYFNDEMAPIEVKTK’

-
-
Return type:
-

str

-
-
-
-

Notes

-

This function performs two operations in sequence: -1. Removes flanking amino acids using remove_pre_and_nxt_aa -2. Removes all modifications using remove_modifications

-
-
- -
-
-optimhc.utils.list_all_files_in_directory(directory_path)[source]
-

Retrieve all files in the specified directory and return a list of file paths.

-
-
Parameters:
-

directory_path (str) – The path to the directory to search in. -Example: ‘/path/to/directory’

-
-
Returns:
-

List of absolute file paths found in the directory and its subdirectories. -Example: [‘/path/to/directory/file1.txt’, ‘/path/to/directory/subdir/file2.txt’]

-
-
Return type:
-

list of str

-
-
-
-

Notes

-

This function recursively searches through all subdirectories and returns -absolute paths for all files found.

-
-
- -
-
-optimhc.utils.extract_unimod_from_peptidoform(peptide, mod_dict)[source]
-

Convert a modified peptide sequence into DeepLC format.

-
-
Parameters:
-
    -
  • peptide (str) – The input peptide sequence with modifications in brackets. -Example: ‘AANDAGYFNDEM[15.9949]APIEVK[42.0106]TK’

  • -
  • mod_dict (dict) – Dictionary mapping modification names (in peptide) to corresponding Unimod names. -Example: {‘15.9949’: ‘Oxidation’, ‘42.0106’: ‘Acetyl’}

  • -
-
-
Returns:
-

-
(seq, modifications):
-
seqstr

The unmodified peptide sequence.

-
-
modificationsstr

String of modifications formatted as position|UnimodName, separated by pipes |.

-
-
-
-
-

-
-
Return type:
-

tuple

-
-
-
- -
-
-optimhc.utils.convert_to_unimod_format(peptide, mod_dict)[source]
-

Convert a modified peptide sequence into Unimod format.

-
-
Parameters:
-
    -
  • peptide (str) – The input peptide sequence with modifications in brackets. -Example: ‘AANDAGYFNDEM[15.9949]APIEVK[42.0106]TK’

  • -
  • mod_dict (dict) – Dictionary mapping modification names (in peptide) to corresponding Unimod names. -Example: {‘15.9949’: ‘UNIMOD:4’, ‘42.0106’: ‘UNIMOD:1’}

  • -
-
-
Returns:
-

The peptide sequence formatted for Unimod. -Example: ‘AANDAGYFNDEM[UNIMOD:4]APIEVK[UNIMOD:1]TK’

-
-
Return type:
-

str

-
-
-
-

Notes

-

This function replaces the modification names in brackets with their -corresponding Unimod identifiers while preserving the peptide sequence -structure.

-
-
- -
-
-

Parser Modules

-
-
-optimhc.parser.read_pin(pin_files, retention_time_column=None)[source]
-

Read PSMs from a Percolator INput (PIN) file.

-
-
Parameters:
-
    -
  • pin_files (Union[str, List[str], pd.DataFrame]) – The file path to the PIN file, a list of file paths, or a DataFrame -containing PIN data.

  • -
  • retention_time_column (Optional[str], optional) – The column containing the retention time. If None, no retention time -will be included.

  • -
-
-
Returns:
-

A PsmContainer object containing the PSM data.

-
-
Return type:
-

PsmContainer

-
-
-
-

Notes

-

This function: -1. Reads PIN file(s) into a DataFrame -2. Identifies required columns (case-insensitive) -3. Processes scan IDs and hit ranks -4. Converts data types appropriately -5. Creates a PsmContainer with the processed data

-
-
- -
-
-optimhc.parser.read_pepxml(pepxml_files, decoy_prefix='DECOY_')[source]
-

Read PSMs from a list of PepXML files.

-
-
Parameters:
-
    -
  • pepxml_files (Union[str, List[str]]) – The file path to the PepXML file or a list of file paths.

  • -
  • decoy_prefix (str, optional) – The prefix used to indicate a decoy protein in the description lines -of the FASTA file. Default is “DECOY_”.

  • -
-
-
Returns:
-

A PsmContainer object containing the PSM data.

-
-
Return type:
-

PsmContainer

-
-
Raises:
-

ValueError – If the PepXML files were generated by Percolator or PeptideProphet.

-
-
-
-

Notes

-

This function: -1. Reads and parses PepXML files -2. Calculates mass difference features -3. Processes matched ions and complementary ions -4. Creates charge columns -5. Log-transforms p-values -6. Returns a PsmContainer with the processed data

-
-
- -
-
-optimhc.parser.extract_mzml_data(mzml_filename, scan_ids=None)[source]
-

Extract scan data from an mzML file.

-
-
Parameters:
-
    -
  • mzml_filename (str) – The path to the mzML file.

  • -
  • scan_ids (list[int] or None, optional) – A list of scan IDs to extract. If None, extracts all scans.

  • -
-
-
Returns:
-

A DataFrame containing the extracted scan data with columns: -- source: The source file name -- scan: The scan ID -- mz: The m/z values array -- intensity: The intensity values array -- charge: The charge state -- retention_time: The retention time

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

This function: -1. Reads the mzML file using pyteomics -2. Extracts scan data including retention time, charge state, m/z values, and intensities -3. Filters scans based on provided scan IDs if specified -4. Returns a DataFrame with the extracted data

-
-
- -
-

Parser Submodules

-
-
-optimhc.parser.pin.read_pin(pin_files, retention_time_column=None)[source]
-

Read PSMs from a Percolator INput (PIN) file.

-
-
Parameters:
-
    -
  • pin_files (Union[str, List[str], pd.DataFrame]) – The file path to the PIN file, a list of file paths, or a DataFrame -containing PIN data.

  • -
  • retention_time_column (Optional[str], optional) – The column containing the retention time. If None, no retention time -will be included.

  • -
-
-
Returns:
-

A PsmContainer object containing the PSM data.

-
-
Return type:
-

PsmContainer

-
-
-
-

Notes

-

This function: -1. Reads PIN file(s) into a DataFrame -2. Identifies required columns (case-insensitive) -3. Processes scan IDs and hit ranks -4. Converts data types appropriately -5. Creates a PsmContainer with the processed data

-
-
- -
-
-optimhc.parser.pin._read_single_pin_as_df(pin_file)[source]
-

Read a single PIN file into a DataFrame.

-
-
Parameters:
-

pin_file (str) – The file path to the PIN file.

-
-
Returns:
-

A DataFrame containing the PSM data.

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

This function: -1. Reads the PIN file header -2. Processes the proteins column as a tab-separated list -3. Creates a DataFrame with the processed data

-
-
- -
-
-optimhc.parser.pepxml.read_pepxml(pepxml_files, decoy_prefix='DECOY_')[source]
-

Read PSMs from a list of PepXML files.

-
-
Parameters:
-
    -
  • pepxml_files (Union[str, List[str]]) – The file path to the PepXML file or a list of file paths.

  • -
  • decoy_prefix (str, optional) – The prefix used to indicate a decoy protein in the description lines -of the FASTA file. Default is “DECOY_”.

  • -
-
-
Returns:
-

A PsmContainer object containing the PSM data.

-
-
Return type:
-

PsmContainer

-
-
Raises:
-

ValueError – If the PepXML files were generated by Percolator or PeptideProphet.

-
-
-
-

Notes

-

This function: -1. Reads and parses PepXML files -2. Calculates mass difference features -3. Processes matched ions and complementary ions -4. Creates charge columns -5. Log-transforms p-values -6. Returns a PsmContainer with the processed data

-
-
- -
-
-optimhc.parser.pepxml._parse_pepxml(pepxml_file, decoy_prefix)[source]
-

Parse the PSMs of a PepXML into a DataFrame.

-
-
Parameters:
-
    -
  • pepxml_file (str) – The PepXML file to parse.

  • -
  • decoy_prefix (str) – The prefix used to indicate a decoy protein in the description lines -of the FASTA file.

  • -
-
-
Returns:
-

A DataFrame containing the information about each PSM.

-
-
Return type:
-

pandas.DataFrame

-
-
Raises:
-

ValueError – If the file is not a PepXML file or is malformed.

-
-
-
- -
-
-optimhc.parser.pepxml._parse_msms_run(msms_run, decoy_prefix)[source]
-

Parse a single MS/MS run.

-
-
Parameters:
-
    -
  • msms_run (tuple of anything, lxml.etree.Element) – The second element of the tuple should be the XML element for a single -msms_run. The first is not used, but is necessary for compatibility -with using map().

  • -
  • decoy_prefix (str) – The prefix used to indicate a decoy protein in the description lines -of the FASTA file.

  • -
-
-
Yields:
-

dict – A dictionary describing all of the PSMs in a run.

-
-
-
- -
-
-optimhc.parser.pepxml._parse_spectrum(spectrum, run_info, decoy_prefix)[source]
-

Parse the PSMs for a single mass spectrum.

-
-
Parameters:
-
    -
  • spectrum (lxml.etree.Element) – The XML element for a single spectrum.

  • -
  • run_info (dict) – The parsed run data.

  • -
  • decoy_prefix (str) – The prefix used to indicate a decoy protein in the description lines -of the FASTA file.

  • -
-
-
Yields:
-

dict – A dictionary describing all of the PSMs for a spectrum.

-
-
-
- -
-
-optimhc.parser.pepxml._parse_psm(psm_info, spec_info, decoy_prefix)[source]
-

Parse a single PSM.

-
-
Parameters:
-
    -
  • psm_info (lxml.etree.Element) – The XML element containing information about the PSM.

  • -
  • spec_info (dict) – The parsed spectrum data.

  • -
  • decoy_prefix (str) – The prefix used to indicate a decoy protein in the description lines -of the FASTA file.

  • -
-
-
Returns:
-

A dictionary containing parsed data about the PSM.

-
-
Return type:
-

dict

-
-
-
- -
-
-optimhc.parser.pepxml._log_features(col, features)[source]
-

Log-transform columns that are p-values or E-values.

-
-
Parameters:
-
    -
  • col (pandas.Series) – A column of the dataset.

  • -
  • features (list of str) – The features of the dataset. Only feature columns will be considered -for transformation.

  • -
-
-
Returns:
-

The log-transformed values of the column if the feature was determined -to be a p-value.

-
-
Return type:
-

pandas.Series

-
-
-
-

Notes

-

This function: -1. Detects columns written in scientific notation and log them -2. Uses a simple heuristic to find p-value / E-value features -3. Only transforms if values span >4 orders of magnitude -4. Preserves precision for scientific notation values

-
-
- -
-
-optimhc.parser.mzml.extract_mzml_data(mzml_filename, scan_ids=None)[source]
-

Extract scan data from an mzML file.

-
-
Parameters:
-
    -
  • mzml_filename (str) – The path to the mzML file.

  • -
  • scan_ids (list[int] or None, optional) – A list of scan IDs to extract. If None, extracts all scans.

  • -
-
-
Returns:
-

A DataFrame containing the extracted scan data with columns: -- source: The source file name -- scan: The scan ID -- mz: The m/z values array -- intensity: The intensity values array -- charge: The charge state -- retention_time: The retention time

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

This function: -1. Reads the mzML file using pyteomics -2. Extracts scan data including retention time, charge state, m/z values, and intensities -3. Filters scans based on provided scan IDs if specified -4. Returns a DataFrame with the extracted data

-
-
- -
-
-
-

Feature Generator

-
-

Feature Generator Submodules

-
-
-class optimhc.feature_generator.base_feature_generator.BaseFeatureGenerator[source]
-

Bases: ABC

-

Abstract base class for all feature generators in the rescoring pipeline.

-
-
-abstract property feature_columns: List[str]
-

Returns a list of feature names generated by the feature generator.

-
- -
-
-abstract property id_column: List[str]
-

Returns the column or columns used as key or keys to merge features with PSMs.

-
- -
-
-abstract generate_features()[source]
-

Generates features.

-
-
Return type:
-

DataFrame

-
-
-
- -
- -
-
-class optimhc.feature_generator.basic.BasicFeatureGenerator(peptides, remove_pre_nxt_aa=True, remove_modification=True, *args, **kwargs)[source]
-

Bases: BaseFeatureGenerator

-

Feature generator that generates basic features from peptide sequences.

-

This generator calculates features such as peptide length, proportion of unique amino acids, -Shannon entropy of amino acid distribution, difference between peptide length and average peptide length, -and count of unique amino acids.

-
-
Parameters:
-
    -
  • peptides (List[str]) – List of peptide sequences to generate features for.

  • -
  • remove_pre_nxt_aa (bool, optional) – Whether to remove the amino acids adjacent to the peptide. -If True, removes them. Default is True.

  • -
  • remove_modification (bool, optional) – Whether to remove modifications in the peptide sequences. -If True, removes them. Default is True.

  • -
-
-
-
-

Notes

-

The generated features include: -- length_diff_from_avg: Difference between peptide length and average length -- abs_length_diff_from_avg: Absolute difference between peptide length and average length -- unique_aa_count: Number of unique amino acids in the peptide -- unique_aa_proportion: Proportion of unique amino acids in the peptide -- shannon_entropy: Shannon entropy of amino acid distribution

-
-
-
-property feature_columns: List[str]
-

Return the list of generated feature column names.

-
- -
-
-property id_column: List[str]
-

Return the list of input columns required for feature generation.

-
-
Returns:
-

List of input column names required for feature generation. -Currently only requires ‘Peptide’ column.

-
-
Return type:
-

List[str]

-
-
-
- -
-
-_preprocess_peptide(peptide)[source]
-

Preprocess peptide sequence by removing adjacent amino acids and modifications.

-
-
Return type:
-

str

-
-
Parameters:
-

peptide (str)

-
-
-
-
Parameters:

peptide (str): Original peptide sequence.

-
-
Returns:

str: Preprocessed peptide sequence.

-
-
-
- -
-
-_shannon_entropy(sequence)[source]
-

Calculate the Shannon entropy of a peptide sequence.

-
-
Return type:
-

float

-
-
Parameters:
-

sequence (str)

-
-
-
-
Parameters:

sequence (str): Peptide sequence.

-
-
Returns:

float: Shannon entropy value.

-
-
-
- -
-
-generate_features()[source]
-

Generate basic features for the provided peptides.

-
-
Returns:
-

DataFrame containing peptides and their computed features: -- length_diff_from_avg: Difference from average peptide length -- abs_length_diff_from_avg: Absolute difference from average length -- unique_aa_count: Number of unique amino acids -- unique_aa_proportion: Proportion of unique amino acids -- shannon_entropy: Shannon entropy of amino acid distribution

-
-
Return type:
-

pd.DataFrame

-
-
Raises:
-

ValueError – If NaN values are found in the generated features.

-
-
-
-

Notes

-

All features are converted to float type before returning. -The method calculates average peptide length across all peptides -and uses it as a reference for length-based features.

-
-
- -
- -
-
-class optimhc.feature_generator.DeepLC.DeepLCFeatureGenerator(psms, calibration_criteria_column, lower_score_is_better=False, calibration_set_size=None, processes=1, model_path=None, remove_pre_nxt_aa=True, mod_dict=None, *args, **kwargs)[source]
-

Bases: BaseFeatureGenerator

-

Generate DeepLC-based features for rescoring.

-

This generator uses DeepLC to predict retention times and calculates various -features based on the differences between predicted and observed retention times.

-
-
Parameters:
-
    -
  • psms (PsmContainer) – PSMs to generate features for.

  • -
  • calibration_criteria_column (str) – Column name in the PSMs DataFrame to use for DeepLC calibration.

  • -
  • lower_score_is_better (bool, optional) – Whether a lower PSM score denotes a better matching PSM. Default is False.

  • -
  • calibration_set_size (int or float, optional) – Amount of best PSMs to use for DeepLC calibration. If this value is lower -than the number of available PSMs, all PSMs will be used. Default is 0.15.

  • -
  • processes (int, optional) – Number of processes to use in DeepLC. Default is 1.

  • -
  • model_path (str, optional) – Path to the DeepLC model. If None, the default model will be used.

  • -
  • remove_pre_nxt_aa (bool, optional) – Whether to remove the first and last amino acids from the peptide sequence. -Default is True.

  • -
  • mod_dict (dict, optional) – Dictionary of modifications to be used for DeepLC. If None, no modifications -will be used.

  • -
-
-
-
-

Notes

-

DeepLC retraining is on by default. Add deeplc_retrain: False as a keyword -argument to disable retraining.

-

The generated features include: -- observed_retention_time: Original retention time from the data -- predicted_retention_time: DeepLC predicted retention time -- retention_time_diff: Difference between predicted and observed times -- abs_retention_time_diff: Absolute difference between predicted and observed times -- retention_time_ratio: Ratio of min(pred,obs) to max(pred,obs)

-
-
-
-__init__(psms, calibration_criteria_column, lower_score_is_better=False, calibration_set_size=None, processes=1, model_path=None, remove_pre_nxt_aa=True, mod_dict=None, *args, **kwargs)[source]
-

Generate DeepLC-based features for rescoring.

-

DeepLC retraining is on by default. Add deeplc_retrain: False as a keyword argument to -disable retraining.

-

Parameters: -psms: PsmContainer

-
-

PSMs to generate features for.

-
-
-
calibration_criteria_column: str

Column name in the PSMs DataFrame to use for DeepLC calibration.

-
-
lower_score_is_better

Whether a lower PSM score denotes a better matching PSM. Default: False

-
-
calibration_set_size: int or float

Amount of best PSMs to use for DeepLC calibration. If this value is lower -than the number of available PSMs, all PSMs will be used. (default: 0.15)

-
-
processes: {int, None}

Number of processes to use in DeepLC. Defaults to 1.

-
-
model_path: str

Path to the DeepLC model. If None, the default model will be used.

-
-
remove_pre_nxt_aa: bool

Whether to remove the first and last amino acids from the peptide sequence. -Default: True

-
-
mod_dict: dict

Dictionary of modifications to be used for DeepLC. If None, no modifications will be used.

-
-
*args: list

Additional positional arguments are passed to DeepLC.

-
-
kwargs: dict

Additional keyword arguments are passed to DeepLC.

-
-
-
-
Parameters:
-
    -
  • psms (PsmContainer)

  • -
  • calibration_criteria_column (str)

  • -
  • lower_score_is_better (bool)

  • -
  • calibration_set_size (int | float | None)

  • -
  • processes (int)

  • -
  • model_path (str | None)

  • -
  • remove_pre_nxt_aa (bool)

  • -
  • mod_dict (Dict[str, str] | None)

  • -
-
-
-
- -
-
-property feature_columns: List[str]
-

Return the list of generated feature column names.

-
-
Returns:
-

List of feature column names: -- observed_retention_time -- predicted_retention_time -- retention_time_diff -- abs_retention_time_diff -- retention_time_ratio

-
-
Return type:
-

List[str]

-
-
-
- -
-
-property id_column: List[str]
-

Return the list of input columns required for the feature generator.

-
-
Returns:
-

List of input columns required for feature generation. -Currently returns an empty list as the required columns are -handled internally by the PsmContainer.

-
-
Return type:
-

List[str]

-
-
-
- -
-
-_get_deeplc_df()[source]
-

Extract the format required by DeepLC, while retaining necessary original information.

-
-
Returns:
-

DataFrame with the required DeepLC format and original information: -- original_seq: Original peptide sequence -- label: Target/decoy label -- seq: Cleaned peptide sequence -- modifications: Unimod format modifications -- tr: Retention time -- score: Calibration criteria score

-
-
Return type:
-

pd.DataFrame

-
-
Raises:
-

ValueError – If retention time column is not found in the PSMs DataFrame.

-
-
-
-

Notes

-

This method prepares the data in the format required by DeepLC, -including cleaning peptide sequences and converting modifications -to Unimod format.

-
-
- -
-
-generate_features()[source]
-

Generate DeepLC features for the provided PSMs.

-
-
Returns:
-

DataFrame containing the PSMs with added DeepLC features: -- original_seq: Original peptide sequence -- observed_retention_time: Original retention time -- predicted_retention_time: DeepLC predicted retention time -- retention_time_diff: Difference between predicted and observed times -- abs_retention_time_diff: Absolute difference between predicted and observed times -- retention_time_ratio: Ratio of min(pred,obs) to max(pred,obs)

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

This method: -1. Prepares data in DeepLC format -2. Calibrates DeepLC if calibration set is specified -3. Predicts retention times -4. Calculates various retention time-based features -5. Handles missing values by imputing with median values

-
-
- -
-
-_get_calibration_psms(deeplc_df)[source]
-

Get the best scoring PSMs for calibration based on the calibration criteria.

-
-
Parameters:
-

deeplc_df (pd.DataFrame) – DataFrame containing DeepLC input data.

-
-
Returns:
-

DataFrame of PSMs selected for calibration, containing only target PSMs.

-
-
Return type:
-

pd.DataFrame

-
-
Raises:
-
    -
  • ValueError – If calibration_set_size is a float not between 0 and 1.

  • -
  • TypeError – If calibration_set_size is neither int nor float.

  • -
-
-
-
-

Notes

-

This method: -1. Sorts PSMs based on calibration criteria -2. Selects top N PSMs based on calibration_set_size -3. Filters to keep only target PSMs

-
-
- -
-
-get_full_data()[source]
-

Get the full DeepLC DataFrame.

-
-
Returns:
-

DataFrame containing the DeepLC input data with all columns: -- original_seq: Original peptide sequence -- label: Target/decoy label -- seq: Cleaned peptide sequence -- modifications: Unimod format modifications -- tr: Retention time -- score: Calibration criteria score -- predicted_retention_time: DeepLC predicted retention time -- retention_time_diff: Difference between predicted and observed times

-
-
Return type:
-

pd.DataFrame

-
-
-
- -
-
-property raw_predictions: DataFrame
-

Get the raw predictions DataFrame.

-
-
Returns:
-

DataFrame containing the raw predictions: -- peptide: Cleaned peptide sequence -- predicted_rt: DeepLC predicted retention time -- observed_rt: Original retention time -- modifications: Unimod format modifications

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

If predictions haven’t been generated yet, this will trigger -feature generation automatically.

-
-
- -
-
-get_raw_predictions()[source]
-

Get the raw predictions DataFrame.

-
-
Returns:
-

DataFrame containing the raw predictions: -- peptide: Cleaned peptide sequence -- predicted_rt: DeepLC predicted retention time -- observed_rt: Original retention time -- modifications: Unimod format modifications

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

This is a convenience method that returns the same data as the -raw_predictions property.

-
-
- -
-
-save_raw_predictions(file_path, **kwargs)[source]
-

Save the raw prediction results to a file.

-
-
Parameters:
-
    -
  • file_path (str) – Path to save the file.

  • -
  • **kwargs (dict) – Additional parameters passed to pandas.DataFrame.to_csv. -If ‘index’ is not specified, it defaults to False.

  • -
-
-
Return type:
-

None

-
-
-
-

Notes

-

This method saves the raw predictions DataFrame to a CSV file. -The DataFrame includes: -- peptide: Cleaned peptide sequence -- predicted_rt: DeepLC predicted retention time -- observed_rt: Original retention time -- modifications: Unimod format modifications

-
-
- -
- -
-
-class optimhc.feature_generator.mhcflurry.MHCflurryFeatureGenerator(peptides, alleles, remove_pre_nxt_aa=False, remove_modification=True, *args, **kwargs)[source]
-

Bases: BaseFeatureGenerator

-

Generate MHCflurry features for peptides based on specified MHC class I alleles.

-

This generator calculates MHCflurry presentation scores for each peptide against -the provided MHC class I alleles.

-
-
Parameters:
-
    -
  • peptides (List[str]) – List of peptide sequences.

  • -
  • alleles (List[str]) – List of MHC allele names (e.g., [‘HLA-A01:01’, ‘HLA-B07:02’]).

  • -
  • remove_pre_nxt_aa (bool, optional) – Whether to include the previous and next amino acids in peptides. -If True, remove them. Default is True.

  • -
  • remove_modification (bool, optional) – Whether to include modifications in peptides. -If True, remove them. Default is True.

  • -
-
-
-
-

Notes

-

The generated features include: -- mhcflurry_affinity: Binding affinity score -- mhcflurry_processing_score: Processing score -- mhcflurry_presentation_score: Presentation score -- mhcflurry_presentation_percentile: Presentation percentile

-
-
-
-MIN_PEPTIDE_LENGTH = 8
-
- -
-
-MAX_PEPTIDE_LENGTH = 15
-
- -
-
-property feature_columns: List[str]
-

Return the list of generated feature column names.

-
-
Returns:
-

List of feature column names: -- mhcflurry_affinity -- mhcflurry_processing_score -- mhcflurry_presentation_score -- mhcflurry_presentation_percentile

-
-
Return type:
-

List[str]

-
-
-
- -
-
-property id_column: List[str]
-

Return the list of input columns required for the feature generator.

-
-
Returns:
-

List of input column names.

-
-
Return type:
-

List[str]

-
-
-
- -
-
-_preprocess_peptides(peptide)[source]
-

Preprocess peptide sequence by removing flanking amino acids and modifications.

-
-
Parameters:
-

peptide (str) – Original peptide sequence.

-
-
Returns:
-

Preprocessed peptide sequence.

-
-
Return type:
-

str

-
-
-
- -
-
-_predict()[source]
-

Run MHCflurry predictions using the predict method and cache the result.

-
-
Returns:
-

DataFrame containing the prediction results, with the best prediction -for each sequence.

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

This method: -1. Preprocesses peptides -2. Filters peptides by length (8-15 amino acids) -3. Runs MHCflurry predictions -4. Merges results with original peptides

-
-
- -
-
-property raw_predictions: DataFrame
-

Return the raw predictions DataFrame.

-
-
Returns:
-

DataFrame containing the raw predictions: -- peptide: Cleaned peptide sequence -- allele: MHC allele -- affinity: Binding affinity -- processing_score: Processing score -- presentation_score: Presentation score -- presentation_percentile: Presentation percentile

-
-
Return type:
-

pd.DataFrame

-
-
-
- -
-
-get_raw_predictions()[source]
-

Get the raw prediction results DataFrame from MHCflurry.

-
-
Returns:
-

Raw prediction results DataFrame containing: -- peptide: Cleaned peptide sequence -- allele: MHC allele -- affinity: Binding affinity -- processing_score: Processing score -- presentation_score: Presentation score -- presentation_percentile: Presentation percentile

-
-
Return type:
-

pd.DataFrame

-
-
-
- -
-
-save_raw_predictions(file_path, **kwargs)[source]
-

Save the raw prediction results to a file.

-
-
Parameters:
-
    -
  • file_path (str) – Path to save the file.

  • -
  • **kwargs (dict) – Additional parameters passed to pandas.DataFrame.to_csv. -If ‘index’ is not specified, it defaults to False.

  • -
-
-
Return type:
-

None

-
-
-
-

Notes

-

This method saves the raw predictions DataFrame to a CSV file. -The DataFrame includes: -- peptide: Cleaned peptide sequence -- allele: MHC allele -- affinity: Binding affinity -- processing_score: Processing score -- presentation_score: Presentation score -- presentation_percentile: Presentation percentile

-
-
- -
-
-generate_features()[source]
-

Generate MHCflurry features for the provided peptides and alleles.

-
-
Returns:
-

DataFrame containing the peptides and their predicted MHCflurry features: -- Peptide: Original peptide sequence -- mhcflurry_affinity: Binding affinity -- mhcflurry_processing_score: Processing score -- mhcflurry_presentation_score: Presentation score -- mhcflurry_presentation_percentile: Presentation percentile

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

This method: -1. Runs MHCflurry predictions -2. Renames columns to include ‘mhcflurry_’ prefix -3. Fills missing values with median values -4. Returns the final feature DataFrame

-
-
- -
-
-get_best_allele()[source]
-

Get the best allele for each peptide.

-
-
Returns:
-

DataFrame containing the best alleles for the peptides: -- Peptide: Original peptide sequence -- mhcflurry_best_allele: Best binding allele

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

The best allele is determined by the lowest presentation percentile rank.

-
-
- -
-
-predictions_to_dataframe()[source]
-

Convert the predictions to a DataFrame.

-
-
Returns:
-

DataFrame containing the predictions.

-
-
Return type:
-

pd.DataFrame

-
-
Raises:
-

ValueError – If no predictions are available.

-
-
-
- -
- -
-
-optimhc.feature_generator.netMHCpan._predict_peptide_chunk(peptides_chunk, alleles)[source]
-

Predict NetMHCpan scores for a chunk of peptides.

-
-
Parameters:
-
    -
  • peptides_chunk (List[str]) – List of peptide sequences.

  • -
  • alleles (List[str]) – List of MHC allele names.

  • -
-
-
Returns:
-

DataFrame containing predictions: -- peptide: Peptide sequence -- allele: MHC allele -- score: Raw binding score -- affinity: Binding affinity in nM -- percentile_rank: Percentile rank

-
-
Return type:
-

pd.DataFrame

-
-
-
- -
-
-class optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator(peptides, alleles, mode='best', remove_pre_nxt_aa=False, remove_modification=True, n_processes=1, show_progress=False, *args, **kwargs)[source]
-

Bases: BaseFeatureGenerator

-

Generate NetMHCpan features for peptides based on specified MHC class I alleles.

-

This generator calculates NetMHCpan binding predictions for each peptide against -the provided MHC class I alleles.

-
-
Parameters:
-
    -
  • peptides (List[str]) – List of peptide sequences.

  • -
  • alleles (List[str]) – List of MHC allele names (e.g., [‘HLA-A*02:01’, ‘HLA-B*07:02’]).

  • -
  • mode (str, optional) – Mode of feature generation. Options: -- ‘best’: Return only the best allele information for each peptide. -- ‘all’: Return predictions for all alleles with allele-specific suffixes plus best allele info. -Default is ‘best’.

  • -
  • remove_pre_nxt_aa (bool, optional) – Whether to include the previous and next amino acids in peptides. -If True, remove them. Default is True.

  • -
  • remove_modification (bool, optional) – Whether to include modifications in peptides. -If True, remove them. Default is True.

  • -
  • n_processes (int, optional) – Number of processes to use for multiprocessing. -Default is 1 (no multiprocessing).

  • -
  • show_progress (bool, optional) – Whether to display a progress bar. Default is False.

  • -
-
-
-
-

Notes

-

The generated features include: -- netmhcpan_score: Raw binding score -- netmhcpan_affinity: Binding affinity in nM -- netmhcpan_percentile_rank: Percentile rank of the binding score

-
-
-
-MIN_PEPTIDE_LENGTH = 8
-
- -
-
-MAX_PEPTIDE_LENGTH = 30
-
- -
-
-CHUNKSIZE = 250
-
- -
-
-property feature_columns: List[str]
-

Return the list of generated feature column names, determined by the mode. -Only includes numerical features, excluding any string features like allele names.

-
-
Returns:
-

List of feature column names: -- For ‘all’ mode: netmhcpan_score_{allele}, netmhcpan_affinity_{allele},

-
-

netmhcpan_percentile_rank_{allele} for each allele

-
-
    -
  • For both modes: netmhcpan_best_score, netmhcpan_best_affinity, -netmhcpan_best_percentile_rank

  • -
-

-
-
Return type:
-

List[str]

-
-
-
- -
-
-property id_column: List[str]
-

Return the list of input columns required for the feature generator.

-
-
Returns:
-

List of input column names.

-
-
Return type:
-

List[str]

-
-
-
- -
-
-_preprocess_peptides(peptide)[source]
-

Preprocess the input peptide by removing flanking amino acids, -modifications, and replacing non-standard amino acids.

-
-
Parameters:
-

peptide (str) – The original peptide sequence.

-
-
Returns:
-

The preprocessed peptide sequence.

-
-
Return type:
-

str

-
-
-
- -
-
-_predict_multiprocessing(peptides_to_predict)[source]
-

Run NetMHCpan predictions using multiprocessing.

-
-
Parameters:
-

peptides_to_predict (List[str]) – List of peptides to predict.

-
-
Returns:
-

DataFrame containing the prediction results: -- peptide: Peptide sequence -- allele: MHC allele -- score: Raw binding score -- affinity: Binding affinity in nM -- percentile_rank: Percentile rank

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

This method: -1. Splits peptides into chunks -2. Processes chunks in parallel -3. Combines results into a single DataFrame

-
-
- -
-
-_predict()[source]
-

Run NetMHCpan predictions and cache the result.

-
-
Returns:
-

DataFrame containing the prediction results: -- Peptide: Original peptide sequence -- peptide: Cleaned peptide sequence -- allele: MHC allele -- score: Raw binding score -- affinity: Binding affinity in nM -- percentile_rank: Percentile rank

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

This method: -1. Preprocesses peptides -2. Filters peptides by length (8-30 amino acids) -3. Runs predictions (with or without multiprocessing) -4. Merges results with original peptides

-
-
- -
-
-property raw_predictions: DataFrame
-

Return the raw prediction results from NetMHCpan.

-
-
Returns:
-

Raw prediction results DataFrame containing: -- peptide: Cleaned peptide sequence -- allele: MHC allele -- score: Raw binding score -- affinity: Binding affinity in nM -- percentile_rank: Percentile rank

-
-
Return type:
-

pd.DataFrame

-
-
-
- -
-
-get_raw_predictions()[source]
-

Get the raw prediction results DataFrame from NetMHCpan.

-
-
Returns:
-

Raw prediction results DataFrame containing: -- peptide: Cleaned peptide sequence -- allele: MHC allele -- score: Raw binding score -- affinity: Binding affinity in nM -- percentile_rank: Percentile rank

-
-
Return type:
-

pd.DataFrame

-
-
-
- -
-
-save_raw_predictions(file_path, **kwargs)[source]
-

Save the raw prediction results to a file.

-
-
Parameters:
-
    -
  • file_path (str) – Path to save the file.

  • -
  • **kwargs (dict) – Additional parameters passed to pandas.DataFrame.to_csv. -If ‘index’ is not specified, it defaults to False.

  • -
-
-
Return type:
-

None

-
-
-
-

Notes

-

This method saves the raw predictions DataFrame to a CSV file. -The DataFrame includes: -- peptide: Cleaned peptide sequence -- allele: MHC allele -- score: Raw binding score -- affinity: Binding affinity in nM -- percentile_rank: Percentile rank

-
-
- -
-
-generate_features()[source]
-

Generate the final feature table with NetMHCpan features for each peptide.

-
-
Returns:
-

DataFrame containing peptides and their predicted features: -- Peptide: Original peptide sequence -- For ‘all’ mode: netmhcpan_score_{allele}, netmhcpan_affinity_{allele},

-
-

netmhcpan_percentile_rank_{allele} for each allele

-
-
    -
  • For both modes: netmhcpan_best_score, netmhcpan_best_affinity, -netmhcpan_best_percentile_rank

  • -
-

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

The features generated depend on the mode: -- ‘best’: Only the best allele information for each peptide -- ‘all’: All allele predictions plus best allele information

-

Missing values are handled consistently by filling with median values -for numeric columns.

-
-
- -
-
-_generate_all_allele_features(predictions_df, features_df)[source]
-

Generate features for all alleles.

-
-
Parameters:
-
    -
  • predictions_df (pd.DataFrame) – The predictions DataFrame.

  • -
  • features_df (pd.DataFrame) – The features DataFrame to update.

  • -
-
-
Returns:
-

Updated features DataFrame with all allele features: -- Peptide: Original peptide sequence -- netmhcpan_score_{allele}: Raw binding score for each allele -- netmhcpan_affinity_{allele}: Binding affinity for each allele -- netmhcpan_percentile_rank_{allele}: Percentile rank for each allele

-
-
Return type:
-

pd.DataFrame

-
-
-
- -
-
-_generate_best_allele_features(predictions_df, features_df)[source]
-

Generate features for the best allele.

-
-
Parameters:
-
    -
  • predictions_df (pd.DataFrame) – The predictions DataFrame.

  • -
  • features_df (pd.DataFrame) – The features DataFrame to update.

  • -
-
-
Returns:
-

Updated features DataFrame with best allele features: -- Peptide: Original peptide sequence -- netmhcpan_best_allele: Best binding allele -- netmhcpan_best_score: Best binding score -- netmhcpan_best_affinity: Best binding affinity -- netmhcpan_best_percentile_rank: Best percentile rank

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

The best allele is determined by the lowest percentile rank.

-
-
- -
-
-_fill_missing_values(features_df)[source]
-

Fill missing values in the features DataFrame.

-
-
Parameters:
-

features_df (pd.DataFrame) – The features DataFrame to update.

-
-
Returns:
-

Updated features DataFrame with filled missing values.

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

This method: -1. Fills best allele string values with ‘Unknown’ -2. Fills numeric values with median for all allele features -3. Fills numeric values for best allele features with median

-
-
- -
-
-predictions_to_dataframe()[source]
-

Convert the predictions to a DataFrame.

-
-
Returns:
-

DataFrame containing the predictions.

-
-
Return type:
-

pd.DataFrame

-
-
Raises:
-

ValueError – If no predictions are available.

-
-
-
- -
- -
-
-optimhc.feature_generator.netMHCIIpan._predict_peptide_chunk_class2(peptides_chunk, alleles)[source]
-

Use NetMHCIIpan43_BA to predict a batch of peptides (MHC Class II).

-
-
Return type:
-

DataFrame

-
-
Parameters:
-
    -
  • peptides_chunk (List[str])

  • -
  • alleles (List[str])

  • -
-
-
-
-
Parameters:

peptides_chunk (List[str]): A batch of peptide sequences to predict. -alleles (List[str]): List of MHC Class II alleles, e.g., [‘DRB1_0101’, ‘DRB1_0102’].

-
-
Returns:

pd.DataFrame: A DataFrame containing prediction results.

-
-
-
- -
-
-class optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator(peptides, alleles, mode='best', remove_pre_nxt_aa=True, remove_modification=True, n_processes=1, show_progress=False, *args, **kwargs)[source]
-

Bases: BaseFeatureGenerator

-

Generate NetMHCIIpan features for given peptides based on specified MHC Class II alleles.

-

This feature generator uses the NetMHCIIpan43_BA interface to predict MHC Class II binding -for each peptide and returns scores and features based on the specified parameters.

-
-
Parameters:
-
    -
  • peptides (List[str]) – List of peptide sequences.

  • -
  • alleles (List[str]) – List of MHC Class II alleles, e.g., [‘DRB1_0101’, ‘DRB1_0102’].

  • -
  • mode (str, optional) – Feature generation mode. Options: -- ‘best’: Return only the best result for each peptide across all alleles. -- ‘all’: Return prediction results for each peptide across all alleles (with allele-specific column suffixes). -Default is ‘best’.

  • -
  • remove_pre_nxt_aa (bool, optional) – Whether to remove the amino acids flanking the peptide (e.g., removing X-AA/AA-X forms). -Default is True.

  • -
  • remove_modification (bool, optional) – Whether to remove modification information from peptides, e.g., (Phospho). -Default is True.

  • -
  • n_processes (int, optional) – Number of processes to use. Default is 1 (no multiprocessing).

  • -
  • show_progress (bool, optional) – Whether to display a progress bar. Default is False.

  • -
-
-
-
-

Notes

-

The generated features include: -- netmhciipan_score: Raw binding score -- netmhciipan_affinity: Binding affinity in nM -- netmhciipan_percentile_rank: Percentile rank of the binding score

-
-
-
-MIN_PEPTIDE_LENGTH = 9
-
- -
-
-MAX_PEPTIDE_LENGTH = 50
-
- -
-
-CHUNKSIZE = 500
-
- -
-
-property feature_columns: List[str]
-

Return the list of generated feature column names, determined by the mode. -Only includes numerical features, excluding any string features like allele names.

-
-
Returns:
-

List of feature column names: -- For ‘all’ mode: netmhciipan_score_{allele}, netmhciipan_affinity_{allele},

-
-

netmhciipan_percentile_rank_{allele} for each allele

-
-
    -
  • For both modes: netmhciipan_best_score, netmhciipan_best_affinity, -netmhciipan_best_percentile_rank

  • -
-

-
-
Return type:
-

List[str]

-
-
-
- -
-
-property id_column: List[str]
-

Return the list of input columns required for the feature generator.

-
-
Returns:
-

List of input column names.

-
-
Return type:
-

List[str]

-
-
-
- -
-
-_preprocess_peptides(peptide)[source]
-

Preprocess the input peptide by removing flanking amino acids, -modifications, and replacing non-standard amino acids.

-
-
Parameters:
-

peptide (str) – The original peptide sequence.

-
-
Returns:
-

The preprocessed peptide sequence.

-
-
Return type:
-

str

-
-
-
- -
-
-_predict_multiprocessing(peptides_to_predict)[source]
-

Run NetMHCIIpan predictions using multiprocessing.

-
-
Parameters:
-

peptides_to_predict (List[str]) – List of peptides to predict.

-
-
Returns:
-

DataFrame containing the prediction results: -- peptide: Peptide sequence -- allele: MHC allele -- score: Raw binding score -- affinity: Binding affinity in nM -- percentile_rank: Percentile rank

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

This method: -1. Splits peptides into chunks -2. Processes chunks in parallel -3. Combines results into a single DataFrame

-
-
- -
-
-_predict()[source]
-

Run NetMHCIIpan predictions and cache the result.

-
-
Returns:
-

DataFrame containing the prediction results: -- Peptide: Original peptide sequence -- peptide: Cleaned peptide sequence -- allele: MHC allele -- score: Raw binding score -- affinity: Binding affinity in nM -- percentile_rank: Percentile rank

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

This method: -1. Preprocesses peptides -2. Filters peptides by length (9-30 amino acids) -3. Runs predictions (with or without multiprocessing) -4. Merges results with original peptides

-
-
- -
-
-generate_features()[source]
-

Generate the final feature table with NetMHCIIpan features for each peptide.

-
-
Returns:
-

DataFrame containing peptides and their predicted features: -- Peptide: Original peptide sequence -- For ‘all’ mode: netmhciipan_score_{allele}, netmhciipan_affinity_{allele},

-
-

netmhciipan_percentile_rank_{allele} for each allele

-
-
    -
  • For both modes: netmhciipan_best_score, netmhciipan_best_affinity, -netmhciipan_best_percentile_rank

  • -
-

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

The features generated depend on the mode: -- ‘best’: Only the best allele information for each peptide -- ‘all’: All allele predictions plus best allele information

-

Missing values are handled consistently by filling with median values -for numeric columns.

-
-
- -
-
-_generate_all_allele_features(predictions_df, features_df)[source]
-

Generate features for all alleles.

-
-
Parameters:
-
    -
  • predictions_df (pd.DataFrame) – The predictions DataFrame.

  • -
  • features_df (pd.DataFrame) – The features DataFrame to update.

  • -
-
-
Returns:
-

Updated features DataFrame with all allele features: -- Peptide: Original peptide sequence -- netmhciipan_score_{allele}: Raw binding score for each allele -- netmhciipan_affinity_{allele}: Binding affinity for each allele -- netmhciipan_percentile_rank_{allele}: Percentile rank for each allele

-
-
Return type:
-

pd.DataFrame

-
-
-
- -
-
-_generate_best_allele_features(predictions_df, features_df)[source]
-

Generate features for the best allele.

-
-
Parameters:
-
    -
  • predictions_df (pd.DataFrame) – The predictions DataFrame.

  • -
  • features_df (pd.DataFrame) – The features DataFrame to update.

  • -
-
-
Returns:
-

Updated features DataFrame with best allele features: -- Peptide: Original peptide sequence -- netmhciipan_best_allele: Best binding allele -- netmhciipan_best_score: Best binding score -- netmhciipan_best_affinity: Best binding affinity -- netmhciipan_best_percentile_rank: Best percentile rank

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

The best allele is determined by the lowest percentile rank.

-
-
- -
-
-_fill_missing_values(features_df)[source]
-

Fill missing values in the features DataFrame.

-
-
Parameters:
-

features_df (pd.DataFrame) – The features DataFrame to update.

-
-
Returns:
-

Updated features DataFrame with filled missing values.

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

This method: -1. Fills best allele string values with ‘Unknown’ -2. Fills numeric values with median for all allele features -3. Fills numeric values for best allele features with median

-
-
- -
-
-predictions_to_dataframe()[source]
-

Convert the predictions to a DataFrame.

-
-
Returns:
-

DataFrame containing the predictions.

-
-
Return type:
-

pd.DataFrame

-
-
Raises:
-

ValueError – If no predictions are available.

-
-
-
- -
-
-property raw_predictions: DataFrame
-

Return the raw prediction results from NetMHCIIpan.

-
-
Returns:
-

Raw prediction results DataFrame containing: -- peptide: Cleaned peptide sequence -- allele: MHC allele -- score: Raw binding score -- affinity: Binding affinity in nM -- percentile_rank: Percentile rank

-
-
Return type:
-

pd.DataFrame

-
-
-
- -
-
-get_raw_predictions()[source]
-

Get the raw prediction results DataFrame from NetMHCIIpan.

-
-
Returns:
-

Raw prediction results DataFrame containing: -- peptide: Cleaned peptide sequence -- allele: MHC allele -- score: Raw binding score -- affinity: Binding affinity in nM -- percentile_rank: Percentile rank

-
-
Return type:
-

pd.DataFrame

-
-
-
- -
-
-save_raw_predictions(file_path, **kwargs)[source]
-

Save the raw prediction results to a file.

-
-
Parameters:
-
    -
  • file_path (str) – Path to save the file.

  • -
  • **kwargs (dict) – Additional parameters passed to pandas.DataFrame.to_csv. -If ‘index’ is not specified, it defaults to False.

  • -
-
-
Return type:
-

None

-
-
-
-

Notes

-

This method saves the raw predictions DataFrame to a CSV file. -The DataFrame includes: -- peptide: Cleaned peptide sequence -- allele: MHC allele -- score: Raw binding score -- affinity: Binding affinity in nM -- percentile_rank: Percentile rank

-
-
- -
- -
-
-class optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator(peptides, min_overlap_length=6, min_length=7, max_length=60, min_entropy=0, fill_missing='median', remove_pre_nxt_aa=False, remove_modification=True, *args, **kwargs)[source]
-

Bases: BaseFeatureGenerator

-

Generates features based on peptide sequence overlaps using the Overlap-Layout-Consensus (OLC) algorithm.

-

This generator constructs an overlap graph of peptides, removes transitive edges, simplifies the graph to contigs, -and computes features such as the number of overlaps, log-transformed overlap counts, overlap ranks, and contig lengths. -It also filters out peptides with low entropy or outlier lengths before processing. -Additionally, it records detailed information about brother peptides and contigs, accessible via the get_all_data method.

-
-
Parameters:
-
    -
  • peptides (list of str) – List of peptide sequences.

  • -
  • min_overlap_length (int, optional) – Minimum required overlap length for peptides to be considered overlapping. Default is 6.

  • -
  • min_length (int, optional) – Minimum peptide length to include in processing. Default is 7.

  • -
  • max_length (int, optional) – Maximum peptide length to include in processing. Default is 60.

  • -
  • min_entropy (float, optional) – Minimum Shannon entropy for peptides to include in processing. Default is 0.

  • -
  • fill_missing (str, optional) – Method to fill missing values for filtered peptides. Options are ‘median’ or ‘zero’. Default is ‘median’.

  • -
  • remove_pre_nxt_aa (bool, optional) – Whether to remove the preceding and following amino acids from peptides. Default is False.

  • -
  • remove_modification (bool, optional) – Whether to remove modifications from peptides. Default is True.

  • -
-
-
Variables:
-
    -
  • original_peptides (list of str) – Original list of peptide sequences.

  • -
  • min_overlap_length (int) – Minimum required overlap length.

  • -
  • min_length (int) – Minimum peptide length.

  • -
  • max_length (int) – Maximum peptide length.

  • -
  • min_entropy (float) – Minimum Shannon entropy.

  • -
  • fill_missing (str) – Method to fill missing values.

  • -
  • remove_pre_nxt_aa (bool) – Whether to remove preceding and following amino acids.

  • -
  • remove_modification (bool) – Whether to remove modifications.

  • -
  • filtered_peptides (list of str) – List of peptides after filtering.

  • -
  • filtered_indices (list of int) – Indices of filtered peptides.

  • -
  • peptide_to_index (dict of str to int) – Mapping of peptides to their indices.

  • -
  • overlap_data (pd.DataFrame) – DataFrame containing overlap data.

  • -
  • peptide_to_contig (dict of str to int) – Mapping of peptides to their contig indices.

  • -
  • assembled_contigs (list of dict) – List of assembled contigs.

  • -
  • full_data (pd.DataFrame) – Full data including brother peptides and contig information.

  • -
  • _overlap_graph (nx.DiGraph) – Overlap graph.

  • -
  • _simplified_graph (nx.DiGraph) – Simplified graph with transitive edges removed.

  • -
-
-
-
-

Notes

-
-
Key Data Structures:
    -
  1. contigs: List[List[str]] -- Represents non-branching paths in the overlap graph -- Each inner list contains peptide sequences that form a continuous chain -- Example: [[‘PEPTIDE1’, ‘PEPTIDE2’], [‘PEPTIDE3’]]

  2. -
  3. assembled_contigs: List[Dict] -- Contains the assembled sequences and their constituent peptides -- Each dictionary has two keys:

    -
    -

    ‘sequence’: The merged/assembled sequence of overlapping peptides -‘peptides’: List of peptides that were used to build this contig

    -
    -
      -
    • -
      Example: [
      -
      {

      ‘sequence’: ‘LONGPEPTIDESEQUENCE’, -‘peptides’: [‘LONGPEP’, ‘PEPTIDE’, ‘SEQUENCE’]

      -
      -
      -

      }, -{

      -
      -

      ‘sequence’: ‘SINGLEPEPTIDE’, -‘peptides’: [‘SINGLEPEPTIDE’]

      -
      -

      }

      -
      -
      -
    • -
    -

    ]

    -
  4. -
  5. peptide_to_contig: Dict[str, int] -- Maps each peptide to its contig index in assembled_contigs -- Key: peptide sequence -- Value: index of the contig containing this peptide -- Example: {

    -
    -

    ‘LONGPEP’: 0, -‘PEPTIDE’: 0, -‘SEQUENCE’: 0, -‘SINGLEPEPTIDE’: 1

    -
    -

    }

    -
  6. -
  7. overlap_graph (_overlap_graph): nx.DiGraph -- Directed graph representing all possible overlaps between peptides -- Nodes: peptide sequences -- Edges: overlaps between peptides -- Edge weights: length of overlap

  8. -
  9. simplified_graph (_simplified_graph): nx.DiGraph -- Simplified version of overlap_graph with transitive edges removed -- Used for final contig assembly -- More efficient representation of essential overlaps

  10. -
-
-
-
-
-
-property id_column: List[str]
-

Returns a list of input columns required for the feature generator.

-
-
Returns:

List[str]: List of input columns.

-
-
-
- -
-
-property feature_columns: List[str]
-

Returns the feature column names.

-
- -
-
-property overlap_graph: DiGraph
-

Returns the overlap graph.

-
- -
-
-property simplified_graph: DiGraph
-

Returns the layout graph.

-
- -
-
-property contigs: List[Dict]
-

Returns the assembled contigs.

-
- -
-
-_shannon_entropy(sequence)[source]
-

Calculate the Shannon entropy of a peptide sequence.

-
-
Return type:
-

float

-
-
Parameters:
-

sequence (str)

-
-
-
-
Parameters:

sequence (str): Peptide sequence.

-
-
Returns:

float: Shannon entropy value.

-
-
-
- -
-
-_filter_peptides(peptides)[source]
-

Filter out peptides based on length and entropy.

-
-
Return type:
-

List[str]

-
-
Parameters:
-

peptides (List[str])

-
-
-
-
Parameters:

peptides (List[str]): List of peptide sequences.

-
-
Returns:

List[str]: Filtered list of peptide sequences.

-
-
-
- -
-
-_construct_prefix_index(peptides, min_overlap_length)[source]
-

Construct an index of prefixes for all peptides.

-
-
Return type:
-

Dict[str, List[int]]

-
-
Parameters:
-
    -
  • peptides (List[str])

  • -
  • min_overlap_length (int)

  • -
-
-
-
-
Parameters:

peptides (List[str]): List of peptide sequences.

-
-
Returns:

Dict[str, List[int]]: Dictionary mapping prefixes to list of peptide indices.

-
-
-
- -
-
-_build_overlap_graph(peptides, prefix_index)[source]
-

Build the overlap graph from the list of peptides.

-
-
Return type:
-

DiGraph

-
-
Parameters:
-
    -
  • peptides (List[str])

  • -
  • prefix_index (Dict[str, List[int]])

  • -
-
-
-
-
Parameters:

peptides (List[str]): List of peptide sequences. -prefix_index (Dict[str, List[int]]): Index of prefixes.

-
-
Returns:

nx.DiGraph: Overlap graph.

-
-
-
- -
-
-_remove_transitive_edges(G)[source]
-

Remove transitive edges from the overlap graph G.

-
-
Return type:
-

DiGraph

-
-
Parameters:
-

G (DiGraph)

-
-
-
-
Parameters:

G (nx.DiGraph): Overlap graph.

-
-
Returns:

nx.DiGraph: Simplified graph with transitive edges removed.

-
-
-
- -
-
-_simplify_graph_to_contigs(G)[source]
-

Simplify the graph by finding non-branching paths (contigs).

-
-
Return type:
-

List[List[str]]

-
-
Parameters:
-

G (DiGraph)

-
-
-
-
Parameters:

G (nx.DiGraph): Overlap graph.

-
-
Returns:

List[List[str]]: List of contigs, each contig is a list of peptide sequences.

-
-
-
- -
-
-_assemble_contigs(contigs, G)[source]
-

Assemble sequences for each contig.

-
-
Return type:
-

List[Dict]

-
-
Parameters:
-
    -
  • contigs (List[List[str]])

  • -
  • G (DiGraph)

  • -
-
-
-
-
Parameters:

contigs (List[List[str]]): List of contigs.

-
-
Returns:

List[Dict]: List of assembled contigs with sequence and peptides.

-
-
-
- -
-
-_map_peptides_to_contigs(assembled_contigs)[source]
-

Map each peptide to its contig.

-
-
Parameters:

assembled_contigs (List[Dict]): List of assembled contigs.

-
-
-
-
Parameters:
-

assembled_contigs (List[Dict])

-
-
-
- -
-
-_remove_redundant_peptides(peptides)[source]
-

Remove peptides that are fully contained in other peptides.

-
-
Return type:
-

Tuple[List[str], Dict[str, str]]

-
-
Parameters:
-

peptides (List[str])

-
-
-
-
Parameters:

peptides (List[str]): List of peptide sequences.

-
-
Returns:

accepted_peptides (List[str]): List of accepted peptides not redundant (largest container peptides). -redundant_mapping (Dict[str, str]): Mapping of redundant peptide to its largest container peptide.

-
-
-
- -
-
-_build_full_contig_map(peptides)[source]
-

Build a mapping from contig index to list of peptides (both accepted and redundant).

-
-
Return type:
-

Dict[int, List[str]]

-
-
Parameters:
-

peptides (List[str])

-
-
-
-
Parameters:

peptides (List[str]): Original list of peptides.

-
-
Returns:

Dict[int, List[str]]: Mapping from contig index to list of peptides.

-
-
-
- -
-
-_calculate_overlap_contig_features(peptides)[source]
-

Calculate overlap and contig related features for peptides.

-

This method computes the overlap graph, simplifies it to contigs, assembles contigs, -and generates features for each peptide. It also handles redundant peptides by mapping them -to their container peptides.

-
-
Return type:
-

DataFrame

-
-
Parameters:
-

peptides (List[str])

-
-
-
-
Parameters:

peptides (List[str]): List of peptide sequences.

-
-
Returns:

pd.DataFrame: DataFrame containing the computed features.

-
-
-
- -
-
-_integrate_overlap_features()[source]
-

Compute the features for each peptide, ensuring output aligns with input.

-

This method preprocesses and filters peptides, calculates overlap and contig features, -maps them back to the original peptides, and fills missing values.

-
-
Return type:
-

DataFrame

-
-
-
-
Returns:

pd.DataFrame: DataFrame containing features for each peptide.

-
-
-
- -
-
-generate_features()[source]
-

Generates features for peptide overlaps, including the count of overlapping peptides, contig length, -and log-transformed counts and ranks.

-
-
Return type:
-

DataFrame

-
-
-
-
Returns:

pd.DataFrame: DataFrame containing the features.

-
-
-
- -
-
-get_full_data()[source]
-

Returns the full data including brother peptides and contig information for each peptide. -In the output, the lists of contig peptides and brother peptides include redundant peptides, -so that their counts match the corresponding peptide and contig_member_count.

-
-
Return type:
-

DataFrame

-
-
-
-
Returns:

pd.DataFrame: DataFrame containing peptides and their brother peptides and contigs.

-
-
-
- -
- -
-
-optimhc.feature_generator.overlapping_peptide.assign_brother_aggregated_feature(psms, feature_columns, overlapping_source, source_name='OverlappingGroupFeatures')[source]
-

Assign aggregated features based on brother peptides to the PSMs.

-

For PSMs with the same ContigSequence (brother peptides), compute the mean of specified features -and assign these aggregated features back to each PSM in the group. Additionally, compute -the sum as mean * (contig_member_count + 1). If a PSM does not have a ContigSequence (no brothers), -its new features will be set to the original values.

-
-
Return type:
-

None

-
-
Parameters:
-
    -
  • psms (PsmContainer)

  • -
  • feature_columns (str | List[str])

  • -
  • overlapping_source (str)

  • -
  • source_name (str)

  • -
-
-
-
-
Parameters:

psms (PsmContainer): PSM container containing the peptides and features. -feature_columns (Union[str, List[str]]): Name of the feature column(s) to aggregate. -overlapping_source (str): Source name of the overlapping peptide features. -source_name (str): Name of the new feature source.

-
-
Returns:

None

-
-
-
- -
-
-class optimhc.feature_generator.PWM.PWMFeatureGenerator(peptides, alleles, anchors=2, mhc_class='I', pwm_path=None, remove_pre_nxt_aa=False, remove_modification=True, *args, **kwargs)[source]
-

Bases: BaseFeatureGenerator

-

Generates PWM (Position Weight Matrix) features for peptides based on specified MHC alleles.

-

This generator calculates PWM scores for each peptide against the provided MHC class I or II allele PWMs.

-
-
Parameters:
-
    -
  • peptides (list of str) – Series of peptide sequences.

  • -
  • alleles (list of str) – List of MHC allele names (e.g., [‘HLA-A01:01’, ‘HLA-B07:02’]).

  • -
  • anchors (int, optional) – Number of anchor positions to consider for MHC class I. Default is 2.

  • -
  • mhc_class (str, optional) – MHC class, either ‘I’ or ‘II’. Default is ‘I’.

  • -
  • pwm_path (str or os.PathLike, optional) – Custom path to PWM files. Defaults to ‘../../data/PWMs’.

  • -
  • remove_pre_nxt_aa (bool, optional) – Whether to include the previous and next amino acids in peptides. -If True, remove them. Default is False.

  • -
  • remove_modification (bool, optional) – Whether to include modifications in peptides. -If True, remove them. Default is True.

  • -
-
-
Variables:
-
    -
  • peptides (pd.Series) – Series of peptide sequences.

  • -
  • alleles (list of str) – List of MHC allele names.

  • -
  • mhc_class (str) – MHC class (‘I’ or ‘II’).

  • -
  • pwm_path (str or os.PathLike) – Path to PWM files.

  • -
  • pwms (dict) – Dictionary of PWMs for each allele and mer length.

  • -
  • anchors (int) – Number of anchor positions for MHC class I.

  • -
  • remove_pre_nxt_aa (bool) – Whether to remove pre/post neighboring amino acids.

  • -
  • remove_modification (bool) – Whether to remove modifications.

  • -
-
-
-
-

Notes

-
-
For MHC class I:
    -
  • Generates ‘PWM_Score_{allele}’ and optionally ‘Anchor_Score_{allele}’ columns.

  • -
-
-
For MHC class II:
    -
  • Generates ‘PWM_Score_{allele}’ (core 9-mer),

  • -
  • ‘N_Flank_PWM_Score_{allele}’,

  • -
  • ‘C_Flank_PWM_Score_{allele}’ columns.

  • -
-
-
-
-
-
-CLASS_II_CORE_LENGTH = 9
-
- -
-
-DEFAULT_PWM_PATH = '/data/48/zshang/optiMHC/optimhc/feature_generator/../PWMs'
-
- -
-
-__init__(peptides, alleles, anchors=2, mhc_class='I', pwm_path=None, remove_pre_nxt_aa=False, remove_modification=True, *args, **kwargs)[source]
-

Initializes the PWMFeatureGenerator.

-
-
Parameters:

peptides (List[str]): Series of peptide sequences. -alleles (List[str]): List of MHC allele names (e.g., [‘HLA-A01:01’, ‘HLA-B07:02’]). -mhc_class (str): MHC class, either ‘I’ or ‘II’. Default is ‘I’. -pwm_path (Optional[Union[str, os.PathLike]]): Custom path to PWM files. Defaults to ‘../../data/PWMs’. -remove_pre_nxt_aa (bool): Whether to include the previous and next amino acids in peptides.

-
-

If True, remove them. Default is False.

-
-
-
remove_modification (bool): Whether to include modifications in peptides.

If True, remove them. Default is True.

-
-
-
-
-
-
Parameters:
-
    -
  • peptides (List[str])

  • -
  • alleles (List[str])

  • -
  • anchors (int)

  • -
  • mhc_class (str)

  • -
  • pwm_path (str | PathLike | None)

  • -
  • remove_pre_nxt_aa (bool)

  • -
  • remove_modification (bool)

  • -
-
-
-
- -
-
-property id_column: List[str]
-

Get a list of input columns required for the feature generator.

-
-
Returns:
-

List of column names required for feature generation.

-
-
Return type:
-

list of str

-
-
-
- -
-
-_extract_trailing_numbers(text)[source]
-

Extract the trailing numbers from a string.

-
-
Parameters:
-

text (str) – Input string to extract numbers from.

-
-
Returns:
-

The trailing numbers if found, None otherwise.

-
-
Return type:
-

str or None

-
-
-
-

Examples

-
>>> _extract_trailing_numbers('ABC123')
-'123'
->>> _extract_trailing_numbers('ABC')
-None
->>> _extract_trailing_numbers('L123')
-'123'
-
-
-
-
- -
-
-_default_allele_pwm_files()[source]
-

Construct default PWM file paths for each allele based on MHC class.

-
-
Returns:
-

Dictionary mapping alleles to their PWM files for each mer length. -Format: {allele: {mer_length: file_path}}

-
-
Return type:
-

dict of str to dict of int to str

-
-
-
-

Notes

-
-
For MHC class I:
    -
  • Allele directory format: HLA-A01:01 -> HLA-A01_01

  • -
-
-
For MHC class II:
    -
  • Allele directory format: DRB10101 -> DRB1_0101

  • -
  • Fixed core length of 9

  • -
-
-
-
-
- -
-
-_most_conserved_postions(pwm, n=2)[source]
-

Find the n most conserved positions in the PWM.

-
-
Parameters:
-
    -
  • pwm (pd.DataFrame) – Position Weight Matrix to analyze.

  • -
  • n (int, optional) – Number of positions to return. Default is 2.

  • -
-
-
Returns:
-

Indices of the n most conserved positions.

-
-
Return type:
-

list of int

-
-
Raises:
-

ValueError – If n exceeds the PWM length.

-
-
-
-

Notes

-

In our study, we only use the anchor score for class I MHC. -Conservation is measured using Shannon entropy.

-
-
- -
-
-_load_pwms()[source]
-

Load PWMs for each allele from the constructed file paths.

-
-
Returns:
-

Dictionary of PWMs for each allele and mer length. -Format: {allele: {mer_length: pwm_dataframe}}

-
-
Return type:
-

dict of str to dict of int to pd.DataFrame

-
-
Raises:
-
    -
  • FileNotFoundError – If a PWM file is not found.

  • -
  • Exception – If there is an error loading a PWM file.

  • -
-
-
-
-

Notes

-

PWM files are expected to be space-delimited text files with amino acids as row indices -and positions as column indices.

-
-
- -
-
-_cal_PWM_score_I(peptide, allele)[source]
-

Calculate PWM scores for MHC class I.

-
-
Parameters:
-
    -
  • peptide (str) – The peptide sequence to score.

  • -
  • allele (str) – The MHC allele to score against.

  • -
-
-
Returns:
-

PWM score for the peptide against the allele’s PWM, or None if out of range.

-
-
Return type:
-

float or None

-
-
-
-

Notes

-

If peptide length is out of range, returns None.

-
-
- -
-
-_cal_PWM_score_II(peptide, allele)[source]
-

Calculate PWM scores for MHC class II using a sliding 9-mer window.

-
-
Parameters:
-
    -
  • peptide (str) – The peptide sequence to score.

  • -
  • allele (str) – The MHC allele to score against.

  • -
-
-
Returns:
-

A tuple containing: -- core_score : float or None

-
-

Score for the best 9-mer core.

-
-
    -
  • -
    n_flank_scorefloat or None

    Score for the N-terminal flanking region.

    -
    -
    -
  • -
  • -
    c_flank_scorefloat or None

    Score for the C-terminal flanking region.

    -
    -
    -
  • -
-

Returns (None, None, None) if peptide has length < 9.

-

-
-
Return type:
-

tuple of (float or None, float or None, float or None)

-
-
-
-

Notes

-

The method: -1. Slides over all possible 9-mer windows to find the highest core PWM score. -2. Once the best core is found, extracts up to 3 AA on each flank (N-flank and C-flank). -3. If the flank has fewer than 3 residues, pads with ‘X’. -4. Scores each flank with n_flank_pwm and c_flank_pwm.

-
-
- -
-
-_cal_PWM_score(peptide, allele)[source]
-

Calculates PWM scores for a single peptide across all applicable mer lengths for a given allele.

-
-
Return type:
-

Union[float, Tuple[float, float, float]]

-
-
Parameters:
-
    -
  • peptide (str)

  • -
  • allele (str)

  • -
-
-
-
-
For MHC class I:

Returns a single float (or pd.NA).

-
-
For MHC class II:

Returns a tuple of (core_score, n_flank_score, c_flank_score) or (pd.NA, pd.NA, pd.NA).

-
-
-
- -
-
-_cal_anchor_score(peptide, allele, anchor_dict)[source]
-

Calculate anchor score for a single peptide across all applicable mer lengths for a given allele.

-
-
Parameters:
-
    -
  • peptide (str) – The peptide sequence to score.

  • -
  • allele (str) – The MHC allele to score against.

  • -
  • anchor_dict (dict of int to list of int) – Dictionary containing the most conserved positions for each mer length.

  • -
-
-
Returns:
-

Anchor score for the peptide against the allele’s PWM, or None if out of range.

-
-
Return type:
-

float or None

-
-
-
-

Notes

-

Only implemented for MHC class I. For MHC class II, returns None.

-
-
- -
-
-set_pwms(pwms)[source]
-

Set PWMs directly, allowing for custom PWMs to be provided.

-
-
Parameters:
-

pwms (dict of str to dict of int to pd.DataFrame) – Dictionary of PWMs for each allele and mer length. -Format: {allele: {mer_length: pwm_dataframe}}

-
-
-
- -
-
-generate_features()[source]
-

Generate PWM features for all peptides across specified alleles.

-
-
Returns:
-

DataFrame containing generated features: -For MHC class I:

-
-
    -
  • ’PWM_Score_{allele}’ and optionally ‘Anchor_Score_{allele}’ columns.

  • -
-
-
-
For MHC class II:
    -
  • ’PWM_Score_{allele}’ (core 9-mer),

  • -
  • ’N_Flank_PWM_Score_{allele}’,

  • -
  • ’C_Flank_PWM_Score_{allele}’ columns.

  • -
-
-
-

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

Missing values are imputed with the median value for each feature.

-
-
- -
-
-property feature_columns: List[str]
-

Returns a list of feature names generated by the feature generator.

-
- -
- -
-
-class optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator(spectrum_ids, peptides, charges, scan_ids, mz_file_paths, model_type, collision_energies=None, instruments=None, fragmentation_types=None, remove_pre_nxt_aa=False, mod_dict=None, url='koina.wilhelmlab.org:443', top_n=36, tolerance_ppm=20)[source]
-

Bases: BaseFeatureGenerator

-

Feature generator for calculating similarity between experimental and predicted spectra.

-

This class works through the following steps: -1. Extract experimental spectral data from mzML files -2. Use Koina for theoretical spectra prediction -3. Align experimental and predicted spectra -4. Calculate similarity metrics as features

-
-
Parameters:

peptides (List[str]): List of peptide sequences -charges (List[int]): List of charge states -scan_ids (List[int]): List of scan IDs -mz_file_paths (List[str]): List of mzML file paths -model_type (str): Prediction model type, either “HCD” or “CID” -collision_energies (List[float]): List of collision energies, required when model_type is “HCD” -remove_pre_nxt_aa (bool): Whether to remove preceding and next amino acids, default is True -remove_modification (bool): Whether to remove modifications, default is True -url (str): Koina server URL, default is “koina.wilhelmlab.org:443” -top_n (int): Number of top peaks to use for alignment, default is 12 -tolerance_ppm (float): Mass tolerance for alignment in ppm, default is 20

-
-
-
-
Parameters:
-
    -
  • spectrum_ids (List[str])

  • -
  • peptides (List[str])

  • -
  • charges (List[int])

  • -
  • scan_ids (List[int])

  • -
  • mz_file_paths (List[str])

  • -
  • model_type (str)

  • -
  • collision_energies (List[float])

  • -
  • instruments (List[str])

  • -
  • fragmentation_types (List[str])

  • -
  • remove_pre_nxt_aa (bool)

  • -
  • mod_dict (Dict[str, str] | None)

  • -
  • url (str)

  • -
  • top_n (int)

  • -
  • tolerance_ppm (float)

  • -
-
-
-
-
-property id_column: List[str]
-

Returns a list of input columns required for the feature generator.

-
- -
-
-property feature_columns: List[str]
-

Returns a list of feature columns generated by the feature generator.

-
- -
-
-input_df()[source]
-

Return the generated features as a DataFrame.

-
-
Return type:
-

DataFrame

-
-
-
-
Returns:

pd.DataFrame: DataFrame containing the generated features

-
-
-
- -
-
-_preprocess_peptide(peptide)[source]
-

Preprocess peptide sequence.

-

As Prosit does not support non-standard amino acid ‘U’, we replace it with ‘C’.

-
-
Parameters:
-

peptide (str) – Original peptide sequence.

-
-
Returns:
-

Processed peptide sequence.

-
-
Return type:
-

str

-
-
-
-

Notes

-

This is nonsense when it comes to spectral prediction, but we need to keep it -for compatibility with Koina. In the future, this should be prohibited at the input level.

-
-
- -
-
-_extract_experimental_spectra()[source]
-

Extract experimental spectral data from mzML files.

-
-
Returns:
-

DataFrame containing experimental spectral data.

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

The method groups scan IDs by file path for efficiency and extracts -spectral data from each mzML file. The resulting DataFrame contains -m/z values, intensities, and associated metadata for each spectrum.

-
-
- -
-
-_predict_theoretical_spectra(processed_peptides, charges)[source]
-

Use Koina to predict theoretical spectra.

-
-
Parameters:
-
    -
  • processed_peptides (list of str) – List of preprocessed peptide sequences.

  • -
  • charges (list of int) – List of charge states.

  • -
-
-
Returns:
-

DataFrame containing predicted spectral data.

-
-
Return type:
-

pd.DataFrame

-
-
Raises:
-

Exception – If there is an error during Koina prediction.

-
-
-
-

Notes

-

The method uses Koina to predict theoretical spectra for each peptide. -For AlphaPeptDeep_ms2_generic model, inputs are split into batches -grouped by peptide length for prediction.

-
-
- -
-
-property raw_predictions: DataFrame
-

Returns the raw prediction results from Koina.

-
-
Returns:

pd.DataFrame: Raw prediction results DataFrame

-
-
-
- -
-
-get_raw_predictions()[source]
-

Get the raw prediction results DataFrame from Koina.

-
-
Return type:
-

DataFrame

-
-
-
-
Returns:

pd.DataFrame: Raw prediction results DataFrame

-
-
-
- -
-
-save_raw_predictions(file_path, **kwargs)[source]
-

Save the raw prediction results to a file.

-
-
Return type:
-

None

-
-
Parameters:
-

file_path (str)

-
-
-
-
Parameters:

file_path (str): Path to save the file -**kwargs: Other parameters passed to pandas.DataFrame.to_csv

-
-
-
- -
-
-_sort_spectrum_by_mz(mz, intensity, annotation=None)[source]
-

Sort spectrum (m/z, intensity, and optionally annotation) by m/z values.

-
-
Parameters:
-
    -
  • mz (list of float) – m/z values.

  • -
  • intensity (list of float) – Intensity values.

  • -
  • annotation (list of str, optional) – Fragment annotations.

  • -
-
-
Returns:
-

Sorted m/z, intensity, and annotation arrays. -If annotation is None, the third element will be None.

-
-
Return type:
-

tuple of (np.ndarray, np.ndarray, np.ndarray or None)

-
-
-
- -
-
-_align_spectra_all_peaks(exp_mz, exp_intensity, pred_mz, pred_intensity, pred_annotation=None, use_ppm=True)[source]
-

Align experimental and predicted spectra.

-
-
Return type:
-

Tuple[ndarray, ndarray, List[Tuple], Dict]

-
-
Parameters:
-
    -
  • exp_mz (List[float])

  • -
  • exp_intensity (List[float])

  • -
  • pred_mz (List[float])

  • -
  • pred_intensity (List[float])

  • -
  • pred_annotation (List[str] | None)

  • -
  • use_ppm (bool)

  • -
-
-
-
-
Parameters:

exp_mz (List[float]): Experimental m/z values -exp_intensity (List[float]): Experimental intensity values -pred_mz (List[float]): Predicted m/z values -pred_intensity (List[float]): Predicted intensity values -pred_annotation (Optional[List[str]]): Predicted fragment annotations -use_ppm (bool): Whether to use ppm tolerance or Da tolerance

-
-
Returns:
-
Tuple[np.ndarray, np.ndarray, List[Tuple], Dict]:
    -
  • Aligned experimental intensity vector

  • -
  • Predicted intensity vector

  • -
  • Matching index pairs

  • -
  • Additional info including original sorted arrays

  • -
-
-
-
-
-
- -
-
-_get_top_peaks_vectors(aligned_exp_intensity, aligned_pred_intensity, matched_indices, top_n)[source]
-

Extract top N peaks based on predicted intensity for similarity calculation

-
-
Return type:
-

Tuple[ndarray, ndarray, List[Tuple]]

-
-
Parameters:
-
    -
  • aligned_exp_intensity (ndarray)

  • -
  • aligned_pred_intensity (ndarray)

  • -
  • matched_indices (List[Tuple])

  • -
  • top_n (int)

  • -
-
-
-
-
Parameters:

aligned_exp_intensity (np.ndarray): Aligned experimental intensity vector -aligned_pred_intensity (np.ndarray): Aligned predicted intensity vector -matched_indices (List[Tuple]): Matching index pairs (pred_idx, exp_idx) -top_n (int): Number of top peaks to extract

-
-
Returns:
-
Tuple[np.ndarray, np.ndarray, List[Tuple]]:
    -
  • Top N experimental intensity vector

  • -
  • Top N predicted intensity vector

  • -
  • Top N matching index pairs

  • -
-
-
-
-
-
- -
-
-_align_spectra(exp_mz, exp_intensity, pred_mz, pred_intensity, pred_annotation=None)[source]
-

Align experimental and predicted spectra.

-

This is a wrapper around _align_spectra_all_peaks and _get_top_peaks_vectors -to maintain backward compatibility while using the improved algorithm.

-
-
Parameters:
-
    -
  • exp_mz (list of float) – Experimental m/z values.

  • -
  • exp_intensity (list of float) – Experimental intensity values.

  • -
  • pred_mz (list of float) – Predicted m/z values.

  • -
  • pred_intensity (list of float) – Predicted intensity values.

  • -
  • pred_annotation (list of str, optional) – Predicted fragment annotations.

  • -
-
-
Returns:
-

    -
  • Aligned experimental intensity vector

  • -
  • Predicted intensity vector

  • -
  • Matching index pairs (for top N peaks)

  • -
-

-
-
Return type:
-

tuple of (np.ndarray, np.ndarray, list of tuple)

-
-
-
-

Notes

-

The method first aligns all peaks using _align_spectra_all_peaks, then -extracts the top N peaks using _get_top_peaks_vectors for compatibility -with existing code.

-
-
- -
-
-_normalize_vector_l2(vector)[source]
-

Normalize a vector using L2 normalization (unit vector).

-
-
Parameters:
-

vector (np.ndarray) – Input vector to normalize.

-
-
Returns:
-

L2-normalized vector.

-
-
Return type:
-

np.ndarray

-
-
-
-

Notes

-

If the input vector has zero norm, the original vector is returned unchanged.

-
-
- -
-
-_normalize_vector_sum(vector)[source]
-

Perform sum normalization (probability normalization) on a vector.

-
-
Parameters:
-

vector (np.ndarray) – Input vector.

-
-
Returns:
-

Sum normalized vector (sum to 1).

-
-
Return type:
-

np.ndarray

-
-
-
-

Notes

-

If the input vector has zero sum, the original vector is returned unchanged.

-
-
- -
-
-_calculate_spectral_angle_similarity(exp_vector, pred_vector)[source]
-

Calculate the spectral angle between experimental and predicted vectors.

-

Normalize the angle to [0, 1] where 1 is the best similarity.

-
-
Parameters:
-
    -
  • exp_vector (np.ndarray) – Experimental intensity vector.

  • -
  • pred_vector (np.ndarray) – Predicted intensity vector.

  • -
-
-
Returns:
-

Spectral angle similarity (0-1, higher is better).

-
-
Return type:
-

float

-
-
-
-

Notes

-

The spectral angle is calculated as: SA = 1 - (2 * angle / π), -where angle is the angle between the normalized vectors in radians.

-
-
- -
-
-_calculate_cosine_similarity(exp_vector, pred_vector)[source]
-

Calculate the cosine similarity between experimental and predicted vectors.

-
-
Parameters:
-
    -
  • exp_vector (np.ndarray) – Experimental intensity vector.

  • -
  • pred_vector (np.ndarray) – Predicted intensity vector.

  • -
-
-
Returns:
-

Cosine similarity (0-1, higher is better).

-
-
Return type:
-

float

-
-
-
-

Notes

-

The cosine similarity is calculated as 1 - cosine_distance. -If either vector has zero sum, returns 0.0.

-
-
- -
-
-_calculate_spearman_correlation(exp_vector, pred_vector)[source]
-

Calculate Spearman correlation coefficient between experimental and predicted vectors.

-
-
Parameters:
-
    -
  • exp_vector (np.ndarray) – Experimental intensity vector.

  • -
  • pred_vector (np.ndarray) – Predicted intensity vector.

  • -
-
-
Returns:
-

Spearman correlation coefficient (-1 to 1, higher is better).

-
-
Return type:
-

float

-
-
-
-

Notes

-

If either vector has no variation (std = 0), returns 0.0.

-
-
- -
-
-_calculate_pearson_correlation(exp_vector, pred_vector)[source]
-

Calculate Pearson correlation coefficient between experimental and predicted vectors.

-
-
Parameters:
-
    -
  • exp_vector (np.ndarray) – Experimental intensity vector.

  • -
  • pred_vector (np.ndarray) – Predicted intensity vector.

  • -
-
-
Returns:
-

Pearson correlation coefficient (-1 to 1, higher is better).

-
-
Return type:
-

float

-
-
-
-

Notes

-

If either vector has no variation (std = 0), returns 0.0.

-
-
- -
-
-_calculate_mean_squared_error(exp_vector, pred_vector)[source]
-

Calculate mean squared error between experimental and predicted vectors.

-
-
Parameters:
-
    -
  • exp_vector (np.ndarray) – Experimental intensity vector.

  • -
  • pred_vector (np.ndarray) – Predicted intensity vector.

  • -
-
-
Returns:
-

Mean squared error (lower is better).

-
-
Return type:
-

float

-
-
-
-

Notes

-

The vectors are normalized using L2 normalization before calculating -the mean squared error for fair comparison.

-
-
- -
-
-_calculate_entropy(vector)[source]
-

Calculate Shannon entropy of a vector that has already been sum-1 normalized.

-
-
Parameters:
-

vector (np.ndarray) – Input vector, which has already been sum-1 normalized.

-
-
Returns:
-

Shannon entropy.

-
-
Return type:
-

float

-
-
-
-

Notes

-

Only non-zero probabilities are considered for entropy calculation. -If all probabilities are zero, returns 0.0.

-
-
- -
-
-_calculate_unweighted_entropy_similarity(exp_vector, pred_vector)[source]
-

Calculate unweighted spectral entropy between experimental and predicted vectors.

-
-
Parameters:
-
    -
  • exp_vector (np.ndarray) – Experimental intensity vector.

  • -
  • pred_vector (np.ndarray) – Predicted intensity vector.

  • -
-
-
Returns:
-

Spectral entropy similarity.

-
-
Return type:
-

float

-
-
-
-

Notes

-

Based on the method described in https://www.nature.com/articles/s41592-021-01331-z. -The spectral entropy is calculated using the formula: -1 - (2*S_PM - S_P - S_M)/ln(4), where S_PM is the entropy of the mixed -distribution, and S_P and S_M are the entropies of the individual distributions.

-
-
- -
-
-_calculate_predicted_counts(exp_vector, pred_vector)[source]
-

Calculate counts of predicted peaks seen/not seen in experimental spectrum.

-
-
Parameters:
-
    -
  • exp_vector (np.ndarray) – Experimental intensity vector.

  • -
  • pred_vector (np.ndarray) – Predicted intensity vector.

  • -
-
-
Returns:
-

    -
  • predicted_seen_nonzero: Number of predicted peaks that are also present in experimental spectrum

  • -
  • predicted_not_seen: Number of predicted peaks that are not present in experimental spectrum

  • -
-

-
-
Return type:
-

tuple of (int, int)

-
-
-
- -
-
-_calculate_bray_curtis_similarity(exp_vector, pred_vector)[source]
-

Calculate Bray-Curtis similarity between experimental and predicted vectors

-
-
Return type:
-

float

-
-
Parameters:
-
    -
  • exp_vector (ndarray)

  • -
  • pred_vector (ndarray)

  • -
-
-
-
-
Parameters:

exp_vector (np.ndarray): Experimental intensity vector -pred_vector (np.ndarray): Predicted intensity vector

-
-
Returns:

float: Bray-Curtis similarity (0-1, higher is better)

-
-
-
- -
-
-_calculate_similarity_features(exp_vector, pred_vector)[source]
-

Calculate all similarity features between experimental and predicted vectors.

-
-
Parameters:
-
    -
  • exp_vector (np.ndarray) – Experimental intensity vector.

  • -
  • pred_vector (np.ndarray) – Predicted intensity vector.

  • -
-
-
Returns:
-

Dictionary of similarity features, including: -- spectral_angle_similarity: Spectral angle similarity (0-1) -- cosine_similarity: Cosine similarity (0-1) -- pearson_correlation: Pearson correlation (-1 to 1) -- spearman_correlation: Spearman correlation (-1 to 1) -- mean_squared_error: Mean squared error -- unweighted_entropy_similarity: Spectral entropy similarity -- predicted_seen_nonzero: Number of predicted peaks seen in experimental spectrum -- predicted_not_seen: Number of predicted peaks not seen in experimental spectrum -- bray_curtis_similarity: Bray-Curtis similarity (0-1)

-
-
Return type:
-

dict of str to float

-
-
-
- -
-
-_generate_features()[source]
-

Generate spectral similarity features

-
-
Return type:
-

DataFrame

-
-
-
-
Returns:

pd.DataFrame: DataFrame containing generated features

-
-
-
- -
-
-generate_features()[source]
-

Public interface for generating spectral similarity features.

-
-
Returns:
-

DataFrame containing the generated features.

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

This method is a wrapper around _generate_features that ensures -the results are cached and only computed once.

-
-
- -
-
-get_full_data()[source]
-

Return the full DataFrame with all columns.

-
-
Returns:
-

Full DataFrame with all columns.

-
-
Return type:
-

pd.DataFrame

-
-
-
-

Notes

-

This method returns the complete DataFrame including all intermediate -results and raw data used in feature generation.

-
-
- -
- -
-
-
-

Rescore Modules

-
-
-

Visualization

-
-
-optimhc.visualization.plot_feature_importance(models, rescoring_features, save_path=None, sort=False, error=False, **kwargs)[source]
-

Unified function to plot average feature importance across multiple models.

-
-
This function supports:
    -
  • Linear models (e.g., Linear SVR) which provide an ‘estimator’ attribute with a ‘coef_’. -The absolute value of the coefficients is used for importance, and hatch patterns are applied -to differentiate between positive and negative coefficients.

  • -
  • XGBoost models which provide a ‘feature_importances_’ attribute. Since these values are -always positive, no hatch patterns are applied.

  • -
-
-
-
-
Parameters:
-
    -
  • models (list) – A list of model objects. -For linear models, each model should have an ‘estimator’ with ‘coef_’. -For XGBoost models, each model should have a ‘feature_importances_’ attribute.

  • -
  • rescoring_features (dict) – A dictionary where keys are sources and values are lists of features.

  • -
  • save_path (str, optional) – If provided, saves the plot to the specified path.

  • -
  • sort (bool, optional) – If True, sorts the features by their importance in descending order. -Default is False.

  • -
  • error (bool, optional) – If True, adds error bars to the plot. Default is False.

  • -
  • **kwargs (dict) – Additional plotting parameters such as ‘figsize’ and ‘dpi’.

  • -
-
-
-
-

Notes

-

The function automatically detects the model type based on the presence of the corresponding attribute. -For linear models, it uses hatch patterns to differentiate between positive and negative coefficients. -For XGBoost models, it uses solid bars since the importances are always positive.

-
-
- -
-
-optimhc.visualization.visualize_target_decoy_features(psms, num_cols=5, save_path=None, **kwargs)[source]
-

Visualize the distribution of features in a DataFrame using kernel density estimation plots.

-
-
Parameters:
-
    -
  • psms (PsmContainer) – A PsmContainer object containing the features to visualize.

  • -
  • num_cols (int, optional) – The number of columns in the plot grid. Default is 5.

  • -
  • save_path (str, optional) – The file path to save the plot. If not provided, the plot is displayed.

  • -
  • **kwargs (dict) – Additional plotting parameters such as figsize and dpi, etc.

  • -
-
-
-
-

Notes

-

This function: -1. Extracts rescoring features from the PsmContainer -2. Filters out features with only one unique value -3. Creates a grid of plots showing the distribution of each feature -4. Separates target and decoy PSMs in each plot -5. Uses kernel density estimation to show the distribution shape

-
-
- -
-
-optimhc.visualization.visualize_feature_correlation(psms, save_path=None, **kwargs)[source]
-

Visualize the correlation between features in a DataFrame using a heatmap.

-
-
Parameters:
-
    -
  • psms (PsmContainer) – A PsmContainer object containing the features to visualize.

  • -
  • save_path (str, optional) – The file path to save the plot. If not provided, the plot is displayed.

  • -
  • **kwargs (dict) – Additional plotting parameters such as figsize and dpi, etc.

  • -
-
-
-
-

Notes

-

This function: -1. Extracts all rescoring features from the PsmContainer -2. Calculates the correlation matrix between features -3. Creates a heatmap visualization of the correlations -4. Uses a coolwarm colormap to show positive and negative correlations

-
-
- -
-
-optimhc.visualization.save_or_show_plot(save_path, logger, tight_layout=True)[source]
-
- -
-
-optimhc.visualization.plot_qvalues(results, save_path=None, dpi=300, figsize=(15, 10), threshold=0.05, colors=None, **kwargs)[source]
-

Plot q-values for the given results.

-
-
Parameters:
-
    -
  • results (object or list) – A list of results objects or a single result object. -Each result object should have a method plot_qvalues.

  • -
  • save_path (str, optional) – If provided, saves the plot to the specified path.

  • -
  • dpi (int, optional) – The resolution of the plot. Default is 300.

  • -
  • figsize (tuple, optional) – The size of the figure. Default is (15, 10).

  • -
  • threshold (float, optional) – The q-value threshold for plotting. Default is 0.05.

  • -
  • colors (list, optional) – A list of colors for the plots. If not provided, uses default colors.

  • -
  • **kwargs (dict) – Additional plotting parameters.

  • -
-
-
Returns:
-

The function displays or saves the plot.

-
-
Return type:
-

None

-
-
-
-

Notes

-

This function: -1. Creates a figure with two subplots for PSMs and peptides -2. Plots q-values for each result with different colors -3. Adds legends and titles to each subplot -4. Saves or displays the plot based on save_path

-
-
- -
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/examples.html b/docs/source/_build/html/examples.html deleted file mode 100644 index 016b6c6..0000000 --- a/docs/source/_build/html/examples.html +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - Examples — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- -
-

Examples

-

Example notebooks and code will be added here.

-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/genindex.html b/docs/source/_build/html/genindex.html deleted file mode 100644 index 1a1b151..0000000 --- a/docs/source/_build/html/genindex.html +++ /dev/null @@ -1,912 +0,0 @@ - - - - - - - - Index — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- - -

Index

- -
- _ - | A - | B - | C - | D - | E - | F - | G - | I - | L - | M - | N - | O - | P - | R - | S - | T - | V - | W - -
-

_

- - - -
- -

A

- - - -
- -

B

- - - -
- -

C

- - - -
- -

D

- - - -
- -

E

- - - -
- -

F

- - - -
- -

G

- - - -
- -

I

- - - -
- -

L

- - -
- -

M

- - - -
- -

N

- - - -
- -

O

- - - -
    -
  • - optimhc.cli - -
  • -
  • - optimhc.core - -
  • -
  • - optimhc.core.logging_helper - -
  • -
  • - optimhc.feature_generator - -
  • -
  • - optimhc.feature_generator.base_feature_generator - -
  • -
  • - optimhc.feature_generator.basic - -
  • -
  • - optimhc.feature_generator.DeepLC - -
  • -
  • - optimhc.feature_generator.mhcflurry - -
  • -
  • - optimhc.feature_generator.netMHCIIpan - -
  • -
  • - optimhc.feature_generator.netMHCpan - -
  • -
  • - optimhc.feature_generator.overlapping_peptide - -
  • -
- -

P

- - - -
- -

R

- - - -
- -

S

- - - -
- -

T

- - -
- -

V

- - - -
- -

W

- - -
- - - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/index.html b/docs/source/_build/html/index.html deleted file mode 100644 index 8333d65..0000000 --- a/docs/source/_build/html/index.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - - - - Welcome to optiMHC’s documentation! — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- -
-

Welcome to optiMHC’s documentation!

- -
-

Introduction

-

optiMHC is a high-performance rescoring pipeline for immunopeptidomics data to significantly enhance peptide identification performance.

-
-
-

Features

-
    -
  • High-performance peptide identification

  • -
  • Advanced rescoring algorithms

  • -
  • Comprehensive documentation

  • -
  • Easy to use API

  • -
-
-
-
-

Indices and tables

- -
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/installation.html b/docs/source/_build/html/installation.html deleted file mode 100644 index 230a4ae..0000000 --- a/docs/source/_build/html/installation.html +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - Installation — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- -
-

Installation

-
-

Requirements

-
    -
  • Python 3.9 or higher

  • -
  • pip or conda

  • -
-
-
-

Development Installation

-

To install the development version:

-
git clone https://github.com/yourusername/optimhc.git
-cd optimhc
-pip install -e .
-
-
-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/objects.inv b/docs/source/_build/html/objects.inv deleted file mode 100644 index 1afe207..0000000 Binary files a/docs/source/_build/html/objects.inv and /dev/null differ diff --git a/docs/source/_build/html/py-modindex.html b/docs/source/_build/html/py-modindex.html deleted file mode 100644 index 2f4a792..0000000 --- a/docs/source/_build/html/py-modindex.html +++ /dev/null @@ -1,229 +0,0 @@ - - - - - - - - Python Module Index — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- - -

Python Module Index

- -
- o -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
- o
- optimhc -
    - optimhc.cli -
    - optimhc.core -
    - optimhc.core.logging_helper -
    - optimhc.feature_generator -
    - optimhc.feature_generator.base_feature_generator -
    - optimhc.feature_generator.basic -
    - optimhc.feature_generator.DeepLC -
    - optimhc.feature_generator.mhcflurry -
    - optimhc.feature_generator.netMHCIIpan -
    - optimhc.feature_generator.netMHCpan -
    - optimhc.feature_generator.overlapping_peptide -
    - optimhc.feature_generator.PWM -
    - optimhc.feature_generator.spectra_similarity -
    - optimhc.parser -
    - optimhc.parser.mzml -
    - optimhc.parser.pepxml -
    - optimhc.parser.pin -
    - optimhc.psm_container -
    - optimhc.rescore -
    - optimhc.utils -
    - optimhc.visualization -
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/_build/html/search.html b/docs/source/_build/html/search.html deleted file mode 100644 index 46e20fc..0000000 --- a/docs/source/_build/html/search.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - Search — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- - - - -
- -
- -
-
- -
-
-
-
- - - - - - - - - \ No newline at end of file diff --git a/docs/source/_build/html/searchindex.js b/docs/source/_build/html/searchindex.js deleted file mode 100644 index dc91559..0000000 --- a/docs/source/_build/html/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({"alltitles": {"API Reference": [[0, null]], "CLI Interface": [[0, "cli-interface"]], "Contents:": [[2, null]], "Core Modules": [[0, "module-optimhc.core"]], "Development Installation": [[3, "development-installation"]], "Examples": [[0, null], [0, null], [0, null], [1, null]], "Feature Generator": [[0, "feature-generator"]], "Feature Generator Submodules": [[0, "feature-generator-submodules"]], "Features": [[2, "features"]], "Indices and tables": [[2, "indices-and-tables"]], "Installation": [[3, null]], "Introduction": [[2, "introduction"]], "Logging": [[0, "module-optimhc.core.logging_helper"]], "Notes": [[0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null]], "PSM Container": [[0, "psm-container"]], "Parser Modules": [[0, "parser-modules"]], "Parser Submodules": [[0, "parser-submodules"]], "Requirements": [[3, "requirements"]], "Rescore Modules": [[0, "rescore-modules"]], "Usage": [[4, null]], "Utilities": [[0, "utilities"]], "Visualization": [[0, "visualization"]], "Welcome to optiMHC\u2019s documentation!": [[2, null]]}, "docnames": ["api", "examples", "index", "installation", "usage"], "envversion": {"nbsphinx": 4, "sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.intersphinx": 1, "sphinx.ext.viewcode": 1}, "filenames": ["api.rst", "examples.rst", "index.rst", "installation.rst", "usage.rst"], "indexentries": {"__init__() (optimhc.core.pipeline method)": [[0, "optimhc.core.Pipeline.__init__", false]], "__init__() (optimhc.feature_generator.deeplc.deeplcfeaturegenerator method)": [[0, "optimhc.feature_generator.DeepLC.DeepLCFeatureGenerator.__init__", false]], "__init__() (optimhc.feature_generator.pwm.pwmfeaturegenerator method)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator.__init__", false]], "__len__() (optimhc.psm_container.psmcontainer method)": [[0, "optimhc.psm_container.PsmContainer.__len__", false]], "__repr__() (optimhc.psm_container.psmcontainer method)": [[0, "optimhc.psm_container.PsmContainer.__repr__", false]], "_align_spectra() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._align_spectra", false]], "_align_spectra_all_peaks() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._align_spectra_all_peaks", false]], "_assemble_contigs() (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator method)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator._assemble_contigs", false]], "_build_full_contig_map() (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator method)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator._build_full_contig_map", false]], "_build_overlap_graph() (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator method)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator._build_overlap_graph", false]], "_cal_anchor_score() (optimhc.feature_generator.pwm.pwmfeaturegenerator method)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator._cal_anchor_score", false]], "_cal_pwm_score() (optimhc.feature_generator.pwm.pwmfeaturegenerator method)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator._cal_PWM_score", false]], "_cal_pwm_score_i() (optimhc.feature_generator.pwm.pwmfeaturegenerator method)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator._cal_PWM_score_I", false]], "_cal_pwm_score_ii() (optimhc.feature_generator.pwm.pwmfeaturegenerator method)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator._cal_PWM_score_II", false]], "_calculate_bray_curtis_similarity() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._calculate_bray_curtis_similarity", false]], "_calculate_cosine_similarity() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._calculate_cosine_similarity", false]], "_calculate_entropy() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._calculate_entropy", false]], "_calculate_mean_squared_error() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._calculate_mean_squared_error", false]], "_calculate_overlap_contig_features() (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator method)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator._calculate_overlap_contig_features", false]], "_calculate_pearson_correlation() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._calculate_pearson_correlation", false]], "_calculate_predicted_counts() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._calculate_predicted_counts", false]], "_calculate_similarity_features() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._calculate_similarity_features", false]], "_calculate_spearman_correlation() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._calculate_spearman_correlation", false]], "_calculate_spectral_angle_similarity() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._calculate_spectral_angle_similarity", false]], "_calculate_unweighted_entropy_similarity() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._calculate_unweighted_entropy_similarity", false]], "_construct_prefix_index() (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator method)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator._construct_prefix_index", false]], "_default_allele_pwm_files() (optimhc.feature_generator.pwm.pwmfeaturegenerator method)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator._default_allele_pwm_files", false]], "_extract_experimental_spectra() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._extract_experimental_spectra", false]], "_extract_trailing_numbers() (optimhc.feature_generator.pwm.pwmfeaturegenerator method)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator._extract_trailing_numbers", false]], "_fill_missing_values() (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator._fill_missing_values", false]], "_fill_missing_values() (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator._fill_missing_values", false]], "_filter_peptides() (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator method)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator._filter_peptides", false]], "_generate_all_allele_features() (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator._generate_all_allele_features", false]], "_generate_all_allele_features() (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator._generate_all_allele_features", false]], "_generate_best_allele_features() (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator._generate_best_allele_features", false]], "_generate_best_allele_features() (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator._generate_best_allele_features", false]], "_generate_features() (optimhc.core.pipeline method)": [[0, "optimhc.core.Pipeline._generate_features", false]], "_generate_features() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._generate_features", false]], "_get_calibration_psms() (optimhc.feature_generator.deeplc.deeplcfeaturegenerator method)": [[0, "optimhc.feature_generator.DeepLC.DeepLCFeatureGenerator._get_calibration_psms", false]], "_get_deeplc_df() (optimhc.feature_generator.deeplc.deeplcfeaturegenerator method)": [[0, "optimhc.feature_generator.DeepLC.DeepLCFeatureGenerator._get_deeplc_df", false]], "_get_top_peaks_vectors() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._get_top_peaks_vectors", false]], "_integrate_overlap_features() (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator method)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator._integrate_overlap_features", false]], "_load_pwms() (optimhc.feature_generator.pwm.pwmfeaturegenerator method)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator._load_pwms", false]], "_log_features() (in module optimhc.parser.pepxml)": [[0, "optimhc.parser.pepxml._log_features", false]], "_map_peptides_to_contigs() (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator method)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator._map_peptides_to_contigs", false]], "_most_conserved_postions() (optimhc.feature_generator.pwm.pwmfeaturegenerator method)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator._most_conserved_postions", false]], "_normalize_vector_l2() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._normalize_vector_l2", false]], "_normalize_vector_sum() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._normalize_vector_sum", false]], "_parse_msms_run() (in module optimhc.parser.pepxml)": [[0, "optimhc.parser.pepxml._parse_msms_run", false]], "_parse_pepxml() (in module optimhc.parser.pepxml)": [[0, "optimhc.parser.pepxml._parse_pepxml", false]], "_parse_psm() (in module optimhc.parser.pepxml)": [[0, "optimhc.parser.pepxml._parse_psm", false]], "_parse_spectrum() (in module optimhc.parser.pepxml)": [[0, "optimhc.parser.pepxml._parse_spectrum", false]], "_predict() (optimhc.feature_generator.mhcflurry.mhcflurryfeaturegenerator method)": [[0, "optimhc.feature_generator.mhcflurry.MHCflurryFeatureGenerator._predict", false]], "_predict() (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator._predict", false]], "_predict() (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator._predict", false]], "_predict_multiprocessing() (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator._predict_multiprocessing", false]], "_predict_multiprocessing() (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator._predict_multiprocessing", false]], "_predict_peptide_chunk() (in module optimhc.feature_generator.netmhcpan)": [[0, "optimhc.feature_generator.netMHCpan._predict_peptide_chunk", false]], "_predict_peptide_chunk_class2() (in module optimhc.feature_generator.netmhciipan)": [[0, "optimhc.feature_generator.netMHCIIpan._predict_peptide_chunk_class2", false]], "_predict_theoretical_spectra() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._predict_theoretical_spectra", false]], "_preprocess_peptide() (optimhc.feature_generator.basic.basicfeaturegenerator method)": [[0, "optimhc.feature_generator.basic.BasicFeatureGenerator._preprocess_peptide", false]], "_preprocess_peptide() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._preprocess_peptide", false]], "_preprocess_peptides() (optimhc.feature_generator.mhcflurry.mhcflurryfeaturegenerator method)": [[0, "optimhc.feature_generator.mhcflurry.MHCflurryFeatureGenerator._preprocess_peptides", false]], "_preprocess_peptides() (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator._preprocess_peptides", false]], "_preprocess_peptides() (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator._preprocess_peptides", false]], "_read_single_pin_as_df() (in module optimhc.parser.pin)": [[0, "optimhc.parser.pin._read_single_pin_as_df", false]], "_remove_redundant_peptides() (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator method)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator._remove_redundant_peptides", false]], "_remove_transitive_edges() (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator method)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator._remove_transitive_edges", false]], "_run_single_experiment() (optimhc.core.pipeline method)": [[0, "optimhc.core.Pipeline._run_single_experiment", false]], "_shannon_entropy() (optimhc.feature_generator.basic.basicfeaturegenerator method)": [[0, "optimhc.feature_generator.basic.BasicFeatureGenerator._shannon_entropy", false]], "_shannon_entropy() (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator method)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator._shannon_entropy", false]], "_simplify_graph_to_contigs() (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator method)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator._simplify_graph_to_contigs", false]], "_sort_spectrum_by_mz() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator._sort_spectrum_by_mz", false]], "add_features() (optimhc.psm_container.psmcontainer method)": [[0, "optimhc.psm_container.PsmContainer.add_features", false]], "add_features_by_index() (optimhc.psm_container.psmcontainer method)": [[0, "optimhc.psm_container.PsmContainer.add_features_by_index", false]], "add_metadata() (optimhc.psm_container.psmcontainer method)": [[0, "optimhc.psm_container.PsmContainer.add_metadata", false]], "add_results() (optimhc.psm_container.psmcontainer method)": [[0, "optimhc.psm_container.PsmContainer.add_results", false]], "assign_brother_aggregated_feature() (in module optimhc.feature_generator.overlapping_peptide)": [[0, "optimhc.feature_generator.overlapping_peptide.assign_brother_aggregated_feature", false]], "basefeaturegenerator (class in optimhc.feature_generator.base_feature_generator)": [[0, "optimhc.feature_generator.base_feature_generator.BaseFeatureGenerator", false]], "basicfeaturegenerator (class in optimhc.feature_generator.basic)": [[0, "optimhc.feature_generator.basic.BasicFeatureGenerator", false]], "charges (optimhc.psm_container.psmcontainer property)": [[0, "optimhc.psm_container.PsmContainer.charges", false]], "chunksize (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator attribute)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator.CHUNKSIZE", false]], "chunksize (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator attribute)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator.CHUNKSIZE", false]], "class_ii_core_length (optimhc.feature_generator.pwm.pwmfeaturegenerator attribute)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator.CLASS_II_CORE_LENGTH", false]], "columns (optimhc.psm_container.psmcontainer property)": [[0, "optimhc.psm_container.PsmContainer.columns", false]], "contigs (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator property)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator.contigs", false]], "convert_pfm_to_pwm() (in module optimhc.utils)": [[0, "optimhc.utils.convert_pfm_to_pwm", false]], "convert_to_unimod_format() (in module optimhc.utils)": [[0, "optimhc.utils.convert_to_unimod_format", false]], "copy() (optimhc.psm_container.psmcontainer method)": [[0, "optimhc.psm_container.PsmContainer.copy", false]], "debug_logging() (in module optimhc.core.logging_helper)": [[0, "optimhc.core.logging_helper.debug_logging", false]], "decoy_psms (optimhc.psm_container.psmcontainer property)": [[0, "optimhc.psm_container.PsmContainer.decoy_psms", false]], "deeplcfeaturegenerator (class in optimhc.feature_generator.deeplc)": [[0, "optimhc.feature_generator.DeepLC.DeepLCFeatureGenerator", false]], "default_pwm_path (optimhc.feature_generator.pwm.pwmfeaturegenerator attribute)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator.DEFAULT_PWM_PATH", false]], "drop_features() (optimhc.psm_container.psmcontainer method)": [[0, "optimhc.psm_container.PsmContainer.drop_features", false]], "drop_source() (optimhc.psm_container.psmcontainer method)": [[0, "optimhc.psm_container.PsmContainer.drop_source", false]], "extract_mzml_data() (in module optimhc.parser)": [[0, "optimhc.parser.extract_mzml_data", false]], "extract_mzml_data() (in module optimhc.parser.mzml)": [[0, "optimhc.parser.mzml.extract_mzml_data", false]], "extract_unimod_from_peptidoform() (in module optimhc.utils)": [[0, "optimhc.utils.extract_unimod_from_peptidoform", false]], "feature_columns (optimhc.feature_generator.base_feature_generator.basefeaturegenerator property)": [[0, "optimhc.feature_generator.base_feature_generator.BaseFeatureGenerator.feature_columns", false]], "feature_columns (optimhc.feature_generator.basic.basicfeaturegenerator property)": [[0, "optimhc.feature_generator.basic.BasicFeatureGenerator.feature_columns", false]], "feature_columns (optimhc.feature_generator.deeplc.deeplcfeaturegenerator property)": [[0, "optimhc.feature_generator.DeepLC.DeepLCFeatureGenerator.feature_columns", false]], "feature_columns (optimhc.feature_generator.mhcflurry.mhcflurryfeaturegenerator property)": [[0, "optimhc.feature_generator.mhcflurry.MHCflurryFeatureGenerator.feature_columns", false]], "feature_columns (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator property)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator.feature_columns", false]], "feature_columns (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator property)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator.feature_columns", false]], "feature_columns (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator property)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator.feature_columns", false]], "feature_columns (optimhc.feature_generator.pwm.pwmfeaturegenerator property)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator.feature_columns", false]], "feature_columns (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator property)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator.feature_columns", false]], "feature_columns (optimhc.psm_container.psmcontainer property)": [[0, "optimhc.psm_container.PsmContainer.feature_columns", false]], "feature_sources (optimhc.psm_container.psmcontainer property)": [[0, "optimhc.psm_container.PsmContainer.feature_sources", false]], "generate_features() (optimhc.feature_generator.base_feature_generator.basefeaturegenerator method)": [[0, "optimhc.feature_generator.base_feature_generator.BaseFeatureGenerator.generate_features", false]], "generate_features() (optimhc.feature_generator.basic.basicfeaturegenerator method)": [[0, "optimhc.feature_generator.basic.BasicFeatureGenerator.generate_features", false]], "generate_features() (optimhc.feature_generator.deeplc.deeplcfeaturegenerator method)": [[0, "optimhc.feature_generator.DeepLC.DeepLCFeatureGenerator.generate_features", false]], "generate_features() (optimhc.feature_generator.mhcflurry.mhcflurryfeaturegenerator method)": [[0, "optimhc.feature_generator.mhcflurry.MHCflurryFeatureGenerator.generate_features", false]], "generate_features() (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator.generate_features", false]], "generate_features() (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator.generate_features", false]], "generate_features() (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator method)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator.generate_features", false]], "generate_features() (optimhc.feature_generator.pwm.pwmfeaturegenerator method)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator.generate_features", false]], "generate_features() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator.generate_features", false]], "get_best_allele() (optimhc.feature_generator.mhcflurry.mhcflurryfeaturegenerator method)": [[0, "optimhc.feature_generator.mhcflurry.MHCflurryFeatureGenerator.get_best_allele", false]], "get_full_data() (optimhc.feature_generator.deeplc.deeplcfeaturegenerator method)": [[0, "optimhc.feature_generator.DeepLC.DeepLCFeatureGenerator.get_full_data", false]], "get_full_data() (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator method)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator.get_full_data", false]], "get_full_data() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator.get_full_data", false]], "get_raw_predictions() (optimhc.feature_generator.deeplc.deeplcfeaturegenerator method)": [[0, "optimhc.feature_generator.DeepLC.DeepLCFeatureGenerator.get_raw_predictions", false]], "get_raw_predictions() (optimhc.feature_generator.mhcflurry.mhcflurryfeaturegenerator method)": [[0, "optimhc.feature_generator.mhcflurry.MHCflurryFeatureGenerator.get_raw_predictions", false]], "get_raw_predictions() (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator.get_raw_predictions", false]], "get_raw_predictions() (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator.get_raw_predictions", false]], "get_raw_predictions() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator.get_raw_predictions", false]], "get_top_hits() (optimhc.psm_container.psmcontainer method)": [[0, "optimhc.psm_container.PsmContainer.get_top_hits", false]], "id_column (optimhc.feature_generator.base_feature_generator.basefeaturegenerator property)": [[0, "optimhc.feature_generator.base_feature_generator.BaseFeatureGenerator.id_column", false]], "id_column (optimhc.feature_generator.basic.basicfeaturegenerator property)": [[0, "optimhc.feature_generator.basic.BasicFeatureGenerator.id_column", false]], "id_column (optimhc.feature_generator.deeplc.deeplcfeaturegenerator property)": [[0, "optimhc.feature_generator.DeepLC.DeepLCFeatureGenerator.id_column", false]], "id_column (optimhc.feature_generator.mhcflurry.mhcflurryfeaturegenerator property)": [[0, "optimhc.feature_generator.mhcflurry.MHCflurryFeatureGenerator.id_column", false]], "id_column (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator property)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator.id_column", false]], "id_column (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator property)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator.id_column", false]], "id_column (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator property)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator.id_column", false]], "id_column (optimhc.feature_generator.pwm.pwmfeaturegenerator property)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator.id_column", false]], "id_column (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator property)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator.id_column", false]], "identifier_columns (optimhc.psm_container.psmcontainer property)": [[0, "optimhc.psm_container.PsmContainer.identifier_columns", false]], "input_df() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator.input_df", false]], "list_all_files_in_directory() (in module optimhc.utils)": [[0, "optimhc.utils.list_all_files_in_directory", false]], "max_peptide_length (optimhc.feature_generator.mhcflurry.mhcflurryfeaturegenerator attribute)": [[0, "optimhc.feature_generator.mhcflurry.MHCflurryFeatureGenerator.MAX_PEPTIDE_LENGTH", false]], "max_peptide_length (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator attribute)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator.MAX_PEPTIDE_LENGTH", false]], "max_peptide_length (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator attribute)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator.MAX_PEPTIDE_LENGTH", false]], "metadata (optimhc.psm_container.psmcontainer property)": [[0, "optimhc.psm_container.PsmContainer.metadata", false]], "mhcflurryfeaturegenerator (class in optimhc.feature_generator.mhcflurry)": [[0, "optimhc.feature_generator.mhcflurry.MHCflurryFeatureGenerator", false]], "min_peptide_length (optimhc.feature_generator.mhcflurry.mhcflurryfeaturegenerator attribute)": [[0, "optimhc.feature_generator.mhcflurry.MHCflurryFeatureGenerator.MIN_PEPTIDE_LENGTH", false]], "min_peptide_length (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator attribute)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator.MIN_PEPTIDE_LENGTH", false]], "min_peptide_length (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator attribute)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator.MIN_PEPTIDE_LENGTH", false]], "module": [[0, "module-optimhc.cli", false], [0, "module-optimhc.core", false], [0, "module-optimhc.core.logging_helper", false], [0, "module-optimhc.feature_generator", false], [0, "module-optimhc.feature_generator.DeepLC", false], [0, "module-optimhc.feature_generator.PWM", false], [0, "module-optimhc.feature_generator.base_feature_generator", false], [0, "module-optimhc.feature_generator.basic", false], [0, "module-optimhc.feature_generator.mhcflurry", false], [0, "module-optimhc.feature_generator.netMHCIIpan", false], [0, "module-optimhc.feature_generator.netMHCpan", false], [0, "module-optimhc.feature_generator.overlapping_peptide", false], [0, "module-optimhc.feature_generator.spectra_similarity", false], [0, "module-optimhc.parser", false], [0, "module-optimhc.parser.mzml", false], [0, "module-optimhc.parser.pepxml", false], [0, "module-optimhc.parser.pin", false], [0, "module-optimhc.psm_container", false], [0, "module-optimhc.rescore", false], [0, "module-optimhc.utils", false], [0, "module-optimhc.visualization", false]], "ms_data_files (optimhc.psm_container.psmcontainer property)": [[0, "optimhc.psm_container.PsmContainer.ms_data_files", false]], "netmhciipanfeaturegenerator (class in optimhc.feature_generator.netmhciipan)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator", false]], "netmhcpanfeaturegenerator (class in optimhc.feature_generator.netmhcpan)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator", false]], "optimhc.cli": [[0, "module-optimhc.cli", false]], "optimhc.core": [[0, "module-optimhc.core", false]], "optimhc.core.logging_helper": [[0, "module-optimhc.core.logging_helper", false]], "optimhc.feature_generator": [[0, "module-optimhc.feature_generator", false]], "optimhc.feature_generator.base_feature_generator": [[0, "module-optimhc.feature_generator.base_feature_generator", false]], "optimhc.feature_generator.basic": [[0, "module-optimhc.feature_generator.basic", false]], "optimhc.feature_generator.deeplc": [[0, "module-optimhc.feature_generator.DeepLC", false]], "optimhc.feature_generator.mhcflurry": [[0, "module-optimhc.feature_generator.mhcflurry", false]], "optimhc.feature_generator.netmhciipan": [[0, "module-optimhc.feature_generator.netMHCIIpan", false]], "optimhc.feature_generator.netmhcpan": [[0, "module-optimhc.feature_generator.netMHCpan", false]], "optimhc.feature_generator.overlapping_peptide": [[0, "module-optimhc.feature_generator.overlapping_peptide", false]], "optimhc.feature_generator.pwm": [[0, "module-optimhc.feature_generator.PWM", false]], "optimhc.feature_generator.spectra_similarity": [[0, "module-optimhc.feature_generator.spectra_similarity", false]], "optimhc.parser": [[0, "module-optimhc.parser", false]], "optimhc.parser.mzml": [[0, "module-optimhc.parser.mzml", false]], "optimhc.parser.pepxml": [[0, "module-optimhc.parser.pepxml", false]], "optimhc.parser.pin": [[0, "module-optimhc.parser.pin", false]], "optimhc.psm_container": [[0, "module-optimhc.psm_container", false]], "optimhc.rescore": [[0, "module-optimhc.rescore", false]], "optimhc.utils": [[0, "module-optimhc.utils", false]], "optimhc.visualization": [[0, "module-optimhc.visualization", false]], "overlap_graph (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator property)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator.overlap_graph", false]], "overlappingpeptidefeaturegenerator (class in optimhc.feature_generator.overlapping_peptide)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator", false]], "parse_cli_config() (in module optimhc.cli)": [[0, "optimhc.cli.parse_cli_config", false]], "peptides (optimhc.psm_container.psmcontainer property)": [[0, "optimhc.psm_container.PsmContainer.peptides", false]], "pipeline (class in optimhc.core)": [[0, "optimhc.core.Pipeline", false]], "plot_feature_importance() (in module optimhc.visualization)": [[0, "optimhc.visualization.plot_feature_importance", false]], "plot_qvalues() (in module optimhc.visualization)": [[0, "optimhc.visualization.plot_qvalues", false]], "predictions_to_dataframe() (optimhc.feature_generator.mhcflurry.mhcflurryfeaturegenerator method)": [[0, "optimhc.feature_generator.mhcflurry.MHCflurryFeatureGenerator.predictions_to_dataframe", false]], "predictions_to_dataframe() (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator.predictions_to_dataframe", false]], "predictions_to_dataframe() (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator.predictions_to_dataframe", false]], "preprocess_peptide() (in module optimhc.utils)": [[0, "optimhc.utils.preprocess_peptide", false]], "psmcontainer (class in optimhc.psm_container)": [[0, "optimhc.psm_container.PsmContainer", false]], "psms (optimhc.psm_container.psmcontainer property)": [[0, "optimhc.psm_container.PsmContainer.psms", false]], "pwmfeaturegenerator (class in optimhc.feature_generator.pwm)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator", false]], "raw_predictions (optimhc.feature_generator.deeplc.deeplcfeaturegenerator property)": [[0, "optimhc.feature_generator.DeepLC.DeepLCFeatureGenerator.raw_predictions", false]], "raw_predictions (optimhc.feature_generator.mhcflurry.mhcflurryfeaturegenerator property)": [[0, "optimhc.feature_generator.mhcflurry.MHCflurryFeatureGenerator.raw_predictions", false]], "raw_predictions (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator property)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator.raw_predictions", false]], "raw_predictions (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator property)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator.raw_predictions", false]], "raw_predictions (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator property)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator.raw_predictions", false]], "read_input() (optimhc.core.pipeline method)": [[0, "optimhc.core.Pipeline.read_input", false]], "read_pepxml() (in module optimhc.parser)": [[0, "optimhc.parser.read_pepxml", false]], "read_pepxml() (in module optimhc.parser.pepxml)": [[0, "optimhc.parser.pepxml.read_pepxml", false]], "read_pin() (in module optimhc.parser)": [[0, "optimhc.parser.read_pin", false]], "read_pin() (in module optimhc.parser.pin)": [[0, "optimhc.parser.pin.read_pin", false]], "remove_modifications() (in module optimhc.utils)": [[0, "optimhc.utils.remove_modifications", false]], "remove_pre_and_nxt_aa() (in module optimhc.utils)": [[0, "optimhc.utils.remove_pre_and_nxt_aa", false]], "rescore() (optimhc.core.pipeline method)": [[0, "optimhc.core.Pipeline.rescore", false]], "run() (optimhc.core.pipeline method)": [[0, "optimhc.core.Pipeline.run", false]], "run_experiments() (optimhc.core.pipeline method)": [[0, "optimhc.core.Pipeline.run_experiments", false]], "save_or_show_plot() (in module optimhc.visualization)": [[0, "optimhc.visualization.save_or_show_plot", false]], "save_raw_predictions() (optimhc.feature_generator.deeplc.deeplcfeaturegenerator method)": [[0, "optimhc.feature_generator.DeepLC.DeepLCFeatureGenerator.save_raw_predictions", false]], "save_raw_predictions() (optimhc.feature_generator.mhcflurry.mhcflurryfeaturegenerator method)": [[0, "optimhc.feature_generator.mhcflurry.MHCflurryFeatureGenerator.save_raw_predictions", false]], "save_raw_predictions() (optimhc.feature_generator.netmhciipan.netmhciipanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator.save_raw_predictions", false]], "save_raw_predictions() (optimhc.feature_generator.netmhcpan.netmhcpanfeaturegenerator method)": [[0, "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator.save_raw_predictions", false]], "save_raw_predictions() (optimhc.feature_generator.spectra_similarity.spectrasimilarityfeaturegenerator method)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator.save_raw_predictions", false]], "save_results() (optimhc.core.pipeline method)": [[0, "optimhc.core.Pipeline.save_results", false]], "scan_ids (optimhc.psm_container.psmcontainer property)": [[0, "optimhc.psm_container.PsmContainer.scan_ids", false]], "set_pwms() (optimhc.feature_generator.pwm.pwmfeaturegenerator method)": [[0, "optimhc.feature_generator.PWM.PWMFeatureGenerator.set_pwms", false]], "setup_loggers() (in module optimhc.core.logging_helper)": [[0, "optimhc.core.logging_helper.setup_loggers", false]], "simplified_graph (optimhc.feature_generator.overlapping_peptide.overlappingpeptidefeaturegenerator property)": [[0, "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator.simplified_graph", false]], "spectrasimilarityfeaturegenerator (class in optimhc.feature_generator.spectra_similarity)": [[0, "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator", false]], "spectrum_ids (optimhc.psm_container.psmcontainer property)": [[0, "optimhc.psm_container.PsmContainer.spectrum_ids", false]], "target_psms (optimhc.psm_container.psmcontainer property)": [[0, "optimhc.psm_container.PsmContainer.target_psms", false]], "visualize_feature_correlation() (in module optimhc.visualization)": [[0, "optimhc.visualization.visualize_feature_correlation", false]], "visualize_results() (optimhc.core.pipeline method)": [[0, "optimhc.core.Pipeline.visualize_results", false]], "visualize_target_decoy_features() (in module optimhc.visualization)": [[0, "optimhc.visualization.visualize_target_decoy_features", false]], "write_pin() (optimhc.psm_container.psmcontainer method)": [[0, "optimhc.psm_container.PsmContainer.write_pin", false]]}, "objects": {"optimhc": [[0, 0, 0, "-", "cli"], [0, 0, 0, "-", "core"], [0, 0, 0, "-", "feature_generator"], [0, 0, 0, "-", "parser"], [0, 0, 0, "-", "psm_container"], [0, 0, 0, "-", "rescore"], [0, 0, 0, "-", "utils"], [0, 0, 0, "-", "visualization"]], "optimhc.cli": [[0, 1, 1, "", "parse_cli_config"]], "optimhc.core": [[0, 2, 1, "", "Pipeline"], [0, 0, 0, "-", "logging_helper"]], "optimhc.core.Pipeline": [[0, 3, 1, "", "__init__"], [0, 3, 1, "", "_generate_features"], [0, 3, 1, "", "_run_single_experiment"], [0, 3, 1, "", "read_input"], [0, 3, 1, "", "rescore"], [0, 3, 1, "", "run"], [0, 3, 1, "", "run_experiments"], [0, 3, 1, "", "save_results"], [0, 3, 1, "", "visualize_results"]], "optimhc.core.logging_helper": [[0, 1, 1, "", "debug_logging"], [0, 1, 1, "", "setup_loggers"]], "optimhc.feature_generator": [[0, 0, 0, "-", "DeepLC"], [0, 0, 0, "-", "PWM"], [0, 0, 0, "-", "base_feature_generator"], [0, 0, 0, "-", "basic"], [0, 0, 0, "-", "mhcflurry"], [0, 0, 0, "-", "netMHCIIpan"], [0, 0, 0, "-", "netMHCpan"], [0, 0, 0, "-", "overlapping_peptide"], [0, 0, 0, "-", "spectra_similarity"]], "optimhc.feature_generator.DeepLC": [[0, 2, 1, "", "DeepLCFeatureGenerator"]], "optimhc.feature_generator.DeepLC.DeepLCFeatureGenerator": [[0, 3, 1, "", "__init__"], [0, 3, 1, "", "_get_calibration_psms"], [0, 3, 1, "", "_get_deeplc_df"], [0, 4, 1, "", "feature_columns"], [0, 3, 1, "", "generate_features"], [0, 3, 1, "", "get_full_data"], [0, 3, 1, "", "get_raw_predictions"], [0, 4, 1, "", "id_column"], [0, 4, 1, "", "raw_predictions"], [0, 3, 1, "", "save_raw_predictions"]], "optimhc.feature_generator.PWM": [[0, 2, 1, "", "PWMFeatureGenerator"]], "optimhc.feature_generator.PWM.PWMFeatureGenerator": [[0, 5, 1, "", "CLASS_II_CORE_LENGTH"], [0, 5, 1, "", "DEFAULT_PWM_PATH"], [0, 3, 1, "", "__init__"], [0, 3, 1, "", "_cal_PWM_score"], [0, 3, 1, "", "_cal_PWM_score_I"], [0, 3, 1, "", "_cal_PWM_score_II"], [0, 3, 1, "", "_cal_anchor_score"], [0, 3, 1, "", "_default_allele_pwm_files"], [0, 3, 1, "", "_extract_trailing_numbers"], [0, 3, 1, "", "_load_pwms"], [0, 3, 1, "", "_most_conserved_postions"], [0, 4, 1, "", "feature_columns"], [0, 3, 1, "", "generate_features"], [0, 4, 1, "", "id_column"], [0, 3, 1, "", "set_pwms"]], "optimhc.feature_generator.base_feature_generator": [[0, 2, 1, "", "BaseFeatureGenerator"]], "optimhc.feature_generator.base_feature_generator.BaseFeatureGenerator": [[0, 4, 1, "", "feature_columns"], [0, 3, 1, "", "generate_features"], [0, 4, 1, "", "id_column"]], "optimhc.feature_generator.basic": [[0, 2, 1, "", "BasicFeatureGenerator"]], "optimhc.feature_generator.basic.BasicFeatureGenerator": [[0, 3, 1, "", "_preprocess_peptide"], [0, 3, 1, "", "_shannon_entropy"], [0, 4, 1, "", "feature_columns"], [0, 3, 1, "", "generate_features"], [0, 4, 1, "", "id_column"]], "optimhc.feature_generator.mhcflurry": [[0, 2, 1, "", "MHCflurryFeatureGenerator"]], "optimhc.feature_generator.mhcflurry.MHCflurryFeatureGenerator": [[0, 5, 1, "", "MAX_PEPTIDE_LENGTH"], [0, 5, 1, "", "MIN_PEPTIDE_LENGTH"], [0, 3, 1, "", "_predict"], [0, 3, 1, "", "_preprocess_peptides"], [0, 4, 1, "", "feature_columns"], [0, 3, 1, "", "generate_features"], [0, 3, 1, "", "get_best_allele"], [0, 3, 1, "", "get_raw_predictions"], [0, 4, 1, "", "id_column"], [0, 3, 1, "", "predictions_to_dataframe"], [0, 4, 1, "", "raw_predictions"], [0, 3, 1, "", "save_raw_predictions"]], "optimhc.feature_generator.netMHCIIpan": [[0, 2, 1, "", "NetMHCIIpanFeatureGenerator"], [0, 1, 1, "", "_predict_peptide_chunk_class2"]], "optimhc.feature_generator.netMHCIIpan.NetMHCIIpanFeatureGenerator": [[0, 5, 1, "", "CHUNKSIZE"], [0, 5, 1, "", "MAX_PEPTIDE_LENGTH"], [0, 5, 1, "", "MIN_PEPTIDE_LENGTH"], [0, 3, 1, "", "_fill_missing_values"], [0, 3, 1, "", "_generate_all_allele_features"], [0, 3, 1, "", "_generate_best_allele_features"], [0, 3, 1, "", "_predict"], [0, 3, 1, "", "_predict_multiprocessing"], [0, 3, 1, "", "_preprocess_peptides"], [0, 4, 1, "", "feature_columns"], [0, 3, 1, "", "generate_features"], [0, 3, 1, "", "get_raw_predictions"], [0, 4, 1, "", "id_column"], [0, 3, 1, "", "predictions_to_dataframe"], [0, 4, 1, "", "raw_predictions"], [0, 3, 1, "", "save_raw_predictions"]], "optimhc.feature_generator.netMHCpan": [[0, 2, 1, "", "NetMHCpanFeatureGenerator"], [0, 1, 1, "", "_predict_peptide_chunk"]], "optimhc.feature_generator.netMHCpan.NetMHCpanFeatureGenerator": [[0, 5, 1, "", "CHUNKSIZE"], [0, 5, 1, "", "MAX_PEPTIDE_LENGTH"], [0, 5, 1, "", "MIN_PEPTIDE_LENGTH"], [0, 3, 1, "", "_fill_missing_values"], [0, 3, 1, "", "_generate_all_allele_features"], [0, 3, 1, "", "_generate_best_allele_features"], [0, 3, 1, "", "_predict"], [0, 3, 1, "", "_predict_multiprocessing"], [0, 3, 1, "", "_preprocess_peptides"], [0, 4, 1, "", "feature_columns"], [0, 3, 1, "", "generate_features"], [0, 3, 1, "", "get_raw_predictions"], [0, 4, 1, "", "id_column"], [0, 3, 1, "", "predictions_to_dataframe"], [0, 4, 1, "", "raw_predictions"], [0, 3, 1, "", "save_raw_predictions"]], "optimhc.feature_generator.overlapping_peptide": [[0, 2, 1, "", "OverlappingPeptideFeatureGenerator"], [0, 1, 1, "", "assign_brother_aggregated_feature"]], "optimhc.feature_generator.overlapping_peptide.OverlappingPeptideFeatureGenerator": [[0, 3, 1, "", "_assemble_contigs"], [0, 3, 1, "", "_build_full_contig_map"], [0, 3, 1, "", "_build_overlap_graph"], [0, 3, 1, "", "_calculate_overlap_contig_features"], [0, 3, 1, "", "_construct_prefix_index"], [0, 3, 1, "", "_filter_peptides"], [0, 3, 1, "", "_integrate_overlap_features"], [0, 3, 1, "", "_map_peptides_to_contigs"], [0, 3, 1, "", "_remove_redundant_peptides"], [0, 3, 1, "", "_remove_transitive_edges"], [0, 3, 1, "", "_shannon_entropy"], [0, 3, 1, "", "_simplify_graph_to_contigs"], [0, 4, 1, "", "contigs"], [0, 4, 1, "", "feature_columns"], [0, 3, 1, "", "generate_features"], [0, 3, 1, "", "get_full_data"], [0, 4, 1, "", "id_column"], [0, 4, 1, "", "overlap_graph"], [0, 4, 1, "", "simplified_graph"]], "optimhc.feature_generator.spectra_similarity": [[0, 2, 1, "", "SpectraSimilarityFeatureGenerator"]], "optimhc.feature_generator.spectra_similarity.SpectraSimilarityFeatureGenerator": [[0, 3, 1, "", "_align_spectra"], [0, 3, 1, "", "_align_spectra_all_peaks"], [0, 3, 1, "", "_calculate_bray_curtis_similarity"], [0, 3, 1, "", "_calculate_cosine_similarity"], [0, 3, 1, "", "_calculate_entropy"], [0, 3, 1, "", "_calculate_mean_squared_error"], [0, 3, 1, "", "_calculate_pearson_correlation"], [0, 3, 1, "", "_calculate_predicted_counts"], [0, 3, 1, "", "_calculate_similarity_features"], [0, 3, 1, "", "_calculate_spearman_correlation"], [0, 3, 1, "", "_calculate_spectral_angle_similarity"], [0, 3, 1, "", "_calculate_unweighted_entropy_similarity"], [0, 3, 1, "", "_extract_experimental_spectra"], [0, 3, 1, "", "_generate_features"], [0, 3, 1, "", "_get_top_peaks_vectors"], [0, 3, 1, "", "_normalize_vector_l2"], [0, 3, 1, "", "_normalize_vector_sum"], [0, 3, 1, "", "_predict_theoretical_spectra"], [0, 3, 1, "", "_preprocess_peptide"], [0, 3, 1, "", "_sort_spectrum_by_mz"], [0, 4, 1, "", "feature_columns"], [0, 3, 1, "", "generate_features"], [0, 3, 1, "", "get_full_data"], [0, 3, 1, "", "get_raw_predictions"], [0, 4, 1, "", "id_column"], [0, 3, 1, "", "input_df"], [0, 4, 1, "", "raw_predictions"], [0, 3, 1, "", "save_raw_predictions"]], "optimhc.parser": [[0, 1, 1, "", "extract_mzml_data"], [0, 0, 0, "-", "mzml"], [0, 0, 0, "-", "pepxml"], [0, 0, 0, "-", "pin"], [0, 1, 1, "", "read_pepxml"], [0, 1, 1, "", "read_pin"]], "optimhc.parser.mzml": [[0, 1, 1, "", "extract_mzml_data"]], "optimhc.parser.pepxml": [[0, 1, 1, "", "_log_features"], [0, 1, 1, "", "_parse_msms_run"], [0, 1, 1, "", "_parse_pepxml"], [0, 1, 1, "", "_parse_psm"], [0, 1, 1, "", "_parse_spectrum"], [0, 1, 1, "", "read_pepxml"]], "optimhc.parser.pin": [[0, 1, 1, "", "_read_single_pin_as_df"], [0, 1, 1, "", "read_pin"]], "optimhc.psm_container": [[0, 2, 1, "", "PsmContainer"]], "optimhc.psm_container.PsmContainer": [[0, 3, 1, "", "__len__"], [0, 3, 1, "", "__repr__"], [0, 3, 1, "", "add_features"], [0, 3, 1, "", "add_features_by_index"], [0, 3, 1, "", "add_metadata"], [0, 3, 1, "", "add_results"], [0, 4, 1, "", "charges"], [0, 4, 1, "", "columns"], [0, 3, 1, "", "copy"], [0, 4, 1, "", "decoy_psms"], [0, 3, 1, "", "drop_features"], [0, 3, 1, "", "drop_source"], [0, 4, 1, "", "feature_columns"], [0, 4, 1, "", "feature_sources"], [0, 3, 1, "", "get_top_hits"], [0, 4, 1, "", "identifier_columns"], [0, 4, 1, "", "metadata"], [0, 4, 1, "", "ms_data_files"], [0, 4, 1, "", "peptides"], [0, 4, 1, "", "psms"], [0, 4, 1, "", "scan_ids"], [0, 4, 1, "", "spectrum_ids"], [0, 4, 1, "", "target_psms"], [0, 3, 1, "", "write_pin"]], "optimhc.utils": [[0, 1, 1, "", "convert_pfm_to_pwm"], [0, 1, 1, "", "convert_to_unimod_format"], [0, 1, 1, "", "extract_unimod_from_peptidoform"], [0, 1, 1, "", "list_all_files_in_directory"], [0, 1, 1, "", "preprocess_peptide"], [0, 1, 1, "", "remove_modifications"], [0, 1, 1, "", "remove_pre_and_nxt_aa"]], "optimhc.visualization": [[0, 1, 1, "", "plot_feature_importance"], [0, 1, 1, "", "plot_qvalues"], [0, 1, 1, "", "save_or_show_plot"], [0, 1, 1, "", "visualize_feature_correlation"], [0, 1, 1, "", "visualize_target_decoy_features"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "function", "Python function"], "2": ["py", "class", "Python class"], "3": ["py", "method", "Python method"], "4": ["py", "property", "Python property"], "5": ["py", "attribute", "Python attribute"]}, "objtypes": {"0": "py:module", "1": "py:function", "2": "py:class", "3": "py:method", "4": "py:property", "5": "py:attribute"}, "terms": {"": 0, "0": 0, "01": 0, "0106": 0, "01331": 0, "02": 0, "021": 0, "05": 0, "07": 0, "1": 0, "10": 0, "12": 0, "123": 0, "15": 0, "2": 0, "20": 0, "250": 0, "3": [0, 3], "30": 0, "300": 0, "36": 0, "4": 0, "42": 0, "443": 0, "48": 0, "5": 0, "50": 0, "500": 0, "6": 0, "60": 0, "7": 0, "8": 0, "9": [0, 3], "9949": 0, "A": 0, "As": 0, "For": 0, "If": 0, "In": 0, "It": 0, "The": 0, "To": 3, "With": 0, "__init__": 0, "__len__": 0, "__repr__": 0, "_align_spectra": 0, "_align_spectra_all_peak": 0, "_assemble_contig": 0, "_build_full_contig_map": 0, "_build_overlap_graph": 0, "_cal_anchor_scor": 0, "_cal_pwm_scor": 0, "_cal_pwm_score_i": 0, "_cal_pwm_score_ii": 0, "_calculate_bray_curtis_similar": 0, "_calculate_cosine_similar": 0, "_calculate_entropi": 0, "_calculate_mean_squared_error": 0, "_calculate_overlap_contig_featur": 0, "_calculate_pearson_correl": 0, "_calculate_predicted_count": 0, "_calculate_similarity_featur": 0, "_calculate_spearman_correl": 0, "_calculate_spectral_angle_similar": 0, "_calculate_unweighted_entropy_similar": 0, "_construct_prefix_index": 0, "_deeplc": 0, "_default_allele_pwm_fil": 0, "_extract_experimental_spectra": 0, "_extract_trailing_numb": 0, "_fill_missing_valu": 0, "_filter_peptid": 0, "_generate_all_allele_featur": 0, "_generate_best_allele_featur": 0, "_generate_featur": 0, "_get_calibration_psm": 0, "_get_deeplc_df": 0, "_get_top_peaks_vector": 0, "_integrate_overlap_featur": 0, "_load_pwm": 0, "_log_featur": 0, "_map_peptides_to_contig": 0, "_most_conserved_post": 0, "_netmhc": 0, "_new": 0, "_normalize_vector_l2": 0, "_normalize_vector_sum": 0, "_overlap_graph": 0, "_parse_msms_run": 0, "_parse_pepxml": 0, "_parse_psm": 0, "_parse_spectrum": 0, "_predict": 0, "_predict_multiprocess": 0, "_predict_peptide_chunk": 0, "_predict_peptide_chunk_class2": 0, "_predict_theoretical_spectra": 0, "_preprocess_peptid": 0, "_read_single_pin_as_df": 0, "_remove_redundant_peptid": 0, "_remove_transitive_edg": 0, "_run_single_experi": 0, "_shannon_entropi": 0, "_simplified_graph": 0, "_simplify_graph_to_contig": 0, "_sort_spectrum_by_mz": 0, "a01": 0, "a01_01": 0, "aa": 0, "aandagyfndem": 0, "aandagyfndemapievktk": 0, "abc": 0, "abc123": 0, "about": 0, "abs_length_diff_from_avg": 0, "abs_retention_time_diff": 0, "absolut": 0, "abstract": 0, "accept": 0, "accepted_peptid": 0, "access": 0, "acetyl": 0, "acid": 0, "across": 0, "ad": [0, 1, 4], "adapt": 0, "add": 0, "add_featur": 0, "add_features_by_index": 0, "add_metadata": 0, "add_result": 0, "addit": 0, "addition": 0, "adjac": 0, "advanc": 2, "affin": 0, "after": 0, "against": 0, "aggreg": 0, "algorithm": [0, 2], "align": 0, "aligned_exp_intens": 0, "aligned_pred_intens": 0, "all": 0, "allel": 0, "allow": 0, "alphapeptdeep_ms2_gener": 0, "alreadi": 0, "also": 0, "alwai": 0, "amino": 0, "amount": 0, "an": 0, "analysi": 0, "analyz": 0, "anchor": 0, "anchor_dict": 0, "anchor_score_": 0, "angl": 0, "ani": 0, "annot": 0, "anyth": 0, "api": 2, "apievk": 0, "apievktk": 0, "appli": 0, "applic": 0, "appropri": 0, "ar": 0, "arg": 0, "argument": 0, "around": 0, "arrai": 0, "articl": 0, "assembl": 0, "assembled_contig": 0, "assembli": 0, "assign": 0, "assign_brother_aggregated_featur": 0, "associ": 0, "attribut": 0, "automat": 0, "avail": 0, "averag": 0, "avoid": 0, "b": 0, "b07": 0, "back": 0, "background": 0, "background_freq": 0, "backward": 0, "bar": 0, "base": 0, "base_feature_gener": 0, "basefeaturegener": 0, "basic": 0, "basicfeaturegener": 0, "batch": 0, "becom": 0, "been": 0, "befor": 0, "best": 0, "better": 0, "between": 0, "bind": 0, "bool": 0, "both": 0, "bracket": 0, "brai": 0, "branch": 0, "bray_curtis_similar": 0, "brother": 0, "build": 0, "c": 0, "c_flank_pwm": 0, "c_flank_pwm_score_": 0, "c_flank_scor": 0, "cach": 0, "calcul": 0, "calibr": 0, "calibration_criteria_column": 0, "calibration_set_s": 0, "case": 0, "cd": 3, "chain": 0, "chang": 0, "charg": 0, "charge_column": 0, "check": 0, "chunk": 0, "chunksiz": 0, "cid": 0, "class": 0, "class_ii_core_length": 0, "clean": 0, "cli": 2, "clone": 3, "code": [0, 1], "coef_": 0, "coeffici": 0, "col": 0, "collis": 0, "collision_energi": 0, "color": 0, "colormap": 0, "column": 0, "com": [0, 3], "combin": 0, "come": 0, "comparison": 0, "compat": 0, "complementari": 0, "complet": 0, "comprehens": 2, "comput": 0, "conda": 3, "config": 0, "configur": 0, "conflict": 0, "consensu": 0, "conserv": 0, "consid": 0, "consist": 0, "consol": 0, "constitu": 0, "construct": 0, "contain": 2, "contig": 0, "contig_member_count": 0, "contigsequ": 0, "continu": 0, "conveni": 0, "convers": 0, "convert": 0, "convert_pfm_to_pwm": 0, "convert_to_unimod_format": 0, "coolwarm": 0, "copi": 0, "core": 2, "core_scor": 0, "correl": 0, "correspond": 0, "cosin": 0, "cosine_dist": 0, "cosine_similar": 0, "count": 0, "creat": 0, "criteria": 0, "csv": 0, "current": 0, "curti": 0, "custom": 0, "da": 0, "data": [0, 2], "datafram": 0, "dataset": 0, "debug": 0, "debug_log": 0, "decoi": 0, "decoy_": 0, "decoy_prefix": 0, "decoy_psm": 0, "deep": 0, "deeplc": 0, "deeplc_df": 0, "deeplc_retrain": 0, "deeplcfeaturegener": 0, "default": 0, "default_pwm_path": 0, "delimit": 0, "denot": 0, "densiti": 0, "depend": 0, "descend": 0, "describ": 0, "descript": 0, "detail": 0, "detect": 0, "determin": 0, "develop": 2, "dict": 0, "dictionari": 0, "differ": 0, "differenti": 0, "digraph": 0, "direct": 0, "directli": 0, "directori": 0, "directory_path": 0, "disabl": 0, "disk": 0, "displai": 0, "distinguish": 0, "distribut": 0, "do": 0, "doe": 0, "dpi": 0, "drb10101": 0, "drb1_0101": 0, "drb1_0102": 0, "drop": 0, "drop_featur": 0, "drop_sourc": 0, "duplic": 0, "dure": 0, "e": [0, 3], "each": 0, "easi": 2, "edg": 0, "effici": 0, "either": 0, "element": 0, "empti": 0, "encapsul": 0, "energi": 0, "engin": 0, "enhanc": 2, "ensur": 0, "entropi": 0, "error": 0, "essenti": 0, "estim": 0, "etc": 0, "etre": 0, "exampl": 2, "exce": 0, "except": 0, "exclud": 0, "execut": 0, "exist": 0, "exp_config": 0, "exp_dir": 0, "exp_idx": 0, "exp_intens": 0, "exp_mz": 0, "exp_nam": 0, "exp_vector": 0, "expect": 0, "experi": 0, "experiment": 0, "extern": 0, "extract": 0, "extract_mzml_data": 0, "extract_unimod_from_peptidoform": 0, "fail": 0, "fair": 0, "fals": 0, "fasta": 0, "fdr": 0, "feature1": 0, "feature2": 0, "feature3": 0, "feature_column": 0, "feature_gener": 0, "feature_importances_": 0, "feature_kei": 0, "feature_sourc": 0, "featuren": 0, "features_df": 0, "features_df1": 0, "features_df2": 0, "fewer": 0, "figsiz": 0, "figur": 0, "file": 0, "file1": 0, "file2": 0, "file_path": 0, "file_root": 0, "filehandl": 0, "filenotfounderror": 0, "fill": 0, "fill_miss": 0, "filter": 0, "filtered_indic": 0, "filtered_peptid": 0, "final": 0, "find": 0, "first": 0, "fix": 0, "flank": 0, "float": 0, "follow": 0, "form": 0, "format": 0, "formula": 0, "found": 0, "fragment": 0, "fragmentation_typ": 0, "frequenc": 0, "from": 0, "full": 0, "full_data": 0, "fulli": 0, "function": 0, "futur": 0, "g": 0, "gener": 2, "generate_featur": 0, "get": 0, "get_all_data": 0, "get_best_allel": 0, "get_full_data": 0, "get_raw_predict": 0, "get_top_hit": 0, "git": 3, "github": 3, "given": 0, "graph": 0, "grid": 0, "group": 0, "ha": 0, "handl": 0, "hatch": 0, "have": 0, "haven": 0, "hcd": 0, "header": 0, "heatmap": 0, "help": 0, "here": [1, 4], "heurist": 0, "high": 2, "higher": [0, 3], "highest": 0, "hit": 0, "hit_rank_column": 0, "hla": 0, "http": [0, 3], "i": [0, 2], "id": 0, "id_column": 0, "identif": 2, "identifi": 0, "identifier_column": 0, "ii": 0, "immunopeptidom": [0, 2], "implement": 0, "import": 0, "improv": 0, "imput": 0, "includ": 0, "index": [0, 2], "indic": 0, "individu": 0, "info": 0, "inform": 0, "initi": 0, "inner": 0, "input": 0, "input_df": 0, "insensit": 0, "insid": 0, "instal": 2, "instruct": 4, "instrument": 0, "int": 0, "integr": 0, "intens": 0, "interfac": 2, "intermedi": 0, "intern": 0, "involv": 0, "io": 0, "ion": 0, "isol": 0, "its": 0, "job": 0, "join": 0, "keep": 0, "keep_modif": 0, "kei": 0, "kept": 0, "kernel": 0, "keyword": 0, "koina": 0, "kwarg": 0, "l123": 0, "l2": 0, "label": 0, "label_column": 0, "largest": 0, "last": 0, "layout": 0, "left": 0, "legend": 0, "length": 0, "length_diff_from_avg": 0, "level": 0, "like": 0, "line": 0, "linear": 0, "list": 0, "list_all_files_in_directori": 0, "ln": 0, "load": 0, "log": 2, "log2": 0, "log_fil": 0, "log_level": 0, "logger": 0, "logging_help": 0, "logic": 0, "longpep": 0, "longpeptidesequ": 0, "low": 0, "lower": 0, "lower_score_is_bett": 0, "lowest": 0, "lxml": 0, "m": 0, "magnitud": 0, "main": 0, "maintain": 0, "malform": 0, "manag": 0, "map": 0, "mass": 0, "match": 0, "matched_indic": 0, "matrix": 0, "max": 0, "max_length": 0, "max_peptide_length": 0, "maximum": 0, "mean": 0, "mean_squared_error": 0, "measur": 0, "median": 0, "mer": 0, "mer_length": 0, "merg": 0, "messag": 0, "metadata": 0, "metadata_column": 0, "metadata_df": 0, "metadata_kei": 0, "method": 0, "metric": 0, "mhc": 0, "mhc_class": 0, "mhcflurri": 0, "mhcflurry_": 0, "mhcflurry_affin": 0, "mhcflurry_best_allel": 0, "mhcflurry_presentation_percentil": 0, "mhcflurry_presentation_scor": 0, "mhcflurry_processing_scor": 0, "mhcflurryfeaturegener": 0, "might": 0, "min": 0, "min_entropi": 0, "min_length": 0, "min_overlap_length": 0, "min_peptide_length": 0, "minimum": 0, "miss": 0, "mix": 0, "mod_dict": 0, "mode": 0, "model": 0, "model_path": 0, "model_typ": 0, "modif": 0, "modifi": 0, "modul": 2, "mokapot": 0, "more": 0, "most": 0, "ms_data_fil": 0, "ms_data_file_column": 0, "msms_run": 0, "multipl": 0, "multiprocess": 0, "must": 0, "mz": 0, "mz_file_path": 0, "mzml": 0, "mzml_filenam": 0, "n": 0, "n_flank_pwm": 0, "n_flank_pwm_score_": 0, "n_flank_scor": 0, "n_job": 0, "n_process": 0, "na": 0, "name": 0, "nan": 0, "natur": 0, "ndarrai": 0, "necessari": 0, "need": 0, "neg": 0, "neighbor": 0, "neither": 0, "nest": 0, "netmhc": 0, "netmhciipan": 0, "netmhciipan43_ba": 0, "netmhciipan_affin": 0, "netmhciipan_affinity_": 0, "netmhciipan_best_affin": 0, "netmhciipan_best_allel": 0, "netmhciipan_best_percentile_rank": 0, "netmhciipan_best_scor": 0, "netmhciipan_percentile_rank": 0, "netmhciipan_percentile_rank_": 0, "netmhciipan_scor": 0, "netmhciipan_score_": 0, "netmhciipanfeaturegener": 0, "netmhcpan": 0, "netmhcpan_affin": 0, "netmhcpan_affinity_": 0, "netmhcpan_best_affin": 0, "netmhcpan_best_allel": 0, "netmhcpan_best_percentile_rank": 0, "netmhcpan_best_scor": 0, "netmhcpan_percentile_rank": 0, "netmhcpan_percentile_rank_": 0, "netmhcpan_scor": 0, "netmhcpan_score_": 0, "netmhcpanfeaturegener": 0, "new": 0, "next": 0, "nm": 0, "node": 0, "non": 0, "none": 0, "nonsens": 0, "nor": 0, "norm": 0, "normal": 0, "notat": 0, "notebook": 1, "np": 0, "num_col": 0, "number": 0, "numer": 0, "nx": 0, "o": 0, "ob": 0, "object": 0, "observ": 0, "observed_retention_tim": 0, "observed_rt": 0, "olc": 0, "onc": 0, "one": 0, "onli": 0, "oper": 0, "optimhc": [0, 3], "option": 0, "orchestr": 0, "order": 0, "org": 0, "origin": 0, "original_peptid": 0, "original_seq": 0, "other": 0, "otherwis": 0, "our": 0, "out": 0, "outlier": 0, "output": 0, "output_dir": 0, "output_fil": 0, "over": 0, "overlap": 0, "overlap_data": 0, "overlap_graph": 0, "overlapping_peptid": 0, "overlapping_sourc": 0, "overlappinggroupfeatur": 0, "overlappingpeptidefeaturegener": 0, "overwritten": 0, "own": 0, "oxid": 0, "p": 0, "pad": 0, "page": 2, "pair": 0, "panda": 0, "parallel": 0, "paramet": 0, "pars": 0, "parse_cli_config": 0, "parser": 2, "pass": 0, "path": 0, "pathlik": 0, "pattern": 0, "pd": 0, "peak": 0, "pearson": 0, "pearson_correl": 0, "peptid": [0, 2], "peptide1": 0, "peptide2": 0, "peptide3": 0, "peptide_column": 0, "peptide_to_contig": 0, "peptide_to_index": 0, "peptideprophet": 0, "peptides_chunk": 0, "peptides_to_predict": 0, "pepxml": 0, "pepxml_fil": 0, "percentil": 0, "percentile_rank": 0, "percol": 0, "perform": [0, 2], "pfm": 0, "pfm_filenam": 0, "phospho": 0, "pin": 0, "pin_fil": 0, "pip": 3, "pipe": 0, "pipelin": [0, 2], "plot": 0, "plot_feature_import": 0, "plot_qvalu": 0, "plu": 0, "posit": 0, "possibl": 0, "post": 0, "ppm": 0, "pre": 0, "preced": 0, "precis": 0, "pred": 0, "pred_annot": 0, "pred_idx": 0, "pred_intens": 0, "pred_mz": 0, "pred_vector": 0, "predict": 0, "predicted_not_seen": 0, "predicted_retention_tim": 0, "predicted_rt": 0, "predicted_seen_nonzero": 0, "predictions_df": 0, "predictions_to_datafram": 0, "prefix": 0, "prefix_index": 0, "prepar": 0, "preprocess": 0, "preprocess_peptid": 0, "presenc": 0, "present": 0, "presentation_percentil": 0, "presentation_scor": 0, "preserv": 0, "prevent": 0, "previou": 0, "print": 0, "probabl": 0, "process": 0, "processed_peptid": 0, "processing_scor": 0, "progress": 0, "prohibit": 0, "properli": 0, "properti": 0, "proport": 0, "prosit": 0, "protein": 0, "protein_column": 0, "proteinid1": 0, "proteinidm": 0, "provid": 0, "pseudocount": 0, "psm": 2, "psm_contain": 0, "psm_info": 0, "psmcontain": 0, "psms_kei": 0, "public": 0, "pwm": 0, "pwm_datafram": 0, "pwm_path": 0, "pwm_score_": 0, "pwmfeaturegener": 0, "pyteom": 0, "python": 3, "q": 0, "radian": 0, "rais": 0, "randomforest": 0, "rang": 0, "rank": 0, "ratio": 0, "raw": 0, "raw_predict": 0, "read": 0, "read_input": 0, "read_pepxml": 0, "read_pin": 0, "readthedoc": 0, "record": 0, "recurs": 0, "redund": 0, "redundant_map": 0, "refer": 2, "region": 0, "relat": 0, "remov": 0, "remove_modif": 0, "remove_pre_and_nxt_aa": 0, "remove_pre_nxt_aa": 0, "renam": 0, "replac": 0, "repres": 0, "represent": 0, "requir": [0, 2], "rescor": 2, "rescoring_featur": 0, "residu": 0, "resolut": 0, "resourc": 0, "result": 0, "result_kei": 0, "results_df": 0, "retain": 0, "retent": 0, "retention_tim": 0, "retention_time_column": 0, "retention_time_diff": 0, "retention_time_ratio": 0, "retrain": 0, "retriev": 0, "return": 0, "root": 0, "row": 0, "run": 0, "run_experi": 0, "run_info": 0, "s41592": 0, "s_m": 0, "s_p": 0, "s_pm": 0, "sa": 0, "same": 0, "save": 0, "save_or_show_plot": 0, "save_path": 0, "save_raw_predict": 0, "save_result": 0, "scan": 0, "scan_column": 0, "scan_id": 0, "scannr": 0, "scientif": 0, "score": 0, "score_new": 0, "search": [0, 2], "second": 0, "seen": 0, "select": 0, "separ": 0, "seq": 0, "sequenc": 0, "seri": 0, "server": 0, "set": 0, "set_pwm": 0, "setup_logg": 0, "shannon": 0, "shannon_entropi": 0, "shape": 0, "should": 0, "show": 0, "show_progress": 0, "significantli": 2, "similar": 0, "simpl": 0, "simplifi": 0, "simplified_graph": 0, "sinc": 0, "singl": 0, "singlepeptid": 0, "size": 0, "slide": 0, "so": 0, "solid": 0, "sort": 0, "sourc": 0, "source1": 0, "source2": 0, "source_nam": 0, "space": 0, "span": 0, "spearman": 0, "spearman_correl": 0, "spec_info": 0, "specid": 0, "specif": 0, "specifi": 0, "spectra": 0, "spectra_similar": 0, "spectral": 0, "spectral_angle_similar": 0, "spectrasimilarityfeaturegener": 0, "spectrum": 0, "spectrum_column": 0, "spectrum_id": 0, "split": 0, "squar": 0, "standard": 0, "start": 0, "state": 0, "std": 0, "step": 0, "store": 0, "str": 0, "streamhandl": 0, "string": 0, "structur": 0, "studi": 0, "subdir": 0, "subdirectori": 0, "subplot": 0, "succeed": 0, "suffix": 0, "sum": 0, "summari": 0, "support": 0, "svr": 0, "t": 0, "tab": 0, "tabl": 0, "target": 0, "target_psm": 0, "termin": 0, "test_fdr": 0, "text": 0, "than": 0, "them": 0, "theoret": 0, "thi": 0, "third": 0, "those": 0, "threshold": 0, "through": 0, "tight_layout": 0, "time": 0, "titl": 0, "tk": 0, "to_csv": 0, "toler": 0, "tolerance_ppm": 0, "top": 0, "top_n": 0, "tr": 0, "track": 0, "trail": 0, "train": 0, "transform": 0, "transit": 0, "trigger": 0, "true": 0, "tupl": 0, "two": 0, "txt": 0, "type": 0, "typeerror": 0, "u": 0, "unchang": 0, "under": 0, "unifi": 0, "unimod": 0, "unimodnam": 0, "union": 0, "uniqu": 0, "unique_aa_count": 0, "unique_aa_proport": 0, "unit": 0, "unknown": 0, "unmodifi": 0, "unsupport": 0, "unweight": 0, "unweighted_entropy_similar": 0, "up": 0, "updat": 0, "url": 0, "us": [0, 2], "usag": 2, "use_ppm": 0, "util": 2, "valid": 0, "valu": 0, "valueerror": 0, "variabl": 0, "variat": 0, "variou": 0, "vector": 0, "verifi": 0, "version": [0, 3], "via": 0, "visual": 2, "visualize_feature_correl": 0, "visualize_result": 0, "visualize_target_decoy_featur": 0, "wa": 0, "want": 0, "warn": 0, "we": 0, "weight": 0, "were": 0, "when": 0, "where": 0, "whether": 0, "which": 0, "while": 0, "wilhelmlab": 0, "window": 0, "without": 0, "work": 0, "workflow": 0, "would": 0, "wrapper": 0, "write": 0, "write_pin": 0, "written": 0, "www": 0, "x": 0, "xgboost": 0, "xml": 0, "yaml": 0, "yet": 0, "yield": 0, "you": 0, "yourusernam": 3, "z": 0, "zero": 0, "zshang": 0, "\u03c0": 0}, "titles": ["API Reference", "Examples", "Welcome to optiMHC\u2019s documentation!", "Installation", "Usage"], "titleterms": {"": 2, "api": 0, "cli": 0, "contain": 0, "content": 2, "core": 0, "develop": 3, "document": 2, "exampl": [0, 1], "featur": [0, 2], "gener": 0, "indic": 2, "instal": 3, "interfac": 0, "introduct": 2, "log": 0, "modul": 0, "note": 0, "optimhc": 2, "parser": 0, "psm": 0, "refer": 0, "requir": 3, "rescor": 0, "submodul": 0, "tabl": 2, "usag": 4, "util": 0, "visual": 0, "welcom": 2}}) \ No newline at end of file diff --git a/docs/source/_build/html/usage.html b/docs/source/_build/html/usage.html deleted file mode 100644 index 6a4750e..0000000 --- a/docs/source/_build/html/usage.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - Usage — optiMHC 0.1.0 documentation - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- -
-

Usage

-

Usage instructions will be added here.

-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/source/api.rst b/docs/source/api.rst deleted file mode 100644 index d2c8a7b..0000000 --- a/docs/source/api.rst +++ /dev/null @@ -1,141 +0,0 @@ -API Reference -============ - -Core Modules ------------ - -.. automodule:: optimhc.core - :members: - :undoc-members: - :show-inheritance: - -Logging -------- - -.. automodule:: optimhc.core.logging_helper - :members: - :undoc-members: - :show-inheritance: - -CLI Interface ------------- - -.. automodule:: optimhc.cli - :members: - :undoc-members: - :show-inheritance: - - -PSM Container ------------- - -.. automodule:: optimhc.psm_container - :members: - :undoc-members: - :show-inheritance: - -Utilities --------- - -.. automodule:: optimhc.utils - :members: - :undoc-members: - :show-inheritance: - -Parser Modules -------------- - -.. automodule:: optimhc.parser - :members: - :undoc-members: - :show-inheritance: - -Parser Submodules -~~~~~~~~~~~~~~~ - -.. automodule:: optimhc.parser.pin - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.parser.pepxml - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.parser.mzml - :members: - :undoc-members: - :show-inheritance: - -Feature Generator ---------------- - -.. automodule:: optimhc.feature_generator - :members: - :undoc-members: - :show-inheritance: - -Feature Generator Submodules -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: optimhc.feature_generator.base_feature_generator - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.basic - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.DeepLC - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.mhcflurry - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.netMHCpan - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.netMHCIIpan - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.overlapping_peptide - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.PWM - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: optimhc.feature_generator.spectral_similarity - :members: - :undoc-members: - :show-inheritance: - -Rescore Modules --------------- - -.. automodule:: optimhc.rescore - :members: - :undoc-members: - :show-inheritance: - -Visualization ------------- - -.. automodule:: optimhc.visualization - :members: - :undoc-members: - :show-inheritance: \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index 6d8e1c7..0000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,58 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# For the full list of built-in configuration values, see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Project information ----------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information - -project = 'optiMHC' -copyright = '2024, Zixiang Shang' -author = 'Zixiang Shang' -release = '0.1.0' - -# -- General configuration --------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration - -import os -import sys -sys.path.insert(0, os.path.abspath('../..')) - -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.viewcode', - 'sphinx.ext.napoleon', - 'sphinx.ext.intersphinx', - 'sphinx.ext.mathjax', - 'sphinx_autodoc_typehints', - 'nbsphinx' -] - -html_show_sourcelink = False - -# Napoleon settings -napoleon_google_docstring = False -napoleon_numpy_docstring = True -napoleon_include_init_with_doc = True -napoleon_include_private_with_doc = True -napoleon_include_special_with_doc = True -napoleon_use_admonition_for_examples = True -napoleon_use_admonition_for_notes = True -napoleon_use_admonition_for_references = True -napoleon_use_ivar = True -napoleon_use_param = True -napoleon_use_rtype = True -napoleon_type_aliases = None - -templates_path = ['_templates'] -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - -# -- Options for HTML output ------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output - -html_theme = 'sphinx_rtd_theme' -html_static_path = ['_static'] - -# -- Options for autodoc ---------------------------------------------------- -autodoc_member_order = 'bysource' -autodoc_typehints = 'description' diff --git a/docs/source/examples.rst b/docs/source/examples.rst deleted file mode 100644 index 4b7c683..0000000 --- a/docs/source/examples.rst +++ /dev/null @@ -1,4 +0,0 @@ -Examples -======== - -Example notebooks and code will be added here. \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst deleted file mode 100644 index 85e26e5..0000000 --- a/docs/source/index.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. optiMHC documentation master file, created by - sphinx-quickstart on Sun May 11 02:30:52 2025. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to optiMHC's documentation! -================================ - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - installation - usage - api - examples - -Introduction ------------- - -optiMHC is a optimum rescoring pipeline for immunopeptidomics data to significantly enhance peptide identification performance. - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/docs/source/installation.rst b/docs/source/installation.rst deleted file mode 100644 index f2a5b38..0000000 --- a/docs/source/installation.rst +++ /dev/null @@ -1,20 +0,0 @@ -Installation -============ - -Requirements ------------ - -* Python 3.11 or higher -* pip or conda - - -Development Installation ----------------------- - -To install the development version: - -.. code-block:: bash - - git clone https://github.com/5h4ng/optimhc.git - cd optimhc - pip install -e . \ No newline at end of file diff --git a/docs/source/usage.rst b/docs/source/usage.rst deleted file mode 100644 index d0c08a9..0000000 --- a/docs/source/usage.rst +++ /dev/null @@ -1,4 +0,0 @@ -Usage -===== - -Usage instructions will be added here. \ No newline at end of file diff --git a/docs/tutorial/examples.md b/docs/tutorial/examples.md new file mode 100644 index 0000000..a9814ae --- /dev/null +++ b/docs/tutorial/examples.md @@ -0,0 +1,202 @@ +# Examples + +OptiMHC ships with example configuration files under the `examples/` directory. This page walks through each one, explaining every section. + +## MHC Class I Example + +**File:** `examples/classI_example.yaml` + +This configuration demonstrates a full Class I immunopeptidomics rescoring workflow with all available features. + +```yaml +experimentName: classI_example +inputType: pepxml +inputFile: + - ../data/YE_20180428_SK_HLA_A0202_3Ips_a50mio_R1_01.pep.xml +decoyPrefix: DECOY_ +outputDir: ./examples/results +visualization: True +removePreNxtAA: False +numProcesses: 32 +showProgress: True +``` + +**General settings:** + +- `experimentName` — a label used for output file naming. +- `inputType` — the format of your search engine output (`pepxml` or `pin`). +- `inputFile` — one or more paths to PepXML files. +- `decoyPrefix` — the prefix used by the search engine to mark decoy protein accessions (default: `DECOY_`). +- `outputDir` — where results, models, and figures are written. +- `numProcesses` — number of parallel processes for feature generation. + +### Modification Mapping + +```yaml +modificationMap: + "147.035385": "UNIMOD:35" # Oxidation (M) + "160.030649": "UNIMOD:4" # Carbamidomethyl (C) +``` + +Maps the **full modified residue mass** (residue + modification, as recorded in PepXML) to a UNIMOD identifier. This is required by generators that need standardized modification notation (e.g., SpectralSimilarity, DeepLC). + +### Allele Settings + +```yaml +allele: + - HLA-A*02:02 +``` + +Specifies the MHC allele(s) for binding prediction tools. For Class I, use standard HLA nomenclature (e.g., `HLA-A*02:01`). + +### Features + +```yaml +featureGenerator: + - name: Basic + - name: SpectralSimilarity + params: + mzmlDir: ../data + spectrumIdPattern: (.+?)\.\d+\.\d+\.\d+ + model: AlphaPeptDeep_ms2_generic + collisionEnergy: 28 + instrument: LUMOS + tolerance: 20 + numTopPeaks: 36 + url: 127.0.0.1:8500 + ssl: false + - name: DeepLC + params: + calibrationCriteria: expect + lowerIsBetter: True + calibrationSize: 0.1 + - name: OverlappingPeptide + params: + minOverlapLength: 7 + minLength: 7 + maxLength: 20 + overlappingScore: expect + - name: PWM + params: + class: I + - name: MHCflurry + - name: NetMHCpan +``` + +Each entry in `featureGenerator` specifies a feature `name` and optional `params`. Features without `params` use their defaults. See the [Features](features/index.md) section for detailed documentation of each feature. + +### Rescoring Settings + +```yaml +rescore: + testFDR: 0.01 + model: Percolator + numJobs: 4 +``` + +- `testFDR` — the FDR threshold for the test set (default: 0.01). +- `model` — the rescoring model: `Percolator` (linear SVM), `XGBoost`, or `RandomForest`. +- `numJobs` — number of parallel jobs for cross-validation in XGBoost/RandomForest models. + +--- + +## MHC Class II Example + +**File:** `examples/classII_example.yaml` + +This configuration mirrors the Class I example but is adapted for MHC Class II immunopeptidomics. + +```yaml +experimentName: classII_example +inputType: pepxml +inputFile: + - ../data/AG20201214_FAIMS_DPB0101_DPA0201_93e6_1hr.pep.xml +decoyPrefix: DECOY_ +outputDir: ./examples/results +``` + +### Key Differences from Class I + +**Allele notation** uses the Class II alpha-beta chain format: + +```yaml +allele: + - HLA-DPA1*02:01-DPB1*01:01 +``` + +**OverlappingPeptide** parameters are adjusted for the longer peptide lengths typical of Class II: + +```yaml +- name: OverlappingPeptide + params: + minOverlapLength: 8 + minLength: 9 + maxLength: 50 +``` + +**PWM** is set to Class II mode, which uses a sliding 9-mer core window with N- and C-flank scoring: + +```yaml +- name: PWM + params: + class: II +``` + +**NetMHCIIpan** replaces MHCflurry and NetMHCpan (which are Class I only): + +```yaml +- name: NetMHCIIpan +``` + +--- + +## Experiment Mode Example + +**File:** `examples/experiment_example.yaml` + +Experiment mode runs multiple rescoring experiments with different feature subsets on the same input data, allowing you to compare the contribution of individual features. + +The general settings and features are defined once at the top. The `experiments` section then defines each experiment: + +```yaml +experiments: + - name: "Baseline" + source: ["Original"] + model: "Percolator" + - name: "Complete" + source: ["Original", "OverlappingPeptide", "ContigFeatures", "PWM", "Basic"] + model: "Percolator" + - name: "Shuffle" + source: ["Original", "Basic", "OverlappingPeptide", "ContigFeatures", "PWM"] + model: "Percolator" +``` + +Each experiment specifies: + +- `name` — a label for the experiment, used for output subdirectories. +- `source` — a list of feature sources to include. These correspond to the source names registered by each feature (e.g., `"Original"` from the parser, `"Basic"` from Basic, etc.). +- `model` — the rescoring model to use for this experiment. + +Run experiment mode with: + +```bash +optimhc experiment --config examples/experiment_example.yaml +``` + +!!! tip + Experiment mode is useful for ablation studies — start with `"Original"` as a baseline, then incrementally add feature sources to measure their impact on peptide identification. + +### Available Feature Sources + +| Source Name | Feature | +|---|---| +| `Original` | PepXML / PIN parser (search engine features) | +| `Basic` | Basic peptide features | +| `SpectralSimilarity` | Spectral similarity | +| `DeepLC` | Retention time deviation | +| `OverlappingPeptide` | Overlapping peptide score | +| `ContigFeatures` | Contig-level features from overlapping peptide analysis | +| `PWM` | Position weight matrix score | +| `MHCflurry` | MHCflurry binding prediction | +| `NetMHCpan` | NetMHCpan binding prediction | +| `NetMHCIIpan` | NetMHCIIpan binding prediction | diff --git a/docs/tutorial/features/antigen-presentation.md b/docs/tutorial/features/antigen-presentation.md new file mode 100644 index 0000000..b87d255 --- /dev/null +++ b/docs/tutorial/features/antigen-presentation.md @@ -0,0 +1,125 @@ +# Antigen Processing and Presentation Scores + +OptiMHC integrates three external tools for predicting MHC binding affinity and antigen presentation. These tools provide complementary information about whether a peptide is likely to be naturally presented on the cell surface by MHC molecules. + +## NetMHCpan (MHC Class I) + +**Config name:** `NetMHCpan` | **Source name:** `NetMHCpan` + +[NetMHCpan 4.1](https://services.healthtech.dtu.dk/services/NetMHCpan-4.1/) predicts peptide binding to MHC Class I molecules using artificial neural networks trained on binding affinity and eluted ligand data. + +### Output Columns + +| Column | Description | +|---|---| +| `netmhcpan_best_score` | Binding score for the best allele | +| `netmhcpan_best_affinity` | Predicted binding affinity (nM) for the best allele | +| `netmhcpan_best_percentile_rank` | Percentile rank for the best allele | + +### Computation + +1. **Preprocess** peptide sequences: strip flanking amino acids and remove modifications. +2. **Filter** peptides to length 8–30 (the supported range for NetMHCpan). +3. **Predict** binding for all peptide-allele combinations using `NetMHCpan 4.1`. +4. **Best allele selection**: for each peptide, select the allele \( a^* \) with the minimum percentile rank \( r \): + +\[ +a^* = \arg\min_{a} \; r(\text{peptide}, a) +\] + +Missing values (peptides outside the length range) are filled with the column median. + +### Configuration + +```yaml +featureGenerator: + - name: NetMHCpan +``` + +!!! warning "External installation required" + NetMHCpan is a standalone executable that must be downloaded from [DTU Health Tech](https://services.healthtech.dtu.dk/services/NetMHCpan-4.1/) and added to your `PATH`. See [Installation](../../getting-started/installation.md#netmhcpan-netmhciipan-setup) for details. + +--- + +## NetMHCIIpan (MHC Class II) + +**Config name:** `NetMHCIIpan` | **Source name:** `NetMHCIIpan` + +[NetMHCIIpan 4.3](https://services.healthtech.dtu.dk/services/NetMHCIIpan-4.3/) predicts peptide binding to MHC Class II molecules. It uses binding affinity (BA) mode. + +### Output Columns + +| Column | Description | +|---|---| +| `netmhciipan_best_score` | Binding score for the best allele | +| `netmhciipan_best_affinity` | Predicted binding affinity (nM) for the best allele | +| `netmhciipan_best_percentile_rank` | Percentile rank for the best allele | + +### Computation + +1. **Preprocess** peptide sequences: strip flanking amino acids and remove modifications. +2. **Filter** peptides to length 9–50 (the supported range for NetMHCIIpan). +3. **Predict** binding for all peptide-allele combinations using `NetMHCIIpan 4.3 BA`. +4. **Best allele selection**: for each peptide, select the allele with the minimum percentile rank. + +Missing values are filled with the column median. + +### Configuration + +```yaml +featureGenerator: + - name: NetMHCIIpan +``` + +!!! warning "External installation required" + NetMHCIIpan is a standalone executable that must be downloaded from [DTU Health Tech](https://services.healthtech.dtu.dk/services/NetMHCIIpan-4.3/) and added to your `PATH`. See [Installation](../../getting-started/installation.md#netmhcpan-netmhciipan-setup) for details. + +--- + +## MHCflurry (MHC Class I) + +**Config name:** `MHCflurry` | **Source name:** `MHCflurry` + +[MHCflurry](https://github.com/openvax/mhcflurry) provides Class I MHC binding affinity predictions along with antigen processing and presentation scores. Unlike NetMHCpan, MHCflurry also models the antigen processing pathway (proteasomal cleavage and TAP transport) in addition to MHC binding. + +### Output Columns + +| Column | Description | +|---|---| +| `mhcflurry_affinity` | Predicted binding affinity (nM) | +| `mhcflurry_processing_score` | Antigen processing score (likelihood of proteasomal cleavage and TAP transport) | +| `mhcflurry_presentation_score` | Combined presentation score integrating binding and processing | +| `mhcflurry_presentation_percentile` | Percentile rank of the presentation score | + +### Computation + +1. **Preprocess** peptide sequences: strip flanking amino acids and remove modifications. +2. **Filter** peptides to length 8–15 (the supported range for MHCflurry). +3. **Predict** using `Class1PresentationPredictor`, which returns: + - **Affinity** — predicted IC50 binding affinity in nM. + - **Processing score** — a score reflecting the likelihood that the peptide undergoes proper antigen processing (proteasomal cleavage, TAP transport). + - **Presentation score** — a combined score that integrates binding affinity and processing likelihood. + - **Presentation percentile** — the percentile rank of the presentation score relative to a reference distribution. + +Missing values (peptides outside the 8–15 length range) are filled with the column median. + +### Configuration + +```yaml +featureGenerator: + - name: MHCflurry +``` + +No additional parameters are required. The alleles are taken from the top-level `allele` configuration. + +--- + +## Choosing Between Tools + +| Tool | MHC Class | Peptide Length | Scores | External Setup | +|---|---|---|---|---| +| NetMHCpan | I | 8–30 | Binding score, affinity, percentile rank | Manual download, add to `PATH` | +| NetMHCIIpan | II | 9–50 | Binding score, affinity, percentile rank | Manual download, add to `PATH` | +| MHCflurry | I | 8–15 | Affinity, processing, presentation, percentile | None (pip installed) | + +For **MHC Class I** analyses, you can use both NetMHCpan and MHCflurry simultaneously — they provide complementary predictions. For **MHC Class II**, NetMHCIIpan is the only option among these three tools. diff --git a/docs/tutorial/features/basic.md b/docs/tutorial/features/basic.md new file mode 100644 index 0000000..7e14a00 --- /dev/null +++ b/docs/tutorial/features/basic.md @@ -0,0 +1,71 @@ +# Basic Features + +The **Basic** feature (`name: Basic`) computes simple sequence-level properties of each peptide. These features capture compositional and length characteristics that can help distinguish true identifications from random matches. + +**Source name:** `Basic` + +## Output Columns + +| Column | Description | +|---|---| +| `length_diff_from_avg` | Peptide length minus the average peptide length across all PSMs | +| `abs_length_diff_from_avg` | Absolute value of the length difference | +| `unique_aa_count` | Number of distinct amino acid types in the peptide | +| `unique_aa_proportion` | Proportion of distinct amino acid types relative to peptide length | +| `shannon_entropy` | Shannon entropy of the amino acid frequency distribution | + +## Computation + +### Preprocessing + +Before computing features, each peptide sequence undergoes: + +1. **Flanking amino acid removal** — strips the preceding/next amino acid notation (e.g., `K.PEPTIDE.R` → `PEPTIDE`). +2. **Modification removal** — removes bracketed mass values (e.g., `PEPTM[147.035]IDE` → `PEPTMIDE`). + +### Length Features + +Let \( L \) denote the length of a peptide and \( \bar{L} \) the average peptide length across the entire dataset of \( N \) PSMs: + +\[ +\bar{L} = \frac{1}{N} \sum_{i=1}^{N} L_i +\] + +The length difference \( \delta \) and its absolute value are: + +\[ +\delta = L - \bar{L}, \quad |\delta| = |L - \bar{L}| +\] + +### Unique Amino Acid Features + +Let \( U \) denote the number of distinct amino acid types in a peptide. The unique amino acid proportion \( U_r \) is: + +\[ +U_r = \frac{U}{L} +\] + +### Shannon Entropy + +The Shannon entropy \( H \) quantifies the diversity of amino acid usage within a peptide: + +\[ +H = -\sum_{b \in \mathcal{A}} p_b \log_2(p_b) +\] + +where \( \mathcal{A} \) is the set of unique amino acids in the peptide and \( p_b \) is the frequency of amino acid \( b \): + +\[ +p_b = \frac{n_b}{L} +\] + +with \( n_b \) being the count of amino acid \( b \). A peptide composed of a single repeated amino acid has \( H = 0 \). A peptide with all unique amino acids has maximal entropy \( H = \log_2(L) \). + +## Configuration + +```yaml +featureGenerator: + - name: Basic +``` + +No parameters are required. The Basic feature uses default preprocessing settings (`remove_pre_nxt_aa: true`, `remove_modification: true`). diff --git a/docs/tutorial/features/index.md b/docs/tutorial/features/index.md new file mode 100644 index 0000000..f943953 --- /dev/null +++ b/docs/tutorial/features/index.md @@ -0,0 +1,39 @@ +# Features Overview + +OptiMHC uses a modular feature generation system. Each feature computes a set of scoring columns that are added to the PSM data before rescoring. By combining features from multiple orthogonal sources, the rescoring model can better distinguish true peptide identifications from false ones. + +## Feature Architecture + +All features inherit from `BaseFeatureGenerator` and implement a common interface: + +- **`feature_columns`** — the list of output column names. +- **`id_column`** — the key column(s) used to merge features back into the PsmContainer. +- **`generate_features()`** — computes and returns a DataFrame of features. + +Features are configured in the YAML file as a list of `{name, params}` entries. The pipeline instantiates each feature, calls `generate_features()`, and merges the result into the PsmContainer. + +## Feature Categories + +| Category | Name | Columns | Description | +|---|---|---|---| +| [Original Features](original.md) | PepXML / PIN parser | Variable | Search engine scores and derived mass/ion features | +| [Basic Features](basic.md) | `Basic` | 5 | Peptide sequence properties (length, entropy, AA composition) | +| [Spectral Similarity](spectral-similarity.md) | `SpectralSimilarity` | 8 | Similarity between experimental and predicted spectra | +| [Retention Time Deviation](rt-deviation.md) | `DeepLC` | 5 | Deviation between observed and predicted retention time | +| [Antigen Presentation Scores](antigen-presentation.md) | `NetMHCpan`, `NetMHCIIpan`, `MHCflurry` | 3–4 per tool | MHC binding affinity and presentation predictions | +| [PWM Score](pwm.md) | `PWM` | 1–3 per allele | Position weight matrix binding score | +| [Overlapping Peptide Score](overlapping.md) | `OverlappingPeptide` | 4 | Graph-based peptide overlap and contig assembly features | + +## Preprocessing + +Most features share common preprocessing steps: + +- **Strip flanking amino acids** — remove the preceding and next amino acid notation (e.g., `K.PEPTIDE.R` becomes `PEPTIDE`). +- **Remove modifications** — strip bracketed mass annotations (e.g., `PEPTM[147.035]IDE` becomes `PEPTMIDE`). + +??? note "Selenocysteine (U) handling" + Selenocysteine (`U`) is an extremely rare amino acid that most prediction tools do not support. During preprocessing, `U` is automatically replaced with cysteine (`C`) to ensure compatibility with external tools such as Koina, DeepLC, MHCflurry, NetMHCpan, and the PWM scoring matrices. + +## Missing Value Handling + +When a feature cannot be computed for a PSM (e.g., peptide length outside the supported range for a binding predictor), the missing values are filled with the **median** of the successfully computed values for that column. This ensures no NaN values propagate to the rescoring model. diff --git a/docs/tutorial/features/original.md b/docs/tutorial/features/original.md new file mode 100644 index 0000000..60383d1 --- /dev/null +++ b/docs/tutorial/features/original.md @@ -0,0 +1,110 @@ +# Original Features + +Original features are the initial set of scores and derived quantities extracted during input parsing. They form the baseline feature set (source name: `"Original"`) that every rescoring run includes. The specific features depend on the input format. + +## PepXML Features + +When parsing PepXML files, OptiMHC extracts raw search engine scores and computes several derived features. + +### Search Engine Scores + +All `` elements from the PepXML file are extracted as features. The exact columns depend on the search engine that produced the file. For example, Comet produces: + +- `xcorr` — cross-correlation score +- `deltacn` — delta correlation (difference to next-best match) +- `deltacnstar` — delta CN star +- `spscore` — preliminary score +- `sprank` — SP rank +- `expect` — expectation value (E-value) + +Other search engines (X!Tandem, MSFragger, etc.) produce their own score columns, all of which are automatically included. + +### Mass Difference Features + +Let \( m_\mathrm{exp} \) and \( m_\mathrm{calc} \) denote the experimental and calculated precursor neutral masses, respectively. The mass difference \( \Delta m \) is: + +\[ +\Delta m = m_\mathrm{exp} - m_\mathrm{calc} +\] + +The experimental and calculated m/z values are derived from the charge state \( z \) using the proton mass \( m_\mathrm{H} = 1.00727646677 \) Da: + +\[ +(m/z)_\mathrm{exp} = \frac{m_\mathrm{exp}}{z} + m_\mathrm{H}, \quad +(m/z)_\mathrm{calc} = \frac{m_\mathrm{calc}}{z} + m_\mathrm{H} +\] + +The absolute m/z difference \( \Delta_{mz} \) is: + +\[ +\Delta_{mz} = \left|(m/z)_\mathrm{exp} - (m/z)_\mathrm{calc}\right| +\] + +### Matched Ion Ratio + +When the search engine reports matched and total ions, the matched ion ratio \( R_\mathrm{ion} \) is computed: + +\[ +R_\mathrm{ion} = \frac{n_\mathrm{matched}}{n_\mathrm{total}} +\] + +This feature is only included if both values are present and \( n_\mathrm{total} > 0 \) for all PSMs. + +### Charge One-Hot Encoding + +The precursor charge state is one-hot encoded into binary columns: + +- `charge_1`, `charge_2`, `charge_3`, ... + +For a PSM with charge 2, `charge_2 = 1` and all other charge columns are 0. This allows the rescoring model to learn charge-specific effects without treating charge as a continuous variable. + +### Log Transformation of P-values and E-values + +Many search engine scores span several orders of magnitude (e.g., E-values from \( 10^{-20} \) to \( 10^{2} \)). Following the approach used in mokapot, OptiMHC applies automatic log-transformation to compress these ranges into a scale more suitable for machine learning models. + +**Scientific notation values:** If values contain scientific notation and span 4 or more orders of magnitude, the log transform for a value \( x = a \times 10^{b} \) is: + +\[ +\log_{10}(x) = \log_{10}(a) + b +\] + +For zero values, the log is set to one less than the minimum observed log value. + +**Numeric columns:** For non-negative, non-binary numeric columns where \( x_\mathrm{max} / x_\mathrm{min}^{+} \geq 10{,}000 \) (where \( x_\mathrm{min}^{+} \) is the smallest nonzero value): + +\[ +x' = \begin{cases} +\log_{10}(x) & \text{if } x > 0 \\ +\min\!\big(\log_{10}(x_\mathrm{min}^{+})\big) - 1 & \text{if } x = 0 +\end{cases} +\] + +The `num_matched_peptides` column is always log-transformed as \( \log_{10}(x) \). + +### Decoy Detection + +A PSM is labeled as a **decoy** if the first protein accession starts with the `decoyPrefix` (default: `DECOY_`). If any alternative protein accession does *not* start with the prefix, the PSM is re-labeled as a **target**. + +### Additional Metadata + +The following columns are extracted but treated as metadata (not used as rescoring features): + +- `ms_data_file` — raw file identifier +- `scan` — scan number +- `spectrum` — spectrum ID string +- `label` — target/decoy label +- `calc_mass` — calculated neutral mass +- `peptide` — peptide sequence (with modifications) +- `proteins` — protein accessions +- `charge` — precursor charge (kept as metadata; one-hot columns are the features) +- `retention_time` — retention time in seconds + +--- + +## PIN Features + +When parsing PIN (Percolator Input) files, the feature set is determined by the file itself. All columns that are not recognized as metadata (`Label`, `ScanNr`, `SpecId`, `Peptide`, `Proteins`, `rank`, `Charge`) are treated as **"Original"** rescoring features. + +Column name matching is case-insensitive. Charge columns matching the pattern `charge[_]?\d+` (e.g., `charge1`, `charge_2`) are detected automatically. For each PSM, the charge value is determined by which charge column contains a value of 1. + +The Label column uses the convention `1` for target and `-1` for decoy. diff --git a/docs/tutorial/features/overlapping.md b/docs/tutorial/features/overlapping.md new file mode 100644 index 0000000..b60fd88 --- /dev/null +++ b/docs/tutorial/features/overlapping.md @@ -0,0 +1,108 @@ +# Overlapping Peptide Score + +The **OverlappingPeptide** feature (`name: OverlappingPeptide`) is designed for detecting **ladder-like presentation hotspots** — genomic regions where the MHC presentation machinery repeatedly samples overlapping peptides, producing a characteristic nested/ladder pattern in the immunopeptidome. + +The method is inspired by the **Overlap-Layout-Consensus (OLC)** algorithm from genome assembly. We adapt the first two stages — overlap detection and layout (contig construction) — to build an overlap graph of peptide sequences and assemble them into contigs. + +The core idea: if multiple peptides within the same contig are confidently identified, then other peptides in the same group are also likely to be genuine MHC ligands. + +**Source name:** `OverlappingPeptide` + +## Output Columns + +| Column | Description | +|---|---| +| `contig_member_count` | Number of peptides in the contig that this peptide belongs to | +| `contig_extension_ratio` | How much the contig extends beyond the peptide itself | +| `contig_member_rank` | Rank of the contig by member count (largest contig = rank 1) | +| `contig_length` | Length of the assembled contig sequence | + +## Computation + +### Step 1: Preprocessing and Filtering + +Peptide sequences are preprocessed (flanking AA removal, modification removal) and then filtered by length and entropy thresholds. + +### Step 2: Redundancy Removal + +Peptides that are exact substrings of longer peptides are identified. These **redundant** peptides are removed from the graph but are later mapped to the contig of their containing peptide. This prevents artificial inflation of contig membership. + +### Step 3: Overlap Graph Construction + +A directed graph \( G = (V, E) \) is built where: + +- **Nodes** \( V \) represent non-redundant peptides. +- **Edge** \( A \to B \in E \) exists if a suffix of peptide \( A \) matches a prefix of peptide \( B \) with overlap length \( \geq \ell_\mathrm{min} \) (the `minOverlapLength` parameter). + +For efficient overlap detection, a prefix index is built: for each peptide, all prefixes of length \( \ell_\mathrm{min} \) to \( |seq| \) are stored. Then for each peptide, its suffixes are looked up in the index to find overlapping partners. + +When multiple overlaps exist between the same pair of peptides, only the **longest** overlap is kept. The edge weight stores the overlap length. + +### Step 4: Transitive Edge Removal + +To produce clean, non-branching paths, transitive edges are removed. If path \( A \to B \to C \) exists, the direct edge \( A \to C \) is removed (since the connection is already captured via \( B \)). This extends to longer transitive paths as well. + +### Step 5: Contig Assembly + +Contigs are identified as **non-branching paths** in the graph — chains of nodes where each internal node has exactly one predecessor and one successor. + +Each contig is assembled into a consensus sequence by concatenating the non-overlapping portions. For a contig of peptides \( P_1, P_2, \ldots, P_k \) with overlap lengths \( o_1, o_2, \ldots, o_{k-1} \): + +\[ +C = P_1 \,\|\, P_2[o_1\!:] \,\|\, P_3[o_2\!:] \,\|\, \cdots \,\|\, P_k[o_{k-1}\!:] +\] + +where \( P_i[o:] \) denotes the suffix of \( P_i \) beyond the first \( o \) characters, and \( \| \) denotes concatenation. + +### Step 6: Peptide-to-Contig Mapping + +Each peptide in a contig is assigned the contig's index. Redundant peptides (removed in Step 2) inherit the contig assignment of the peptide that contains them as a substring. + +### Step 7: Feature Computation + +Let \( n_c \) denote the number of peptides in a contig (including redundant ones), \( L_c \) the length of the assembled contig sequence, and \( L \) the length of the individual peptide. + +The **contig member count** is simply \( n_c \). + +The **contig length** is \( L_c \). + +The **contig member rank** \( R_c \) is the rank of the contig by \( n_c \) in descending order (largest contig = rank 1, ties broken by minimum rank). + +The **contig extension ratio** \( E_c \) measures how much the contig extends beyond the peptide: + +\[ +E_c = \frac{L_c - L}{L} +\] + +A value of 0 means the peptide spans the entire contig; larger values indicate the contig extends significantly beyond the peptide. + +### Missing Values + +Peptides that were filtered out (Step 1) receive imputed values. The `fill_missing` parameter controls the strategy: + +- `"median"` (default) — fill with the median of each feature column. +- `"zero"` — fill with zeros. + +## Configuration + +```yaml +featureGenerator: + - name: OverlappingPeptide + params: + minOverlapLength: 7 # Minimum suffix-prefix overlap to form an edge + minLength: 7 # Minimum peptide length to include + maxLength: 20 # Maximum peptide length to include + minEntropy: 0 # Minimum Shannon entropy to include + overlappingScore: expect # Search engine score column (for internal ranking) +``` + +| Parameter | Default | Description | +|---|---|---| +| `minOverlapLength` | `6` | Minimum overlap length for edge creation | +| `minLength` | `7` | Minimum peptide length to include in the graph | +| `maxLength` | `60` | Maximum peptide length to include | +| `minEntropy` | `0` | Minimum Shannon entropy threshold | +| `fill_missing` | `"median"` | Strategy for filling missing values: `"median"` or `"zero"` | + +!!! tip + For MHC Class I, typical settings are `minLength: 7`, `maxLength: 20`, `minOverlapLength: 7`. For MHC Class II, use wider ranges like `minLength: 9`, `maxLength: 50`, `minOverlapLength: 8` to accommodate longer peptides. diff --git a/docs/tutorial/features/pwm.md b/docs/tutorial/features/pwm.md new file mode 100644 index 0000000..985d09d --- /dev/null +++ b/docs/tutorial/features/pwm.md @@ -0,0 +1,106 @@ +# PWM Score + +The **PWM** feature (`name: PWM`) scores peptides against Position Weight Matrices derived from known MHC ligand data. PWM scoring is a classical, fast, and interpretable method for estimating MHC binding potential. + +**Source name:** `PWM` + +The PWM matrices used in OptiMHC are derived from the [SysteMHC Atlas](https://systemhc.sjtu.edu.cn/), a public data repository of immunopeptidomics datasets generated by mass spectrometry: + +> Shao, W.; Pedrioli, P.G.A. *et al.* The SysteMHC Atlas project. *Nucleic Acids Res* (2018). [doi:10.1093/nar/gkx664](https://doi.org/10.1093/nar/gkx664) +> +> Huang, X.; Gan, Z. *et al.* The SysteMHC Atlas v2.0, an updated resource for mass spectrometry-based immunopeptidomics. *Nucleic Acids Res* (2024). [doi:10.1093/nar/gkad1068](https://doi.org/10.1093/nar/gkad1068) + +## Output Columns + +The number and names of output columns depend on the MHC class and alleles. + +### MHC Class I + +| Column | Description | +|---|---| +| `PWM_Score_{allele}` | Total PWM score for the peptide | +| `Anchor_Score_{allele}` | PWM score at the most conserved (anchor) positions only | + +### MHC Class II + +| Column | Description | +|---|---| +| `PWM_Score_{allele}` | Best 9-mer core binding score | +| `N_Flank_PWM_Score_{allele}` | Score for the N-terminal flanking residues | +| `C_Flank_PWM_Score_{allele}` | Score for the C-terminal flanking residues | + +## Computation + +### MHC Class I Scoring + +For Class I, a length-specific PWM is loaded for each allele and peptide length. Each entry \( W_{a,j} \) represents the log-odds weight for amino acid \( a \) at position \( j \). + +**Total PWM score.** The PWM score \( S \) for a peptide of length \( L \) with amino acid \( a_j \) at position \( j \) is: + +\[ +S = \sum_{j=1}^{L} W_{a_j, j} +\] + +**Anchor score.** The anchor positions are identified as the \( n \) most conserved positions in the PWM (default \( n = 2 \)). Conservation is measured by the positional Shannon entropy \( H_j \): + +\[ +H_j = -\sum_{a \in \mathcal{A}} p_{a,j} \log_2(p_{a,j}) +\] + +where the probabilities are derived from the PWM weights: + +\[ +p_{a,j} = \frac{2^{W_{a,j}}}{\sum_{a'} 2^{W_{a',j}}} +\] + +The \( n \) positions with the **lowest** \( H_j \) (highest conservation) are selected as anchor positions. The anchor score \( S_\mathrm{anc} \) is: + +\[ +S_\mathrm{anc} = \sum_{j \in \mathcal{J}_\mathrm{anc}} W_{a_j, j} +\] + +where \( \mathcal{J}_\mathrm{anc} \) is the set of anchor positions. + +### MHC Class II Scoring + +MHC Class II molecules bind peptides through a 9-mer binding core embedded within a longer peptide. The PWM is always 9 positions long. + +**Core binding score.** A sliding window scans all possible 9-mer frames, and the frame with the highest score is selected. Let \( s \) be the starting position: + +\[ +S = \max_{s} \sum_{j=1}^{9} W_{a_{s+j}, j} +\] + +**Flanking scores.** Once the best core position is determined, the flanking residues are scored: + +- **N-terminal flank**: up to 3 residues immediately preceding the binding core. If fewer than 3 residues are available, the sequence is padded with `X` (unknown amino acid). +- **C-terminal flank**: up to 3 residues immediately following the binding core, similarly padded. + +Each flank is scored against its own 3-position PWM. Let \( W^{(N)} \) and \( W^{(C)} \) denote the N-flank and C-flank matrices. The flanking scores \( S_N \) and \( S_C \) are: + +\[ +S_N = \sum_{j=1}^{3} W^{(N)}_{a_j, j}, \quad +S_C = \sum_{j=1}^{3} W^{(C)}_{a_j, j} +\] + +### Preprocessing + +- Flanking amino acids are stripped and modifications are removed. +- Peptides for which no matching PWM is available (e.g., unsupported length for Class I) receive median-imputed values. + +## Configuration + +```yaml +featureGenerator: + - name: PWM + params: + class: I # "I" or "II" + anchors: 2 # Number of anchor positions (Class I only) +``` + +| Parameter | Default | Description | +|---|---|---| +| `class` | *(required)* | MHC class: `"I"` or `"II"` | +| `anchors` | `2` | Number of anchor positions for anchor score computation (Class I only) | + +The alleles are taken from the top-level `allele` configuration. diff --git a/docs/tutorial/features/rt-deviation.md b/docs/tutorial/features/rt-deviation.md new file mode 100644 index 0000000..4559517 --- /dev/null +++ b/docs/tutorial/features/rt-deviation.md @@ -0,0 +1,81 @@ +# Retention Time Deviation + +The **DeepLC** feature (`name: DeepLC`) predicts peptide retention times using the [DeepLC](https://github.com/compomics/DeepLC) deep learning model and computes the deviation between predicted and observed retention times. Correct peptide identifications should show smaller deviations, while incorrect matches tend to have larger discrepancies. + +**Source name:** `DeepLC` + +## Output Columns + +| Column | Description | +|---|---| +| `observed_retention_time` | Retention time recorded in the input data (seconds) | +| `predicted_retention_time` | Retention time predicted by DeepLC | +| `retention_time_diff` | Signed difference: predicted minus observed | +| `abs_retention_time_diff` | Absolute value of the retention time difference | +| `retention_time_ratio` | Ratio of the smaller to the larger of predicted and observed | + +## Computation + +### Step 1: Preprocessing + +Peptide sequences are preprocessed for DeepLC input: + +1. **Flanking amino acids** are stripped (e.g., `K.PEPTIDE.R` → `PEPTIDE`). +2. **Modifications** are converted from mass-annotated format to UNIMOD notation using the `modificationMap`. For example, `M[147.035]` is converted to a UNIMOD-indexed modification string that DeepLC understands. + +### Step 2: Calibration + +DeepLC benefits from calibration on high-confidence PSMs from the same LC-MS run. The calibration procedure: + +1. **Sort** all PSMs by the `calibrationCriteria` column (a search engine score). If `lowerIsBetter` is true, sort ascending; otherwise, sort descending. +2. **Select** the top PSMs as the calibration set: + - If `calibrationSize` is a float (e.g., 0.1), take the top 10% of PSMs. + - If `calibrationSize` is an integer (e.g., 500), take the top 500 PSMs. +3. **Filter** to target PSMs only (decoys are excluded from calibration). +4. **Calibrate** the DeepLC predictor using the observed retention times of the calibration set. + +### Step 3: Prediction + +The calibrated DeepLC model predicts retention times for all PSMs in the dataset. + +### Step 4: Feature Computation + +Let \( t_\mathrm{obs} \) and \( t_\mathrm{pred} \) denote the observed and predicted retention times, respectively. + +The signed difference \( \Delta t \), its absolute value \( |\Delta t| \), and the retention time ratio \( R_t \) are: + +\[ +\Delta t = t_\mathrm{pred} - t_\mathrm{obs} +\] + +\[ +|\Delta t| = |t_\mathrm{pred} - t_\mathrm{obs}| +\] + +\[ +R_t = \frac{\min(t_\mathrm{pred},\; t_\mathrm{obs})}{\max(t_\mathrm{pred},\; t_\mathrm{obs})} +\] + +\( R_t \) is bounded in \((0, 1]\) and equals 1 when the predicted and observed times are identical. + +### Missing Values + +Any PSMs for which DeepLC cannot produce a prediction are filled with the **median** of each feature column. + +## Configuration + +```yaml +featureGenerator: + - name: DeepLC + params: + calibrationCriteria: expect # Column name used for selecting calibration PSMs + lowerIsBetter: true # Whether lower values of the criteria are better + calibrationSize: 0.1 # Fraction (float) or count (int) of top PSMs for calibration +``` + +| Parameter | Default | Description | +|---|---|---| +| `calibrationCriteria` | *(required)* | Name of a search engine score column to rank PSMs for calibration | +| `lowerIsBetter` | `false` | Set to `true` if lower values of the calibration criteria indicate better PSMs (e.g., E-values) | +| `calibrationSize` | `0.15` | Fraction of PSMs (float) or absolute count (int) for the calibration set | + diff --git a/docs/tutorial/features/spectral-similarity.md b/docs/tutorial/features/spectral-similarity.md new file mode 100644 index 0000000..d216eee --- /dev/null +++ b/docs/tutorial/features/spectral-similarity.md @@ -0,0 +1,152 @@ +# Spectral Similarity + +The **SpectralSimilarity** feature (`name: SpectralSimilarity`) compares experimentally observed MS2 spectra against theoretical spectra predicted by a deep learning model served via [Koina](https://koina.proteomicsdb.org/). Koina is an open-source, web-accessible model repository that democratizes access to machine learning models for proteomics research, enabling remote execution of prediction models via standard HTTP/S requests without requiring specialized hardware ([Lautenbacher *et al.*, Nature Communications, 2025](https://doi.org/10.1038/s41467-025-64870-5)). The similarity between observed and predicted fragmentation patterns is a powerful indicator of correct peptide-spectrum matches. + +**Source name:** `SpectralSimilarity` + +## Output Columns + +| Column | Description | +|---|---| +| `spectral_angle_similarity` | Normalized spectral angle between experimental and predicted spectra | +| `cosine_similarity` | Cosine similarity between intensity vectors | +| `pearson_correlation` | Pearson correlation coefficient | +| `spearman_correlation` | Spearman rank correlation coefficient | +| `mean_squared_error` | Mean squared error on L2-normalized intensity vectors | +| `unweighted_entropy_similarity` | Entropy-based similarity measure | +| `predicted_seen_nonzero` | Count of predicted peaks that were observed in the experimental spectrum | +| `predicted_not_seen` | Count of predicted peaks that were not observed | + +## Computation + +### Step 1: Spectrum Extraction + +Experimental MS2 spectra are extracted from mzML files. Each spectrum is associated with a PSM through the spectrum ID pattern. The m/z and intensity arrays are sorted by m/z. + +### Step 2: Theoretical Spectrum Prediction + +Peptide sequences (with modifications mapped to UNIMOD notation via `modificationMap`) and charge states are sent to a Koina server, which returns predicted fragment ion m/z values and intensities using the specified deep learning model (e.g., `AlphaPeptDeep_ms2_generic`). + +### Step 3: Peak Alignment + +For each predicted peak at m/z value \( m \), a tolerance window is computed in parts-per-million (ppm): + +\[ +m_\mathrm{low} = m \cdot \left(1 - \frac{\tau}{10^6}\right), \quad +m_\mathrm{high} = m \cdot \left(1 + \frac{\tau}{10^6}\right) +\] + +where \( \tau \) is the ppm tolerance (default 20). Within this window, the experimental peak with the **highest intensity** is selected as the match. If no experimental peak falls within the window, the aligned experimental intensity is set to 0. + +This produces an aligned experimental intensity vector \( \mathbf{e} \) of the same length as the predicted intensity vector \( \mathbf{p} \). + +### Step 4: Top-N Peak Selection + +Only the top \( N \) peaks by predicted intensity are retained for similarity computation (default \( N = 36 \)). This focuses the comparison on the most informative fragment ions. + +### Step 5: Similarity Metrics + +All metrics are computed on the aligned, top-N intensity vectors \( \mathbf{e} \) (experimental) and \( \mathbf{p} \) (predicted). + +#### Spectral Angle Similarity + +The spectral angle similarity \( S_\mathrm{SA} \) is derived from the cosine of the angle \( \theta \) between the two vectors: + +\[ +\cos\theta = \frac{\mathbf{e} \cdot \mathbf{p}}{\|\mathbf{e}\| \, \|\mathbf{p}\|} +\] + +\[ +S_\mathrm{SA} = 1 - \frac{2 \arccos(\cos\theta)}{\pi} +\] + +\( S_\mathrm{SA} \) ranges from 0 (orthogonal spectra) to 1 (identical spectra). The \( 2/\pi \) normalization maps the angle from \([0, \pi/2]\) to \([0, 1]\). + +#### Cosine Similarity + +The cosine similarity \( S_{\mathrm{cos}} \) is: + +\[ +S_{\mathrm{cos}} = \frac{\mathbf{e} \cdot \mathbf{p}}{\|\mathbf{e}\| \, \|\mathbf{p}\|} +\] + +#### Pearson Correlation + +The Pearson correlation coefficient \( r \) is: + +\[ +r = \frac{\sum_{i} (e_i - \bar{e})(p_i - \bar{p})}{\sqrt{\sum_{i} (e_i - \bar{e})^2} \sqrt{\sum_{i} (p_i - \bar{p})^2}} +\] + +where \( \bar{e} \) and \( \bar{p} \) are the means of the experimental and predicted vectors. + +#### Spearman Correlation + +The Spearman correlation \( \rho \) is the Pearson correlation computed on the **ranks** of the values (using average ranking for ties) rather than the values themselves. + +#### Mean Squared Error + +The MSE is computed on L2-normalized vectors. Let \( \hat{e}_i = e_i / \|\mathbf{e}\| \) and \( \hat{p}_i = p_i / \|\mathbf{p}\| \). Then: + +\[ +\mathrm{MSE} = \frac{1}{n} \sum_{i=1}^{n} (\hat{e}_i - \hat{p}_i)^2 +\] + +#### Unweighted Entropy Similarity + +The unweighted entropy similarity is based on the concept of spectral entropy introduced by Li *et al.* ([Nature Methods, 2021](https://doi.org/10.1038/s41592-021-01331-z)). + +First, normalize intensities to probability distributions \( \mathbf{q}^{(e)} \) and \( \mathbf{q}^{(p)} \): + +\[ +q_i^{(e)} = \frac{e_i}{\sum_j e_j}, \quad q_i^{(p)} = \frac{p_i}{\sum_j p_j} +\] + +Compute the Shannon entropy of each distribution and their mixture \( \mathbf{m} \): + +\[ +S_e = -\sum_i q_i^{(e)} \ln q_i^{(e)}, \quad +S_p = -\sum_i q_i^{(p)} \ln q_i^{(p)} +\] + +\[ +m_i = \frac{1}{2}\left(q_i^{(e)} + q_i^{(p)}\right), \quad +S_m = -\sum_i m_i \ln m_i +\] + +The entropy similarity \( S_\mathrm{ent} \) is: + +\[ +S_\mathrm{ent} = 1 - \frac{2 S_m - S_e - S_p}{\ln 4} +\] + +The numerator is related to the Jensen-Shannon divergence. Dividing by \( \ln 4 \) normalizes the result to \([0, 1]\). + +#### Peak Matching Counts + +- **`predicted_seen_nonzero`** — the number of predicted peaks (with intensity > 0) that have a matched experimental peak with intensity > 0. +- **`predicted_not_seen`** — the number of predicted peaks (with intensity > 0) that have no matching experimental peak (aligned intensity = 0). + +## Configuration + +```yaml +featureGenerator: + - name: SpectralSimilarity + params: + mzmlDir: ../data # Directory containing mzML files + spectrumIdPattern: (.+?)\.\d+\.\d+\.\d+ # Regex to link PSMs to mzML files + model: AlphaPeptDeep_ms2_generic # Koina prediction model + collisionEnergy: 28 # Collision energy for prediction + instrument: LUMOS # Instrument type for prediction + tolerance: 20 # Peak matching tolerance in ppm + numTopPeaks: 36 # Number of top peaks to compare + url: koina.wilhelmlab.org:443 # Koina server gRPC endpoint + ssl: true # Use SSL for gRPC connection +``` + +!!! note + For a local Koina/Triton server, set `url: 127.0.0.1:8500` and `ssl: false`. The default public endpoint is `koina.wilhelmlab.org:443` with SSL enabled. + +## Requirements + +This feature requires access to a **Koina server** — either the [public endpoint](https://koina.proteomicsdb.org/) or a self-hosted [Triton Inference Server](https://github.com/wilhelm-lab/koina). diff --git a/docs/tutorial/index.md b/docs/tutorial/index.md new file mode 100644 index 0000000..ceb4cf5 --- /dev/null +++ b/docs/tutorial/index.md @@ -0,0 +1,13 @@ +# Tutorial + +This section provides a hands-on guide to using OptiMHC effectively. + +## What You Will Learn + +- **[Examples](examples.md)** — Walk through complete configuration files for MHC Class I, Class II, and experiment-mode analyses. +- **[Pipeline Workflow](workflow.md)** — Understand each stage of the pipeline from input parsing through rescoring to visualization. +- **[Features](features/index.md)** — Deep dive into every feature: what it computes, the underlying formulas, and how to configure it. + +## Prerequisites + +Before starting the tutorials, make sure you have [installed OptiMHC](../getting-started/installation.md) and verified it with `optimhc --help`. If you plan to use advanced features (SpectralSimilarity, DeepLC, MHCflurry, NetMHCpan), ensure their external dependencies are also installed. diff --git a/docs/tutorial/workflow.md b/docs/tutorial/workflow.md new file mode 100644 index 0000000..780c1bf --- /dev/null +++ b/docs/tutorial/workflow.md @@ -0,0 +1,149 @@ +# Pipeline Workflow + +This page explains the complete OptiMHC pipeline from input to output. Understanding the workflow helps you choose the right configuration for your data and troubleshoot issues. + +## Overview + +The pipeline executes in five sequential stages: + +``` +1. Configuration loading +2. Input parsing +3. Feature generation +4. Rescoring +5. Output & visualization +``` + +## Stage 1: Configuration + +OptiMHC uses a layered configuration system. Settings are resolved in the following precedence order (highest to lowest): + +1. **CLI flags** — command-line arguments override everything. +2. **YAML file** — values from the `--config` file. +3. **Default config** — built-in defaults for all settings. + +The default configuration provides sensible starting values: + +```yaml +outputDir: ./results +inputType: pepxml +decoyPrefix: DECOY_ +visualization: true +saveModels: true +toFlashLFQ: true +numProcesses: 4 +logLevel: INFO +rescore: + testFDR: 0.01 + trainFDR: 0.01 + model: Percolator + numJobs: 1 +``` + +The `Config` class deep-merges your YAML file with these defaults, so you only need to specify what differs. + +## Stage 2: Input Parsing + +OptiMHC accepts two input formats: + +### PepXML + +The PepXML parser extracts PSMs from the XML structure produced by search engines (e.g., Comet, X!Tandem). For each PSM it extracts: + +- Spectrum metadata (scan number, spectrum ID, charge, retention time) +- Mass values (experimental and calculated neutral mass) +- Peptide sequence with modifications +- Protein accessions +- All search engine scores + +The parser then computes derived features: mass differences, m/z differences, matched ion ratios, log-transformed p-values, and charge one-hot encoding. These become the **"Original"** feature set. See [Original Features](features/original.md) for details. + +### PIN (Percolator Input) + +The PIN parser reads tab-separated Percolator input files. All columns that are not metadata (Label, ScanNr, SpecId, Peptide, Proteins) are treated as the **"Original"** feature set. + +### PsmContainer + +Regardless of input format, parsing produces a `PsmContainer` — the central data structure of the pipeline. It wraps a pandas DataFrame of PSMs and maintains a registry of feature groups: + +```python +psms.rescoring_features = { + "Original": ["xcorr", "deltacn", "mass_diff", ...], +} +``` + +Every subsequent feature adds its own entry to this registry. This design allows experiment mode to select specific feature subsets by source name. + +## Stage 3: Feature Generation + +The pipeline iterates over the `featureGenerator` list from the configuration. For each entry, it: + +1. Instantiates the feature class by name. +2. Calls `generate_features()`, which returns a DataFrame. +3. Merges the result into the PsmContainer via `add_features()` (join by key columns) or `add_features_by_index()` (join by DataFrame index). +4. Registers the new columns under the feature's source name in `rescoring_features`. + +After all generators have run, the PsmContainer holds the complete feature matrix. + +Available generators are documented in detail in the [Features](features/index.md) section: + +| Feature | Source Name | Join Key | +|---|---|---| +| Basic | `Basic` | index | +| SpectralSimilarity | `SpectralSimilarity` | spectrum + peptide + charge | +| DeepLC | `DeepLC` | index | +| OverlappingPeptide | `OverlappingPeptide` | peptide | +| PWM | `PWM` | peptide | +| MHCflurry | `MHCflurry` | peptide | +| NetMHCpan | `NetMHCpan` | peptide | +| NetMHCIIpan | `NetMHCIIpan` | peptide | + +## Stage 4: Rescoring + +Rescoring uses the [mokapot](https://mokapot.readthedocs.io/) framework. The pipeline: + +1. **Builds a mokapot dataset** — converts the PsmContainer into a `LinearPsmDataset` with the selected rescoring features, target/decoy labels, spectrum IDs, and peptide sequences. +2. **Trains a model** — `mokapot.brew()` performs semi-supervised learning with 3-fold cross-validation: + - Trains the model on the training fold. + - Scores PSMs in the test fold. + - Repeats for all folds. +3. **Assigns q-values** using target-decoy competition at the specified `testFDR`. + +### Available Models + +| Model | Description | +|---|---| +| **Percolator** | Linear SVM (default). Fast and robust. Uses `mokapot.PercolatorModel`. | +| **XGBoost** | Gradient-boosted trees. Hyperparameters tuned via `GridSearchCV` (3-fold CV, ROC-AUC). Searches over `scale_pos_weight`, `max_depth`, `min_child_weight`, `gamma`. | +| **RandomForest** | Random forest classifier. Hyperparameters tuned via `GridSearchCV` (3-fold CV, ROC-AUC). Searches over `class_weight`, `max_depth`, `min_samples_split`, `min_impurity_decrease`. | + +## Stage 5: Output & Visualization + +### Output Files + +- **mokapot result files** — PSM-level and peptide-level results with scores and q-values. +- **PIN file** — the complete feature matrix in Percolator input format (useful for downstream tools). +- **Models** — serialized rescoring models (when `saveModels: true`). +- **FlashLFQ file** — quantification-ready output (when `toFlashLFQ: true`). + +### Visualizations + +When `visualization: true`, the pipeline produces: + +| Plot | Description | +|---|---| +| **qvalues.png** | Number of PSMs and peptides accepted at each q-value threshold. | +| **feature_importance.png** | Bar chart showing the weight or importance of each feature in the trained model. | +| **feature_correlation.png** | Heatmap of pairwise Pearson correlations among all rescoring features. | +| **target_decoy_histogram.png** | KDE histograms comparing the distribution of each feature for targets vs. decoys (top-ranked hits only). | + +## Experiment Mode + +Experiment mode (`optimhc experiment --config ...`) shares stages 1–3 with the standard pipeline but then runs multiple rescoring experiments in parallel. Each experiment uses a different subset of feature sources and/or a different model. + +This is useful for: + +- **Ablation studies** — measuring the contribution of each feature group. +- **Model comparison** — comparing Percolator vs. XGBoost vs. RandomForest on the same data. + +Each experiment runs in its own process and writes results to a separate subdirectory. A shared PIN file and feature correlation plot are generated once for the full feature set. diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..d72d5d0 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,93 @@ +site_name: OptiMHC +site_description: A high-performance rescoring pipeline for immunopeptidomics data to significantly enhance peptide identification performance +site_author: Zixiang Shang +repo_url: https://github.com/5h4ng/OptiMHC +repo_name: 5h4ng/OptiMHC + +theme: + name: material + palette: + - scheme: default + primary: indigo + accent: indigo + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - scheme: slate + primary: indigo + accent: indigo + toggle: + icon: material/brightness-4 + name: Switch to light mode + features: + - navigation.instant + - navigation.tracking + - navigation.tabs + - navigation.sections + - navigation.indexes + - navigation.top + - search.suggest + - search.highlight + - content.code.copy + +plugins: + - search + - mkdocstrings: + handlers: + python: + options: + show_source: true + show_root_heading: true + show_root_full_path: false + members_order: source + docstring_style: numpy + merge_init_into_class: true + show_if_no_docstring: false + +markdown_extensions: + - admonition + - pymdownx.details + - pymdownx.superfences + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.tabbed: + alternate_style: true + - toc: + permalink: true + - pymdownx.arithmatex: + generic: true + +extra_javascript: + - javascripts/mathjax.js + - https://unpkg.com/mathjax@3/es5/tex-mml-chtml.js + +nav: + - Home: index.md + - Getting Started: + - Installation: getting-started/installation.md + - Quick Start: getting-started/quickstart.md + - Tutorial: + - Overview: tutorial/index.md + - Examples: tutorial/examples.md + - Pipeline Workflow: tutorial/workflow.md + - Features: + - Overview: tutorial/features/index.md + - Original Features: tutorial/features/original.md + - Basic Features: tutorial/features/basic.md + - Spectral Similarity: tutorial/features/spectral-similarity.md + - Retention Time Deviation: tutorial/features/rt-deviation.md + - Antigen Presentation Scores: tutorial/features/antigen-presentation.md + - PWM Score: tutorial/features/pwm.md + - Overlapping Peptide Score: tutorial/features/overlapping.md + - API Reference: + - Overview: api/index.md + - Core: api/core.md + - I/O: api/io.md + - Feature Generators: api/features.md + - Rescoring: api/rescore.md + - Visualization: api/visualization.md + - CLI: api/cli.md + - Development: + - development/index.md