OpenMS
Loading...
Searching...
No Matches
NuXLFDR Class Reference

Class-separated FDR calculation for nucleic-acid cross-links (NuXL). More...

#include <OpenNuXL/include/NuXLFDR.h>

Public Types

enum class  XLGroups { OFF , ON , AUTO }
 Separate FDR group for short cross-links. More...
 

Public Member Functions

 NuXLFDR (size_t report_top_hits, XLGroups xl_groups=XLGroups::OFF)
 Construct with a top-hit cap that controls how FalseDiscoveryRate is parametrised.
 
bool useShortXLGroup (const PeptideIdentificationList &xl_pi, Size &short_decoys) const
 Whether the short cross-links of xl_pi get their own FDR group.
 
void splitIntoPeptidesAndXLs (const PeptideIdentificationList &peptide_ids, PeptideIdentificationList &pep_pi, PeptideIdentificationList &xl_pi) const
 Partition peptide_ids into plain-peptide and cross-link PSM lists by their single top hit.
 
void mergePeptidesAndXLs (const PeptideIdentificationList &pep_pi, const PeptideIdentificationList &xl_pi, PeptideIdentificationList &peptide_ids) const
 Re-merge a previously split (splitIntoPeptidesAndXLs) pair of lists into one PSM list.
 
void QValueAtPSMLevel (PeptideIdentificationList &peptide_ids) const
 Compute PSM-level q-values across peptide_ids without splitting by class.
 
void calculatePeptideAndXLQValueAtPSMLevel (const PeptideIdentificationList &peptide_ids, PeptideIdentificationList &pep_pi, PeptideIdentificationList &xl_pi) const
 Compute PSM- and peptide-level q-values separately for plain and cross-linked PSMs.
 
void calculatePeptideAndXLQValueAndFilterAtPSMLevel (const std::vector< ProteinIdentification > &protein_ids, const PeptideIdentificationList &peptide_ids, PeptideIdentificationList &pep, double peptide_PSM_qvalue_threshold, double peptide_peptide_qvalue_threshold, PeptideIdentificationList &xl_pi, std::vector< double > xl_PSM_qvalue_thresholds, std::vector< double > xl_peptidelevel_qvalue_thresholds, const std::string &out_idxml, int decoy_factor) const
 Compute separated FDRs, apply class-specific filters, and write per-threshold idXML / TSV reports.
 

Static Public Member Functions

static XLGroups xlGroupsFromString (const std::string &s)
 Parse "auto", "on" or "off" (the values of the NuXL:fdr_groups option)
 
static bool isShortXL (const PeptideHit &hit)
 Whether hit is a short cross-link: NuXL:isXL, at most SHORT_XL_MAX_LENGTH residues and at least SHORT_XL_MIN_NA_LENGTH nucleotides (NuXL:NA_length)
 

Static Public Attributes

static constexpr Size SHORT_XL_MAX_LENGTH = 9
 short cross-link: at most this many residues ...
 
static constexpr Size SHORT_XL_MIN_NA_LENGTH = 2
 ... and at least this many nucleotides in the adduct
 
static constexpr Size AUTO_MIN_DECOYS = 10
 XLGroups::AUTO: decoys the short cross-links need ...
 
static constexpr double AUTO_FDR = 0.1
 ... among the cross-links at this FDR over all cross-links
 

Private Member Functions

void setUpFDR_ (FalseDiscoveryRate &fdr) const
 Set the parameters described at QValueAtPSMLevel() on fdr.
 

Private Attributes

size_t report_top_hits_
 Top-hit cap consulted by the FDR-applying methods to toggle use_all_hits.
 
XLGroups xl_groups_
 Whether short cross-links form their own FDR group.
 

Detailed Description

Class-separated FDR calculation for nucleic-acid cross-links (NuXL).

Splits PSMs into a "plain peptide" class (NuXL:isXL == 0) and a "cross-link" class (NuXL:isXL != 0), computes FDR separately for each, and (optionally) writes one filtered idXML per requested XL q-value threshold plus a TSV protein report for stringent (<= 10%) XL FDRs.

