예제 #1
0
        private void ExamineNarrowPeaks(PeakDataContainer peakData, udtSICPeakFinderOptionsType peakFinderOptions)
        {
            if (peakData.Peaks.Count <= 0)
            {
                // No peaks were found; create a new peak list using the original peak location index as the peak center
                peakData.Peaks = new List <clsPeak>
                {
                    new clsPeak(peakData.OriginalPeakLocationIndex)
                };

                return;
            }

            if (!peakFinderOptions.ReturnClosestPeak)
            {
                return;
            }

            // Make sure one of the peaks is within 1 of the original peak location
            var blnSuccess = false;

            foreach (var peak in peakData.Peaks)
            {
                if (peak.LocationIndex - peakData.OriginalPeakLocationIndex <= 1)
                {
                    blnSuccess = true;
                    break;
                }
            }

            if (blnSuccess)
            {
                // One of the peaks includes data point peakData.OriginalPeakLocationIndex
                return;
            }

            // None of the peaks includes peakData.OriginalPeakLocationIndex
            var newPeak = new clsPeak(peakData.OriginalPeakLocationIndex)
            {
                Area = peakData.YData[peakData.OriginalPeakLocationIndex]
            };

            peakData.Peaks.Add(newPeak);
        }
예제 #2
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="peakDetector"></param>
        /// <param name="scanNumbers"></param>
        /// <param name="peakData"></param>
        /// <param name="peakFinderOptions"></param>
        /// <returns>Detected peaks will be in the peakData object</returns>
        private bool FindPeaksWork(
            PeakFinder peakDetector,
            IList <int> scanNumbers,
            PeakDataContainer peakData,
            udtSICPeakFinderOptionsType peakFinderOptions)
        {
            const float sngPeakMaximum = 0;

            bool validPeakFound;

            // Smooth the Y data, and store in peakData.SmoothedYData
            // Note that if using a Butterworth filter, then we increase peakData.PeakWidthPointsMinimum if too small, compared to 1/SamplingFrequency
            var peakWidthPointsMinimum = peakData.PeakWidthPointsMinimum;

            var dataIsSmoothed = SmoothData(peakData.YData, peakData.DataCount, peakFinderOptions, ref peakWidthPointsMinimum, out var smoothedYData, out var errorMessage);

            // peakWidthPointsMinimum may have been auto-updated
            peakData.PeakWidthPointsMinimum = peakWidthPointsMinimum;

            // Store the smoothed data in the data container
            peakData.SetSmoothedData(smoothedYData);

            var        peakDetectIntensityThresholdPercentageOfMaximum = (int)Math.Round(peakFinderOptions.IntensityThresholdFractionMax * 100);
            const int  peakWidthInSigma               = 2;
            const bool useValleysForPeakWidth         = true;
            const bool movePeakLocationToMaxIntensity = true;

            if (peakFinderOptions.FindPeaksOnSmoothedData && dataIsSmoothed)
            {
                peakData.Peaks = peakDetector.DetectPeaks(
                    peakData.XData, peakData.SmoothedYData,
                    peakFinderOptions.IntensityThresholdAbsoluteMinimum,
                    peakData.PeakWidthPointsMinimum,
                    peakDetectIntensityThresholdPercentageOfMaximum,
                    peakWidthInSigma,
                    useValleysForPeakWidth,
                    movePeakLocationToMaxIntensity);
            }
            else
            {
                // Look for the peaks, using peakData.PeakWidthPointsMinimum as the minimum peak width
                peakData.Peaks = peakDetector.DetectPeaks(
                    peakData.XData, peakData.YData,
                    peakFinderOptions.IntensityThresholdAbsoluteMinimum,
                    peakData.PeakWidthPointsMinimum,
                    peakDetectIntensityThresholdPercentageOfMaximum,
                    peakWidthInSigma,
                    useValleysForPeakWidth,
                    movePeakLocationToMaxIntensity);
            }

            if (peakData.Peaks == null)
            {
                // Fatal error occurred while finding peaks
                return(false);
            }

            if (peakData.PeakWidthPointsMinimum == MINIMUM_PEAK_WIDTH)
            {
                // Testing the minimum peak width; run some checks
                ExamineNarrowPeaks(peakData, peakFinderOptions);
            }

            if (peakData.Peaks.Count <= 0)
            {
                // No peaks were found
                return(false);
            }

            foreach (var peak in peakData.Peaks)
            {
                peak.IsValid = false;

                // Find the center and boundaries of this peak

                // Make sure peak.LocationIndex is between peak.LeftEdge and peak.RightEdge
                if (peak.LeftEdge > peak.LocationIndex)
                {
                    Console.WriteLine("peak.LeftEdge is > peak.LocationIndex; this is probably a programming error");
                    peak.LeftEdge = peak.LocationIndex;
                }

                if (peak.RightEdge < peak.LocationIndex)
                {
                    Console.WriteLine("peak.RightEdge is < peak.LocationIndex; this is probably a programming error");
                    peak.RightEdge = peak.LocationIndex;
                }

                // See if the peak boundaries (left and right edges) need to be narrowed or expanded
                // Do this by stepping left or right while the intensity is decreasing.  If an increase is found, but the
                // next point after the increasing point is less than the current point, then possibly keep stepping; the
                // test for whether to keep stepping is that the next point away from the increasing point must be less
                // than the current point.  If this is the case, replace the increasing point with the average of the
                // current point and the point two points away
                //
                // Use smoothed data for this step
                // Determine the smoothing window based on peakData.PeakWidthPointsMinimum
                // If peakData.PeakWidthPointsMinimum <= 4 then do not filter

                if (!dataIsSmoothed)
                {
                    // Need to smooth the data now
                    peakWidthPointsMinimum = peakData.PeakWidthPointsMinimum;

                    dataIsSmoothed = SmoothData(
                        peakData.YData,
                        peakData.DataCount,
                        peakFinderOptions,
                        ref peakWidthPointsMinimum,
                        out smoothedYData,
                        out errorMessage);

                    // peakWidthPointsMinimum may have been auto-updated
                    peakData.PeakWidthPointsMinimum = peakWidthPointsMinimum;

                    // Store the smoothed data in the data container
                    peakData.SetSmoothedData(smoothedYData);
                }

                // First see if we need to narrow the peak by looking for decreasing intensities moving toward the peak center
                // We'll use the unsmoothed data for this
                while (peak.LeftEdge < peak.LocationIndex - 1)
                {
                    if (peakData.YData[peak.LeftEdge] > peakData.YData[peak.LeftEdge + 1])
                    {
                        // OrElse (usedSmoothedDataForPeakDetection AndAlso peakData.SmoothedYData[peak.LeftEdge) < 0) Then
                        peak.LeftEdge += 1;
                    }
                    else
                    {
                        break;
                    }
                }

                while (peak.RightEdge > peak.LocationIndex + 1)
                {
                    if (peakData.YData[peak.RightEdge - 1] < peakData.YData[peak.RightEdge])
                    {
                        // OrElse (usedSmoothedDataForPeakDetection AndAlso peakData.SmoothedYData[peak.RightEdge) < 0) Then
                        peak.RightEdge -= 1;
                    }
                    else
                    {
                        break;
                    }
                }

                // Now see if we need to expand the peak by looking for decreasing intensities moving away from the peak center,
                //  but allowing for small increases
                // We'll use the smoothed data for this; if we encounter negative values in the smoothed data, we'll keep going until we reach the low point since huge peaks can cause some odd behavior with the Butterworth filter
                // Keep track of the number of times we step over an increased value

                ExpandPeakLeftEdge(peakData, peakFinderOptions, peak, sngPeakMaximum, dataIsSmoothed);

                ExpandPeakRightEdge(peakData, peakFinderOptions, peak, sngPeakMaximum, dataIsSmoothed);

                peak.IsValid = true;
                if (!peakFinderOptions.ReturnClosestPeak)
                {
                    continue;
                }

                // If peakData.OriginalPeakLocationIndex is not between peak.LeftEdge and peak.RightEdge, then check
                //  if the scan number for peakData.OriginalPeakLocationIndex is within .MaxDistanceScansNoOverlap scans of
                //  either of the peak edges; if not, then mark the peak as invalid since it does not contain the
                //  scan for the parent ion
                if (peakData.OriginalPeakLocationIndex < peak.LeftEdge)
                {
                    if (
                        Math.Abs(scanNumbers[peakData.OriginalPeakLocationIndex] -
                                 scanNumbers[peak.LeftEdge]) >
                        peakFinderOptions.MaxDistanceScansNoOverlap)
                    {
                        peak.IsValid = false;
                    }
                }
                else if (peakData.OriginalPeakLocationIndex > peak.RightEdge)
                {
                    if (
                        Math.Abs(scanNumbers[peakData.OriginalPeakLocationIndex] -
                                 scanNumbers[peak.RightEdge]) >
                        peakFinderOptions.MaxDistanceScansNoOverlap)
                    {
                        peak.IsValid = false;
                    }
                }
            }

            // Find the peak with the largest area that has peakData.PeakIsValid = True
            peakData.BestPeak = null;
            var bestPeakArea = double.MinValue;

            foreach (var peak in peakData.Peaks)
            {
                if (peak.IsValid)
                {
                    if (peak.Area > bestPeakArea)
                    {
                        peakData.BestPeak = peak;
                        bestPeakArea      = peak.Area;
                    }
                }
            }

            if (peakData.BestPeak != null)
            {
                validPeakFound = true;
            }
            else
            {
                validPeakFound = false;
            }

            return(validPeakFound);
        }
