Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [2.0.2] - 2026-07-16

### Added
- Sphinx documentation integration with CHANGELOG.md included in docs

### Changed
- Updated code examples in docstrings and documentation to reflect current API
- Optimized PSM list filtering to use shallow copy instead of full deepcopy for better memory efficiency on large datasets

### Fixed
- CLI argument passing to `predict` function when calibration data is not provided

## [2.0.1] - 2026-07-15

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Alternatively, use comma-separated values (CSV) with the following columns:

**Example:**

```csv
```text
seq,modifications,charge,CCS
VVDDFADITTPLK,,2,422.9984309464991
GVEVLSLTPSFMDIPEK,12|Oxidation,2,464.6568644356109
Expand Down
5 changes: 5 additions & 0 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*********
Changelog
*********

.. mdinclude:: ../../CHANGELOG.md
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
:maxdepth: 2

Home <self>
Changelog <changelog>


.. toctree::
Expand Down
4 changes: 2 additions & 2 deletions im2deep/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
Example:
Basic usage for CCS prediction:

>>> from im2deep.im2deep import predict_ccs
>>> from im2deep import predict
>>> from psm_utils.psm_list import PSMList
>>> predictions = predict_ccs(psm_list, calibration_data)
>>> predictions = predict(psm_list)

"""

Expand Down
2 changes: 1 addition & 1 deletion im2deep/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ def _run_predict(*args, **kwargs):
LOGGER.info(
"No calibration file provided (calibration is HIGHLY recommended), performing prediction only..."
)
predictions = core.predict(*args, **kwargs)
predictions = core.predict(psm_list, multi=kwargs.get("multi", False))

# Output results
LOGGER.info("IM2Deep CCS prediction completed successfully!")
Expand Down
35 changes: 22 additions & 13 deletions im2deep/_io_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from __future__ import annotations

import logging
from copy import deepcopy
from pathlib import Path
from typing import Any

Expand Down Expand Up @@ -57,7 +56,7 @@ def parse_input(

Parameters
----------
file_path : str, Path, or PSMList
input_file : str, Path, or PSMList
Path to the input file or a PSMList object.

Returns
Expand Down Expand Up @@ -229,6 +228,12 @@ def validate_psm_list(psm_list: PSMList, needs_target: bool = False) -> PSMList:
Validate that the PSM list contains necessary fields. And homogenizes the data.
Also removes entries with charge state higher than 6.

Notes
-----
When ``needs_target=True``, this function mutates the PSMs in ``psm_list`` in place
(normalizing/writing ``psm.metadata["CCS"]``) rather than copying them first. Callers
that need to keep their original PSMList untouched should pass a copy.

Parameters
----------
psm_list : PSMList
Expand All @@ -253,12 +258,10 @@ def validate_psm_list(psm_list: PSMList, needs_target: bool = False) -> PSMList:
charges = np.array([psm.peptidoform.precursor_charge for psm in psm_list])
psm_list_filtered = psm_list[(charges != None) & (charges <= 6)] # noqa: E711

# Filtering above shares PSM instances with the input psm_list (no copy). A deep copy is only
# needed when the block below will mutate PSMs in place (writing psm.metadata["CCS"]); for the
# plain prediction path (needs_target=False) nothing downstream mutates PSMs, so skip it there
# to avoid an O(n) full-object-graph copy on large PSM lists.
if needs_target:
psm_list_filtered = deepcopy(psm_list_filtered)
# Filtering above shares PSM instances with the input psm_list (no copy), and the
# needs_target block below writes psm.metadata["CCS"] directly onto those shared PSMs.
# This function therefore mutates the caller's psm_list in place when needs_target=True;
# no defensive copy is made, to avoid the memory cost of copying large PSM lists.

if len(psm_list_filtered) < original_size:
filtered_count = original_size - len(psm_list_filtered)
Expand All @@ -268,13 +271,13 @@ def validate_psm_list(psm_list: PSMList, needs_target: bool = False) -> PSMList:
)

if len(psm_list_filtered) == 0:
raise IM2DeepError("No PSMs present in provided PSMLists.")
raise IM2DeepError("No PSMs present in provided PSMLists after validation.")

all_has_targets = True
if needs_target:
# Check if PSMs have either ion_mobility or CCS
all_has_targets = all(
psm.ion_mobility is not None or psm.metadata.get("CCS") is not None
psm.ion_mobility is not None or (psm.metadata or {}).get("CCS") is not None
for psm in psm_list_filtered
)

Expand All @@ -284,13 +287,19 @@ def validate_psm_list(psm_list: PSMList, needs_target: bool = False) -> PSMList:
if psm.metadata is None:
psm.metadata = {}

if psm.ion_mobility is not None:
theoretical_mz = psm.peptidoform.theoretical_mz
precursor_charge = psm.peptidoform.precursor_charge
if (
psm.ion_mobility is not None
and theoretical_mz is not None
and precursor_charge is not None
):
if "CCS" not in psm.metadata:
psm.metadata["CCS"] = _normalize_ccs_metadata_value(
im2ccs(
psm.ion_mobility,
psm.peptidoform.theoretical_mz,
psm.peptidoform.precursor_charge,
theoretical_mz,
precursor_charge,
)
)
elif psm.metadata.get("CCS") is not None:
Expand Down
4 changes: 2 additions & 2 deletions im2deep/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class IM2DeepError(Exception):
Examples
--------
>>> try:
... predict_ccs(invalid_data)
... predict(invalid_psm_list)
... except IM2DeepError as exc:
... print(f"IM2Deep error occurred: {exc}")
"""
Expand Down Expand Up @@ -68,7 +68,7 @@ class CalibrationError(IM2DeepError):
Examples
--------
>>> try:
... linear_calibration(pred_df, cal_df, ref_df)
... predict_and_calibrate(psm_list, psm_list_cal)
... except CalibrationError as exc:
... print(f"Calibration failed: {exc}")
"""
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "im2deep"
version = "2.0.1"
version = "2.0.2"
description = "Framework for prediction of collisional cross-section of peptides."
readme = "README.md"
license = "Apache-2.0"
Expand Down
Loading
Loading