/// <summary> /// Generates candidate matches between alignee features and baseline features. /// It does so by finding all alignee-baseline feature pairs that match within a provided /// mass tolerance window. /// This method matches in mass only. /// </summary> /// <param name="aligneeFeatures"></param> /// <param name="baselineFeatures"></param> /// <param name="separationTypes">Separation types to include in matching.</param> public void GenerateCandidateMatches(List <UMCLight> aligneeFeatures, List <UMCLight> baselineFeatures, IEnumerable <FeatureLight.SeparationTypes> separationTypes = null) { // Sort features by mass var massComparer = new UMCLight.UmcMassComparer(); aligneeFeatures.Sort(massComparer); baselineFeatures.Sort(massComparer); // Go through each MassTimeFeature and see if the next baseline MassTimeFeature matches it var baselineFeatureIndex = 0; var featureMatches = new List <LcmsWarpFeatureMatch>(); foreach (var aligneeFeature in aligneeFeatures) { // Convert tolerance from ppm to Dalton var massToleranceDa = aligneeFeature.MassMonoisotopic * this.options.MassTolerance / 1000000; // Backtrack baselineFeatureIndex while the baseline feature's mass is greater than the candidate feature's mass minus massToleranceDa while (baselineFeatureIndex == baselineFeatures.Count || baselineFeatureIndex >= 0 && (baselineFeatures[baselineFeatureIndex].MassMonoisotopic > aligneeFeature.MassMonoisotopic - massToleranceDa)) { baselineFeatureIndex--; } baselineFeatureIndex++; // Add candidate matches while (baselineFeatureIndex < baselineFeatures.Count && (baselineFeatures[baselineFeatureIndex].MassMonoisotopic < (aligneeFeature.MassMonoisotopic + massToleranceDa))) { var baselineFeature = baselineFeatures[baselineFeatureIndex]; if (baselineFeature.MassMonoisotopic > (aligneeFeature.MassMonoisotopic - massToleranceDa)) { // Feature is within mass tolerance, add the match. var matchToAdd = new LcmsWarpFeatureMatch { AligneeFeature = aligneeFeature, BaselineFeature = baselineFeature, Net = aligneeFeature.Net, BaselineNet = baselineFeature.Net }; featureMatches.Add(matchToAdd); } baselineFeatureIndex++; } } // Filter out ambiguous matches featureMatches = this.RemovePromiscuousMatches(featureMatches); this.Matches = featureMatches; }
public List <LcmsWarpFeatureMatch> CalculateAlignmentMatches(List <UMCLight> aligneeFeatures, List <UMCLight> baselineFeatures, double netStdDev, double massStdDev) { // Sort features by mass var massComparer = new UMCLight.UmcMassComparer(); aligneeFeatures.Sort(massComparer); baselineFeatures.Sort(massComparer); var baselineFeatureIndex = 0; var featureMatches = new List <LcmsWarpFeatureMatch>(); var minMatchScore = -0.5 * (this.options.MassTolerance * this.options.MassTolerance) / (massStdDev * massStdDev); minMatchScore -= 0.5 * (this.options.NetTolerance * this.options.NetTolerance) / (netStdDev * netStdDev); foreach (var aligneeFeature in aligneeFeatures) { // Convert tolerance from ppm to Dalton var massTolerance = aligneeFeature.MassMonoisotopic * this.options.MassTolerance / 1000000; while (baselineFeatureIndex == baselineFeatures.Count || baselineFeatureIndex >= 0 && baselineFeatures[baselineFeatureIndex].MassMonoisotopic > aligneeFeature.MassMonoisotopic - massTolerance) { baselineFeatureIndex--; } baselineFeatureIndex++; LcmsWarpFeatureMatch bestMatchFeature = null; var bestMatchScore = minMatchScore; while (baselineFeatureIndex < baselineFeatures.Count && baselineFeatures[baselineFeatureIndex].MassMonoisotopic < aligneeFeature.MassMonoisotopic + massTolerance) { var baselineFeature = baselineFeatures[baselineFeatureIndex]; if (baselineFeature.MassMonoisotopic > aligneeFeature.MassMonoisotopic - massTolerance) { // Calculate the mass and net errors // Compute as observedValue - expectedValue var netDiff = aligneeFeature.NetAligned - baselineFeature.Net; var driftDiff = aligneeFeature.DriftTime - baselineFeature.DriftTime; var massDiff = aligneeFeature.MassMonoisotopic - baselineFeature.MassMonoisotopic; var massDiffPpm = massDiff * 1000000.0 / baselineFeature.MassMonoisotopic; var massDiffOriginal = aligneeFeature.MassMonoisotopicOriginal - baselineFeature.MassMonoisotopic; var originalMassDiffPpm = massDiffOriginal * 1000000.0 / baselineFeature.MassMonoisotopic; // Calculate the match score. var matchScore = -0.5 * (netDiff * netDiff) / (netStdDev * netStdDev); matchScore -= 0.5 * (massDiffPpm * massDiffPpm) / (massStdDev * massStdDev); // If the match score is greater than the best match score, update the holding item. if (matchScore > bestMatchScore) { bestMatchScore = matchScore; bestMatchFeature = new LcmsWarpFeatureMatch { AligneeFeature = aligneeFeature, BaselineFeature = baselineFeature, BaselineFeatureIndex = baselineFeatureIndex, Net = aligneeFeature.Net, NetError = netDiff, MassError = massDiff, PpmMassError = massDiffPpm, PpmMassErrorOriginal = originalMassDiffPpm, DriftError = driftDiff, BaselineNet = baselineFeatures[baselineFeatureIndex].Net }; } } baselineFeatureIndex++; } // If we found a match, add it to the list of matches. if (bestMatchFeature != null) { featureMatches.Add(bestMatchFeature); } } return(featureMatches); }