Built on top of FalseDiscoveryRate. Decoys are kept in the FDR result (so downstream Percolator and similar tools can still see them); the convenience filter method calls IDFilter::removeDecoyHits before writing.

Member Enumeration Documentation

◆ XLGroups

enum class XLGroups
strong

Separate FDR group for short cross-links.

Short cross-links (see isShortXL()) carry little sequence-specific fragment evidence next to their large adduct, so at the same score far more of them are false than of the other cross-links. With a single cross-link FDR, most of the false cross-links accepted at a given threshold are short ones. In their own group, both groups are held at the requested FDR.

Enumerator
OFF 

one FDR over all cross-links

ON 

short cross-links form their own FDR group

AUTO 

as ON if the short cross-links have enough decoys to estimate their own FDR (see useShortXLGroup()), else as OFF

Constructor & Destructor Documentation

◆ NuXLFDR()

NuXLFDR ( size_t  report_top_hits,
XLGroups  xl_groups = XLGroups::OFF 
)
explicit

Construct with a top-hit cap that controls how FalseDiscoveryRate is parametrised.

report_top_hits >= 2 toggles use_all_hits = true on the underlying FalseDiscoveryRate so q-values are computed across all hits per PSM, not only the top hit. The value is otherwise stored verbatim and consulted by the QValueAtPSMLevel and calculatePeptideAndXLQValueAtPSMLevel methods.

Parameters
[in]report_top_hitsTop-hit cap; values >= 2 enable the underlying FDR engine's use_all_hits mode.
[in]xl_groupsWhether short cross-links form their own FDR group (see XLGroups).

Member Function Documentation

◆ calculatePeptideAndXLQValueAndFilterAtPSMLevel()

void calculatePeptideAndXLQValueAndFilterAtPSMLevel ( const std::vector< ProteinIdentification > &  protein_ids,
const PeptideIdentificationList &  peptide_ids,
PeptideIdentificationList &  pep,
double  peptide_PSM_qvalue_threshold,
double  peptide_peptide_qvalue_threshold,
PeptideIdentificationList &  xl_pi,
std::vector< double >  xl_PSM_qvalue_thresholds,
std::vector< double >  xl_peptidelevel_qvalue_thresholds,
const std::string &  out_idxml,
int  decoy_factor 
) const

Compute separated FDRs, apply class-specific filters, and write per-threshold idXML / TSV reports.

Pipeline (every step is observable behaviour, not implementation detail):

  1. Calls calculatePeptideAndXLQValueAtPSMLevel to populate pep and xl_pi with FDR-annotated PSMs.
  2. When decoy_factor != 1, divides every cross-link hit's score by decoy_factor (peptide hits are not touched).
  3. Adds a deterministic score-range-normalised tie-breaker of magnitude <= 1e-5 to every cross-link score so PSMs that originally tied on the q-value resolve to a unique ordering. Uses "svm_score" when at least one XL hit carries it, otherwise "NuXL:score".
    Warning
    the tie-break divides by the (max - min) score range and is undefined when all XL hits share the same score.
  4. Calls IDFilter::removeDecoyHits on both pep and xl_pi.
  5. Filters pep on PEPTIDE_Q_VALUE when peptide_peptide_qvalue_threshold is in (0, 1); values <= 0 or >= 1 disable the filter.
  6. Filters pep PSMs by score when peptide_PSM_qvalue_threshold is in (0, 1).
  7. Writes the plain-peptide idXML to {out_idxml}{threshold:.4f}_peptides.idXML (with out_idxml the literal prefix and threshold the configured cut formatted to four decimal places), with unreferenced proteins pruned.
  8. Normalises xl_PSM_qvalue_thresholds: 0.0 entries are rewritten to 1.0 (disabled filter = 100% FDR) and the vector is then sorted in descending order so increasingly stringent filters are applied progressively to the same xl_pi (filters compound across iterations).
  9. For each (XL-PSM threshold, XL-peptide-level threshold) pair (paired by index; sizes must match — otherwise Exception::InvalidValue is thrown):
    • filter cross-links on PEPTIDE_Q_VALUE when the peptide-level threshold is in (0, 1);
    • filter cross-links by PSM-level q-value when the XL-PSM threshold is in (0, 1);
    • prune unreferenced proteins from a fresh copy of protein_ids;
    • compute coverage by cross-linked peptides on the first ProteinIdentification of that copy;
    • write the XL idXML to {out_idxml}{threshold:.4f}_XLs.idXML;
    • if the XL FDR is <= 0.1, also write a TSV protein report to {out_idxml}proteins{threshold:.4f}_XLs.tsv (skipped for permissive FDRs to bound output size).
