/// <summary> /// Initializes a new instance of the <see cref="ObservedPeak"/> class with peak and its calculated statistics. /// </summary> /// <param name="group"> /// The group. /// </param> /// <param name="peak"> /// The peak. /// </param> /// <param name="statistics"> /// The Statistics. /// </param> public ObservedPeak(VoltageGroup group, StandardImsPeak peak, PeakScores statistics) { this.VoltageGroup = group; this.Peak = peak; this.Statistics = statistics; this.ObservationType = ObservationType.Peak; this.mobilityPoint = null; }
public bool ExportMzML(string sourceUIMFPath, string outputPath, VoltageGroup voltageGroup, DataReader originalUIMF, bool averageNotSum) { FrameParams frameParam = originalUIMF.GetFrameParams(voltageGroup.FirstFrameNumber); GlobalParams globalParams = originalUIMF.GetGlobalParams(); this.scans = (int)frameParam.GetValueDouble(FrameParamKeyType.Scans); this.bins = (int)globalParams.GetValueDouble(GlobalParamKeyType.Bins); this.calibrationSlope = frameParam.GetValueDouble(FrameParamKeyType.CalibrationSlope); this.calibrationIntercept = frameParam.GetValueDouble(FrameParamKeyType.CalibrationIntercept); this.binWidth = globalParams.GetValueDouble(GlobalParamKeyType.BinWidth); this.tofCorrectionTime = globalParams.GetValueDouble(GlobalParamKeyType.TOFCorrectionTime); if (File.Exists(outputPath)) { File.Delete(outputPath); } this.dateTime = globalParams.GetValue(GlobalParamKeyType.DateStarted); string datasetName = Path.GetFileNameWithoutExtension(outputPath); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; using (XmlWriter writer = XmlWriter.Create(outputPath, settings)) { writer.WriteStartDocument(); writer.WriteStartElement("mzML", "http://psi.hupo.org/ms/mzml"); writer.WriteAttributeString("id", datasetName); writer.WriteAttributeString("version", "1.1.0"); writer.WriteAttributeString("xmlns", "xls", string.Empty, "http://www.w3.org/2001/XMLSchema-instance"); writer.WriteAttributeString("xmlns", "schemaLocation", string.Empty, "http://psi.hupo.org/ms/mzml http://psidev.info/files/ms/mzML/xsd/mzML1.1.0.xsd"); this.WriteCVList(writer); this.WriteFileDescription(writer, sourceUIMFPath); this.WriteSoftwareList(writer); this.WriteInstrumentConfigurationList(writer); this.WriteDataProcessingList(writer); this.WriteRun(writer, outputPath, originalUIMF, voltageGroup); writer.WriteEndElement(); writer.WriteEndDocument(); return true; } }
/// <summary> /// Binary increment the selected peaks /// </summary> /// <param name="groups"> /// The groups. /// </param> /// <param name="selectedPeaks"> /// The selected peaks. /// </param> /// <param name="onIndex"> /// The on index. /// </param> /// <param name="graph"> /// The base peak map. /// </param> /// <param name="minFitPoints"> /// The min Fit Points. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> /// If incrementing overflows occurs/ all combinations were gone through. /// <exception cref="Exception"> /// </exception> private bool IncrementCombination(VoltageGroup[] groups, ref ObservedPeak[] selectedPeaks, int onIndex, ObservationTransitionGraph<IonTransition> graph) { int peakIndex; if (groups.Length != selectedPeaks.Length) { throw new ArgumentException("Votlage group does not match selected peaks"); } int length = selectedPeaks.Length; // Base case if (onIndex >= length) { return true; } ObservedPeak incrementPeak = selectedPeaks[onIndex]; VoltageGroup group = groups[onIndex]; IList<ObservedPeak> candidates = graph.FindPeaksInVoltageGroup(group).ToList(); // Empty peak if (incrementPeak == null) { peakIndex = -1; } else { peakIndex = candidates.IndexOf(incrementPeak); if (peakIndex < 0) { throw new Exception("The combination to be incremented is not in the base peak map space."); } } if (peakIndex + 1 >= candidates.Count) { if (selectedPeaks[onIndex] != null) { this.trackPointsCounter--; } selectedPeaks[onIndex] = null; return this.IncrementCombination(groups, ref selectedPeaks, onIndex + 1, graph); } else { if (peakIndex + 1 == 0) { this.trackPointsCounter++; } selectedPeaks[onIndex] = candidates[peakIndex + 1]; return false; } }
/// <summary> /// The increment combination. /// </summary> /// <param name="groups"> /// The groups. /// </param> /// <param name="selectedPeaks"> /// The selected peaks. /// </param> /// <param name="basePeakMap"> /// The base peak map. /// </param> /// <param name="minFitPoints"> /// The min Fit Points. /// </param> /// <returns> /// If the increment overflows the "counter"<see cref="bool"/>. /// </returns> private bool IncrementCombination(VoltageGroup[] groups, ref ObservedPeak[] selectedPeaks, ObservationTransitionGraph<IonTransition> graph, int minFitPoints) { bool overflow = false; // So-called do while loop overflow = this.IncrementCombination(groups, ref selectedPeaks, 0, graph); while (!overflow && this.trackPointsCounter < minFitPoints) { overflow = this.IncrementCombination(groups, ref selectedPeaks, 0, graph); } return overflow; }
public static double MaxGlobalIntensities(VoltageGroup group, DataReader reader) { GlobalParams global = reader.GetGlobalParams(); FrameParams param = reader.GetFrameParams(@group.FirstFrameNumber); int firstFrame = group.FirstFrameNumber; int lastFrame = group.LastFrameNumber; int firstScan = 1; int lastScan = param.Scans; int firstBin = 0; int lastBin = global.Bins; int windowOfBins = 1000; int windowOfScans = 20; double maxIntensities = 0; for (int scan = firstScan; scan <= lastScan; scan += windowOfScans) { int endScan = (scan + windowOfScans > lastScan) ? lastScan : scan + windowOfScans; for (int bin = firstBin; bin <= lastBin; bin += windowOfBins) { int endBin = (bin + windowOfBins > lastBin) ? lastBin : bin + windowOfBins; double localMaxIntensities = MaxGlobalIntensities(reader, scan, endScan, bin, endBin, firstFrame, lastFrame); maxIntensities = (localMaxIntensities > maxIntensities) ? localMaxIntensities : maxIntensities; } } return maxIntensities; }
/// <summary> /// The is last voltage group. /// </summary> /// <param name="group"> /// The group. /// </param> /// <param name="totalFrames"> /// The total frames. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> public static bool IsLastVoltageGroup(VoltageGroup group, int totalFrames) { return group.LastFrameNumber == totalFrames; }
public static double DeNormalizeDriftTime(double driftTime, VoltageGroup group) { double normalizedPressure = Metrics.Nondimensionalized2Torr(group.MeanPressureNondimensionalized) / Metrics.StandardImsPressureInTorr; double normalizedTemperature = Metrics.Nondimensionalized2Kelvin(group.MeanTemperatureNondimensionalized) / Metrics.RoomTemperatureInKelvin; return driftTime * normalizedPressure; }
/// <summary> /// return the maximum intensity value possible for a given voltage group /// Note currently supports 8-bit digitizers, proceeds with caution when /// dealing with 12-bit digitizers /// </summary> /// <param name="group"> /// The group. /// </param> /// <param name="reader"> /// The reader. /// </param> /// <returns> /// The <see cref="double"/>. /// </returns> public static double MaxIntensityAfterFrameAccumulation(VoltageGroup group, DataReader reader) { return 255 * group.FrameAccumulationCount * reader.GetFrameParams(group.FirstFrameNumber).GetValueInt32(FrameParamKeyType.Accumulations); }
/// <summary> /// The score feature. /// </summary> /// <param name="peak"> /// The peak. /// </param> /// <param name="globalMaxIntensity"> /// The global max intensity. /// </param> /// <param name="uimfReader"> /// The uimf reader. /// </param> /// <param name="massToleranceInPpm"> /// The mass tolerance in ppm. /// </param> /// <param name="driftTimeToleranceInMs"> /// The drift time tolerance in ms. /// </param> /// <param name="voltageGroup"> /// The voltage group. /// </param> /// <param name="voltageGroupScans"> /// The voltage group scans. /// </param> /// <param name="target"> /// The target. /// </param> /// <param name="isotopicScoreMethod"> /// The isotopic score method. /// </param> /// <param name="theoreticalIsotopicProfile"> /// The theoretical isotopic profile. /// </param> /// <returns> /// The <see cref="PeakScores"/>. /// </returns> public static PeakScores ScoreFeature(this StandardImsPeak peak, double globalMaxIntensity, DataReader uimfReader, double massToleranceInPpm, double driftTimeToleranceInMs, VoltageGroup voltageGroup, int voltageGroupScans, IImsTarget target, IsotopicScoreMethod isotopicScoreMethod, List<Peak> theoreticalIsotopicProfile) { double intensityScore = IntensityScore(peak, globalMaxIntensity); double peakShapeScore = PeakShapeScore( peak, uimfReader, massToleranceInPpm, driftTimeToleranceInMs, voltageGroup, globalMaxIntensity, voltageGroupScans); double isotopicScore = 0; if (target.HasCompositionInfo) { isotopicScore = IsotopicProfileScore( peak, uimfReader, target, theoreticalIsotopicProfile, voltageGroup, IsotopicScoreMethod.Angle, globalMaxIntensity, voltageGroupScans); } return new PeakScores(intensityScore, isotopicScore, peakShapeScore); }
/// <summary> /// The Peak shape score. Evaluating how "good" the peak looks. A good /// peak shape score indicates that the peak is not a result of noise /// or instrument errors. Mostly the feature intensity along is sufficient /// to exclude noise but a good shape score helps evaluating the experiment /// and thus the reliability of the data analysis result. /// </summary> /// <param name="reader"> /// The reader. /// </param> /// <param name="massToleranceInPpm"> /// The mass Tolerance In Ppm. /// </param> /// <param name="driftTimeToleranceInMs"> /// The drift Time Tolerance In Scans. /// </param> /// <param name="imsPeak"> /// The imsPeak. /// </param> /// <param name="voltageGroup"> /// The voltage group. /// </param> /// <param name="targetMz"> /// The Target MZ. /// </param> /// <param name="globalMaxIntensities"> /// The global Max Intensities. /// </param> /// <param name="numberOfScans"> /// The number Of Scans. /// </param> /// <returns> /// The <see cref="double"/>. /// </returns> public static double PeakShapeScore(StandardImsPeak imsPeak, DataReader reader, double massToleranceInPpm, double driftTimeToleranceInMs, VoltageGroup voltageGroup, double globalMaxIntensities, int numberOfScans) { int scanRep = imsPeak.PeakApex.DriftTimeCenterInScanNumber; double toleranceInMz = massToleranceInPpm / 1e6 * imsPeak.PeakApex.MzCenterInDalton; int scanWidth = (int)Math.Ceiling(driftTimeToleranceInMs / 1000 / voltageGroup.AverageTofWidthInSeconds); int scanWindowSize = scanWidth * 2 + 1; int scanNumberMin = scanRep - scanWidth; int scanNumberMax = scanRep + scanWidth; if ((scanNumberMin < 0) || (scanNumberMax > numberOfScans - 1)) { return 0; } int[][] intensityWindow = reader.GetFramesAndScanIntensitiesForAGivenMz( voltageGroup.FirstFrameNumber, voltageGroup.LastFrameNumber, DataReader.FrameType.MS1, scanNumberMin, scanNumberMax, imsPeak.PeakApex.MzCenterInDalton, toleranceInMz); // Average the intensity window across frames int frames = intensityWindow.GetLength(0); double[] averagedPeak = new double[scanWindowSize]; double highestPeak = 0; for (int i = 0; i < scanWindowSize; i++) { for (int j = 0; j < frames; j++) { averagedPeak[i] += intensityWindow[j][i]; } averagedPeak[i] /= frames; highestPeak = (averagedPeak[i] > highestPeak) ? averagedPeak[i] : highestPeak; } // For peaks with peak width lower than 3, return a peak score of 0 // TODO get the intensity threshold here from the noise level instead. if (scanWindowSize >= 3) { if (averagedPeak[scanRep - scanNumberMin] < globalMaxIntensities * 0.0001 || averagedPeak[scanRep - scanNumberMin - 1] < globalMaxIntensities * 0.0001 || averagedPeak[scanRep - scanNumberMin + 1] < globalMaxIntensities * 0.0001) { return 0; } } // Perform a statistical normality test double normalityScore = NormalityTest.PeakNormalityTest(averagedPeak, NormalityTest.JaqueBeraTest, 100, globalMaxIntensities); return normalityScore; }
/// <summary> /// The score feature using isotopic profile. /// </summary> /// <param name="imsPeak"> /// The ims Peak. /// </param> /// <param name="reader"> /// The reader. /// </param> /// <param name="target"> /// The Target. /// </param> /// <param name="isotopicPeakList"> /// The isotopic peak list. /// </param> /// <param name="voltageGroup"> /// The voltage Group. /// </param> /// <param name="selectedMethod"> /// The selected Method. /// </param> /// <param name="globalMaxIntensities"> /// </param> /// <returns> /// The <see cref="double"/>. /// </returns> /// <exception cref="InvalidOperationException"> /// </exception> public static double IsotopicProfileScore(StandardImsPeak imsPeak, DataReader reader, IImsTarget target, List<Peak> isotopicPeakList, VoltageGroup voltageGroup, IsotopicScoreMethod selectedMethod, double globalMaxIntensities, double totalScans) { // No need to move on if the isotopic profile is not found // if (observedIsotopicProfile == null || observedIsotopicProfile.MonoIsotopicMass < 1) // { // result.AnalysisStatus = AnalysisStatus.IsotopicProfileNotFound; // continue; // } // Find Isotopic Profile // List<Peak> massSpectrumPeaks; // IsotopicProfile observedIsotopicProfile = _msFeatureFinder.IterativelyFindMSFeature(massSpectrum, theoreticalIsotopicProfile, out massSpectrumPeaks); if (target.CompositionWithoutAdduct == null) { throw new InvalidOperationException("Cannot score feature using isotopic profile for Ims Target without CompositionWithoutAdduct provided."); } // Bad Feature, so get out if (imsPeak == null) { return 0; } // Get the scanWindow size int scanNumberMax = imsPeak.PeakApex.DriftTimeFullWidthHalfMaxHigherBondInScanNumber; int scanNumberMin = imsPeak.PeakApex.DriftTimeFullWidthHalfMaxLowerBondInScanNumber; if ((scanNumberMin < 0) || (scanNumberMax > totalScans - 1)) { return 0; } // Get the mass error from the observed feature peak from the Target theoretical peak double mzOffset = imsPeak.PeakApex.MzCenterInDalton - target.MassWithAdduct; List<double> observedIsotopicPeakList = new List<double>(); int totalIsotopicIndex = isotopicPeakList.Count; int[] isotopicIndexMask = new int[totalIsotopicIndex]; // Find an unsaturated peak in the isotopic profile for (int i = 0; i < totalIsotopicIndex; i++) { // Isotopic centerMz double Mz = isotopicPeakList[i].XValue; var peakList = reader.GetXic(Mz + mzOffset, imsPeak.PeakApex.MzWindowToleranceInPpm, voltageGroup.FirstFrameNumber, voltageGroup.LastFrameNumber, scanNumberMin, scanNumberMax, DataReader.FrameType.MS1, DataReader.ToleranceType.PPM); // Sum the intensities double sumIntensities = 0; foreach (var point in peakList) { sumIntensities += point.Intensity; if (point.IsSaturated) { isotopicIndexMask[i] = 1; } } sumIntensities /= voltageGroup.FrameAccumulationCount; observedIsotopicPeakList.Add(sumIntensities); } // Return 0 if the intensity sum is really small if (observedIsotopicPeakList.Sum() < globalMaxIntensities * 0.0003) { return 0; } // If the unsaturated isotopes are below a certain threshold if (totalIsotopicIndex - isotopicIndexMask.Sum() <= 1) { return 0; } if (selectedMethod == IsotopicScoreMethod.Angle) { return IsotopicProfileScoreAngle(observedIsotopicPeakList, isotopicPeakList); } else if (selectedMethod == IsotopicScoreMethod.EuclideanDistance) { return IsotopicProfileScoreEuclidean(observedIsotopicPeakList, isotopicPeakList); } else if (selectedMethod == IsotopicScoreMethod.PearsonCorrelation) { return PearsonCorrelation(observedIsotopicPeakList, isotopicPeakList); } else if (selectedMethod == IsotopicScoreMethod.Bhattacharyya) { return BhattacharyyaDistance(observedIsotopicPeakList, isotopicPeakList); } else if (selectedMethod == IsotopicScoreMethod.EuclideanDistanceAlternative) { return EuclideanAlternative(observedIsotopicPeakList, isotopicPeakList); } else { throw new NotImplementedException(); } }
/// <summary> /// The max global intensities 2. /// </summary> /// <param name="group"> /// The group. /// </param> /// <param name="reader"> /// The reader. /// </param> /// <returns> /// The <see cref="double"/>. /// </returns> public static double MaxGlobalIntensities2(VoltageGroup group, DataReader reader) { Stack<int[]> data = reader.GetFrameAndScanListByDescendingIntensity(); return data.Pop()[0]; }
/// <summary> /// The find peaks based on xic. /// </summary> /// <param name="group"> /// The group. /// </param> /// <param name="chromatogram"> /// The chromatogram. /// </param> /// <param name="target"> /// The target. /// </param> /// <returns> /// The <see cref="IList"/>. /// </returns> /// <exception cref="NotImplementedException"> /// </exception> private List<StandardImsPeak> FindPeaksBasedOnXIC(VoltageGroup voltageGroup, ExtractedIonChromatogram chromatogram, IImsTarget target) { if (this.Parameters.PeakDetectorSelection == PeakDetectorEnum.WaterShed) { // Find peaks using multidimensional peak finder. List<IntensityPoint> intensityPoints = chromatogram.IntensityPoints; List<FeatureBlob> featureBlobs = PeakFinding.FindPeakUsingWatershed(intensityPoints, this.smoother, this.Parameters.FeatureFilterLevel); // Recapture the 2D peak using the 1D feature blob from multidimensional peak finder. return featureBlobs.Select(featureBlob => new StandardImsPeak(featureBlob, this.uimfReader, voltageGroup, target.MassWithAdduct, this.Parameters.MzWindowHalfWidthInPpm)).ToList(); } else if (this.Parameters.PeakDetectorSelection == PeakDetectorEnum.MASICPeakFinder) { // Find peaks using MASIC peak finder List<IntensityPoint> intensityPoints = chromatogram.IntensityPoints; IList<clsPeak> masicPeaks = PeakFinding.FindPeakUsingMasic(intensityPoints, this.NumberOfScans); // Recapture the 2D peak using the 1D feature blob from multidimensional peak finder. return masicPeaks.Select(peak => new StandardImsPeak(peak)).ToList(); } else { throw new NotImplementedException(string.Format("{0} not supported", this.Parameters.PeakDetectorSelection)); } }
private void WriteRun(XmlWriter writer, string outputPath, DataReader reader, VoltageGroup voltageGroup) { writer.WriteStartElement("run"); string dataset = Path.GetFileNameWithoutExtension(outputPath); writer.WriteAttributeString("id", dataset); writer.WriteAttributeString("defaultInstrumentConfigurationRef", "IC"); writer.WriteAttributeString("startTimeStamp", this.dateTime); writer.WriteStartElement("spectrumList"); writer.WriteAttributeString("count", this.scans.ToString(CultureInfo.InvariantCulture)); int startingFrame = voltageGroup.FirstFrameNumber; int endingFrame = voltageGroup.LastFrameNumber; Console.Write("Summing frame[{0} - {1}]... ", startingFrame, endingFrame); double[,] summedIntensities = reader.AccumulateFrameData(startingFrame, endingFrame, false, 1, this.scans, 1, this.bins, -1, -1); // Use dirft time scan as LC scan to massage skyline for (int lcScan = 1; lcScan <= this.scans; lcScan++) { float[] mzArray; float[] intensityArray; this.GetMzIntensityArrayAtScan(summedIntensities, lcScan, out mzArray, out intensityArray); double mzLow = mzArray[0]; double mzHigh = mzArray[mzArray.Count()-1]; // Write the bins as mass spectrum writer.WriteStartElement("spectrum"); writer.WriteAttributeString("index", String.Format("{0}", lcScan - 1)); writer.WriteAttributeString("id", String.Format("frame={0} scan={1} frameType={2}", 1, lcScan, 1)); writer.WriteAttributeString("defaultArrayLength", mzArray.Count().ToString(CultureInfo.InvariantCulture)); writer.WriteStartElement("cvParam"); writer.WriteAttributeString("cvRef", "MS"); writer.WriteAttributeString("accession", "MS:1000511"); writer.WriteAttributeString("name", "ms level"); writer.WriteAttributeString("value", "1"); writer.WriteEndElement(); writer.WriteStartElement("cvParam"); writer.WriteAttributeString("cvRef", "MS"); writer.WriteAttributeString("accession", "MS:1000579"); writer.WriteAttributeString("name", "MS1 spectrum"); writer.WriteAttributeString("value", ""); writer.WriteEndElement(); writer.WriteStartElement("cvParam"); writer.WriteAttributeString("cvRef", "MS"); writer.WriteAttributeString("accession", "MS:1000128"); writer.WriteAttributeString("name", "profile spectrum"); writer.WriteAttributeString("value", ""); writer.WriteEndElement(); writer.WriteStartElement("scanList"); writer.WriteAttributeString("count", "1"); writer.WriteStartElement("cvParam"); writer.WriteAttributeString("cvRef", "MS"); writer.WriteAttributeString("accession", "MS:1000795"); writer.WriteAttributeString("name", "no combination"); writer.WriteAttributeString("value", ""); writer.WriteEndElement(); writer.WriteStartElement("scan"); writer.WriteStartElement("cvParam"); writer.WriteAttributeString("cvRef", "MS"); writer.WriteAttributeString("accession", "MS:1000016"); writer.WriteAttributeString("name", "scan start time"); writer.WriteAttributeString("value", reader.GetDriftTime(voltageGroup.FirstFrameNumber, lcScan, true).ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString("unitCvRef", "UO"); writer.WriteAttributeString("unitAccession", "UO:0000031"); writer.WriteAttributeString("unitName", "minute"); writer.WriteEndElement(); writer.WriteStartElement("scanWindowList"); writer.WriteAttributeString("count", "1"); writer.WriteStartElement("scanWindow"); writer.WriteStartElement("cvParam"); writer.WriteAttributeString("cvRef", "MS"); writer.WriteAttributeString("accession", "MS:1000501"); writer.WriteAttributeString("name", "scan window lower limit"); writer.WriteAttributeString("value", mzLow.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString("unitCvRef", "MS"); writer.WriteAttributeString("unitAccession", "MS:1000040"); writer.WriteAttributeString("unitName", "m/z"); writer.WriteEndElement(); writer.WriteStartElement("cvParam"); writer.WriteAttributeString("cvRef", "MS"); writer.WriteAttributeString("accession", "MS:1000500"); writer.WriteAttributeString("name", "scan window upper limit"); writer.WriteAttributeString("value", mzHigh.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString("unitCvRef", "MS"); writer.WriteAttributeString("unitAccession", "MS:1000040"); writer.WriteAttributeString("unitName", "m/z"); writer.WriteEndElement(); // scan window writer.WriteEndElement(); // scan window list writer.WriteEndElement(); // scan writer.WriteEndElement(); // scan list writer.WriteEndElement(); this.WriteBinaryDataArrays(mzArray, intensityArray, writer); // spectrum writer.WriteEndElement(); } // spectrum list writer.WriteEndElement(); // run writer.WriteEndElement(); }
/// <summary> /// A voltage group has stability higher score if the voltage group has more accumulations, less variations /// on voltage, temperature and pressure. /// </summary> /// <param name="group"> /// The group. /// </param> /// <returns> /// The <see cref="double"/>. /// </returns> public static double ComputeVoltageGroupStabilityScore(VoltageGroup group) { double stability = group.VariancePressureNondimensionalized * group.VarianceTemperature * group.VarianceVoltage; return ScoreUtil.MapToZeroOneTrignometry(stability, true, 0.00000000000000001); }