/// <summary> /// Removes the overlap from the boxcar ranges /// so that each boxcar shares a startpoint with the endpoint of the previous car /// and an endpoint with the startpoint of the next car. /// /// Does this process by taking the mean between the boxcar ranges and setting the cars' start and endpoints to the means of their neighbors. /// Overlap removal is important because when these boxcar ranges are matched with the scan data, they will remove the edge effects from the scan. /// Edge effects: sometimes the scan picks up on peaks that it shouldn't near the beginning and end of that boxcar. /// /// EXAMPLE: /// /// boxcars[0] boxcars[1] boxcars[2] ... (if you took more kinds of boxcar scans they would be additional columns) /// (400, 426) (424, 451) (449, 476) /// (474, 501) (499, 526) (524, 551) (these rows are the BoxcarRanges) /// . . . /// . . . /// . . . /// (1124, 1151) (1149, 1176) (1174,1201) /// /// How the loop works: /// there are x columns and y rows. /// Each column is a SetOfBoxcarRanges (which is stored in an arraylist) /// Each row contains some BoxcarRanges, and if you read across the rows or down the columns they increase. /// The nested for loops look at the rows first, and then the columns. /// This way, each BoxcarRange that is looped through is greater than the last BoxcarRange and you can calculate the means. /// /// So, once the function loops through, the entries are changed to: /// /// boxcars[0] boxcars[1] boxcars[2] ... /// (400, 425) (425, 450) (450, 475) /// (475, 500) (500, 525) (525, 550) /// . . . /// . . . /// . . . /// (1125, 1150) (1150, 1175) (1175,1201) - the last entry (and the first entry) doesn't change because there's no next entry to compute the mean /// /// In reality, the intervals are not all the same, so it's slightly more complicated than the example /// /// </summary> /// <param name="boxcars"></param> /// <returns></returns> public static SetOfBoxcarRanges[] RemoveOverlap(SetOfBoxcarRanges[] boxcars) { int numBoxcarScans = boxcars.Count(); SetOfBoxcarRanges[] newBoxcars = new SetOfBoxcarRanges[numBoxcarScans]; if (numBoxcarScans > 1) { BoxcarRange rangeA = boxcars[0].ElementAt(0); BoxcarRange rangeB = boxcars[1].ElementAt(0); // loop through all complete rows and columns of the "matrix": for (int y = 0; y < boxcars[0].Count(); y++) // loops through all rows including a possibly incomplete last row w/ nulls/empty cells in it { for (int x = 0; x < numBoxcarScans; x++) { if (x < (numBoxcarScans - 1) && (y <= boxcars[x + 1].Count())) // if you aren't on the last column and there is an entry in the yth row of the next column, the rangeB is in the next column { rangeB = boxcars[x + 1].ElementAt(y); } else if (x == (numBoxcarScans - 1) && (y != (boxcars[0].Count() - 1))) // if you're on the last column and there is another row (even a partial row), rangeB is the first column in the next row { rangeB = boxcars[0].ElementAt(y + 1); } else // if you've reached the last entry { return(boxcars); } // find the mean of the endpoint of rangeA and the startpoint of rangeB double endA = rangeA.End; double startB = rangeB.Start; double mean = CalculateMean(endA, startB); // change the endpoint of rangeA and the startpoint of rangeB to be that number rangeA.End = mean; rangeB.Start = mean; // insert rangeA and rangeB boxcars[x].ReplaceAtIndex(rangeA, y); if (x < (numBoxcarScans - 1) && (y <= boxcars[x + 1].Count())) // if you aren't on the last column, insert rangeB into the next column { boxcars[x + 1].ReplaceAtIndex(rangeB, y); } else if (x == (numBoxcarScans - 1) && (y != boxcars[0].Count())) // if you're on the last column, insert rangeB into the first column in the next row { boxcars[0].ReplaceAtIndex(rangeB, y + 1); } rangeA = rangeB; } } return(boxcars); } else { return(boxcars); } }
/// <summary> /// Helper method for FindBoxcarScan /// Finds the first and last index in the given scan where the m/z value in the scan is between the startpoint and endpoint of the given boxcar. /// The m/z value at the first index will be > the startpoint of the boxcar. /// The m/z value at the last index will be >= the endpoint of the boxcar. /// </summary> /// <param name="scan"></param> /// <param name="boxcar"></param> /// <returns></returns> BestScan object with a first index, last index, and total intensity of the scan. public static BestScan FindScanRange(MsDataScan scan, BoxcarRange range) { double[] xArray = scan.MassSpectrum.XArray; double[] yArray = scan.MassSpectrum.YArray; int len = xArray.Count(); // find the first index where the m/z values of the scan are in the range of the boxcar int firstIndex = 0; double firstValue = xArray[firstIndex]; bool nextIndexExists = true; while ((firstValue < range.Start) && nextIndexExists) { firstIndex++; firstValue = xArray[firstIndex]; if (!(firstIndex + 1 < len)) { nextIndexExists = false; } } // find the last index where the m/z values of the scan are in the range of the boxcar int lastIndex = firstIndex; double totalIntensity = yArray[lastIndex]; if (nextIndexExists) { int nextIndex = lastIndex + 1; double nextValue = xArray[nextIndex]; while (nextValue <= range.End && nextIndexExists) { lastIndex++; nextIndex++; totalIntensity += yArray[lastIndex]; if (nextIndex < len) { nextValue = xArray[nextIndex]; } else { nextIndexExists = false; } } } return(new BestScan(firstIndex, lastIndex, totalIntensity, scan)); }
/// <summary> /// Helper method for CombineScans /// Identifies which scan in the set has the most points in the boxcar range /// </summary> /// <param name="set"></param> /// <param name="boxcar"></param> BestScan object with first index, last index, total intensity, and scan public static BestScan FindBoxcarScan(SetOfScans set, BoxcarRange range, int count) { double bestIntensity = 0; MsDataScan bestScan = set.BoxcarScans[0]; int bestFirstIndex = 0; int bestLastIndex = 0; int bestLen = 0; List <MsDataScan> boxcarScans = set.BoxcarScans; // Check each scan to find the one that corresponds best to the boxcar scan range foreach (var scan in boxcarScans) { BestScan bestrange = FindScanRange(scan, range); // the scan is better than the previous scan if: // it has more m/z values in the range, and if // the total intensity is higher int firstIndex = bestrange.FirstIndex; int lastIndex = bestrange.LastIndex; int currentLen = lastIndex - firstIndex; if (currentLen > bestLen) { if (bestrange.TotalIntensity < bestIntensity) { Console.WriteLine("ACK! scan#: " + count + " has a lot of very low intensity scans and may have been incorrectly counted in a boxcar range!"); } // if the scan has more m/z points in the boxcar range than the previously best scan, update variables. bestLen = currentLen; bestLastIndex = lastIndex; bestFirstIndex = firstIndex; bestScan = scan; } } return(new BestScan(bestFirstIndex, bestLastIndex, bestIntensity, bestScan)); }
/// <summary> /// Inserts the given BoxcarRange object at the specified index. /// Replaces the previous object at that index. /// </summary> /// <param name="boxcarRange"></param> /// <param name="index"></param> public void ReplaceAtIndex(BoxcarRange boxcarRange, int index) { Set[index] = boxcarRange; }
/// <summary> /// Add a BoxcarRange object at the end of the SetOfBoxcarRanges. /// </summary> /// <param name="boxcarRange"></param> public void AddBoxcarRange(BoxcarRange boxcarRange) { Set.Add(boxcarRange); }