Parameters
[in]protein_idsProtein identifications used as the source for the per-file protein lists; unreferenced proteins are pruned per output.
[in]peptide_idsInput PSMs (mixed plain + XL).
[out]pepReceives the filtered plain-peptide PSMs (after step 6).
[in]peptide_PSM_qvalue_thresholdPlain-peptide PSM-level q-value cut; values outside (0, 1) disable the filter.
[in]peptide_peptide_qvalue_thresholdPlain-peptide peptide-level q-value cut; values outside (0, 1) disable the filter.
[out]xl_piReceives the filtered cross-link PSMs (state at the most stringent XL threshold).
[in]xl_PSM_qvalue_thresholdsCross-link PSM-level q-value thresholds; 0.0 is rewritten to 1.0 (disabled = 100% FDR). The vector is sorted descending in place.
[in]xl_peptidelevel_qvalue_thresholdsCross-link peptide-level q-value thresholds paired by index with xl_PSM_qvalue_thresholds (must have the same size).
[in]out_idxmlOutput filename prefix; the FDR threshold and a suffix (_peptides.idXML / _XLs.idXML / proteins...XLs.tsv) are appended.
[in]decoy_factorScore scale-down factor for the XL class; ignored when equal to 1.
Exceptions
OpenMS::Exception::InvalidValuewhen xl_PSM_qvalue_thresholds and xl_peptidelevel_qvalue_thresholds differ in size.

References NuXLProteinReport::annotateProteinModificationForTopHits(), NuXLFDR::calculatePeptideAndXLQValueAtPSMLevel(), IDFilter::filterHitsByScore(), OpenMS::StringUtils::number(), OPENMS_LOG_INFO, OpenMS::Constants::UserParam::PEPTIDE_Q_VALUE, IDFilter::removeDecoyHits(), IDFilter::removeUnreferencedProteins(), TextFile::store(), IdXMLFile::store(), and OpenMS::StringUtils::toStr().

◆ calculatePeptideAndXLQValueAtPSMLevel()

void calculatePeptideAndXLQValueAtPSMLevel ( const PeptideIdentificationList &  peptide_ids,
PeptideIdentificationList &  pep_pi,
PeptideIdentificationList &  xl_pi 
) const

Compute PSM- and peptide-level q-values separately for plain and cross-linked PSMs.

Internally calls splitIntoPeptidesAndXLs and then applies FalseDiscoveryRate to each output list with the same parameters as QValueAtPSMLevel. The q-values are computed independently on each class so XL and non-XL FDR control are not confounded. If useShortXLGroup() is true, the q-values of the short and the other cross-links are computed separately, every cross-link hit is annotated with its group (meta value NuXL:fdr_group: short_XL or XL), and xl_pi keeps the order of the input.

Parameters
[in]peptide_idsInput mixed plain-peptide / cross-link PSMs.
[out]pep_piPlain-peptide PSMs with PSM- and peptide-level q-values annotated.
[out]xl_piCross-link PSMs with PSM- and peptide-level q-values annotated.

References FalseDiscoveryRate::apply(), NuXLFDR::AUTO, NuXLFDR::AUTO_FDR, NuXLFDR::AUTO_MIN_DECOYS, ExposedVector< VectorElement >::clear(), ExposedVector< VectorElement >::empty(), NuXLFDR::isShortXL(), OPENMS_LOG_INFO, ExposedVector< VectorElement >::push_back(), NuXLFDR::setUpFDR_(), NuXLFDR::SHORT_XL_MAX_LENGTH, NuXLFDR::SHORT_XL_MIN_NA_LENGTH, ExposedVector< VectorElement >::size(), NuXLFDR::splitIntoPeptidesAndXLs(), NuXLFDR::useShortXLGroup(), and NuXLFDR::xl_groups_.

