From 398292498913df16ebc64e1517e47e2b16ea1c0d Mon Sep 17 00:00:00 2001 From: josecorcab Date: Wed, 6 May 2026 22:52:31 +0200 Subject: [PATCH 1/7] ci: move ci.yml to .github/workflows and update actions to v6 --- ci.yml => .github/workflows/ci.yml | 8 ++++---- .github/workflows/release.yml | 21 +++++++++++++++------ 2 files changed, 19 insertions(+), 10 deletions(-) rename ci.yml => .github/workflows/ci.yml (93%) diff --git a/ci.yml b/.github/workflows/ci.yml similarity index 93% rename from ci.yml rename to .github/workflows/ci.yml index f1ce0ec..38168d6 100644 --- a/ci.yml +++ b/.github/workflows/ci.yml @@ -21,10 +21,10 @@ jobs: python-version: ['3.9', '3.14'] steps: - - uses: actions/checkout@v4 - + - uses: actions/checkout@v6 + - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} @@ -38,7 +38,7 @@ jobs: python -m pip install --upgrade pip setuptools packaging - name: Cache src directory - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ${{ github.workspace }}/src/ key: ${{ runner.os }}-src-grch37 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f6109e7..b507a48 100755 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,10 +17,12 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 + with: + fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.14" @@ -64,14 +66,21 @@ jobs: raise SystemExit(f"Error: New version ({new_version}) is not higher than latest release ({latest_version})") EOF - - name: Generate CHANGELOG.md with git-cliff + - name: Generate release notes and update CHANGELOG.md run: | - git-cliff --unreleased --strip all --tag "v${{ steps.get_version.outputs.VERSION }}" > CHANGELOG.md + git-cliff --unreleased --strip all --tag "v${{ steps.get_version.outputs.VERSION }}" > RELEASE_NOTES.md + git-cliff --unreleased --tag "v${{ steps.get_version.outputs.VERSION }}" --prepend CHANGELOG.md - - name: Create Git tag + - name: Commit and push CHANGELOG.md run: | git config user.name "github-actions" git config user.email "github-actions@github.com" + git add CHANGELOG.md + git commit -m "chore: update CHANGELOG.md for v${{ steps.get_version.outputs.VERSION }}" + git push origin HEAD:master + + - name: Create Git tag + run: | git tag v${{ steps.get_version.outputs.VERSION }} git push origin v${{ steps.get_version.outputs.VERSION }} @@ -79,7 +88,7 @@ jobs: uses: softprops/action-gh-release@v2 with: tag_name: v${{ steps.get_version.outputs.VERSION }} - body_path: CHANGELOG.md + body_path: RELEASE_NOTES.md env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 3e02bb368dce96a36e59b904f0502673f863f38b Mon Sep 17 00:00:00 2001 From: josecorcab Date: Fri, 8 May 2026 10:52:10 +0200 Subject: [PATCH 2/7] docs: add MkDocs documentation site Add full MkDocs documentation following the SigProfilerAssignment structure: home page, installation, quick start example, workflow, input parameters, and output description. Includes Material theme config, OSF TOC hook, extra CSS, and GitHub Actions workflow for automated deployment to GitHub Pages. Update citation in README to published Bioinformatics paper and add Nature 2022 paper as second reference. --- .github/workflows/docs.yml | 54 +++++++++++++++ README.md | 4 +- docs/1_installation.md | 82 +++++++++++++++++++++++ docs/2_quick_start_example.md | 75 +++++++++++++++++++++ docs/3_workflow.md | 49 ++++++++++++++ docs/4_using_the_tool_input.md | 105 ++++++++++++++++++++++++++++++ docs/5_using_the_tool_output.md | 83 +++++++++++++++++++++++ docs/assets/stylesheets/extra.css | 29 +++++++++ docs/index.md | 36 ++++++++++ mkdocs.yml | 60 +++++++++++++++++ mkdocs_hooks.py | 13 ++++ 11 files changed, 589 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/docs.yml create mode 100644 docs/1_installation.md create mode 100644 docs/2_quick_start_example.md create mode 100644 docs/3_workflow.md create mode 100644 docs/4_using_the_tool_input.md create mode 100644 docs/5_using_the_tool_output.md create mode 100644 docs/assets/stylesheets/extra.css create mode 100644 docs/index.md create mode 100644 mkdocs.yml create mode 100644 mkdocs_hooks.py diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..9b8bc57 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,54 @@ +name: Docs + +on: + push: + branches: + - master + - dev + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: github-pages + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Configure Pages + uses: actions/configure-pages@v5 + + - name: Install docs dependencies + run: | + python -m pip install --upgrade pip + python -m pip install mkdocs mkdocs-material pymdown-extensions + + - name: Build site + run: | + mkdocs build --clean + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: site + + deploy: + runs-on: ubuntu-latest + needs: build + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index 962b1bb..8285fa0 100755 --- a/README.md +++ b/README.md @@ -132,7 +132,9 @@ Two examples are provided under examples/. These directories include all expecte CITATIONS -Erik N Bergstrom, Mousumy Kundu, Noura Tbeileh, Ludmil B Alexandrov. bioRxiv 2022.02.11.480117; doi: https://doi.org/10.1101/2022.02.11.480117 +Erik N Bergstrom, Mousumy Kundu, Noura Tbeileh, Ludmil B Alexandrov. Examining clustered somatic mutations with SigProfilerClusters. Bioinformatics. 2022;38(13):3470–3473. https://doi.org/10.1093/bioinformatics/btac335 + +Bergstrom EN, Luebeck J, Petljak M, et al. Mapping clustered mutations in cancer reveals APOBEC3 mutagenesis of ecDNA. Nature. 2022;602:510–517. https://doi.org/10.1038/s41586-022-04398-6 COPYRIGHT diff --git a/docs/1_installation.md b/docs/1_installation.md new file mode 100644 index 0000000..05c4fd2 --- /dev/null +++ b/docs/1_installation.md @@ -0,0 +1,82 @@ +# Installation + + +---------- + + +This section will help you set up the necessary software and packages required to run SigProfilerClusters. + +---------- + + +## Prerequisites ## + +- [Python][1] version >= 3.9 +- [SigProfilerMatrixGenerator][2] with a downloaded reference genome (required to generate mutational matrices and simulations) +- [SigProfilerSimulator][3] (required to generate the background model used for IMD threshold calculation) +- Other dependencies are installed automatically during package installation + +## Installation ## + +SigProfilerClusters can be executed on any Windows/macOS/Unix system. First follow the [SigProfilerMatrixGenerator][2] guide for installing `Python` and `pip`. Next, follow the instructions below for the latest stable release or the current GitHub version. + +### Installation with `pip` ### + +Install the latest `SigProfilerClusters` PyPI version using `pip`: +``` +$ pip install SigProfilerClusters +``` + +To upgrade an existing installation to the most recent version: +``` +$ pip install SigProfilerClusters --upgrade +``` + +### Install specific GitHub Release ### + +First, download the [zip file][4] or clone the GitHub repository: +``` +$ git clone https://github.com/SigProfilerSuite/SigProfilerClusters.git +``` + +Next, enter the downloaded directory and install the package: +``` +$ cd SigProfilerClusters +$ pip install . +``` + +## Download Reference Genome ## + +SigProfilerClusters requires a reference genome to generate simulations and mutational matrices. Current reference genomes supported include GRCh37, GRCh38, mm9, and mm10. To install the reference genome/s, you need to use [SigProfilerMatrixGenerator][2]. + +The last PyPI [SigProfilerMatrixGenerator][2] version is installed with SigProfilerClusters by default. You can also install a specific version following the instructions in the [SigProfilerMatrixGenerator Wiki][2]. + +Once [SigProfilerMatrixGenerator][2] is installed, install your desired reference genome from the command line/terminal as follows. + +### Installation from command line ### + +``` +$ SigProfilerMatrixGenerator install GRCh37 +``` + +### Installation from Python terminal ### + +``` python +$ python +>>> from SigProfilerMatrixGenerator import install as genInstall +>>> genInstall.install('GRCh37', rsync=False, bash=True) +``` + +If you have a firewall on your server, you may need to install `rsync` and use the `rsync=True` parameter. Similarly, if you do not have bash available, use `bash=False`. + +In case you prefer to install a reference genome from a local copy: +``` python +$ python +>>> from SigProfilerMatrixGenerator import install as genInstall +>>> genInstall.install('GRCh37', offline_files_path='path/to/directory/containing/GRCh37.tar.gz') +``` + + [1]: https://www.python.org/downloads + [2]: https://sigprofilersuite.github.io/SigProfilerMatrixGenerator/ + [3]: https://osf.io/usxjz/wiki/home/ + [4]: https://github.com/SigProfilerSuite/SigProfilerClusters/releases diff --git a/docs/2_quick_start_example.md b/docs/2_quick_start_example.md new file mode 100644 index 0000000..0e5151b --- /dev/null +++ b/docs/2_quick_start_example.md @@ -0,0 +1,75 @@ +# Quick Start Example + + +---------- + + +This section provides a minimal example to get started with SigProfilerClusters. The following example uses a breast cancer sample (`BRCA`) or a melanoma sample (`MELA`) and demonstrates the complete workflow: generating simulations with [SigProfilerSimulator][2] and detecting clustered mutations with SigProfilerClusters. + +---------- + +## Prerequisites ## + +This tutorial requires that you have completed all steps in the [installation guide][1], specifically: + +- Installed SigProfilerClusters +- Installed [SigProfilerSimulator][2] +- Downloaded the **GRCh37** reference genome using [SigProfilerMatrixGenerator][3] + +## Downloading input example data ## + +Download one of the example VCF files below and place it in a new project directory (e.g., `path/to/data/`): + +- [BRCA_example_subs.vcf][5] — breast cancer substitutions +- [BRCA_example_indels.vcf][7] — breast cancer indels +- [MELA_example.vcf][6] — melanoma substitutions + +These samples and their expected outputs are also available under the `examples/` directory within the [GitHub repository](https://github.com/SigProfilerSuite/SigProfilerClusters). + +!!! note + The `simulations/` folder is not included in the GitHub repository to reduce its memory footprint. + +## Running SigProfilerClusters ## + +First, start a Python interactive shell and import [SigProfilerMatrixGenerator][3], [SigProfilerSimulator][2], and SigProfilerClusters. + +``` python +$ python +>>> from SigProfilerMatrixGenerator import install as genInstall +>>> from SigProfilerSimulator import SigProfilerSimulator as sigSim +>>> from SigProfilerClusters import SigProfilerClusters as hp +``` + +### Step 1: Run SigProfilerSimulator ### + +Generate a background model by running at least 100 simulations of inter-mutational distances for your data. **Note**: Update `"path/to/data"` with the actual path to the directory containing the VCF file. + +``` python +>>> sigSim.SigProfilerSimulator("BRCA", "path/to/data", "GRCh37", contexts=["288"], chrom_based=True, simulations=100) +``` + +### Step 2: Run SigProfilerClusters ### + +Cluster mutations based on the simulated background distribution. **Note**: Update `"path/to/data"` with the actual path to your project directory. + +``` python +>>> hp.analysis("BRCA", "GRCh37", "96", ["288"], "path/to/data", + analysis="all", sortSims=True, subClassify=True, + correction=True, calculateIMD=True, max_cpu=4, + variant_caller="standard") +``` + +After SigProfilerClusters has finished running, the output will be organized under `path/to/data/output/`. Partitioned mutations are placed under `output/clustered/` and `output/nonClustered/`, and visualizations are found under `output/plots/`. To learn more about all output files, please refer to the [Using the Tool - Output][4] section. + +## Additional Information ## + +In the above example, unspecified parameters use their default values. All function arguments and their types are described in detail in the [Using the Tool - Input][8] section. To learn more about the generated output files, refer to [Using the Tool - Output][4]. + + [1]: https://sigprofilersuite.github.io/SigProfilerClusters/1_installation.html + [2]: https://osf.io/usxjz/wiki/home/ + [3]: https://sigprofilersuite.github.io/SigProfilerMatrixGenerator/ + [4]: https://sigprofilersuite.github.io/SigProfilerClusters/5_using_the_tool_output.html + [5]: https://osf.io/gy2bh/ + [6]: https://osf.io/a8bq5/ + [7]: https://osf.io/tyw3u/ + [8]: https://sigprofilersuite.github.io/SigProfilerClusters/4_using_the_tool_input.html diff --git a/docs/3_workflow.md b/docs/3_workflow.md new file mode 100644 index 0000000..af26901 --- /dev/null +++ b/docs/3_workflow.md @@ -0,0 +1,49 @@ +# Workflow + + +---------- + + +This section describes the methodology used by SigProfilerClusters to identify and classify clustered mutations. The tool follows a four-stage process that distinguishes genuinely clustered mutations from those distributed randomly across the genome. + +!!! note "Minimum mutational burden" + Reliable analysis requires a mutational burden greater than **0.005 mutations per megabase**. Samples below this threshold may not produce meaningful IMD-based clustering results. + +---------- + +## Stage 1: Background Model Generation ## + +A per-sample background model is created using [SigProfilerSimulator][1]. This tool randomizes each mutation across the genome while preserving: + +- The desired trinucleotide sequence context +- Transcriptional strand bias ratios +- The mutational burden on each chromosome + +A minimum of **100 simulations** is recommended to ensure a robust background distribution. + +## Stage 2: IMD Threshold Calculation ## + +SigProfilerClusters compares the distribution of real inter-mutational distances (IMD) with the simulated background to compute a **sample-specific IMD threshold**. The threshold is set at the point where at least **90% of mutations falling within it are unlikely to have occurred by chance**, i.e., their spacing cannot be explained by the background model. + +This approach ensures that the clustering criterion adapts to the mutation burden and genomic context of each individual sample. + +## Stage 3: Regional Correction ## + +The global IMD threshold is further refined by applying a **localized correction across 1-megabase genomic windows**. This step accounts for the uneven distribution of mutations throughout the genome — regions with higher mutation density will have a proportionally adjusted threshold — preventing over- or under-calling of clustered events in heterogeneous mutational landscapes. + +## Stage 4: Mutation Classification ## + +After thresholding and correction, mutations are assigned to one of the following categories based on their inter-mutational spacing and, when available, variant allele frequencies (VAF): + +| Class | Category | Description | +|-------|----------|-------------| +| Non-clustered | — | Mutations whose spacing is consistent with random expectation | +| Clustered | **DBS** (Doublet Base Substitution) | Exactly 2 adjacent SNVs on the same chromosomal position | +| Clustered | **MBS** (Multi-Base Substitution) | 3 or more adjacent SNVs on consecutive chromosomal positions | +| Clustered | **Omikli** | 2–3 clustered SNVs with IMD pattern consistent with a localized event | +| Clustered | **Kataegis** | 4 or more clustered SNVs with IMD pattern consistent with a regional hypermutation event | +| Clustered | **Other** | Clustered mutations with inconsistent allele frequencies; only identified when VAF/CCF data is available | + +Indels are classified as clustered or non-clustered but are not further subclassified into the categories above. + + [1]: https://osf.io/usxjz/wiki/home/ diff --git a/docs/4_using_the_tool_input.md b/docs/4_using_the_tool_input.md new file mode 100644 index 0000000..fa43e46 --- /dev/null +++ b/docs/4_using_the_tool_input.md @@ -0,0 +1,105 @@ +# Using SigProfilerClusters + + +---------- + + +This section describes SigProfilerClusters' main function for clustered mutation analysis, the accepted input file formats, and all available parameters. + +---------- + +## Function ## + +The main function in SigProfilerClusters is `analysis`. It partitions somatic mutations into clustered and non-clustered groups and, optionally, subclassifies clustered SNVs into DBS, MBS, Omikli, and Kataegis events. + +### Input files ### + +SigProfilerClusters accepts four input file types. All input files for a given project must be placed in the same directory (`input_path`): + +- **VCF** — one file per sample, with the sample ID as the filename. +- **MAF** — standard Mutation Annotation Format file. +- **Simple text file** — tab-delimited plain text format, as described in [SigProfilerMatrixGenerator][1]. +- **ICGC Format** — ICGC simple somatic mutation format. + +Additionally, a background model generated by [SigProfilerSimulator][2] must be present in the same project directory before running the `analysis` function. + +### Running the function ### + +First, start a Python interactive shell and import SigProfilerClusters: + +``` python +$ python +>>> from SigProfilerClusters import SigProfilerClusters as hp +``` + +Then call the `analysis` function with the required parameters: + +``` python +>>> hp.analysis(project, genome, contexts, simContext, input_path) +``` + +You can also run SigProfilerClusters from the command line: + +``` bash +$ SigProfilerClusters analysis project genome contexts simContext input_path +``` + +### Required parameters ### + +| Parameter | Variable Type | Parameter Description | +|-----------|---------------|-----------------------| +| `project` | String | Unique name for the given project | +| `genome` | String | Reference genome to use. Must be installed using [SigProfilerMatrixGenerator][1]. Supported genomes: GRCh37, GRCh38, mm9, mm10 | +| `contexts` | String | Mutational context for SNV analysis. Accepted values: `"96"` (substitutions) or `"ID"` (indels) | +| `simContext` | List of Strings | Mutation context used when generating the background model with [SigProfilerSimulator][2]. For example: `["288"]`, `["6144"]`, or `["96"]` | +| `input_path` | String | Path to the directory containing the input files. Must end with `/` (e.g., `"path/to/the/input_file/"`) | + +### Optional parameters ### + +| Parameter | Variable Type | Parameter Description | +|-----------|---------------|-----------------------| +| `analysis` | String | Analysis pipeline to run. Options: `"all"` (default), `"subClassify"`, `"hotspot"` | +| `sortSims` | Boolean | Sort simulated files to ensure accuracy. Default: `True` | +| `interdistance` | String | Mutation types for which to calculate IMDs. Use only for indel analysis. Default: `"ID"` | +| `calculateIMD` | Boolean | Whether to calculate IMDs. Set to `False` to rerun subclassification only. Default: `True` | +| `max_cpu` | Integer | Number of CPUs to use for parallel processing. Default: all available CPUs | +| `subClassify` | Boolean | Subclassify clustered mutations into DBS, MBS, Omikli, and Kataegis (requires VAF scores in TCGA/Sanger format if `includedVAFs=True`). Default: `False` | +| `plotIMDfigure` | Boolean | Generate IMD and mutational spectra plots for each sample. Default: `True` | +| `plotRainfall` | Boolean | Generate rainfall plots using subclassified clustered events. Default: `True` | + +### Parameters used when `subClassify=True` ### + +| Parameter | Variable Type | Parameter Description | +|-----------|---------------|-----------------------| +| `includedVAFs` | Boolean | Indicates that VAF scores are present in the input files and should be used for subclassification. Default: `True` | +| `includedCCFs` | Boolean | Indicates that Cancer Cell Fraction (CCF) estimates are used instead of VAFs. When `True`, set `includedVAFs=False`. Default: `True` | +| `variant_caller` | String | Format of the VAF column in the input VCF. Accepted values: `"standard"` (default), `"caveman"`, `"mutect2"` | +| `windowSize` | Integer | Window size (in bp) for calculating mutation density in rainfall plots. Default: `10000000` | +| `correction` | Boolean | Apply genome-wide mutational density correction to the IMD threshold. Default: `False` | +| `probability` | Boolean | Calculate the probability of observing each clustered event within its local genomic region. Results are appended as an extra column in the output VCF files under `[project_path]/output/clustered/`. Default: `False` | + +## VAF Formats ## + +SigProfilerClusters uses variant allele frequencies (VAF) to subclassify clustered mutations when `subClassify=True` and `includedVAFs=True`. The VAF may be recorded in different formats depending on the variant caller used. Select the appropriate `variant_caller` value using the table below: + +| `variant_caller` value | VAF column location | +|------------------------|---------------------| +| `"caveman"` | 11th column; last colon-delimited value (e.g., `0:0:0.25`) | +| `"standard"` | 8th or 10th column as `VAF=xx` or `AF=xx` | +| `"mutect2"` | 10th or 11th column as `AF=xx` | + +!!! tip "No VAF available" + If your input files contain no VAF information, set `includedVAFs=False`. SigProfilerClusters will still subclassify clusters based solely on the calculated IMD values (when `subClassify=True`). + +!!! note "Non-VCF input" + If you are using a non-VCF input format (MAF, simple text, ICGC), VAFs cannot be extracted from the file. In this case, set `subClassify=True` and `includedVAFs=False`. + +## CCF Format ## + +As an alternative to VAFs, SigProfilerClusters accepts **cancer cell fraction (CCF)** estimates to correct for copy number amplifications. To use CCFs: + +- Set `includedCCFs=True` and `includedVAFs=False`. +- Add the CCF value as a tab-separated extra column at the end of each variant row in the input VCF file. + + [1]: https://sigprofilersuite.github.io/SigProfilerMatrixGenerator/ + [2]: https://osf.io/usxjz/wiki/home/ diff --git a/docs/5_using_the_tool_output.md b/docs/5_using_the_tool_output.md new file mode 100644 index 0000000..7d22807 --- /dev/null +++ b/docs/5_using_the_tool_output.md @@ -0,0 +1,83 @@ +# Using SigProfilerClusters - Output + + +---------- + + +This section describes the output files and directories produced by SigProfilerClusters. All results are written under `[project_path]/output/`, organized into several subdirectories. + +---------- + +## Output Overview ## + +After a successful run, the output directory contains the following subdirectories: + +``` +[project_path]/output/ +├── clustered/ +├── nonClustered/ +├── plots/ +├── simulations/ +└── vcf_files_corrected/ +``` + +All errors and progress checkpoints are also saved as log files in the project directory: + +- `SigProfilerClusters_[project]_[genome].err` — error log +- `SigProfilerClusters_[project]_[genome].out` — progress log + +## `clustered/` Directory ## + +Contains the partitioned clustered mutations, organized into subclass subdirectories. Within each subclass folder, one VCF file is saved per sample. The subclasses correspond to the categories described in the [Workflow][1] section: + +| Subclass | Description | +|----------|-------------| +| `DBS/` | Doublet base substitutions (2 adjacent SNVs) | +| `MBS/` | Multi-base substitutions (3+ adjacent SNVs) | +| `omikli/` | Localized clustered events (2–3 SNVs) | +| `kataegis/` | Regional hypermutation events (4+ SNVs) | +| `other/` | Clustered events with inconsistent allele frequencies (only present when VAF/CCF data is available) | + +When `probability=True`, an extra column is appended to each VCF file containing the probability of observing that clustered event within its local genomic region. + +## `nonClustered/` Directory ## + +Contains VCF files with the non-clustered mutations for each sample. Substitution and indel mutations are stored in separate files, one per sample. + +## `plots/` Directory ## + +Contains two types of visualizations generated per sample: + +### IMD plots ### +Show the mutational spectra alongside the distribution of inter-mutational distances (IMD) for both the real data and the simulated background. These plots allow visual inspection of the clustering signal relative to the background model. + +### Rainfall plots ### +Display the IMD distribution across genomic coordinates for each sample, with the sample-dependent IMD threshold overlaid. Clustered events are color-coded by subclass, providing a genome-wide view of mutational clustering patterns. + +## `simulations/` Directory ## + +Contains three subdirectories with intermediate simulation data used for threshold calculation: + +| Subdirectory | Contents | +|--------------|----------| +| `imd_data/` | IMD values across all simulations | +| `original_imds/` | IMD values for the original (real) mutations | +| `sorted_sims/` | Simulation files sorted alphanumerically, used during threshold computation | + +## `vcf_files_corrected/` Directory ## + +Contains VCF files for both clustered and non-clustered mutations after the **localized IMD correction** step (Stage 3 of the [workflow][1]). These files reflect the final mutation assignments incorporating regional mutation density adjustments. + +## Mutation Class Summary ## + +Output files use the following class labels to describe the nature of each clustered event: + +| Class | Categories included | Description | +|-------|---------------------|-------------| +| Class 1 | DBS, MBS, Omikli | Small-scale clustered events involving 2–3 mutations | +| Class 2 | Kataegis | Larger hypermutation events; further subdivided by processivity | +| Class 3 | Other clustered | Remaining clustered mutations not fitting Class 1 or Class 2 criteria | + +Each output VCF row includes the mutation coordinates, alleles, IMD value, and allele frequency, enabling detailed downstream analysis of individual clustered events. + + [1]: https://sigprofilersuite.github.io/SigProfilerClusters/3_workflow.html diff --git a/docs/assets/stylesheets/extra.css b/docs/assets/stylesheets/extra.css new file mode 100644 index 0000000..8b3e941 --- /dev/null +++ b/docs/assets/stylesheets/extra.css @@ -0,0 +1,29 @@ +:root { + --md-text-font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"; +} + +.md-main__inner { + max-width: 80%; +} + +.md-grid { + max-width: 80%; +} + + +/* Improve readability of long tables from OSF wiki. */ +.md-typeset table:not([class]) { + display: block; + overflow-x: auto; + white-space: nowrap; +} + +/* Make big OSF screenshots feel less cramped. */ +.md-typeset img { + border-radius: 8px; +} + +p { + text-align: justify +} diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..b8434c2 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,36 @@ +# SigProfilerClusters + +---------- + +**SigProfilerClusters** is a Python framework for analyzing the inter-mutational distances (IMD) between SNV-SNV and INDEL-INDEL mutations. The tool separates mutations into clustered and non-clustered groups on a sample-dependent basis, and subclassifies all SNVs into one of four categories of clustered event: i) doublet base substitutions (DBS); ii) multi-base substitutions (MBS); iii) omikli; and iv) kataegis. Indels are identified as clustered or non-clustered but are not further subclassified. + +SigProfilerClusters builds a sample-specific background model using [SigProfilerSimulator][1] and applies an IMD-based threshold — corrected for regional mutation density — to distinguish mutations that are unlikely to have co-occurred by chance. This approach allows reliable detection of clustered mutational events across a wide range of tumor types and mutation burdens. + +**SigProfilerClusters** makes use of [SigProfilerSimulator][1] and [SigProfilerMatrixGenerator][2], enabling seamless integration with other tools in the SigProfiler suite. + +The SigProfilerClusters library is available on [GitHub](https://github.com/SigProfilerSuite/SigProfilerClusters) and [PyPI](https://pypi.org/project/SigProfilerClusters). + +!!! note "Previously known as SigProfilerHotSpots" + This tool was previously distributed under the name **SigProfilerHotSpots**. For all usage instructions, `SigProfilerClusters` and `SigProfilerHotSpots` may be used interchangeably when working with the older version of the tool. + +---------- + +### Citation + +Bergstrom EN, Kundu M, Tbeileh N, Alexandrov LB. Examining clustered somatic mutations with SigProfilerClusters. *Bioinformatics*. 2022;38(13):3470–3473. [https://doi.org/10.1093/bioinformatics/btac335](https://doi.org/10.1093/bioinformatics/btac335) + +Bergstrom EN, Luebeck J, Petljak M, et al. Mapping clustered mutations in cancer reveals APOBEC3 mutagenesis of ecDNA. *Nature*. 2022;602:510–517. [https://doi.org/10.1038/s41586-022-04398-6](https://doi.org/10.1038/s41586-022-04398-6) + +### License + +This software is copyrighted © 2022 by Erik Bergstrom, Alexandrov Lab. SigProfilerClusters is distributed under a BSD 2-Clause License. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the copyright notice and disclaimer are retained. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED. + +### Contact + +For questions, support requests, or bug reports, please contact the development team via GitHub [issues](https://github.com/SigProfilerSuite/SigProfilerClusters/issues) or by email: + +- Erik Bergstrom: [ebergstr@health.ucsd.edu](mailto:ebergstr@health.ucsd.edu) +- Mousumy Kundu: [mkundu@health.ucsd.edu](mailto:mkundu@health.ucsd.edu) + + [1]: https://osf.io/usxjz/wiki/home/ + [2]: https://osf.io/s93d5/wiki/home/ diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..1028b77 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,60 @@ +site_name: SigProfilerClusters +repo_name: SigProfilerSuite/SigProfilerClusters +repo_url: https://github.com/SigProfilerSuite/SigProfilerClusters + +docs_dir: docs +site_url: https://sigprofilersuite.github.io/SigProfilerClusters/ + +use_directory_urls: false + +theme: + name: material + features: + - navigation.instant + - navigation.tracking + - navigation.tabs + - navigation.sections + - navigation.top + - toc.follow + - content.code.copy + palette: + - scheme: default + primary: indigo + accent: indigo + - scheme: slate + primary: indigo + accent: indigo + toggle: + icon: material/weather-night + name: Switch to dark mode + +nav: + - Home: index.md + - Getting Started: + - '1. Installation': '1_installation.md' + - '2. Quick Start Example': '2_quick_start_example.md' + - How It Works: + - '3. Workflow': '3_workflow.md' + - Using The Tool: + - '4. Using the Tool - Input': '4_using_the_tool_input.md' + - '5. Using the Tool - Output': '5_using_the_tool_output.md' + +plugins: + - search + +markdown_extensions: + - toc: + permalink: true + - attr_list + - md_in_html + - admonition + - pymdownx.superfences + - pymdownx.details + - pymdownx.tabbed: + alternate_style: true + +extra_css: + - assets/stylesheets/extra.css + +hooks: + - mkdocs_hooks.py diff --git a/mkdocs_hooks.py b/mkdocs_hooks.py new file mode 100644 index 0000000..9c8e57b --- /dev/null +++ b/mkdocs_hooks.py @@ -0,0 +1,13 @@ +import re + + +_OSF_TOC_RE = re.compile(r"^\s*@\[toc\]\([^)]+\)\s*$", re.IGNORECASE) + + +def on_page_markdown(markdown: str, /, *, page, config, files): + # OSF wiki pages contain a non-standard marker like `@[toc](Sections)` which MkDocs + # otherwise renders as a broken relative link. We keep the source files unchanged + # (to match OSF) and strip the marker at build time. + lines = markdown.splitlines() + filtered = [line for line in lines if not _OSF_TOC_RE.match(line)] + return "\n".join(filtered) + ("\n" if markdown.endswith("\n") else "") From 132e2ae7eaf9509dd14444af6b0fc9e213e4cf9c Mon Sep 17 00:00:00 2001 From: josecorcab Date: Fri, 8 May 2026 10:59:22 +0200 Subject: [PATCH 3/7] GHA workflow updated and deploy constrained to master --- .github/workflows/docs.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9b8bc57..4a4a4e9 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,7 +4,6 @@ on: push: branches: - master - - dev workflow_dispatch: permissions: @@ -20,14 +19,14 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.x" - name: Configure Pages - uses: actions/configure-pages@v5 + uses: actions/configure-pages@v6 - name: Install docs dependencies run: | @@ -39,7 +38,7 @@ jobs: mkdocs build --clean - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v5 with: path: site @@ -51,4 +50,4 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 From c9ac3ea14f2f0a5443541a716ed69775ff012f79 Mon Sep 17 00:00:00 2001 From: josecorcab Date: Fri, 8 May 2026 11:12:17 +0200 Subject: [PATCH 4/7] docs: add logo and update docs workflow Add SigProfilerClusters logo (56 KB) to README and documentation. Update docs.yml to deploy only from master and bump all action versions to their latest releases. --- .github/workflows/docs.yml | 11 +++++------ README.md | 2 ++ docs/assets/images/SigProfilerClusters.png | Bin 0 -> 57716 bytes docs/index.md | 2 ++ 4 files changed, 9 insertions(+), 6 deletions(-) create mode 100755 docs/assets/images/SigProfilerClusters.png diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9b8bc57..4a4a4e9 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,7 +4,6 @@ on: push: branches: - master - - dev workflow_dispatch: permissions: @@ -20,14 +19,14 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.x" - name: Configure Pages - uses: actions/configure-pages@v5 + uses: actions/configure-pages@v6 - name: Install docs dependencies run: | @@ -39,7 +38,7 @@ jobs: mkdocs build --clean - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v5 with: path: site @@ -51,4 +50,4 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 diff --git a/README.md b/README.md index 8285fa0..8862697 100755 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ # SigProfilerClusters + +![Logo](SigProfilerClusters.png) Tool for analyzing the inter-mutational distances between SNV-SNV and INDEL-INDEL mutations. Tool separates mutations into clustered and non-clustered groups on a sample-dependent basis and subclassifies all SNVs into a set category of clustered event: i) DBS; ii) MBS; iii) omikli; and iv) kataegis. Indels are not subclassifed. This tool was previously under the project name of SigProfilerHotSpots, but has been renamed to SigProfilerClusters. For all instructions below, SigProfilerClusters may be interchanged with SigProfilerHotSpots if the older version of the tools is being used. diff --git a/docs/assets/images/SigProfilerClusters.png b/docs/assets/images/SigProfilerClusters.png new file mode 100755 index 0000000000000000000000000000000000000000..50c35b0131780f10315874eed4ce7ca1cb80eb48 GIT binary patch literal 57716 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 o?}qe$ssDc|>;JEd;pDAHO#VcxDh$>T7#ix+P Date: Fri, 8 May 2026 11:15:25 +0200 Subject: [PATCH 5/7] Logo in readme updated --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8862697..b00d23e 100755 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # SigProfilerClusters -![Logo](SigProfilerClusters.png) +![Logo](docs/assets/images/SigProfilerClusters.png) Tool for analyzing the inter-mutational distances between SNV-SNV and INDEL-INDEL mutations. Tool separates mutations into clustered and non-clustered groups on a sample-dependent basis and subclassifies all SNVs into a set category of clustered event: i) DBS; ii) MBS; iii) omikli; and iv) kataegis. Indels are not subclassifed. This tool was previously under the project name of SigProfilerHotSpots, but has been renamed to SigProfilerClusters. For all instructions below, SigProfilerClusters may be interchanged with SigProfilerHotSpots if the older version of the tools is being used. From 8a7d2827fa777537c85d3df25ab062a51d20a6f1 Mon Sep 17 00:00:00 2001 From: josecorcab Date: Fri, 8 May 2026 11:17:50 +0200 Subject: [PATCH 6/7] Readme has been reduced --- README.md | 168 +++++++++++------------------------------------------- 1 file changed, 33 insertions(+), 135 deletions(-) diff --git a/README.md b/README.md index b00d23e..5f30887 100755 --- a/README.md +++ b/README.md @@ -1,155 +1,53 @@ -[![Docs](https://img.shields.io/badge/docs-latest-blue.svg)](https://osf.io/qpmzw/wiki/home/) [![License](https://img.shields.io/badge/License-BSD\%202--Clause-orange.svg)](https://opensource.org/licenses/BSD-2-Clause) [![Build Status](https://app.travis-ci.com/AlexandrovLab/SigProfilerClusters.svg?token=Fyqun3zxxDr3YzDRaKKm&branch=master)](https://app.travis-ci.com/AlexandrovLab/SigProfilerClusters) +[![Docs](https://img.shields.io/badge/docs-latest-blue.svg)](https://sigprofilersuite.github.io/SigProfilerClusters/) +[![License](https://img.shields.io/badge/License-BSD\%202--Clause-orange.svg)](https://opensource.org/licenses/BSD-2-Clause) +[![CI](https://github.com/SigProfilerSuite/SigProfilerClusters/actions/workflows/ci.yml/badge.svg)](https://github.com/SigProfilerSuite/SigProfilerClusters/actions/workflows/ci.yml) +SigProfilerClusters # SigProfilerClusters +SigProfilerClusters analyzes inter-mutational distances (IMD) between SNV-SNV and INDEL-INDEL mutations, separating them into clustered and non-clustered groups on a sample-dependent basis. Clustered SNVs are further subclassified into doublet base substitutions (DBS), multi-base substitutions (MBS), omikli, and kataegis events. The tool uses [SigProfilerSimulator](https://github.com/SigProfilerSuite/SigProfilerSimulator) to build a per-sample background model and applies a regionally corrected IMD threshold to identify mutations unlikely to have co-occurred by chance. -![Logo](docs/assets/images/SigProfilerClusters.png) -Tool for analyzing the inter-mutational distances between SNV-SNV and INDEL-INDEL mutations. Tool separates mutations into clustered and non-clustered groups on a sample-dependent basis and subclassifies all SNVs into a set category of clustered event: i) DBS; ii) MBS; iii) omikli; and iv) kataegis. Indels are not subclassifed. +## Documentation +Detailed documentation can be found at https://sigprofilersuite.github.io/SigProfilerClusters. -This tool was previously under the project name of SigProfilerHotSpots, but has been renamed to SigProfilerClusters. For all instructions below, SigProfilerClusters may be interchanged with SigProfilerHotSpots if the older version of the tools is being used. +## Quick Start Guide -**INTRODUCTION** +### Installation -The purpose of this document is to provide a guide for using the SigProfilerClusters framework. An extensive Wiki page detailing the usage of this tool can be found at https://osf.io/qpmzw/wiki/home/. - - -**PREREQUISITES** - -The framework is written in PYTHON, and uses additional SigProfiler packages: - - * PYTHON version 3.8 or newer - * SigProfilerMatrixGenerator (https://github.com/AlexandrovLab/SigProfilerMatrixGenerator) - * SigProfilerSimulator (https://github.com/AlexandrovLab/SigProfilerSimulator) - -Please visit their respective GitHub pages for detailed installation and usage instructions. - -**QUICK START GUIDE** - -This section will guide you through the minimum steps required to perform clustered analysis: - -1a. Install the python package using pip (current package): - pip install SigProfilerClusters - -1b. Install the python package using pip (deprecated version): - pip install SigProfilerHotSpots - - -Install your desired reference genome from the command line/terminal as follows (available reference genomes are: GRCh37, GRCh38, mm9, and mm10): +Install the current stable PyPI version of SigProfilerClusters: ``` -$ python ->> from SigProfilerMatrixGenerator import install as genInstall ->> genInstall.install('GRCh37', rsync=False, bash=True) +$ pip install SigProfilerClusters ``` -This will install the human 37 assembly as a reference genome. You may install as many genomes as you wish. If you have a firewall on your server, you may need to install rsync and use the rsync=True parameter. Similarly, if you do not have bash, -use bash=False. -2. Place your vcf files in your desired output folder. It is recommended that you name this folder based on your project's name. Before you can analyze clustered mutations, you need to generate a background model for each of your samples. To do this, generate a minimum of 100 simulations for your project (see SigProfilerSimulator for a detailed list of parameters): -``` ->>from SigProfilerSimulator import SigProfilerSimulator as sigSim ->>sigSim.SigProfilerSimulator(project, project_path, genome, contexts=["96"], simulations=100, chrom_based=True) -``` -3. Now the original mutations can be partitioned into clustered and non-clustered sets using the required parameters below: -``` ->> from SigProfilerClusters import SigProfilerClusters as hp ->> hp.analysis(project, genome, contexts, simContext, input_path) +Install your desired reference genome (available genomes: GRCh37, GRCh38, mm9, mm10): +```python +$ python +from SigProfilerMatrixGenerator import install as genInstall +genInstall.install('GRCh37') ``` -See below for a detailed list of available parameters - -4. The partitioned vcf files are placed under [project_path]/ouput/clustered/ and [project_path]/ouput/nonClustered/. You can visualize the results by looking at the IMD plots available under [project_path]/ouput/plots/. - - -**AVAILABLE PARAMETERS** - -### Required Parameters - -| Parameter | Variable Type | Description | -|--------------|--------------------|-------------| -| `project` | String | Unique name for the given project. | -| `genome` | String | Reference genome to use. Must be installed using SigProfilerMatrixGenerator. | -| `contexts` | String | Contexts needs to be one of the following: `"96"`, `"ID"`. | -| `simContext` | List of Strings | Mutation context used for generating the background model (e.g., `["6144"]` or `["96"]`). | -| `input_path` | String | Path to the input files. Must end with a `/`, e.g., `"path/to/the/input_file/"`. | - ---- - -### Optional Parameters - -| Parameter | Variable Type | Description | -|----------------|----------------|-------------| -| `analysis` | String | Desired analysis pipeline. Options include `"all"` (default), `"subClassify"`, and `"hotspot"`. | -| `sortSims` | Boolean | Option to sort simulated files. Ensures accuracy. Default: `True`. | -| `interdistance` | String | Mutation types to calculate IMDs between. Use only for indel analysis. Default: `"ID"`. | -| `calculateIMD` | Boolean | Whether to calculate IMDs. Useful for rerunning subclassification only. Default: `True`. | -| `max_cpu` | Integer | Number of CPUs to use. Default: all available CPUs. | -| `subClassify` | Boolean | Subclassify clustered mutations (requires VAF scores in TCGA/Sanger format). Default: `False`. | -| `plotIMDfigure` | Boolean | Generate IMD and mutational spectra plots for each sample. Default: `True`. | -| `plotRainfall` | Boolean | Generate rainfall plots using subclassified clustered events. Default: `True`. | - ---- - -### Parameters Used if `subClassify=True` -| Parameter | Variable Type | Description | -|------------------|---------------|-------------| -| `includedVAFs` | Boolean | Indicates VAFs are included in the dataset. Default: `True`. | -| `includedCCFs` | Boolean | Indicates CCFs are included. If `True`, set `includedVAFs=False`. Default: `True`. | -| `variant_caller` | String | Format of VAF scores (e.g., `"standard"`). Default: `"standard"`. | -| `windowSize` | Integer | Window size for calculating mutation density in rainfall plots. Default: `10000000`. | -| `correction` | Boolean | Perform genome-wide mutational density correction. Default: `False`. | -| `probability` | Boolean | Calculate the probability of observing each clustered event in its local region. Output saved in `[project_path]/output/clustered/`. Default: `False`. | +### Running +First, generate a background model using [SigProfilerSimulator](https://github.com/SigProfilerSuite/SigProfilerSimulator) (minimum 100 simulations recommended): +```python +from SigProfilerSimulator import SigProfilerSimulator as sigSim +sigSim.SigProfilerSimulator(project, project_path, genome, contexts=["288"], chrom_based=True, simulations=100) +``` -**VAF Format** - -SigProfilerClusters uses the VAF recorded in the input files to subclassify clustered mutations when subclassify=True and includedVAFs=True. However, depending on the VCF file, the VAF may be recorded in a different format. Below are the accepted formats corresponding to the correct settings to use for this tool. - -If you are not using VCFs as input files, VAFs cannot be used in the subclassification step. Therefore, to subclassify clusters using other input file types set subclassify=True and includedVAFs=False. - -If your VAF is recorded in the 11th column of your VCF as the last number of the colon delimited values, set variant_caller="caveman". - -If your VAF is recorded in the 8th or 10th column of your VCF as VAF=xx or AF=xx, set variant_caller="standard". - -If your VAF is recorded in the 10th or 11th column of your VCF as AF=xx, set variant_caller="mutect2". - -If your VCFs have no recorded VAFs set includedVAFs=False. This will run SigProfilerClusters, subclassify clusters based on just the calculated IMD (provided that you set subclassify=True). - - -**CCF Format** -Alternatively, SigProfilerClusters accepts cancer cell fraction (CCF) estimates in place of VAFs, when includedCCFs=True and includedVAFs=False to correct for copy number amplifications. To use CCFs, one must add the CCF values in the last column of each row within each VCF file (tab separated columns). - - -**Output Format** -All mutations are categorized into either clustered or non-clustered mutations. Each are saved under their respective directories within VCF files ([project_path]/output/clustered/ or [project_path]/output/nonClustered/; respectively). The clustered folder contains the partition of clustered mutations, which have been subclassified into one of four categories (five if using VAFs/CCFs). Within each subclass subfolder, a single VCF file is saved for each sample. All visualizations including rainfall plots are found within the output directory ([project_oath]/output/plots/). -Please refer to the Output page on the OSF wiki page for more details on all available output including available plots (https://osf.io/qpmzw/wiki/4.%20Output/). - -**LOG FILES** - -All errors and progress checkpoints are saved into SigProfilerClusters_[project]_[genome].err and SigProfilerClusters_[project]_[genome].out, respectively. For all errors, please email the error and progress log files to the primary contact under CONTACT INFORMATION. - - - -**Example Files** -Two examples are provided under examples/. These directories include all expected output except for the simulations folder to reduce memory size (please see the OSF page for more details: https://osf.io/qpmzw/wiki/4.%20Output/). - - -CITATIONS - -Erik N Bergstrom, Mousumy Kundu, Noura Tbeileh, Ludmil B Alexandrov. Examining clustered somatic mutations with SigProfilerClusters. Bioinformatics. 2022;38(13):3470–3473. https://doi.org/10.1093/bioinformatics/btac335 - -Bergstrom EN, Luebeck J, Petljak M, et al. Mapping clustered mutations in cancer reveals APOBEC3 mutagenesis of ecDNA. Nature. 2022;602:510–517. https://doi.org/10.1038/s41586-022-04398-6 - -COPYRIGHT - -Copyright (c) 2023, Erik Bergstrom [Alexandrov Lab] All rights reserved. +Then, partition mutations into clustered and non-clustered sets: +```python +from SigProfilerClusters import SigProfilerClusters as hp +hp.analysis(project, genome, contexts, simContext, input_path) +``` -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Results are placed under `[project_path]/output/clustered/` and `[project_path]/output/nonClustered/`. Visualizations are found under `[project_path]/output/plots/`. -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +## Reference -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Bergstrom EN, Kundu M, Tbeileh N, Alexandrov LB. Examining clustered somatic mutations with SigProfilerClusters. *Bioinformatics*. 2022;38(13):3470–3473. [https://doi.org/10.1093/bioinformatics/btac335](https://doi.org/10.1093/bioinformatics/btac335) -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Bergstrom EN, Luebeck J, Petljak M, et al. Mapping clustered mutations in cancer reveals APOBEC3 mutagenesis of ecDNA. *Nature*. 2022;602:510–517. [https://doi.org/10.1038/s41586-022-04398-6](https://doi.org/10.1038/s41586-022-04398-6) -CONTACT INFORMATION +## Contact -Please address any queries or bug reports to Erik Bergstrom at ebergstr@health.ucsd.edu or Mousumy Kundu at mkundu@health.ucsd.edu +For questions, support requests, or bug reports, please contact the development team via GitHub [issues](https://github.com/SigProfilerSuite/SigProfilerClusters/issues) or by email at [ebergstr@health.ucsd.edu](mailto:ebergstr@health.ucsd.edu) or [mkundu@health.ucsd.edu](mailto:mkundu@health.ucsd.edu). From 8e7e06b7169282f45ed3da0dea54bdbd8c99b638 Mon Sep 17 00:00:00 2001 From: josecorcab Date: Fri, 8 May 2026 11:18:52 +0200 Subject: [PATCH 7/7] contact updated --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f30887..7bc932e 100755 --- a/README.md +++ b/README.md @@ -50,4 +50,4 @@ Bergstrom EN, Luebeck J, Petljak M, et al. Mapping clustered mutations in cancer ## Contact -For questions, support requests, or bug reports, please contact the development team via GitHub [issues](https://github.com/SigProfilerSuite/SigProfilerClusters/issues) or by email at [ebergstr@health.ucsd.edu](mailto:ebergstr@health.ucsd.edu) or [mkundu@health.ucsd.edu](mailto:mkundu@health.ucsd.edu). +For questions, support requests, or bug reports, please contact the SigProfilerSuite team via GitHub [issues](https://github.com/SigProfilerSuite/SigProfilerClusters/issues) or by email at [contact@sigprofilersuite.org](mailto:contact@sigprofilersuite.org).