Skip to content

Commit c246476

Browse files
committed
Update mzTab protein parsing
1 parent 7b116d0 commit c246476

22 files changed

Lines changed: 138 additions & 99 deletions

File tree

.github/workflows/rust.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,30 +33,30 @@ jobs:
3333
cargo build -p rustyms --no-default-features --features imgt,rayon
3434
cargo build -p rustyms --no-default-features --features imgt,rayon,align
3535
cargo build -p rustyms --no-default-features --features imgt,align
36-
36+
3737
fmt:
3838
runs-on: ubuntu-latest
39-
39+
4040
needs: build
4141
steps:
4242
- uses: actions/checkout@v4
4343
- name: Run fmt
4444
run: cargo fmt --check
45-
45+
4646
clippy:
4747
runs-on: ubuntu-latest
48-
48+
4949
needs: build
5050
steps:
5151
- uses: actions/checkout@v4
5252
- name: Run clippy
5353
run: cargo clippy
54-
54+
5555
test:
5656
runs-on: ubuntu-latest
57-
env:
57+
env:
5858
CARGO_ENCODED_RUSTFLAGS: --cfg=github_action
59-
59+
6060
needs: build
6161
steps:
6262
- uses: actions/checkout@v4

CITATION.cff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ cff-version: 1.2.0
1717
identifiers:
1818
- description: Main paper (preprint)
1919
type: doi
20-
value: 10.1101/2025.01.18.633732
20+
value: 10.1101/2025.01.18.633732
2121
- description: Mass alignment algorithm paper
2222
type: doi
2323
value: 10.1021/acs.jproteome.4c00188

examples/ion-explorer/figure.r

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ library(dplyr)
99
# geom_point() +
1010
# scale_size_continuous(range = c(0.25, 3)) +
1111
# xlab("Difference to theoretical ion (Da)") +
12-
# theme_bw()
12+
# theme_bw()
1313