Referenced by NuXLFDR::calculatePeptideAndXLQValueAndFilterAtPSMLevel().

◆ isShortXL()

bool isShortXL ( const PeptideHit &  hit)
static

Whether hit is a short cross-link: NuXL:isXL, at most SHORT_XL_MAX_LENGTH residues and at least SHORT_XL_MIN_NA_LENGTH nucleotides (NuXL:NA_length)

References MetaInfoInterface::getMetaValue(), PeptideHit::getSequence(), NuXLFDR::SHORT_XL_MAX_LENGTH, NuXLFDR::SHORT_XL_MIN_NA_LENGTH, and AASequence::size().

Referenced by NuXLFDR::calculatePeptideAndXLQValueAtPSMLevel(), and NuXLFDR::useShortXLGroup().

◆ mergePeptidesAndXLs()

void mergePeptidesAndXLs ( const PeptideIdentificationList &  pep_pi,
const PeptideIdentificationList &  xl_pi,
PeptideIdentificationList &  peptide_ids 
) const

Re-merge a previously split (splitIntoPeptidesAndXLs) pair of lists into one PSM list.

pep_pi entries are appended verbatim. For each xl_pi entry, the meta value "spectrum_reference" is used as the merge key:

  • if no plain-peptide entry shares this spectrum reference, the XL entry is appended as a new PeptideIdentification;
  • if a plain-peptide entry already exists for this spectrum, the XL hits are appended to its hit list and that hit list is re-sorted in place.
Note
For lists produced by splitIntoPeptidesAndXLs the second case never occurs: that split assigns each spectrum's single top hit to exactly one class, so a given "spectrum_reference" appears in at most one of pep_pi / xl_pi. The append branch only matters when merging lists assembled by other means.

peptide_ids is cleared on entry.

Parameters
[in]pep_piPlain-peptide PSMs (typically from splitIntoPeptidesAndXLs).
[in]xl_piCross-link PSMs (typically from splitIntoPeptidesAndXLs).
[out]peptide_idsReceives the merged PSM list keyed by "spectrum_reference".

References ExposedVector< VectorElement >::clear(), and ExposedVector< VectorElement >::push_back().

◆ QValueAtPSMLevel()

void QValueAtPSMLevel ( PeptideIdentificationList &  peptide_ids) const

Compute PSM-level q-values across peptide_ids without splitting by class.

Wraps FalseDiscoveryRate with add_decoy_proteins = true, add_decoy_peptides = true (decoys are retained in the result for downstream tools such as Percolator), and use_all_hits = true iff report_top_hits_ passed to the constructor is >= 2. Also computes the peptide-level q-value meta value Constants::UserParam::PEPTIDE_Q_VALUE on each hit.

Parameters
[in,out]peptide_idsPSMs to score in place; each hit receives PSM- and peptide-level q-value meta values.

References FalseDiscoveryRate::apply(), and NuXLFDR::setUpFDR_().

◆ setUpFDR_()

◆ splitIntoPeptidesAndXLs()

void splitIntoPeptidesAndXLs ( const PeptideIdentificationList &  peptide_ids,
PeptideIdentificationList &  pep_pi,
PeptideIdentificationList &  xl_pi 
) const

Partition peptide_ids into plain-peptide and cross-link PSM lists by their single top hit.

For each input PeptideIdentification this keeps only the single top-ranked hit (the first hit; hits are assumed sorted best-first) and assigns it to exactly one output list according to the integer meta value "NuXL:isXL" (zero → plain peptide, → pep_pi; anything else → cross-link, → xl_pi). This is a winner-takes-the-spectrum class competition: a spectrum is claimed by whichever class its best hit belongs to and therefore contributes to pep_pi or xl_pi but never to both. Lower-ranked hits of the other class are intentionally dropped so that, e.g., a spectrum best explained as a plain peptide does not also inflate the cross-link FDR pool. This single-top-hit split is applied regardless of report_top_hits (which only toggles use_all_hits inside the underlying FalseDiscoveryRate q-value estimation, not the class split here).

