public Dictionary <string, dynamic> AssembleMetrics() { return(new Dictionary <string, dynamic> { { "QC:4000053", RTDuration }, { "QC:02", swathSizeDifference }, { "QC:4000059", Run.Ms1Scans.Count() }, { "QC:4000060", Run.Ms2Scans.Count() }, { "QC:04", SwathMetrics.swathTargets.Count() }, { "QC:05", SwathMetrics.swathTargets }, { "QC:06", Density.Sum() }, { "QC:07", Density.ElementAt(Density.Count() / 2) }, { "QC:08", InterQuartileRangeCalculator.CalcIQR(Density) }, { "QC:09", SwathMetrics.numOfSwathPerGroup }, { "QC:10", SwathMetrics.mzRange }, { "QC:11", SwathMetrics.SwathProportionOfTotalTIC }, { "QC:12", SwathMetrics.swDensity50 }, { "QC:13", SwathMetrics.swDensityIQR }, { "QC:14", RtMetrics.Peakwidths }, { "QC:15", RtMetrics.PeakCapacity }, { "QC:16", RtMetrics.MS1PeakPrecision }, { "QC:17", RtMetrics.TicChange50List }, { "QC:18", RtMetrics.TicChangeIqrList }, { "QC:19", RtMetrics.CycleTime }, { "QC:20", RtMetrics.MS2Density }, { "QC:21", RtMetrics.MS1Density }, { "QC:22", RtMetrics.MS2TicTotal }, { "QC:23", RtMetrics.MS1TicTotal }, { "QC:24", RtMetrics.TailingFactor } }); }
public SwathMetrics GroupBySwath(Run <Scan> run) { //Create list of target isolationwindows to serve as swathnumber List <double> swathTargets = new List <double>(); // Force ordering of swathTargets so that our tests don't depend on the order that Distinct() happens to impose. swathTargets = run.Ms2Scans.Select(x => x.IsolationWindowTargetMz).Distinct().OrderBy(x => x).ToList(); double totalTIC = 0; foreach (var scan in run.Ms2Scans) { totalTIC += scan.TotalIonCurrent; } //Loop through every swath of a certain swathNumber: List <int> numOfSwathPerGroup = new List <int>(); List <double> TICs = new List <double>(); List <double> swDensity = new List <double>(); List <double> swDensity50 = new List <double>(); List <double?> swDensityIQR = new List <double?>(); List <double> mzTargetRange = new List <double>(); List <double> averageMzTargetRange = new List <double>(); List <double> SwathProportionOfTotalTIC = new List <double>(); List <double> TotalSwathProportionPredictedSingleCharge = new List <double>(); List <double> SwathProportionPredictedSingleChargeAvg = new List <double>(); //Loop through all the swaths of the same number and add to for (int swathNumber = 0; swathNumber < swathTargets.Count(); swathNumber++) { int track = 0; double TICthisSwath = 0; var orderedMS2Scans = run.Ms2Scans .Where(x => x.IsolationWindowTargetMz == swathTargets[swathNumber]); // Turns out ordering is not required; users of this either don't care about order or sort their own results. foreach (var scan in orderedMS2Scans) { mzTargetRange.Add(scan.IsolationWindowUpperOffset + scan.IsolationWindowLowerOffset); TICthisSwath += scan.TotalIonCurrent; swDensity.Add(scan.Density); TotalSwathProportionPredictedSingleCharge.Add(scan.ProportionChargeStateOne); //The chargestate one's we pick up is where there is a match for M+1. Therefore we need to double it to add the M. track++; } averageMzTargetRange.Add(mzTargetRange.Average()); numOfSwathPerGroup.Add(track); TICs.Add(TICthisSwath); TICthisSwath = 0; SwathProportionPredictedSingleChargeAvg.Add(TotalSwathProportionPredictedSingleCharge.Average()); TotalSwathProportionPredictedSingleCharge.Clear(); swDensity.Sort(); swDensity50.Add(Math.Truncate(Math.Ceiling(swDensity.Average()))); if (swDensity.Count > 4) { swDensityIQR.Add(Math.Ceiling(InterQuartileRangeCalculator.CalcIQR(swDensity))); } else { swDensityIQR.Add(default);
public RTMetrics DivideByRT(MzmlParser.Run run, int division, double rtDuration) { double rtSegment = rtDuration / division; rtSegs = new double[division]; List <string> segmentBoundaries = new List <string>(); for (int i = 0; i < division; i++) { rtSegs[i] = run.StartTime + rtSegment * i; if (i > 0) { segmentBoundaries.Add(rtSegs[i - 1] + "_" + rtSegs[i]);//segmentBoundaries is a string denoting the startOfTheRTsegment_endOfTheRTsegment for reference } else { segmentBoundaries.Add(run.StartTime + "_" + rtSegs[i]); } } //dividing basepeaks into segments foreach (MzmlParser.BasePeak basepeak in run.BasePeaks) { //Check to see in which RTsegment this basepeak is: foreach (double rt in basepeak.BpkRTs) { if (rt > rtSegs.Last()) { basepeak.RTsegments.Add(rtSegs.Count() - 1); } else if (rtSegs.Count() > 1 && rt < rtSegs[1]) { basepeak.RTsegments.Add(0); } else { for (int segmentboundary = 2; segmentboundary < rtSegs.Count(); segmentboundary++) { if (rt > rtSegs[segmentboundary - 1] && rt < rtSegs[segmentboundary]) { basepeak.RTsegments.Add(segmentboundary - 1); segmentboundary = rtSegs.Count();//move to the next bpkRT } } } } } double experimentWideMS2TICSquared = 0; //dividing ms2scans into segments of RT foreach (MzmlParser.Scan scan in run.Ms2Scans) { //if the scan starttime falls into the rtsegment, give it the correct rtsegment number //We assign to the segmentdivider below. So if >a and <b, it is assigned to segment a. if (scan.ScanStartTime > rtSegs.Last())//If the scan is after the last segment divider, it falls into the last segment { scan.RTsegment = rtSegs.Count() - 1; } else if (rtSegs.Count() > 1 && scan.ScanStartTime < rtSegs[1])//If the scan is before the second segment divider it should fall in the first segment. (assuming that the user has selected to have more than one segment) { scan.RTsegment = 0; } else { for (int segmentboundary = 2; segmentboundary < rtSegs.Count(); segmentboundary++) //If the scan is not in the first or last segment { if (scan.ScanStartTime > rtSegs[segmentboundary - 1] && scan.ScanStartTime < rtSegs[segmentboundary]) // If the scan is larger than the previous boundary and smaller than this boundary, assign to the previous segment. { scan.RTsegment = segmentboundary - 1; } } } experimentWideMS2TICSquared += Math.Pow(scan.TotalIonCurrent, 2); } //dividing ms1scans into segments of RT foreach (MzmlParser.Scan scan in run.Ms1Scans) { if (scan.ScanStartTime > rtSegs.Last())//If the scan is after the last segment divider, it falls into the last segment { scan.RTsegment = rtSegs.Count() - 1; } else if (rtSegs.Count() > 1 && scan.ScanStartTime < rtSegs[1])//If the scan is before the second segment divider it should fall in the first segment. (assuming that the user has selected to have more than one segment) { scan.RTsegment = 0; } else { for (int segmentboundary = 2; segmentboundary < rtSegs.Count(); segmentboundary++) //If the scan is not in the first or last segment { if (scan.ScanStartTime > rtSegs[segmentboundary - 1] && scan.ScanStartTime < rtSegs[segmentboundary]) // If the scan is larger than the previous boundary and smaller than this boundary, assign to the previous segment. { scan.RTsegment = segmentboundary - 1; } } } } //Retrieve TICChange metrics and divide into rtsegs List <double> ticChange50List = new List <double>(); List <double> ticChangeIqrList = new List <double>(); var tempTic = run.Ms2Scans.OrderBy(x => x.ScanStartTime).GroupBy(x => x.RTsegment).Select(d => d.Select(g => g.TotalIonCurrent).ToList()); double magnitude = Math.Pow(experimentWideMS2TICSquared, 0.5); for (int i = 0; i < tempTic.Count(); i++) { var temp = tempTic.ElementAt(i); List <double> tempList = new List <double>(); for (int j = 1; j < temp.Count(); j++) { tempList.Add(Math.Abs(temp.ElementAt(j) - temp.ElementAt(j - 1)) / magnitude);//Normalised to TIC magnitude. } tempList.Sort(); ticChange50List.Add(tempList.Average()); if (tempList.Count() > 4) { ticChangeIqrList.Add(InterQuartileRangeCalculator.CalcIQR(tempList)); } else { logger.Error("There are only {0} MS2Scans in this segment, which is too few to calculate the IQR of the TIC Change. This value has been set to zero.", tempTic.Count()); ticChangeIqrList.Add(0); } } //Calculations for peakprecision MS2: var meanIntensityOfAllBpks = run.BasePeaks.Select(x => x.Intensities.Sum()).Average(); var meanMzOfAllBpks = run.BasePeaks.Select(x => x.Mz).Average(); //Calculations for peakprecision MS1: //double mIMS1Bpks; //if (run.Ms1Scans.Count()>0) mIMS1Bpks = run.Ms1Scans.Select(x => x.BasePeakIntensity).Average(); //var mMMS1Bpks = run.Ms1Scans.Select(x => x.BasePeakMz).Average(); List <double> peakWidths = new List <double>(); List <double> TailingFactor = new List <double>(); List <double> peakCapacity = new List <double>(); List <double> peakPrecision = new List <double>(); List <double> ms1PeakPrecision = new List <double>(); List <int> ms1Density = new List <int>(); List <int> ms2Density = new List <int>(); List <double> cycleTime = new List <double>(); List <double> ms1TicTotal = new List <double>(); List <double> Ms2TicTotal = new List <double>(); for (int segment = 0; segment < division; segment++) { List <double> peakWidthsTemp = new List <double>(); List <double> peakSymTemp = new List <double>(); List <double> fullWidthBaselinesTemp = new List <double>(); List <double> peakPrecisionTemp = new List <double>(); foreach (MzmlParser.BasePeak basepeak in run.BasePeaks) { for (int i = 0; i < basepeak.RTsegments.Count(); i++) { if (basepeak.RTsegments[i] == segment) { peakWidthsTemp.Add(basepeak.FWHMs[i]); peakSymTemp.Add(basepeak.Peaksyms[i]); peakPrecisionTemp.Add(basepeak.Intensities[i] / (meanIntensityOfAllBpks * Math.Pow(2, meanMzOfAllBpks / basepeak.Mz))); fullWidthBaselinesTemp.Add(basepeak.FullWidthBaselines[i]); } } } List <double> firstScansOfCycle = new List <double>(); List <double> lastScansOfCycle = new List <double>(); List <double> difference = new List <double>(); double ms1TicTotalTemp = 0; List <int> ms1DensityTemp = new List <int>(); List <double> ms1PeakPrecisionTemp = new List <double>(); foreach (MzmlParser.Scan scan in run.Ms1Scans.OrderBy(x => x.ScanStartTime)) { if (scan.RTsegment == segment) { ms1PeakPrecisionTemp.Add(scan.BasePeakIntensity / (meanIntensityOfAllBpks * Math.Pow(2, meanMzOfAllBpks / scan.BasePeakMz))); ms1DensityTemp.Add(scan.Density); ms1TicTotalTemp += scan.TotalIonCurrent; } } int minNumberMS1Or2 = 0; //To get scan speed for both ms1 and ms2 we have to also scan through ms2: if (run.Ms1Scans.Count() > 1) { lastScansOfCycle = run.Ms2Scans.Where(x => x.RTsegment == segment).GroupBy(g => g.Cycle).Select(x => x.Max(y => y.ScanStartTime)).ToList(); firstScansOfCycle = run.Ms1Scans.Where(x => x.RTsegment == segment).Select(y => y.ScanStartTime).ToList(); } else { lastScansOfCycle = run.Ms2Scans.Where(x => x.RTsegment == segment).GroupBy(g => g.Cycle).Select(x => x.Max(y => y.ScanStartTime)).ToList(); firstScansOfCycle = run.Ms2Scans.Where(x => x.RTsegment == segment).GroupBy(g => g.Cycle).Select(x => x.Min(y => y.ScanStartTime)).ToList(); } minNumberMS1Or2 = Math.Min(lastScansOfCycle.Count(), firstScansOfCycle.Count()); for (int i = 0; i < minNumberMS1Or2; i++) { difference.Add(lastScansOfCycle[i] - firstScansOfCycle[i]); } cycleTime.Add(difference.Average() * 60); List <int> ms2DensityTemp = run.Ms2Scans.Where(x => x.RTsegment == segment).Select(x => x.Density).ToList(); double ms2TicTotalTemp = run.Ms2Scans.Where(x => x.RTsegment == segment).Select(x => x.TotalIonCurrent).Sum(); if (peakWidthsTemp.Count > 0) { peakWidths.Add(peakWidthsTemp.Average()); TailingFactor.Add(peakSymTemp.Average()); peakCapacity.Add(rtSegment / fullWidthBaselinesTemp.Average());//PeakCapacity is calculated as per Dolan et al.,2009, PubMed 10536823); peakPrecision.Add(peakPrecisionTemp.Average()); } else { peakWidths.Add(0); TailingFactor.Add(0); peakCapacity.Add(0); peakPrecision.Add(0); } if (ms1PeakPrecisionTemp.Count > 0) { ms1PeakPrecision.Add(ms1PeakPrecisionTemp.Average()); ms1Density.Add(Convert.ToInt32(Math.Ceiling(ms1DensityTemp.Average()))); } else { ms1PeakPrecision.Add(0); ms1Density.Add(0); } ms2Density.Add(Convert.ToInt32(Math.Round(ms2DensityTemp.Average(), 0))); ms1TicTotal.Add(ms1TicTotalTemp); Ms2TicTotal.Add(ms2TicTotalTemp); } RTMetrics rtMetrics = new RTMetrics(ms1TicTotal, Ms2TicTotal, cycleTime, ticChange50List, ticChangeIqrList, ms1Density, ms2Density, peakWidths, TailingFactor, peakCapacity, peakPrecision, ms1PeakPrecision, segmentBoundaries); return(rtMetrics); }
public Dictionary <string, dynamic> GenerateMetrics(Run run, int division, string inputFilePath, bool irt, bool combine, bool lastFile, string date) { Run = run; if (run.LastScanTime != 0 && run.StartTime != 1000000) { //Acquire RTDuration: last minus first RTDuration = run.LastScanTime - run.StartTime; } else { Logger.Error("StartTime {0} or lastScanTime {0} for the run is null. RTDuration is therefore zero", run.StartTime, run.LastScanTime); RTDuration = 0; } //Interpolate, Smooth, create chromatogram and generate chromatogram metrics ChromatogramMetricGenerator chromatogramMetrics = new ChromatogramMetricGenerator(); chromatogramMetrics.GenerateChromatogram(run); if (irt) { chromatogramMetrics.GenerateiRTChromatogram(run); } //Calculating the largestswath if (run.Ms2Scans.Max(x => x.IsolationWindowUpperOffset) == 100000 || run.Ms2Scans.Max(x => x.IsolationWindowLowerOffset) == 100000) { Logger.Error(("IsolationWindowUpperOffset {0} or IsolationWindowLowerOffset {0} for the one of the scans has not been changed from default value. swathSizeDifference is therefore zero", run.Ms2Scans.Max(x => x.IsolationWindowUpperOffset), run.Ms2Scans.Max(x => x.IsolationWindowLowerOffset))); swathSizeDifference = 0; } else { swathSizeDifference = run.Ms2Scans.Max(x => x.IsolationWindowUpperOffset + x.IsolationWindowLowerOffset) - run.Ms2Scans.Min(x => x.IsolationWindowUpperOffset + x.IsolationWindowLowerOffset); } // This method will group the scans into swaths of the same number, return the number of swaths in a full cycle (maxswath) and call a FileMaker method to write out the metrics. SwathGrouper swathGrouper = new SwathGrouper { }; SwathMetrics = swathGrouper.GroupBySwath(run); //Retrieving Density metrics Density = run.Ms2Scans.OrderBy(g => g.Density).Select(g => g.Density).ToList(); RTGrouper rtGrouper = new RTGrouper { }; RtMetrics = rtGrouper.DivideByRT(run, division, RTDuration); FileMaker fileMaker = new FileMaker(division, inputFilePath, run, SwathMetrics, RtMetrics, RTDuration, swathSizeDifference, run.Ms2Scans.Count(), Density.Sum(), Density.ElementAt(Density.Count() / 2), InterQuartileRangeCalculator.CalcIQR(Density), run.Ms1Scans.Count(), date); fileMaker.MakeComprehensiveMetricsFile(); if (run.IRTPeaks != null && run.IRTPeaks.Count() > 0) { fileMaker.MakeiRTmetricsFile(run); } fileMaker.MakeMetricsPerRTsegmentFile(RtMetrics); fileMaker.MakeMetricsPerSwathFile(SwathMetrics); if (combine && lastFile) { if (run.IRTPeaks != null && run.IRTPeaks.Count() > 0) { string[] iRTFilename = { "AllIRTMetrics_", date, ".tsv" }; fileMaker.CombineMultipleFilesIntoSingleFile(date + "_iRTMetrics_*", string.Join("", iRTFilename)); } string[] swathFilename = { "AllMetricsBySwath_", date, ".tsv" }; string[] rtFilename = { "AllRTDividedMetrics_", date, ".tsv" }; string[] ComprehensiveFilename = { "AllComprehensiveMetrics_", date, ".tsv" }; fileMaker.CheckOutputDirectory(inputFilePath); fileMaker.CombineMultipleFilesIntoSingleFile(date + "_MetricsBySwath_*", string.Join("", swathFilename)); fileMaker.CombineMultipleFilesIntoSingleFile(date + "_RTDividedMetrics_*", string.Join("", rtFilename)); fileMaker.CombineMultipleFilesIntoSingleFile(date + "_ComprehensiveMetrics_*", string.Join("", ComprehensiveFilename)); } return(AssembleMetrics()); }
public SwathMetrics GroupBySwath(MzmlParser.Run run) { //Create list of target isolationwindows to serve as swathnumber List <double> swathTargets = new List <double>(); swathTargets = run.Ms2Scans.OrderBy(y => y.ScanStartTime).Select(x => x.IsolationWindowTargetMz).Distinct().ToList(); double totalTIC = 0; foreach (var scan in run.Ms2Scans) { totalTIC += scan.TotalIonCurrent; } //Loop through every swath of a certain swathNumber: List <int> numOfSwathPerGroup = new List <int>(); List <double> TICs = new List <double>(); List <double> swDensity = new List <double>(); List <double> swDensity50 = new List <double>(); List <double> swDensityIQR = new List <double>(); List <double> mzTargetRange = new List <double>(); List <double> averageMzTargetRange = new List <double>(); List <double> SwathProportionOfTotalTIC = new List <double>(); List <double> TotalSwathProportionPredictedSingleCharge = new List <double>(); List <double> SwathProportionPredictedSingleChargeAvg = new List <double>(); //Loop through all the swaths of the same number and add to for (int swathNumber = 0; swathNumber < swathTargets.Count(); swathNumber++) { int track = 0; double TICthisSwath = 0; var orderedMS2Scans = run.Ms2Scans.OrderBy(s => s.ScanStartTime) .Where(x => x.MsLevel == 2 && x.IsolationWindowTargetMz == swathTargets[swathNumber]); foreach (var scan in orderedMS2Scans) { mzTargetRange.Add(scan.IsolationWindowUpperOffset + scan.IsolationWindowLowerOffset); TICthisSwath = TICthisSwath + scan.TotalIonCurrent; swDensity.Add(scan.Density); TotalSwathProportionPredictedSingleCharge.Add(scan.ProportionChargeStateOne); //The chargestate one's we pick up is where there is a match for M+1. Therefore we need to double it to add the M. track++; } averageMzTargetRange.Add(mzTargetRange.Average()); numOfSwathPerGroup.Add(track); TICs.Add(TICthisSwath); TICthisSwath = 0; SwathProportionPredictedSingleChargeAvg.Add(TotalSwathProportionPredictedSingleCharge.Average()); TotalSwathProportionPredictedSingleCharge.Clear(); swDensity.Sort(); swDensity50.Add(Math.Truncate(Math.Ceiling(swDensity.Average()))); swDensityIQR.Add(Math.Truncate(Math.Ceiling(InterQuartileRangeCalculator.CalcIQR(swDensity)))); swDensity.Clear(); mzTargetRange.Clear(); } for (int num = 0; num < swathTargets.Count(); num++) { SwathProportionOfTotalTIC.Add(TICs[num] / totalTIC); } SwathMetrics swathMetrics = new SwathMetrics(swathTargets, totalTIC, numOfSwathPerGroup, averageMzTargetRange, TICs, swDensity50, swDensityIQR, SwathProportionOfTotalTIC, SwathProportionPredictedSingleChargeAvg); return(swathMetrics); }