예제 #3
0
        private void ExpandPeakRightEdge(
            PeakDataContainer peakData,
            udtSICPeakFinderOptionsType peakFinderOptions,
            clsPeak peak,
            float sngPeakMaximum,
            bool dataIsSmoothed)
        {
            var intStepOverIncreaseCount = 0;

            while (peak.RightEdge < peakData.DataCount - 1)
            {
                if (peakData.SmoothedYData[peak.RightEdge + 1] < peakData.SmoothedYData[peak.RightEdge])
                {
                    // The adjacent point is lower than the current point
                    peak.RightEdge += 1;
                }
                else if (Math.Abs(peakData.SmoothedYData[peak.RightEdge + 1] -
                                  peakData.SmoothedYData[peak.RightEdge]) < double.Epsilon)
                {
                    // The adjacent point is equal to the current point
                    peak.RightEdge += 1;
                }
                else
                {
                    // The next point to the right is not lower; what about the point after it?
                    if (peak.RightEdge < peakData.DataCount - 2)
                    {
                        if (peakData.SmoothedYData[peak.RightEdge + 2] <=
                            peakData.SmoothedYData[peak.RightEdge])
                        {
                            // Only allow ignoring an upward spike if the delta from this point to the next is <= .MaxAllowedUpwardSpikeFractionMax of sngPeakMaximum
                            if (peakData.SmoothedYData[peak.RightEdge + 1] -
                                peakData.SmoothedYData[peak.RightEdge] >
                                peakFinderOptions.MaxAllowedUpwardSpikeFractionMax * sngPeakMaximum)
                            {
                                break;
                            }

                            if (dataIsSmoothed)
                            {
                                // Only ignore an upward spike twice if the data is smoothed
                                if (intStepOverIncreaseCount >= 2)
                                {
                                    break;
                                }
                            }

                            peak.RightEdge += 1;

                            intStepOverIncreaseCount += 1;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }
예제 #4
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="peakFinderOptions"></param>
        /// <param name="xyData"></param>
        /// <param name="originalPeakLocationIndex">
        /// Data point index in the x values that should be a part of the peak
        /// Used for determining the best peak</param>
        /// <param name="smoothedYData">Smoothed Y values</param>
        /// <returns></returns>
        public List <clsPeak> FindPeaks(
            udtSICPeakFinderOptionsType peakFinderOptions,
            List <KeyValuePair <int, double> > xyData,
            int originalPeakLocationIndex,
            out List <double> smoothedYData)
        {
            if (xyData.Count == 0)
            {
                smoothedYData = new List <double>();
                return(new List <clsPeak>());
            }

            // Compute the potential peak area for this SIC
            var udtSICPotentialAreaStatsForPeak = FindMinimumPotentialPeakArea(xyData, peakFinderOptions);

            // Estimate the noise level
            var        noiseAnalyzer         = new NoiseLevelAnalyzer();
            const bool ignoreNonPositiveData = false;

            var intensityData = new double[xyData.Count];
            var scanNumbers   = new int[xyData.Count];

            for (var index = 0; index < xyData.Count; index++)
            {
                scanNumbers[index]   = xyData[index].Key;
                intensityData[index] = xyData[index].Value;
            }

            noiseAnalyzer.ComputeTrimmedNoiseLevel(intensityData, 0, intensityData.Length - 1,
                                                   peakFinderOptions.SICBaselineNoiseOptions, ignoreNonPositiveData,
                                                   out var udtBaselineNoiseStats);

            // Find maximumPotentialPeakArea and dataPointCountAboveThreshold
            var maximumPotentialPeakArea = FindMaximumPotentialPeakArea(intensityData, peakFinderOptions, udtBaselineNoiseStats, out var dataPointCountAboveThreshold);

            if (maximumPotentialPeakArea < 1)
            {
                maximumPotentialPeakArea = 1;
            }

            var areaBasedSignalToNoise = maximumPotentialPeakArea / udtSICPotentialAreaStatsForPeak.MinimumPotentialPeakArea;

            if (areaBasedSignalToNoise < 1)
            {
                areaBasedSignalToNoise = 1;
            }

            var peakDetector = new PeakFinder();

            var peakData = new PeakDataContainer();

            peakData.SetData(xyData);

            if (Math.Abs(peakFinderOptions.ButterworthSamplingFrequency) < float.Epsilon)
            {
                peakFinderOptions.ButterworthSamplingFrequency = 0.25f;
            }

            peakData.PeakWidthPointsMinimum = (int)Math.Round(peakFinderOptions.InitialPeakWidthScansScaler * Math.Log10(Math.Floor(areaBasedSignalToNoise)) * 10);

            // Assure that .InitialPeakWidthScansMaximum is no greater than .InitialPeakWidthScansMaximum
            //  and no greater than dataPointCountAboveThreshold/2 (rounded up)
            peakData.PeakWidthPointsMinimum = Math.Min(peakData.PeakWidthPointsMinimum, peakFinderOptions.InitialPeakWidthScansMaximum);
            peakData.PeakWidthPointsMinimum = Math.Min(peakData.PeakWidthPointsMinimum, (int)Math.Ceiling(dataPointCountAboveThreshold / 2.0));

            if (peakData.PeakWidthPointsMinimum > peakData.DataCount * 0.8)
            {
                peakData.PeakWidthPointsMinimum = (int)Math.Floor(peakData.DataCount * 0.8);
            }

            if (peakData.PeakWidthPointsMinimum < MINIMUM_PEAK_WIDTH)
            {
                peakData.PeakWidthPointsMinimum = MINIMUM_PEAK_WIDTH;
            }

            peakData.OriginalPeakLocationIndex = originalPeakLocationIndex;

            var peakFoundContainingOriginalPeakLocation = FindPeaksWork(
                peakDetector,
                scanNumbers,
                peakData,
                peakFinderOptions);

            smoothedYData = peakData.SmoothedYData.ToList();

            return(peakData.Peaks);
        }