pep_pi and xl_pi are cleared on entry.

Parameters
[in]peptide_idsInput mixed plain-peptide / cross-link PSMs.
[out]pep_piReceives the PSMs whose top hit is a plain peptide (NuXL:isXL == 0), one hit each.
[out]xl_piReceives the PSMs whose top hit is a cross-link (NuXL:isXL != 0), one hit each.

References ExposedVector< VectorElement >::back(), ExposedVector< VectorElement >::clear(), ExposedVector< VectorElement >::push_back(), and PeptideIdentification::setHits().

Referenced by NuXLFDR::calculatePeptideAndXLQValueAtPSMLevel().

◆ useShortXLGroup()

bool useShortXLGroup ( const PeptideIdentificationList &  xl_pi,
Size &  short_decoys 
) const

Whether the short cross-links of xl_pi get their own FDR group.

XLGroups::ON: if there are short cross-links. XLGroups::AUTO: if, among the cross-links that pass AUTO_FDR with one FDR over all cross-links, at least AUTO_MIN_DECOYS are short decoys. Below that, the FDR of the short cross-links would rest on too few decoys. The decision uses only the decoy counts, not the numbers of identifications the two ways would give. In both modes, the short and the other cross-links each need at least one decoy: FalseDiscoveryRate gives every target of a list without decoys q-value 0.

Parameters
[in]xl_piCross-link identifications, one hit each (as produced by splitIntoPeptidesAndXLs()).
[out]short_decoysNumber of short decoys counted for the decision (XLGroups::AUTO; 0 otherwise).

References FalseDiscoveryRate::apply(), NuXLFDR::AUTO_FDR, NuXLFDR::AUTO_MIN_DECOYS, ExposedVector< VectorElement >::begin(), ExposedVector< VectorElement >::end(), PeptideIdentification::getHits(), NuXLFDR::isShortXL(), NuXLFDR::OFF, NuXLFDR::ON, OPENMS_LOG_WARN, NuXLFDR::setUpFDR_(), and NuXLFDR::xl_groups_.

Referenced by NuXLFDR::calculatePeptideAndXLQValueAtPSMLevel().

◆ xlGroupsFromString()

NuXLFDR::XLGroups xlGroupsFromString ( const std::string &  s)
static

Parse "auto", "on" or "off" (the values of the NuXL:fdr_groups option)

References NuXLFDR::AUTO, NuXLFDR::OFF, and NuXLFDR::ON.

Member Data Documentation

◆ AUTO_FDR

constexpr double AUTO_FDR = 0.1
staticconstexpr

... among the cross-links at this FDR over all cross-links

Referenced by NuXLFDR::calculatePeptideAndXLQValueAtPSMLevel(), and NuXLFDR::useShortXLGroup().

◆ AUTO_MIN_DECOYS

constexpr Size AUTO_MIN_DECOYS = 10
staticconstexpr

XLGroups::AUTO: decoys the short cross-links need ...

Referenced by NuXLFDR::calculatePeptideAndXLQValueAtPSMLevel(), and NuXLFDR::useShortXLGroup().

◆ report_top_hits_

size_t report_top_hits_
private

Top-hit cap consulted by the FDR-applying methods to toggle use_all_hits.

Referenced by NuXLFDR::setUpFDR_().

◆ SHORT_XL_MAX_LENGTH

constexpr Size SHORT_XL_MAX_LENGTH = 9
staticconstexpr

short cross-link: at most this many residues ...

Referenced by NuXLFDR::calculatePeptideAndXLQValueAtPSMLevel(), and NuXLFDR::isShortXL().

◆ SHORT_XL_MIN_NA_LENGTH

constexpr Size SHORT_XL_MIN_NA_LENGTH = 2
staticconstexpr

... and at least this many nucleotides in the adduct

Referenced by NuXLFDR::calculatePeptideAndXLQValueAtPSMLevel(), and NuXLFDR::isShortXL().

◆ xl_groups_

XLGroups xl_groups_
private

Whether short cross-links form their own FDR group.

Referenced by NuXLFDR::calculatePeptideAndXLQValueAtPSMLevel(), and NuXLFDR::useShortXLGroup().