1414
# if (file == "fragment_y") {
1515
# plot = plot + geom_vline(xintercept=0, linetype="dashed") + geom_vline(xintercept=+18.011-0.984, linetype="dashed") + geom_vline(xintercept=-25.979+18.011, linetype="dashed")
@@ -48,7 +48,7 @@ make_plot = function(fragment, element=NA, mode=NA) {
4848
geom_point() +
4949
scale_size_continuous(range = c(0.25, 3)) +
5050
xlab("Difference to theoretical ion (Da)") +
51-
theme_bw()
51+
theme_bw()
5252

5353
if (fragment == "y") {
5454
plot = plot + geom_vline(xintercept=0, linetype="dashed", alpha=0.25) + geom_vline(xintercept=0.984-18.011, linetype="dashed", alpha=0.25) + geom_vline(xintercept=25.979-18.011, linetype="dashed", alpha=0.25) + xlim(-50, 50)
@@ -74,16 +74,16 @@ compare_plot = function(fragment1, fragment2, element1, element2, mode1, mode2)
7474
geom_point(data=data2, aes(x=mass, y=-count / max2, size=total_intensity / count), colour="red") +
7575
scale_size_continuous(range = c(0.125, 3)) +
7676
xlab("Difference to theoretical ion (Da)") +
77-
theme_bw()
77+
theme_bw()
7878

7979
if (fragment1 == "y" && fragment2 == "y") {
80-
plot = plot +
81-
geom_vline(xintercept=0, linetype="dashed", alpha=0.25) +
82-
geom_vline(xintercept=0.984-18.011, linetype="dashed", alpha=0.25) +
83-
geom_vline(xintercept=0.984-18.011-15.023, linetype="dotted", alpha=0.25) +
84-
geom_vline(xintercept=0.984-18.011-29.039, linetype="dotted", alpha=0.25) +
85-
geom_vline(xintercept=0.984-18.011-43.088, linetype="dotted", alpha=0.25) +
86-
geom_vline(xintercept=25.979-18.011, linetype="dashed", alpha=0.25) +
80+
plot = plot +
81+
geom_vline(xintercept=0, linetype="dashed", alpha=0.25) +
82+
geom_vline(xintercept=0.984-18.011, linetype="dashed", alpha=0.25) +
83+
geom_vline(xintercept=0.984-18.011-15.023, linetype="dotted", alpha=0.25) +
84+
geom_vline(xintercept=0.984-18.011-29.039, linetype="dotted", alpha=0.25) +
85+
geom_vline(xintercept=0.984-18.011-43.088, linetype="dotted", alpha=0.25) +
86+
geom_vline(xintercept=25.979-18.011, linetype="dashed", alpha=0.25) +
8787
xlim(-75, 50)
8888
}
8989
if (fragment1 == "b" && fragment2 == "b") {

examples/multi-annotator/src/main.rs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,28 @@ use fragment::FragmentType;
2121
use itertools::Itertools;
2222
use mzdata::{
2323
io::{MZFileReader, SpectrumSource},
24-
mzpeaks::{peak_set::PeakSetVec, PeakCollection},
24+
mzpeaks::{PeakCollection, peak_set::PeakSetVec},
2525
mzsignal::PeakPicker,
2626
spectrum::{SignalContinuity, SpectrumLike},
2727
};
2828
use rayon::prelude::*;
2929
use rustyms::{
3030
annotation::{
31-
model::{parse_custom_models, FragmentationModel, MatchingParameters}, AnnotatableSpectrum, AnnotatedPeak, Score, Scores
32-
}, chemistry::MassMode, fragment::{DiagnosticPosition, Fragment}, glycan::MonoSaccharide, identification::{csv::write_csv, BasicCSVData, IdentifiedPeptidoformSource}, quantities::Tolerance, sequence::{
33-
parse_custom_modifications, AminoAcid, GnoComposition, SequencePosition, SimpleModificationInner
34-
}, spectrum::PeakSpectrum, system::MassOverCharge, *
31+
AnnotatableSpectrum, AnnotatedPeak, Score, Scores,
32+
model::{FragmentationModel, MatchingParameters, parse_custom_models},
33+
},
34+
chemistry::MassMode,
35+
fragment::{DiagnosticPosition, Fragment},
36+
glycan::MonoSaccharide,
37+
identification::{BasicCSVData, IdentifiedPeptidoformSource, csv::write_csv},
38+
quantities::Tolerance,
39+
sequence::{
40+
AminoAcid, GnoComposition, SequencePosition, SimpleModificationInner,
41+
parse_custom_modifications,
42+
},
43+
spectrum::PeakSpectrum,
44+
system::MassOverCharge,
45+
*,
3546
};
3647

3748
/// The command line interface arguments
@@ -224,16 +235,16 @@ fn main() {
224235
// USI spectra are mostly loaded as the binary array maps instead of peaks regardless of the signal continuity level
225236
spectrum.peaks = spectrum.arrays.as_ref().map(Into::into);
226237
}
227-
if let Some(threshold) = args.tic_noise_threshold && let Some(peaks) = spectrum.peaks.as_mut() {
238+
if let Some(threshold) = args.tic_noise_threshold && let Some(peaks) = spectrum.peaks.as_mut() {
228239
let threshold = peaks.total_ion_current() * threshold;
229-
peaks.peaks.retain(|p: &mzdata::mzpeaks::CentroidPeak| p.intensity > threshold);
240+
peaks.peaks.retain(|p: &mzdata::mzpeaks::CentroidPeak| p.intensity > threshold);
230241
}
231-
if let Some(threshold) = args.basepeak_noise_threshold && let Some(peaks) = spectrum.peaks.as_mut() {
242+
if let Some(threshold) = args.basepeak_noise_threshold && let Some(peaks) = spectrum.peaks.as_mut() {
232243
let threshold = peaks.base_peak().map_or(0.0, |v| v.intensity * threshold);
233-
peaks.peaks.retain(|p: &mzdata::mzpeaks::CentroidPeak| p.intensity > threshold);
244+
peaks.peaks.retain(|p: &mzdata::mzpeaks::CentroidPeak| p.intensity > threshold);
234245
}
235-
if let Some(threshold) = args.absolute_noise_threshold && let Some(peaks) = spectrum.peaks.as_mut() {
236-
peaks.peaks.retain(|p: &mzdata::mzpeaks::CentroidPeak| p.intensity > threshold);
246+
if let Some(threshold) = args.absolute_noise_threshold && let Some(peaks) = spectrum.peaks.as_mut() {
247+
peaks.peaks.retain(|p: &mzdata::mzpeaks::CentroidPeak| p.intensity > threshold);
237248
}
238249
let fragments = line.sequence.generate_theoretical_fragments(line.z, selected_model);
239250
let annotated = spectrum.annotate(

rustyms/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Match those fragments!
22

33
Handle mass spectrometry data in Rust. This crate is set up to handle very complex peptides with
4-
loads of ambiguity and complexity. It pivots around the [`CompoundPeptidoformIon`](crate::sequence::CompoundPeptidoformIon),
5-
[`PeptidoformIon`](crate::sequence::PeptidoformIon) and [`Peptidoform`](crate::sequence::Peptidoform) which encode the
4+
loads of ambiguity and complexity. It pivots around the [`CompoundPeptidoformIon`](crate::sequence::CompoundPeptidoformIon),
5+
[`PeptidoformIon`](crate::sequence::PeptidoformIon) and [`Peptidoform`](crate::sequence::Peptidoform) which encode the
66
[ProForma](https://github.com/HUPO-PSI/ProForma) specification. Additionally, this crate enables the
77
reading of [mgf](spectrum::mgf), doing [spectrum annotation](crate::annotation::AnnotatableSpectrum::annotate)
88
(BU/MD/TD), finding [isobaric sequences](crate::prelude::find_isobaric_sets), doing [alignments of peptides](crate::align::align)
@@ -41,7 +41,7 @@ let fragments = peptide.generate_theoretical_fragments(Charge::new::<e>(2), mode
4141
let parameters = MatchingParameters::default();
4242
// Annotate the raw data with the theoretical fragments
4343
let annotated = spectrum[0].annotate(peptide, &fragments, &parameters, MassMode::Monoisotopic);
44-
// Calculate a peak false discovery rate for this annotation
44+
// Calculate a peak false discovery rate for this annotation
4545
let (fdr, _) = annotated.fdr(&fragments, &parameters, MassMode::Monoisotopic);
4646
// This is the incorrect sequence for this spectrum so the peak FDR will indicate this
4747
# dbg!(&fdr, fdr.peaks_sigma(), fdr.peaks_fdr(), fdr.peaks_score());
@@ -60,9 +60,9 @@ let second_peptide = Peptidoform::pro_forma("LVQVET", None)?.into_simple_linear(
6060
// LVQVET B
6161
// ─ ╶╴
6262
let alignment = align::<4, &Peptidoform<SimpleLinear>, &Peptidoform<SimpleLinear>>(
63-
&first_peptide,
63+
&first_peptide,
6464
&second_peptide,
65-
AlignScoring::default(),
65+
AlignScoring::default(),
6666
AlignType::GLOBAL);
6767
# dbg!(&alignment);
6868
// Calculate some more statistics on this alignment

rustyms/data/real_examples.mgf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3353,7 +3353,7 @@ HIGHEST_OBSERVED_M/Z=25743.64907663
33533353
BASE_PEAK_INTENSITY=62787.5703125
33543354
[THERMO_TRAILER_EXTRA]MICRO_SCAN_COUNT=5
33553355
[THERMO_TRAILER_EXTRA]MONOISOTOPIC_M/Z=-1
3356-
[THERMO_TRAILER_EXTRA]HCD_ENERGY=
3356+
[THERMO_TRAILER_EXTRA]HCD_ENERGY=
33573357
SEQUENCE=EVQLVESGGGLVQPGGSLRLSC[C:Cystine#XL1]AASGFNIKDTYIHWVRQAPGKGLEWVARIYPTNGYTRYADSVKGRFTISADTSKNTAYLQMNSLRAEDTAVYYC[#XL1]SRWGGDGFYAMDYWGQGTLVTVSSASTKGPSVFPLAPSSKSTSGGTAALGC[C:Cystine#XL2]LVKDYFPEPVTVSWNSGALTSGVHTFPAVLQSSGLYSLSSVVTVPSSSLGTQTYIC[#XL2]NVNHKPSNTKVDKKVEPKSC[C:Cystine#XL3]DKT//DIQMTQSPSSLSASVGDRVTITC[C:Cystine#XL4]RASQDVNTAVAWYQQKPGKAPKLLIYSASFLYSGVPSRFSGSRSGTDFTLTISSLQPEDFATYYC[#XL4]QQHYTTPPTFGQGTKVEIKRTVAAPSVFIFPPSDEQLKSGTASVVC[C:Cystine#XL5]LLNNFYPREAKVQWKVDNALQSGNSQESVTEQDSKDSTYSLSSTLTLSKADYEKHKVYAC[#XL5]EVTHQGLSSPVTKSFNRGEC[#XL3]
33583358
2740.35347663 1054.28
33593359
2745.36057663 897.24

rustyms/src/align/mass_alignment.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -214,21 +214,22 @@ pub(super) fn determine_final_score<A, B>(
214214
path: &[Piece],
215215
scoring: AlignScoring<'_>,
216216
) -> Score {
217-
let maximal_score = isize::midpoint(seq_a.sequence()
218-
[start_a..start_a + path.iter().map(|p| p.step_a as usize).sum::<usize>()]
219-
.iter()
220-
.map(|a| {
221-
scoring.matrix[a.aminoacid.aminoacid() as usize][a.aminoacid.aminoacid() as usize]
222-
as isize
223-
})
224-
.sum::<isize>(), seq_b.sequence()
225-
[start_b..start_b + path.iter().map(|p| p.step_b as usize).sum::<usize>()]
217+
let maximal_score = isize::midpoint(
218+
seq_a.sequence()[start_a..start_a + path.iter().map(|p| p.step_a as usize).sum::<usize>()]
226219
.iter()
227220
.map(|a| {
228221
scoring.matrix[a.aminoacid.aminoacid() as usize][a.aminoacid.aminoacid() as usize]
229222
as isize
230223
})
231-
.sum::<isize>());
224+
.sum::<isize>(),
225+
seq_b.sequence()[start_b..start_b + path.iter().map(|p| p.step_b as usize).sum::<usize>()]
226+
.iter()
227+
.map(|a| {
228+
scoring.matrix[a.aminoacid.aminoacid() as usize][a.aminoacid.aminoacid() as usize]
229+
as isize
230+
})
231+
.sum::<isize>(),
232+
);
232233
let absolute_score = path.last().map(|p| p.score).unwrap_or_default();
233234
Score {
234235
absolute: absolute_score,

rustyms/src/chemistry/formula/unimod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ fn parse_unimod_composition_brick(
3838
Ok(Brick::Formula(ms.formula_inner(SequencePosition::default(),0)))
3939
} else {
4040
Err(BoxedError::new(BasicKind::Error,
41-
"Invalid Unimod chemical formula",
41+
"Invalid Unimod chemical formula",
4242
"Unknown Unimod composition brick, use an element or one of the unimod shorthands. Eg: 'H(13) C(12) N O(3)'.",
4343
Context::line_range(None, text, range)))
4444
}, |el| Ok(Brick::Element(el)))

rustyms/src/fragment/mzpaf/read.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,8 @@ fn parse_ion<'a>(
477477
interpretation.and_then(|i| {
478478
i.into_semi_ambiguous()
479479
.ok_or_else(|| BoxedError::new(BasicKind::Error,
480-
"Invalid mzPAF interpretation",
481-
"An mzPAF interpretation should be limited to `base-ProForma compliant` without any labile modifications",
480+
"Invalid mzPAF interpretation",
481+
"An mzPAF interpretation should be limited to `base-ProForma compliant` without any labile modifications",
482482
Context::line_range(None, line, range.start_index()..location)))
483483
.map(|i| (location + 1, Some(i)))
484484
})?

rustyms/src/fragment/neutral_loss.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ impl ParseJson for NeutralLoss {
3737
if let Some(n) = arr[0].as_u64() {
3838
Ok((
3939
u16::try_from(n).map_err(|_| BoxedError::new(BasicKind::Error,
40-
"Invalid NeutralLoss",
41-
format!("The {context} amount is too big, the number has to be below {}", u16::MAX),
40+
"Invalid NeutralLoss",
41+
format!("The {context} amount is too big, the number has to be below {}", u16::MAX),
4242
Context::show(n.to_string())))?,
4343
MolecularFormula::from_json_value(arr.pop().unwrap())?,
4444
))

0 commit comments

Comments
 (0)