Deduplicate the calibration reference by peptidoform (v4.2.0) - #116
Merged
Conversation
Calibration and fine-tuning now fit on one PSM per peptidoform, the first observation in the reference. A reference built from a search result repeats a peptidoform once per spectrum it was identified in, each time with a different observed retention time, so the fit received conflicting targets and weighed peptidoforms by how often they happened to be identified. On the reported MS2Rescore case (201,593 PSMs, single run) the reference chosen by auto-calibration held 6,331 PSMs but only 2,623 peptidoforms, and the repeats disagreed on the observed retention time by up to 236 minutes. Measured on those 2,623 peptidoforms, fitting on the first observations improves the calibration from 9.69 to 4.51 minutes mean absolute error (median 5.98 to 2.09; within five minutes 44.9 % to 81.4 %). Charge states of one peptidoform count as repeats, because retention time does not depend on precursor charge. When repeats disagree by a large fraction of the observed range DeepLC now warns: the reference then mixes runs or contains low-confidence PSMs, which deduplication hides rather than fixes. `deduplicate_reference=False` on calibrate/finetune/predict_and_calibrate/ finetune_and_predict, or `--keep-duplicate-reference-psms` on the command line, restores the previous behaviour. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RalfG
reviewed
Aug 28, 2026
RalfG
left a comment
Member
There was a problem hiding this comment.
Good catch! Seems logical, but one question I had: Can this be set as default behavior without the need for keyword arguments and CLI options? That would keep the API relatively clean and removes the need for pipelining the option through multiple modules/layers of code.
If the dedup option would only need to be disabled in edge cases, we can rely on those users just using the reference selection API directly.
The reported case stores retention times in seconds (the psm_utils convention): identifications run from 243 to 597 s of a roughly ten-minute acquisition, so the worst disagreement between repeats of a peptidoform is 236 s, two thirds of the 354 s range, and the calibration improves from 9.69 to 4.51 s mean absolute error. DeepLC calibrates in whatever unit the input uses and converts nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RalfG
reviewed
Aug 28, 2026
Review feedback on #116: keep the public signatures as they were rather than threading a flag through four functions and the command line. Deduplication now happens inside calibrate() and finetune() with no way to turn it off. It has to live there rather than in the reference-selection layer, because a caller that selects its own reference (MS2Rescore builds one from targets with q <= 0.01, or the top N by score) never passes through select_reference_psms, and that caller is exactly the reported case. The rare caller who wants every reference PSM to count still has a path that needs no parameter: fit a Calibration on its own targets and pass it to predict_and_calibrate, which uses an already fitted calibration as given. train() remains available for full control over a training set. Both are covered by tests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review feedback on #116. modified_sequence is the ProForma string without the charge state, so psm_utils decides what identifies a peptidoform instead of this module cutting the charge off the string. It keeps terminal, global and labile modifications apart, and it cannot be tripped up by a modification label that contains a slash, which the previous rsplit would have truncated. Charge adducts (/2 against /2[+2H]) also collapse correctly now. modified_sequence exists since psm_utils 1.1.0 and DeepLC already requires >= 1.5, so no dependency change is needed. Two tests added: seven peptidoforms that differ only in terminal, global or labile modifications stay distinct, and a charge adduct does not create a second peptidoform. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A user ran MS²Rescore on a PSM list where the same peptidoform appears in many spectra, each with its own observed retention time (
matrix_fix_deeplc/psm_list.tsv: 201,593 PSMs, one run, 177,905 peptidoforms). The reference that auto-calibration selects from it holds 6,331 PSMs but only 2,623 peptidoforms, and the repeats of a peptidoform disagree on the observed RT by up to 236 min (median 0.89 min). The calibration therefore got several conflicting targets per prediction, and each peptidoform was weighed by how often it happened to be identified (up to 261 times).Change
calibrate,finetune,predict_and_calibrateandfinetune_and_predictnow keep one PSM per peptidoform, the first observation, before fitting. Newdeeplc._reference_selection.deduplicate_psms(psm_list, ignore_charge=True).deduplicate_reference=False, or--keep-duplicate-reference-psmson the CLI.Effect on the reported case
Both fits evaluated on the 2,623 first observations (the only self-consistent target):
Verification
pytest tests: 126 passed, including 9 new tests intests/test_deduplication.py(first observation kept, order preserved, idempotent, charge handling, modified peptidoforms distinct, warning on conflicting RTs, missing RTs, calibration behaviour with the flag on and off, parameter forwarded end to end).ruff check/ruff format --checkclean on the changed files.🤖 Generated with Claude Code