1use std::fmt;
2use std::collections::BTreeMap;
3use std::fmt::{Formatter};
4use std::sync::Arc;
5use bincode::{Decode, Encode};
6use itertools;
7use itertools::izip;
8use ordered_float::OrderedFloat;
9use rand::Rng;
10use serde::{Deserialize, Serialize};
11use crate::timstof::spectrum::TimsSpectrum;
12use crate::data::spectrum::{MsType, MzSpectrum, IndexedMzSpectrum, Vectorized, ToResolution};
13use crate::simulation::annotation::{PeakAnnotation, TimsFrameAnnotated};
14use crate::timstof::vec_utils::{filter_with_mask, find_sparse_local_maxima_mask};
15
16#[derive(Clone)]
17pub struct RawTimsFrame {
18 pub frame_id: i32,
19 pub retention_time: f64,
20 pub ms_type: MsType,
21 pub scan: Vec<u32>,
22 pub tof: Vec<u32>,
23 pub intensity: Vec<f64>,
24}
25
26impl RawTimsFrame {
27 pub fn smooth(mut self, window: u32) -> Self {
28 let mut smooth_intensities: Vec<f64> = self.intensity.clone();
29 for (current_index, current_tof) in self.tof.iter().enumerate()
30 {
31 let current_intensity: f64 = self.intensity[current_index];
32 for (_next_index, next_tof) in
33 self.tof[current_index + 1..].iter().enumerate()
34 {
35 let next_index: usize = _next_index + current_index + 1;
36 let next_intensity: f64 = self.intensity[next_index];
37 if (next_tof - current_tof) <= window {
38 smooth_intensities[current_index] += next_intensity;
39 smooth_intensities[next_index] += current_intensity;
40 } else {
41 break;
42 }
43 }
44 }
45 self.intensity = smooth_intensities;
46
47 self
48 }
49 pub fn centroid(mut self, window: u32) -> Self {
50 let local_maxima: Vec<bool> = find_sparse_local_maxima_mask(
51 &self.tof,
52 &self.intensity,
53 window,
54 );
55 self.tof = filter_with_mask(&self.tof, &local_maxima);
56 self.intensity = filter_with_mask(&self.intensity, &local_maxima);
57 self.scan = filter_with_mask(&self.scan, &local_maxima);
58 self
59 }
60}
61
62#[derive(Clone, Debug, Serialize, Deserialize)]
63pub struct ImsFrame {
64 pub retention_time: f64,
65 pub mobility: Arc<Vec<f64>>,
66 pub mz: Arc<Vec<f64>>,
67 pub intensity: Arc<Vec<f64>>,
68}
69
70impl Encode for ImsFrame {
71 fn encode<E: bincode::enc::Encoder>(&self, encoder: &mut E) -> Result<(), bincode::error::EncodeError> {
72 bincode::Encode::encode(&self.retention_time, encoder)?;
73 bincode::Encode::encode(&*self.mobility, encoder)?;
74 bincode::Encode::encode(&*self.mz, encoder)?;
75 bincode::Encode::encode(&*self.intensity, encoder)?;
76 Ok(())
77 }
78}
79
80impl<Context> Decode<Context> for ImsFrame {
81 fn decode<D: bincode::de::Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, bincode::error::DecodeError> {
82 let retention_time: f64 = bincode::Decode::decode(decoder)?;
83 let mobility: Vec<f64> = bincode::Decode::decode(decoder)?;
84 let mz: Vec<f64> = bincode::Decode::decode(decoder)?;
85 let intensity: Vec<f64> = bincode::Decode::decode(decoder)?;
86 Ok(ImsFrame::new(retention_time, mobility, mz, intensity))
87 }
88}
89
90impl Default for ImsFrame {
91 fn default() -> Self {
92 ImsFrame {
93 retention_time: 0.0,
94 mobility: Arc::new(Vec::new()),
95 mz: Arc::new(Vec::new()),
96 intensity: Arc::new(Vec::new()),
97 }
98 }
99}
100
101impl ImsFrame {
102 pub fn new(retention_time: f64, mobility: Vec<f64>, mz: Vec<f64>, intensity: Vec<f64>) -> Self {
119 ImsFrame {
120 retention_time,
121 mobility: Arc::new(mobility),
122 mz: Arc::new(mz),
123 intensity: Arc::new(intensity),
124 }
125 }
126}
127
128impl fmt::Display for ImsFrame {
129 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
130 write!(f, "ImsFrame(rt: {}, data points: {})", self.retention_time, self.mobility.len())
131 }
132}
133
134#[derive(Clone)]
135pub struct ImsFrameVectorized {
136 pub retention_time: f64,
137 pub mobility: Vec<f64>,
138 pub indices: Vec<i32>,
139 pub values: Vec<f64>,
140 pub resolution: i32,
141}
142
143#[derive(Clone, Debug, Serialize, Deserialize)]
144pub struct TimsFrame {
145 pub frame_id: i32,
146 pub ms_type: MsType,
147 pub scan: Vec<i32>,
148 pub tof: Vec<i32>,
149 pub ims_frame: ImsFrame,
150}
151
152impl Encode for TimsFrame {
153 fn encode<E: bincode::enc::Encoder>(&self, encoder: &mut E) -> Result<(), bincode::error::EncodeError> {
154 bincode::Encode::encode(&self.frame_id, encoder)?;
155 bincode::Encode::encode(&self.ms_type, encoder)?;
156 bincode::Encode::encode(&self.scan, encoder)?;
157 bincode::Encode::encode(&self.tof, encoder)?;
158 bincode::Encode::encode(&self.ims_frame, encoder)?;
159 Ok(())
160 }
161}
162
163impl<Context> Decode<Context> for TimsFrame {
164 fn decode<D: bincode::de::Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, bincode::error::DecodeError> {
165 let frame_id: i32 = bincode::Decode::decode(decoder)?;
166 let ms_type: MsType = bincode::Decode::decode(decoder)?;
167 let scan: Vec<i32> = bincode::Decode::decode(decoder)?;
168 let tof: Vec<i32> = bincode::Decode::decode(decoder)?;
169 let ims_frame: ImsFrame = bincode::Decode::decode(decoder)?;
170 Ok(TimsFrame { frame_id, ms_type, scan, tof, ims_frame })
171 }
172}
173
174impl Default for TimsFrame {
175 fn default() -> Self {
176 TimsFrame {
177 frame_id: 0,
178 ms_type: MsType::Unknown,
179 scan: Vec::new(),
180 tof: Vec::new(),
181 ims_frame: ImsFrame::default(),
182 }
183 }
184}
185
186impl TimsFrame {
187 pub fn new(frame_id: i32, ms_type: MsType, retention_time: f64, scan: Vec<i32>, mobility: Vec<f64>, tof: Vec<i32>, mz: Vec<f64>, intensity: Vec<f64>) -> Self {
210 TimsFrame { frame_id, ms_type, scan, tof, ims_frame: ImsFrame::new(retention_time, mobility, mz, intensity) }
211 }
212
213 pub fn get_ims_frame(&self) -> ImsFrame { self.ims_frame.clone() }
226
227 pub fn to_tims_spectra(&self) -> Vec<TimsSpectrum> {
240 let mut spectra = BTreeMap::<i32, (f64, Vec<i32>, Vec<f64>, Vec<f64>)>::new();
242
243 for (scan, mobility, tof, mz, intensity) in itertools::multizip((
245 self.scan.iter(),
246 self.ims_frame.mobility.iter(),
247 self.tof.iter(),
248 self.ims_frame.mz.iter(),
249 self.ims_frame.intensity.iter())) {
250 let entry = spectra.entry(*scan).or_insert_with(|| (*mobility, Vec::new(), Vec::new(), Vec::new()));
251 entry.1.push(*tof);
252 entry.2.push(*mz);
253 entry.3.push(*intensity);
254 }
255
256 let mut tims_spectra: Vec<TimsSpectrum> = Vec::new();
258
259 for (scan, (mobility, tof, mz, intensity)) in spectra {
260 let spectrum = IndexedMzSpectrum::new(tof, mz, intensity);
261 tims_spectra.push(TimsSpectrum::new(self.frame_id, scan, self.ims_frame.retention_time, mobility, self.ms_type.clone(), spectrum));
262 }
263
264 tims_spectra
265 }
266
267 pub fn filter_ranged(&self, mz_min: f64, mz_max: f64, scan_min: i32, scan_max: i32, inv_mob_min: f64, inv_mob_max: f64, intensity_min: f64, intensity_max: f64, tof_min: i32, tof_max: i32) -> TimsFrame {
290
291 let mut scan_vec = Vec::new();
292 let mut mobility_vec = Vec::new();
293 let mut tof_vec = Vec::new();
294 let mut mz_vec = Vec::new();
295 let mut intensity_vec = Vec::new();
296
297 for (mz, intensity, scan, mobility, tof) in itertools::multizip((self.ims_frame.mz.iter(), self.ims_frame.intensity.iter(), self.scan.iter(), self.ims_frame.mobility.iter(), self.tof.iter())) {
298 if mz >= &mz_min && mz <= &mz_max && scan >= &scan_min && scan <= &scan_max && mobility >= &inv_mob_min && mobility <= &inv_mob_max && intensity >= &intensity_min && intensity <= &intensity_max && tof >= &tof_min && tof <= &tof_max {
299 scan_vec.push(*scan);
300 mobility_vec.push(*mobility);
301 tof_vec.push(*tof);
302 mz_vec.push(*mz);
303 intensity_vec.push(*intensity);
304 }
305 }
306
307 TimsFrame::new(self.frame_id, self.ms_type.clone(), self.ims_frame.retention_time, scan_vec, mobility_vec, tof_vec, mz_vec, intensity_vec)
308 }
309
310 pub fn top_n(&self, n: usize) -> TimsFrame {
311 let mut indices: Vec<usize> = (0..self.ims_frame.intensity.len()).collect();
312 indices.sort_by(|a, b| self.ims_frame.intensity[*b].partial_cmp(&self.ims_frame.intensity[*a]).unwrap());
313 indices.truncate(n);
314
315 let scan = indices.iter().map(|&i| self.scan[i]).collect();
316 let mobility = indices.iter().map(|&i| self.ims_frame.mobility[i]).collect();
317 let tof = indices.iter().map(|&i| self.tof[i]).collect();
318 let mz = indices.iter().map(|&i| self.ims_frame.mz[i]).collect();
319 let intensity = indices.iter().map(|&i| self.ims_frame.intensity[i]).collect();
320
321 TimsFrame::new(self.frame_id, self.ms_type.clone(), self.ims_frame.retention_time, scan, mobility, tof, mz, intensity)
322 }
323
324 pub fn to_windows_indexed(&self, window_length: f64, overlapping: bool, min_peaks: usize, min_intensity: f64) -> (Vec<i32>, Vec<i32>, Vec<TimsSpectrum>) {
325 let spectra = self.to_tims_spectra();
327
328 let windows: Vec<_> = spectra.iter().map(|spectrum|
329 spectrum.to_windows(window_length, overlapping, min_peaks, min_intensity))
330 .collect();
331
332 let mut scan_indices = Vec::new();
333
334 for tree in windows.iter() {
335 for (_, window) in tree {
336 scan_indices.push(window.scan)
337 }
338 }
339
340 let mut spectra = Vec::new();
341 let mut window_indices = Vec::new();
342
343 for window in windows {
344 for (i, spectrum) in window {
345 spectra.push(spectrum);
346 window_indices.push(i);
347 }
348 }
349
350 (scan_indices, window_indices, spectra)
351 }
352
353 pub fn to_windows(&self, window_length: f64, overlapping: bool, min_peaks: usize, min_intensity: f64) -> Vec<TimsSpectrum> {
354 let (_, _, widows) = self.to_windows_indexed(window_length, overlapping, min_peaks, min_intensity);
355 widows
356 }
357
358 pub fn from_windows(windows: Vec<TimsSpectrum>) -> TimsFrame {
359
360 let first_window = windows.first().unwrap();
361
362 let mut scan = Vec::new();
363 let mut tof = Vec::new();
364 let mut mzs = Vec::new();
365 let mut intensity = Vec::new();
366 let mut mobility = Vec::new();
367
368 for window in windows.iter() {
369 for (i, mz) in window.spectrum.mz_spectrum.mz.iter().enumerate() {
370 scan.push(window.scan);
371 mobility.push(window.mobility);
372 tof.push(window.spectrum.index[i]);
373 mzs.push(*mz);
374 intensity.push(window.spectrum.mz_spectrum.intensity[i]);
375 }
376 }
377
378 TimsFrame::new(first_window.frame_id, first_window.ms_type.clone(), first_window.retention_time, scan, mobility, tof, mzs, intensity)
379 }
380
381 pub fn from_tims_spectra(spectra: Vec<TimsSpectrum>) -> TimsFrame {
382
383 let quantize = |mz: f64| -> i64 {
385 (mz * 1_000_000.0).round() as i64
386 };
387
388 let first_spec = spectra.first();
390 let frame_id = match first_spec {
391 Some(first_spec) => first_spec.frame_id,
392 _ => 1
393 };
394
395 let ms_type = match first_spec {
396 Some(first_spec) => first_spec.ms_type.clone(),
397 _ => MsType::Unknown,
398 };
399
400 let retention_time = match first_spec {
401 Some(first_spec) => first_spec.retention_time,
402 _ => 0.0
403 };
404
405 let mut frame_map: BTreeMap<i32, (f64, BTreeMap<i64, (i32, f64)>)> = BTreeMap::new();
406 let mut capacity_count = 0;
407
408 for spectrum in &spectra {
410 let inv_mobility = spectrum.mobility;
411 let entry = frame_map.entry(spectrum.scan).or_insert_with(|| (inv_mobility, BTreeMap::new()));
412 for (i, mz) in spectrum.spectrum.mz_spectrum.mz.iter().enumerate() {
413 let tof = spectrum.spectrum.index[i];
414 let intensity = spectrum.spectrum.mz_spectrum.intensity[i];
415 entry.1.entry(quantize(*mz)).and_modify(|e| *e = (tof, e.1 + intensity)).or_insert((tof, intensity));
416 capacity_count += 1;
417 }
418 }
419
420 let mut scan = Vec::with_capacity(capacity_count);
422 let mut mobility = Vec::with_capacity(capacity_count);
423 let mut tof = Vec::with_capacity(capacity_count);
424 let mut mzs = Vec::with_capacity(capacity_count);
425 let mut intensity = Vec::with_capacity(capacity_count);
426
427 for (scan_val, (mobility_val, mz_map)) in frame_map {
428 for (mz_val, (tof_val, intensity_val)) in mz_map {
429 scan.push(scan_val);
430 mobility.push(mobility_val);
431 tof.push(tof_val);
432 mzs.push(mz_val as f64 / 1_000_000.0);
433 intensity.push(intensity_val);
434 }
435 }
436
437 TimsFrame::new(frame_id, ms_type, retention_time, scan, mobility, tof, mzs, intensity)
438 }
439
440 pub fn from_tims_spectra_filtered(
444 spectra: Vec<TimsSpectrum>,
445 mz_min: f64,
446 mz_max: f64,
447 scan_min: i32,
448 scan_max: i32,
449 inv_mob_min: f64,
450 inv_mob_max: f64,
451 intensity_min: f64,
452 intensity_max: f64,
453 ) -> TimsFrame {
454 let quantize = |mz: f64| -> i64 {
455 (mz * 1_000_000.0).round() as i64
456 };
457
458 let first_spec = spectra.first();
459 let frame_id = first_spec.map(|s| s.frame_id).unwrap_or(1);
460 let ms_type = first_spec.map(|s| s.ms_type.clone()).unwrap_or(MsType::Unknown);
461 let retention_time = first_spec.map(|s| s.retention_time).unwrap_or(0.0);
462
463 let mut frame_map: BTreeMap<i32, (f64, BTreeMap<i64, (i32, f64)>)> = BTreeMap::new();
464 let mut capacity_count = 0;
465
466 for spectrum in &spectra {
468 if spectrum.scan < scan_min || spectrum.scan > scan_max {
470 continue;
471 }
472 if spectrum.mobility < inv_mob_min || spectrum.mobility > inv_mob_max {
474 continue;
475 }
476
477 let inv_mobility = spectrum.mobility;
478 let entry = frame_map.entry(spectrum.scan).or_insert_with(|| (inv_mobility, BTreeMap::new()));
479
480 for (i, mz) in spectrum.spectrum.mz_spectrum.mz.iter().enumerate() {
481 if *mz < mz_min || *mz > mz_max {
483 continue;
484 }
485 let intensity = spectrum.spectrum.mz_spectrum.intensity[i];
486 if intensity < intensity_min || intensity > intensity_max {
488 continue;
489 }
490
491 let tof = spectrum.spectrum.index[i];
492 entry.1.entry(quantize(*mz)).and_modify(|e| *e = (tof, e.1 + intensity)).or_insert((tof, intensity));
493 capacity_count += 1;
494 }
495 }
496
497 let mut scan = Vec::with_capacity(capacity_count);
499 let mut mobility = Vec::with_capacity(capacity_count);
500 let mut tof = Vec::with_capacity(capacity_count);
501 let mut mzs = Vec::with_capacity(capacity_count);
502 let mut intensity = Vec::with_capacity(capacity_count);
503
504 for (scan_val, (mobility_val, mz_map)) in frame_map {
505 for (mz_val, (tof_val, intensity_val)) in mz_map {
506 if intensity_val < intensity_min || intensity_val > intensity_max {
508 continue;
509 }
510 scan.push(scan_val);
511 mobility.push(mobility_val);
512 tof.push(tof_val);
513 mzs.push(mz_val as f64 / 1_000_000.0);
514 intensity.push(intensity_val);
515 }
516 }
517
518 TimsFrame::new(frame_id, ms_type, retention_time, scan, mobility, tof, mzs, intensity)
519 }
520
521 pub fn to_dense_windows(&self, window_length: f64, overlapping: bool, min_peaks: usize, min_intensity: f64, resolution: i32) -> (Vec<f64>, Vec<f64>, Vec<f64>, Vec<i32>, Vec<i32>, usize, usize) {
522 let factor = (10.0f64).powi(resolution);
523 let num_colums = ((window_length * factor).round() + 1.0) as usize;
524
525 let (scans, window_indices, spectra) = self.to_windows_indexed(window_length, overlapping, min_peaks, min_intensity);
526 let mut mobilities = Vec::with_capacity(spectra.len());
527 let mut mzs = Vec::with_capacity(spectra.len());
528
529 for spectrum in &spectra {
531 mobilities.push(spectrum.mobility);
532 mzs.push(spectrum.spectrum.mz_spectrum.mz.first().unwrap().clone());
533 }
534
535 let vectorized_spectra = spectra.iter().map(|spectrum| spectrum.vectorized(resolution)).collect::<Vec<_>>();
536
537 let mut flat_matrix: Vec<f64> = vec![0.0; spectra.len() * num_colums];
538
539 for (row_index, (window_index, spectrum)) in itertools::multizip((&window_indices, vectorized_spectra)).enumerate() {
540
541 let vectorized_window_index = match *window_index >= 0 {
542 true => (*window_index as f64 * window_length * factor).round() as i32,
543 false => (((-1.0 * (*window_index as f64)) * window_length - (0.5 * window_length)) * factor).round() as i32,
544 };
545
546 for (i, index) in spectrum.vector.mz_vector.indices.iter().enumerate() {
547 let zero_based_index = (index - vectorized_window_index) as usize;
548 let current_index = row_index * num_colums + zero_based_index;
549 flat_matrix[current_index] = spectrum.vector.mz_vector.values[i];
550 }
551
552 }
553 (flat_matrix, mobilities, mzs, scans, window_indices, spectra.len(), num_colums)
554 }
555
556
557 pub fn to_indexed_mz_spectrum(&self) -> IndexedMzSpectrum {
558 let mut grouped_data: BTreeMap<i32, Vec<(f64, f64)>> = BTreeMap::new();
559
560 for (&tof, (&mz, &intensity)) in self.tof.iter().zip(self.ims_frame.mz.iter().zip(self.ims_frame.intensity.iter())) {
562 grouped_data.entry(tof).or_insert_with(Vec::new).push((mz, intensity));
563 }
564
565 let mut index = Vec::new();
566 let mut mz = Vec::new();
567 let mut intensity = Vec::new();
568
569 for (&tof_val, values) in &grouped_data {
570 let sum_intensity: f64 = values.iter().map(|&(_, i)| i).sum();
571 let avg_mz: f64 = values.iter().map(|&(m, _)| m).sum::<f64>() / values.len() as f64;
572
573 index.push(tof_val);
574 mz.push(avg_mz);
575 intensity.push(sum_intensity);
576 }
577
578 IndexedMzSpectrum {
579 index,
580 mz_spectrum: MzSpectrum::new(mz, intensity),
581 }
582 }
583
584 pub fn generate_random_sample(&self, take_probability: f64) -> TimsFrame {
585 assert!(take_probability >= 0.0 && take_probability <= 1.0);
586
587 let mut rng = rand::thread_rng();
588 let mut scan = Vec::new();
589 let mut mobility = Vec::new();
590 let mut tof = Vec::new();
591 let mut mz = Vec::new();
592 let mut intensity = Vec::new();
593
594 for (s, m, t, mz_val, i) in itertools::multizip((self.scan.iter(), self.ims_frame.mobility.iter(), self.tof.iter(), self.ims_frame.mz.iter(), self.ims_frame.intensity.iter())) {
595 if rng.gen::<f64>() <= take_probability {
596 scan.push(*s);
597 mobility.push(*m);
598 tof.push(*t);
599 mz.push(*mz_val);
600 intensity.push(*i);
601 }
602 }
603
604 TimsFrame::new(self.frame_id, self.ms_type.clone(), self.ims_frame.retention_time, scan, mobility, tof, mz, intensity)
605 }
606
607 pub fn to_noise_annotated_tims_frame(&self) -> TimsFrameAnnotated {
608 let mut annotations = Vec::with_capacity(self.ims_frame.mz.len());
609 let tof_values = self.tof.clone();
610 let mz_values = self.ims_frame.mz.clone();
611 let scan_values = self.scan.clone();
612 let inv_mobility_values = self.ims_frame.mobility.clone();
613 let intensity_values = self.ims_frame.intensity.clone();
614
615 for intensity in intensity_values.iter() {
616 annotations.push(PeakAnnotation::new_random_noise(*intensity));
617 }
618
619 TimsFrameAnnotated::new(
620 self.frame_id,
621 self.ims_frame.retention_time,
622 self.ms_type.clone(),
623 tof_values.iter().map(|&x| x as u32).collect(),
624 (*mz_values).clone(),
625 scan_values.iter().map(|&x| x as u32).collect(),
626 (*inv_mobility_values).clone(),
627 (*intensity_values).clone(),
628 annotations,
629 )
630 }
631
632 pub fn get_inverse_mobility_along_scan_marginal(&self) -> f64 {
633 let mut marginal_map: BTreeMap<i32, (f64, f64)> = BTreeMap::new();
634 for (scan, inv_mob, intensity) in izip!(self.scan.iter(), self.ims_frame.mobility.iter(), self.ims_frame.intensity.iter()) {
636 let key = *scan;
638 let entry = marginal_map.entry(key).or_insert((0.0, 0.0));
640 entry.0 += *intensity;
642 entry.1 = *inv_mob;
644 }
645
646 let (_, max_inv_mob) = marginal_map.iter().max_by(|a, b| a.1.0.partial_cmp(&b.1.0).unwrap_or(std::cmp::Ordering::Equal)).unwrap_or((&0, &(0.0, 0.0))).1;
648
649 *max_inv_mob
650 }
651
652 pub fn get_mobility_mean_and_variance(&self) -> (f64, f64) {
654 let mut mobility_map: BTreeMap<OrderedFloat<f64>, f64> = BTreeMap::new();
655
656 for (inv_mob, intensity) in izip!(self.ims_frame.mobility.iter(), self.ims_frame.intensity.iter()) {
658 let entry = mobility_map.entry(OrderedFloat(*inv_mob)).or_insert(0.0);
659 *entry += *intensity;
660 }
661
662 let mut total_weight = 0.0;
664 let mut weighted_sum = 0.0;
665 for (&inv_mob, &intensity) in &mobility_map {
666 total_weight += intensity;
667 weighted_sum += inv_mob.into_inner() * intensity;
668 }
669 let mean = weighted_sum / total_weight;
670
671 let mut weighted_squared_diff_sum = 0.0;
673 for (&inv_mob, &intensity) in &mobility_map {
674 let diff = inv_mob.into_inner() - mean;
675 weighted_squared_diff_sum += intensity * diff * diff;
676 }
677 let variance = weighted_squared_diff_sum / total_weight;
678
679 (mean, variance)
680 }
681
682 pub fn get_tims_spectrum(&self, scan_number: i32) -> Option<TimsSpectrum> {
683 let mut tof = Vec::new();
684 let mut mz = Vec::new();
685 let mut intensity = Vec::new();
686
687 for (s, t, m, i) in itertools::multizip((self.scan.iter(), self.tof.iter(), self.ims_frame.mz.iter(), self.ims_frame.intensity.iter())) {
688 if *s == scan_number {
689 tof.push(*t);
690 mz.push(*m);
691 intensity.push(*i);
692 }
693 }
694
695 if mz.is_empty() {
696 return None;
697 }
698
699 let mobility = self.ims_frame.mobility.iter()
700 .zip(&self.scan)
701 .find(|(_, s)| **s == scan_number)
702 .map(|(m, _)| *m)?;
703
704 Some(TimsSpectrum {
705 frame_id: self.frame_id,
706 scan: scan_number,
707 retention_time: self.ims_frame.retention_time,
708 mobility,
709 ms_type: self.ms_type.clone(),
710 spectrum: IndexedMzSpectrum::new(tof, mz, intensity),
711 })
712 }
713
714 pub fn fold_along_scan_axis(self, fold_width: usize) -> TimsFrame {
715 let spectra = self.to_tims_spectra();
717
718 let mut merged_spectra: BTreeMap<i32, TimsSpectrum> = BTreeMap::new();
721 for spectrum in spectra {
722 let key = spectrum.scan / fold_width as i32;
723
724 if let Some(existing_spectrum) = merged_spectra.get_mut(&key) {
726
727 let merged_spectrum = existing_spectrum.clone() + spectrum;
728 *existing_spectrum = merged_spectrum;
730
731 } else {
732 merged_spectra.insert(key, spectrum);
734 }
735 }
736
737 TimsFrame::from_tims_spectra(merged_spectra.into_values().collect())
739 }
740}
741
742struct AggregateData {
743 intensity_sum: f64,
744 ion_mobility_sum: f64,
745 tof_sum: i64,
746 count: i32,
747}
748
749impl std::ops::Add for TimsFrame {
750 type Output = Self;
751 fn add(self, other: Self) -> TimsFrame {
752 let mut combined_map: BTreeMap<(i32, i64), AggregateData> = BTreeMap::new();
753
754 let quantize = |mz: f64| -> i64 {
755 (mz * 1_000_000.0).round() as i64
756 };
757
758 let add_to_map = |map: &mut BTreeMap<(i32, i64), AggregateData>, mz, ion_mobility, tof, scan, intensity| {
759 let key = (scan, quantize(mz));
760 let entry = map.entry(key).or_insert(AggregateData { intensity_sum: 0.0, ion_mobility_sum: 0.0, tof_sum: 0, count: 0 });
761 entry.intensity_sum += intensity;
762 entry.ion_mobility_sum += ion_mobility;
763 entry.tof_sum += tof as i64;
764 entry.count += 1;
765 };
766
767 for (mz, tof, ion_mobility, scan, intensity) in izip!(self.ims_frame.mz.iter(), self.tof.iter(), self.ims_frame.mobility.iter(), self.scan.iter(), self.ims_frame.intensity.iter()) {
768 add_to_map(&mut combined_map, *mz, *ion_mobility, *tof, *scan, *intensity);
769 }
770
771 for (mz, tof, ion_mobility, scan, intensity) in izip!(other.ims_frame.mz.iter(), other.tof.iter(), other.ims_frame.mobility.iter(), other.scan.iter(), other.ims_frame.intensity.iter()) {
772 add_to_map(&mut combined_map, *mz, *ion_mobility, *tof, *scan, *intensity);
773 }
774
775 let mut mz_combined = Vec::new();
776 let mut tof_combined = Vec::new();
777 let mut ion_mobility_combined = Vec::new();
778 let mut scan_combined = Vec::new();
779 let mut intensity_combined = Vec::new();
780
781 for ((scan, quantized_mz), data) in combined_map {
782 mz_combined.push(quantized_mz as f64 / 1_000_000.0);
783 tof_combined.push(data.tof_sum / data.count as i64);
784 ion_mobility_combined.push(data.ion_mobility_sum / data.count as f64);
785 scan_combined.push(scan);
786 intensity_combined.push(data.intensity_sum);
787 }
788
789 let frame = TimsFrame {
790 frame_id: self.frame_id,
791 ms_type: if self.ms_type == other.ms_type { self.ms_type.clone() } else { MsType::Unknown },
792 scan: scan_combined,
793 tof: tof_combined.iter().map(|&x| x as i32).collect(),
794 ims_frame: ImsFrame::new(
795 self.ims_frame.retention_time,
796 ion_mobility_combined,
797 mz_combined,
798 intensity_combined,
799 ),
800 };
801
802 return frame;
803 }
804}
805
806impl fmt::Display for TimsFrame {
807 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
808
809 let (mz, i) = self.ims_frame.mz.iter()
810 .zip(self.ims_frame.intensity.iter())
811 .max_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal))
812 .unwrap();
813
814 write!(f, "TimsFrame(id: {}, type: {}, rt: {}, data points: {}, max by intensity: (mz: {}, intensity: {}))",
815 self.frame_id, self.ms_type, self.ims_frame.retention_time, self.scan.len(), format!("{:.3}", mz), i)
816 }
817}
818
819impl Vectorized<TimsFrameVectorized> for TimsFrame {
820 fn vectorized(&self, resolution: i32) -> TimsFrameVectorized {
821 let binned_frame = self.to_resolution(resolution);
822 let indices: Vec<i32> = binned_frame.ims_frame.mz.iter().map(|&mz| (mz * 10f64.powi(resolution)).round() as i32).collect();
824 return TimsFrameVectorized {
826 frame_id: self.frame_id,
827 ms_type: self.ms_type.clone(),
828 scan: binned_frame.scan,
829 tof: binned_frame.tof,
830 ims_frame: ImsFrameVectorized {
831 retention_time: binned_frame.ims_frame.retention_time,
832 mobility: (*binned_frame.ims_frame.mobility).clone(),
833 indices,
834 values: (*binned_frame.ims_frame.intensity).clone(),
835 resolution,
836 },
837 };
838 }
839}
840
841impl ToResolution for TimsFrame {
861 fn to_resolution(&self, resolution: i32) -> TimsFrame {
862 let factor = (10.0f64).powi(resolution);
863
864 let mut bin_map: BTreeMap<(i32, i32), (f64, f64, f64, i32)> = BTreeMap::new();
867
868 for i in 0..self.ims_frame.mz.len() {
869 let rounded_mz = (self.ims_frame.mz[i] * factor).round() as i32;
870 let scan_val = self.scan[i];
871 let intensity_val = self.ims_frame.intensity[i] as f64;
872 let tof_val = self.tof[i] as f64;
873 let mobility_val = self.ims_frame.mobility[i] as f64;
874
875 let entry = bin_map.entry((scan_val, rounded_mz)).or_insert((0.0, 0.0, 0.0, 0));
876 entry.0 += intensity_val;
877 entry.1 += tof_val;
878 entry.2 += mobility_val;
879 entry.3 += 1;
880 }
881
882 let mut new_mz = Vec::with_capacity(bin_map.len());
883 let mut new_scan = Vec::with_capacity(bin_map.len());
884 let mut new_intensity = Vec::with_capacity(bin_map.len());
885 let mut new_tof = Vec::with_capacity(bin_map.len());
886 let mut new_mobility = Vec::with_capacity(bin_map.len());
887
888 for ((scan, mz_bin), (intensity_sum, tof_sum, mobility_sum, count)) in bin_map {
889 new_mz.push(mz_bin as f64 / factor);
890 new_scan.push(scan);
891 new_intensity.push(intensity_sum);
892 new_tof.push((tof_sum / count as f64) as i32);
893 new_mobility.push(mobility_sum / count as f64);
894 }
895
896 TimsFrame {
897 frame_id: self.frame_id,
898 ms_type: self.ms_type.clone(),
899 scan: new_scan,
900 tof: new_tof,
901 ims_frame: ImsFrame::new(
902 self.ims_frame.retention_time,
903 new_mobility,
904 new_mz,
905 new_intensity,
906 ),
907 }
908 }
909}
910
911#[derive(Clone)]
912pub struct TimsFrameVectorized {
913 pub frame_id: i32,
914 pub ms_type: MsType,
915 pub scan: Vec<i32>,
916 pub tof: Vec<i32>,
917 pub ims_frame: ImsFrameVectorized,
918}
919
920impl TimsFrameVectorized {
921 pub fn filter_ranged(&self, mz_min: f64, mz_max: f64, scan_min: i32, scan_max: i32, inv_mob_min: f64, inv_mob_max: f64, intensity_min: f64, intensity_max: f64) -> TimsFrameVectorized {
922 let mut scan_vec = Vec::new();
923 let mut mobility_vec = Vec::new();
924 let mut tof_vec = Vec::new();
925 let mut mz_vec = Vec::new();
926 let mut intensity_vec = Vec::new();
927 let mut indices_vec = Vec::new();
928
929 for (mz, intensity, scan, mobility, tof, index) in itertools::multizip((&self.ims_frame.values, &self.ims_frame.values, &self.scan, &self.ims_frame.mobility, &self.tof, &self.ims_frame.indices)) {
930 if mz >= &mz_min && mz <= &mz_max && scan >= &scan_min && scan <= &scan_max && mobility >= &inv_mob_min && mobility <= &inv_mob_max && intensity >= &intensity_min && intensity <= &intensity_max {
931 scan_vec.push(*scan);
932 mobility_vec.push(*mobility);
933 tof_vec.push(*tof);
934 mz_vec.push(*mz);
935 intensity_vec.push(*intensity);
936 indices_vec.push(*index);
937 }
938 }
939
940 TimsFrameVectorized {
941 frame_id: self.frame_id,
942 ms_type: self.ms_type.clone(),
943 scan: scan_vec,
944 tof: tof_vec,
945 ims_frame: ImsFrameVectorized {
946 retention_time: self.ims_frame.retention_time,
947 mobility: mobility_vec,
948 indices: indices_vec,
949 values: mz_vec,
950 resolution: self.ims_frame.resolution,
951 },
952 }
953 }
954}
955
956impl fmt::Display for TimsFrameVectorized {
957 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
958
959 let (mz, i) = self.ims_frame.values.iter()
960 .zip(&self.ims_frame.values)
961 .max_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal))
962 .unwrap();
963
964 write!(f, "TimsFrame(id: {}, type: {}, rt: {}, data points: {}, max by intensity: (mz: {}, intensity: {}))",
965 self.frame_id, self.ms_type, self.ims_frame.retention_time, self.scan.len(), format!("{:.3}", mz), i)
966 }
967}