Skip to content

Commit

Permalink
feature: add dispatching implementation to PeakDataIter and `RefPea…
Browse files Browse the repository at this point in the history
…kDataIter`
  • Loading branch information
mobiusklein committed Aug 12, 2024
1 parent 4ccd641 commit e7b290c
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 7 deletions.
86 changes: 82 additions & 4 deletions src/spectrum/peaks.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::iter::FusedIterator;

use mzpeaks::prelude::*;
Expand All @@ -7,6 +8,7 @@ use mzpeaks::{
IndexType, MZPeakSetType, MassPeakSetType,
};

use super::bindata::ArrayRetrievalError;
use super::BinaryArrayMap;
use crate::utils::mass_charge_ratio;

Expand Down Expand Up @@ -301,8 +303,13 @@ impl<C: CentroidLike, D: DeconvolutedCentroidLike> PeakDataLevel<C, D> {
///
/// **NOTE**: Values are produced in the order they are stored, so the data are not guaranteed
/// to be ordered by m/z.
pub fn iter(&self) -> PeakDataIter<'_, C, D> {
PeakDataIter::new(self)
pub fn iter(&self) -> PeakDataIterDispatch<'_, C, D> {
match self {
PeakDataLevel::Missing => PeakDataIterDispatch::PeakData(PeakDataIter::new(self)),
PeakDataLevel::RawData(a) => PeakDataIterDispatch::RawData(RawIter::from_binary_array_map(a).unwrap()),
PeakDataLevel::Centroid(_) => PeakDataIterDispatch::PeakData(PeakDataIter::new(self)),
PeakDataLevel::Deconvoluted(_) => PeakDataIterDispatch::PeakData(PeakDataIter::new(self)),
}
}
}

Expand Down Expand Up @@ -394,6 +401,51 @@ impl<C: CentroidLike, D: DeconvolutedCentroidLike> PeakDataLevel<C, D> {

}


pub struct RawIter<'a> {
mz_array: Cow<'a, [f64]>,
intensity_array: Cow<'a, [f32]>,
n: usize,
i: usize,
}

impl<'a> RawIter<'a> {
fn new(mz_array: Cow<'a, [f64]>, intensity_array: Cow<'a, [f32]>) -> Self {
let n = mz_array.len();
let i = 0;
Self {
mz_array,
intensity_array,
n,
i
}
}

fn from_binary_array_map(arrays: &'a BinaryArrayMap) -> Result<Self, ArrayRetrievalError> {
let mz_array = arrays.mzs()?;
let intensity_array = arrays.intensities()?;

Ok(Self::new(mz_array, intensity_array))
}
}

impl<'a> Iterator for RawIter<'a> {
type Item = MZPoint;

fn next(&mut self) -> Option<Self::Item> {
if self.i >= self.n {
None
} else {
let i = self.i;
let mz = self.mz_array[i];
let intens = self.intensity_array[i];
self.i += 1;
Some(MZPoint::new(mz, intens))
}
}
}


pub struct PeakDataIter<'a, C: CentroidLike, D: DeconvolutedCentroidLike> {
peaks: &'a PeakDataLevel<C, D>,
i: usize,
Expand Down Expand Up @@ -450,6 +502,27 @@ impl<'a, C: CentroidLike, D: DeconvolutedCentroidLike> PeakDataIter<'a, C, D> {
}
}


pub enum PeakDataIterDispatch<'a, C: CentroidLike, D: DeconvolutedCentroidLike> {
PeakData(PeakDataIter<'a, C, D>),
RawData(RawIter<'a>),
RefPeakData(RefPeakDataIter<'a, C, D>),
}


impl<'a, C: CentroidLike, D: DeconvolutedCentroidLike> Iterator for PeakDataIterDispatch<'a, C, D> {
type Item = MZPoint;

fn next(&mut self) -> Option<Self::Item> {
match self {
PeakDataIterDispatch::PeakData(a) => a.next(),
PeakDataIterDispatch::RawData(a) => a.next(),
PeakDataIterDispatch::RefPeakData(a) => a.next()
}
}
}


#[derive(Debug)]
/// An variant for dispatching to different strategies of computing
/// common statistics of different levels of peak data.
Expand Down Expand Up @@ -552,8 +625,13 @@ impl<'a, C: CentroidLike, D: DeconvolutedCentroidLike> RefPeakDataLevel<'a, C, D
///
/// **NOTE**: Values are produced in the order they are stored, so the data are not guaranteed
/// to be ordered by m/z.
pub fn iter(&self) -> RefPeakDataIter<'_, C, D> {
RefPeakDataIter::new(self)
pub fn iter(&self) -> PeakDataIterDispatch<'_, C, D> {
match self {
RefPeakDataLevel::Missing => PeakDataIterDispatch::RefPeakData(RefPeakDataIter::new(self)),
RefPeakDataLevel::RawData(a) => PeakDataIterDispatch::RawData(RawIter::from_binary_array_map(a).unwrap()),
RefPeakDataLevel::Centroid(_) => PeakDataIterDispatch::RefPeakData(RefPeakDataIter::new(self)),
RefPeakDataLevel::Deconvoluted(_) => PeakDataIterDispatch::RefPeakData(RefPeakDataIter::new(self)),
}
}

/// Get the `i`th point in the peak data.
Expand Down
52 changes: 49 additions & 3 deletions src/spectrum/spectrum_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1324,13 +1324,59 @@ where

#[cfg(test)]
mod test {
use std::io;

use super::*;
use crate::io::mzml::MzMLReader;
use crate::io::DetailLevel;
use crate::prelude::*;

#[test_log::test]
fn test_peakdata_lazy() -> io::Result<()> {
let mut reader = MzMLReader::open_path("./test/data/small.mzML")?;
reader.detail_level = DetailLevel::Lazy;
let spec = reader.get_spectrum_by_index(0).unwrap();

let peaks = spec.peaks();
let n = peaks.len();
assert_eq!(n, 19913);

let iter = peaks.iter();
let data: Vec<_> = iter.collect();

let n2 = data.len();
assert_eq!(n2, 19913);

let p1 = peaks.get(5000);
let p2 = data.get(5000).cloned();
assert_eq!(p1, p2);
Ok(())
}

#[test_log::test]
fn test_peakdata() -> io::Result<()> {
let mut reader = MzMLReader::open_path("./test/data/small.mzML")?;
let spec = reader.get_spectrum_by_index(0).unwrap();

let peaks = spec.peaks();
let n = peaks.len();
assert_eq!(n, 19913);

let iter = peaks.iter();
let data: Vec<_> = iter.collect();

let n2 = data.len();
assert_eq!(n2, 19913);

let p1 = peaks.get(5000);
let p2 = data.get(5000).cloned();
assert_eq!(p1, p2);
Ok(())
}

#[cfg(feature = "mzsignal")]
#[test_log::test]
fn test_profile_read() {
use super::*;
use crate::io::mzml::MzMLReader;
use crate::prelude::*;
let mut reader = MzMLReader::open_path("./test/data/three_test_scans.mzML")
.expect("Failed to open test file");
reader.reset();
Expand Down

0 comments on commit e7b290c

Please sign in to comment.