public void TestSegment() { List<float> counts = new List<float>() {100, 90, 110, 100, 95, 105}; CanvasSegment seg1 = new CanvasSegment("chr17", 100000000, 110000000, counts); // Silly constructor tests: Assert.AreEqual(seg1.Begin, 100000000); Assert.AreEqual(seg1.End, 110000000); Assert.AreEqual(seg1.BinCount, counts.Count); Assert.AreEqual(seg1.Chr, "chr17"); // Property test: Assert.AreEqual(seg1.MeanCount, 100, 0.01); // Build a second segment, and merge them, and test results: CanvasSegment seg2 = new CanvasSegment("chr17", 110000000, 120000000, counts); seg1.MergeIn(seg2); Assert.AreEqual(seg1.Counts.Count, 12); Assert.AreEqual(seg1.End, seg2.End); }
/// <summary> /// Iterates through a list of segments and merges those which have the same copy number call. /// Also, for segments smaller than MinimumCallSize, assimilate them into the neighbor with the best /// quality score. Two consecutive segments are considered neighbors if they're on the same chromosome /// and the space between them doesn't overlap with any excluded intervals. /// </summary> static public void MergeSegmentsUsingExcludedIntervals(ref List <CanvasSegment> segments, int MinimumCallSize, Dictionary <string, List <SampleGenomicBin> > excludedIntervals) { if (!segments.Any()) { return; } // Assimilate short segments into the *best* available neighbor: List <CanvasSegment> mergedSegments = new List <CanvasSegment>(); int segmentIndex = 0; while (segmentIndex < segments.Count) { if (segments[segmentIndex].End - segments[segmentIndex].Begin >= MinimumCallSize) { mergedSegments.Add(segments[segmentIndex]); segmentIndex++; continue; } int prevIndex = -1; double prevQ = 0; // Look back for a segment: for (int checkIndex = segmentIndex - 1; checkIndex > 0; checkIndex--) { // Stop, if you jump to another chromosome, or cross a forbidden interval: if (segments[checkIndex].Chr != segments[segmentIndex].Chr) { break; } if (segments[checkIndex].End - segments[checkIndex].Begin < MinimumCallSize) { continue; } if (IsForbiddenInterval(segments[checkIndex].Chr, segments[checkIndex].End, segments[segmentIndex].Begin, excludedIntervals)) { break; } prevIndex = checkIndex; prevQ = segments[checkIndex].QScore; break; } // Look forward for a segment: int nextIndex = -1; double nextQ = 0; for (int checkIndex = segmentIndex + 1; checkIndex < segments.Count; checkIndex++) { if (segments[checkIndex].Chr != segments[segmentIndex].Chr) { break; } if (segments[checkIndex].End - segments[checkIndex].Begin < MinimumCallSize) { continue; } if (IsForbiddenInterval(segments[checkIndex].Chr, segments[segmentIndex].End, segments[checkIndex].Begin, excludedIntervals)) { break; } nextIndex = checkIndex; nextQ = segments[checkIndex].QScore; break; } if (prevQ > 0 && prevQ >= nextQ) { // segments[prevIndex] assimilates segments[prevIndex+1...segmentIndex]. // Assimilation of previous segments was already done, so we just need to assimilate this one: segments[prevIndex].MergeIn(segments[segmentIndex]); segmentIndex++; continue; } if (nextQ > 0) { // segments[nextIndex] assimilates segments[segmentIndex...nextIndex - 1] for (int tempIndex = segmentIndex; tempIndex < nextIndex; tempIndex++) { segments[nextIndex].MergeIn(segments[tempIndex]); } segmentIndex = nextIndex; continue; } mergedSegments.Add(segments[segmentIndex]); segmentIndex++; } segments = mergedSegments; // Now, merge together adjacent segments with same calls! mergedSegments = new List <CanvasSegment>(); CanvasSegment lastSegment = segments[0]; mergedSegments.Add(lastSegment); segmentIndex = 1; while (segmentIndex < segments.Count) { // Assimilate an adjacent segment with the same copy number call and heterogeneity flag: if (lastSegment.CopyNumber == segments[segmentIndex].CopyNumber && lastSegment.Chr == segments[segmentIndex].Chr && !IsForbiddenInterval(lastSegment.Chr, lastSegment.End, segments[segmentIndex].Begin, excludedIntervals) && lastSegment.IsHeterogeneous == segments[segmentIndex].IsHeterogeneous) { lastSegment.MergeIn(segments[segmentIndex]); segmentIndex++; continue; } lastSegment = segments[segmentIndex]; mergedSegments.Add(segments[segmentIndex]); segmentIndex++; } segments = mergedSegments; }
/// <summary> /// Iterates through a list of segments and merges those which have the same copy number call. /// Also, for segments smaller than MinimumCallSize, assimilate them into the neighbor with the best /// quality score. Two consecutive segments are considered neighbors if they're on the same chromosome /// and the space between them is not too large. /// </summary> static public void MergeSegments(ref List <CanvasSegment> segments, int MinimumCallSize = 0, int maximumMergeSpan = 10000) { if (!segments.Any()) { return; } // Assimilate short segments into the *best* available neighbor: List <CanvasSegment> mergedSegments = new List <CanvasSegment>(); int segmentIndex = 0; while (segmentIndex < segments.Count) { if (segments[segmentIndex].End - segments[segmentIndex].Begin >= MinimumCallSize) { mergedSegments.Add(segments[segmentIndex]); segmentIndex++; continue; } int prevIndex = -1; double prevQ = -1; // Look back for a segment: for (int checkIndex = segmentIndex - 1; checkIndex >= 0; checkIndex--) { // Stop, if you jump to another chromosome, or cross a forbidden interval: if (segments[checkIndex].Chr != segments[segmentIndex].Chr) { break; } if (segments[checkIndex].End - segments[checkIndex].Begin < MinimumCallSize) { continue; } if (segments[segmentIndex].Begin - segments[checkIndex].End > maximumMergeSpan) { break; } prevIndex = checkIndex; prevQ = segments[checkIndex].QScore; break; } // Look forward for a segment: int nextIndex = -1; double nextQ = -1; for (int checkIndex = segmentIndex + 1; checkIndex < segments.Count; checkIndex++) { if (segments[checkIndex].Chr != segments[segmentIndex].Chr) { break; } if (segments[checkIndex].End - segments[checkIndex].Begin < MinimumCallSize) { continue; } if (segments[checkIndex].Begin - segments[segmentIndex].End > maximumMergeSpan) { continue; } nextIndex = checkIndex; nextQ = segments[checkIndex].QScore; break; } if (prevQ >= 0 && prevQ >= nextQ) { // segments[prevIndex] assimilates segments[prevIndex+1...segmentIndex]. // Assimilation of previous segments was already done, so we just need to assimilate this one: segments[prevIndex].MergeIn(segments[segmentIndex]); segmentIndex++; continue; } if (nextQ >= 0) { // segments[nextIndex] assimilates segments[segmentIndex...nextIndex - 1] for (int tempIndex = segmentIndex; tempIndex < nextIndex; tempIndex++) { segments[nextIndex].MergeIn(segments[tempIndex]); } segmentIndex = nextIndex; continue; } mergedSegments.Add(segments[segmentIndex]); segmentIndex++; } segments = mergedSegments; // Now, merge together adjacent segments with same calls! mergedSegments = new List <CanvasSegment>(); CanvasSegment lastSegment = segments[0]; mergedSegments.Add(lastSegment); segmentIndex = 1; while (segmentIndex < segments.Count) { // Assimilate an adjacent segment with the same copy number call: if (lastSegment.copyNumber == segments[segmentIndex].copyNumber && lastSegment.Chr == segments[segmentIndex].Chr && segments[segmentIndex].Begin - lastSegment.End < maximumMergeSpan) { lastSegment.MergeIn(segments[segmentIndex]); segmentIndex++; continue; } lastSegment = segments[segmentIndex]; mergedSegments.Add(segments[segmentIndex]); segmentIndex++; } segments = mergedSegments; }