Find a local maximum in list without using built-in function FindPeaks











up vote
3
down vote

favorite
1












I am interested finding the peaks in data without using the function FindPeaks. Any suggestion on how to do it effectively?



data1=RandomInteger[{1, 3997670}, 3936];
peaks = FindPeaks[data1]
ListLinePlot[
data1,
Epilog -> {Red, PointSize[0.01], Point[peaks]}
]









share|improve this question




























    up vote
    3
    down vote

    favorite
    1












    I am interested finding the peaks in data without using the function FindPeaks. Any suggestion on how to do it effectively?



    data1=RandomInteger[{1, 3997670}, 3936];
    peaks = FindPeaks[data1]
    ListLinePlot[
    data1,
    Epilog -> {Red, PointSize[0.01], Point[peaks]}
    ]









    share|improve this question


























      up vote
      3
      down vote

      favorite
      1









      up vote
      3
      down vote

      favorite
      1






      1





      I am interested finding the peaks in data without using the function FindPeaks. Any suggestion on how to do it effectively?



      data1=RandomInteger[{1, 3997670}, 3936];
      peaks = FindPeaks[data1]
      ListLinePlot[
      data1,
      Epilog -> {Red, PointSize[0.01], Point[peaks]}
      ]









      share|improve this question















      I am interested finding the peaks in data without using the function FindPeaks. Any suggestion on how to do it effectively?



      data1=RandomInteger[{1, 3997670}, 3936];
      peaks = FindPeaks[data1]
      ListLinePlot[
      data1,
      Epilog -> {Red, PointSize[0.01], Point[peaks]}
      ]






      list-manipulation






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 2 days ago









      kglr

      173k8194400




      173k8194400










      asked 2 days ago









      Kiril Danilchenko

      714316




      714316






















          2 Answers
          2






          active

          oldest

          votes

















          up vote
          4
          down vote













          You could try something like:



          pks = {};

          If[data1[[1]] > data1[[2]],
          AppendTo[pks, {1, data1[[1]]}]];

          For[i = 2, i <= Length[data1] - 1, i++,
          If[data1[[i - 1]] < data1[[i]] > data1[[i + 1]],
          AppendTo[pks, {i, data1[[i]]}]]];

          If[data1[[-1]] > data1[[-2]],
          AppendTo[pks, {Length[data1], data1[[-1]]}]];


          It's a simple and rather inelegant way of finding peaks. Basically, if a point is higher than the two points on either side, it's considered to be a peak. In the case of the first and last point, it's a peak if it's higher than the closest point. I'm not sure if that's what you mean by finding all the peaks.



          Is there a reason you can't use FindPeaks? It's almost always the best way. I didn't use it in my answer because you asked for an answer without, but I can get the exact same output with the following code:



          pks2 = FindPeaks[data1, 0, 0, -Infinity];


          If you want to find the local maximum over some range of values, you could do something like:



          Max[data1[[100;;200]]]


          It's position in the list can be found with:



          Position[data1, Max[data1[[100;;200]]]]


          Since you have a list, this will return the absolute local maximum. Calling Max on the entire list would of course return the global max.






          share|improve this answer








          New contributor




          MassDefect is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.


















          • If your data has noise, defining a peak as a point that is larger than its neighbors might not be useful. In the presence of noise, you could a) smooth the data, b) have a min threshold between the peak point and the closest neighbor, and/or c) require a peak point be a peak in some window in which points on the left increase while points on the right decrease (with some heuristic for constant sequences). Note that (c) is different from MassDefect's maximum over some range of values method. Other methods exist.
            – Robert Jacobson
            2 days ago










          • @RobertJacobson You're absolutely right. My algorithm is pretty weak. I'm not entirely clear on what kind of peaks the poster is looking for.
            – MassDefect
            yesterday


















          up vote
          4
          down vote













          The documentation describes a lot of how FindPeaks works, especially the section Scope > Parameters. It can all be done very efficiently with convolutions and vectorized operations.



          This is basic peak finding:



          findPeaks1[data_] := Module[{rightDiff, leftDiff, peaks},
          rightDiff = ListConvolve[{0, 1, -1}, data];
          leftDiff = ListConvolve[{-1, 1, 0}, data];
          Unitize[Sign[leftDiff] + Sign[rightDiff] - 2]
          ]


          The reason for the 1 at the end of the name will become clear later. The convolutions in this function perform almost the same job as Differences, in the end, we get a list that is 0 in those positions that correspond to values that are larger than both its neighbors.



          findPeaks1[data_, sigma_] := Module[{blurred},
          blurred = GaussianFilter[data, {3, sigma}];
          Unitize[findPeaks1[data] + findPeaks1[blurred]]
          ]


          In this step, we add the second argument, which corresponds to smoothing the data using a Gaussian filter with variance sigma. A zero in this list means that the corresponding element is a peak, using the definition in the previous function, in both the smoothed data and the original data.



          findPeaks1[data_, sigma_, s_] := Module[{sharpness},
          sharpness = ListConvolve[{1, -2, 1}, data];
          Unitize[findPeaks1[data, sigma] + 1 - Unitize[sharpness, s]]
          ]


          In this piece of code, we implemented the third argument, which says that the peaks that don't have a negative second derivative that is larger than s should be discarded. The convolution we use here is the definition for the negative discrete second derivative.



          findPeaks1[data_, sigma_, s_, t_] := Unitize[
          findPeaks1[data, sigma, s] + Unitize[UnitStep[Most@Rest@data - t] - 1]
          ]


          Finally, in this code block, we implement the fourth argument, which is a threshold. It says that the value of a peak must be larger than t.



          For efficiency reasons and because it made the implementation short and simple, these functions all pass lists of integers to each other. They have a 1 suffixed to their names to distinguish them from the interface which we shall now define:



          outpformat[data_, sel_] := Module[{pos, values},
          pos = Position[sel, 0] + 1;
          values = Extract[data, pos];
          Transpose[{Flatten[pos], values}]
          ]

          findPeaks[data_] := outpformat[data, findPeaks1[data]]
          findPeaks[data_, sigma_] := outpformat[data, findPeaks1[data, sigma]]
          findPeaks[data_, sigma_, s_] := outpformat[data, findPeaks1[data, sigma, s]]
          findPeaks[data_, sigma_, s_, t_] := outpformat[data, findPeaks1[data, sigma, s, t]]


          We now have a function findPeaks that does more or less the same thing as FindPeaks. I find that the result differs a bit for the Gaussian filter, they seem to do it a bit differently, but this still captures the idea.



          Of course, there are many other signal processing techniques that we could use to find peaks, but there is no universal answer to how the signal should be processed. If you find that this function is not enough, perhaps because the signal is noisy or something else, then you have to look at the signal to decide what filters and such might be appropriate to make the peaks you are looking for more distinguishable.






          share|improve this answer























            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["$", "$"], ["\\(","\\)"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "387"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














             

            draft saved


            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fmathematica.stackexchange.com%2fquestions%2f186630%2ffind-a-local-maximum-in-list-without-using-built-in-function-findpeaks%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            4
            down vote













            You could try something like:



            pks = {};

            If[data1[[1]] > data1[[2]],
            AppendTo[pks, {1, data1[[1]]}]];

            For[i = 2, i <= Length[data1] - 1, i++,
            If[data1[[i - 1]] < data1[[i]] > data1[[i + 1]],
            AppendTo[pks, {i, data1[[i]]}]]];

            If[data1[[-1]] > data1[[-2]],
            AppendTo[pks, {Length[data1], data1[[-1]]}]];


            It's a simple and rather inelegant way of finding peaks. Basically, if a point is higher than the two points on either side, it's considered to be a peak. In the case of the first and last point, it's a peak if it's higher than the closest point. I'm not sure if that's what you mean by finding all the peaks.



            Is there a reason you can't use FindPeaks? It's almost always the best way. I didn't use it in my answer because you asked for an answer without, but I can get the exact same output with the following code:



            pks2 = FindPeaks[data1, 0, 0, -Infinity];


            If you want to find the local maximum over some range of values, you could do something like:



            Max[data1[[100;;200]]]


            It's position in the list can be found with:



            Position[data1, Max[data1[[100;;200]]]]


            Since you have a list, this will return the absolute local maximum. Calling Max on the entire list would of course return the global max.






            share|improve this answer








            New contributor




            MassDefect is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.


















            • If your data has noise, defining a peak as a point that is larger than its neighbors might not be useful. In the presence of noise, you could a) smooth the data, b) have a min threshold between the peak point and the closest neighbor, and/or c) require a peak point be a peak in some window in which points on the left increase while points on the right decrease (with some heuristic for constant sequences). Note that (c) is different from MassDefect's maximum over some range of values method. Other methods exist.
              – Robert Jacobson
              2 days ago










            • @RobertJacobson You're absolutely right. My algorithm is pretty weak. I'm not entirely clear on what kind of peaks the poster is looking for.
              – MassDefect
              yesterday















            up vote
            4
            down vote













            You could try something like:



            pks = {};

            If[data1[[1]] > data1[[2]],
            AppendTo[pks, {1, data1[[1]]}]];

            For[i = 2, i <= Length[data1] - 1, i++,
            If[data1[[i - 1]] < data1[[i]] > data1[[i + 1]],
            AppendTo[pks, {i, data1[[i]]}]]];

            If[data1[[-1]] > data1[[-2]],
            AppendTo[pks, {Length[data1], data1[[-1]]}]];


            It's a simple and rather inelegant way of finding peaks. Basically, if a point is higher than the two points on either side, it's considered to be a peak. In the case of the first and last point, it's a peak if it's higher than the closest point. I'm not sure if that's what you mean by finding all the peaks.



            Is there a reason you can't use FindPeaks? It's almost always the best way. I didn't use it in my answer because you asked for an answer without, but I can get the exact same output with the following code:



            pks2 = FindPeaks[data1, 0, 0, -Infinity];


            If you want to find the local maximum over some range of values, you could do something like:



            Max[data1[[100;;200]]]


            It's position in the list can be found with:



            Position[data1, Max[data1[[100;;200]]]]


            Since you have a list, this will return the absolute local maximum. Calling Max on the entire list would of course return the global max.






            share|improve this answer








            New contributor




            MassDefect is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.


















            • If your data has noise, defining a peak as a point that is larger than its neighbors might not be useful. In the presence of noise, you could a) smooth the data, b) have a min threshold between the peak point and the closest neighbor, and/or c) require a peak point be a peak in some window in which points on the left increase while points on the right decrease (with some heuristic for constant sequences). Note that (c) is different from MassDefect's maximum over some range of values method. Other methods exist.
              – Robert Jacobson
              2 days ago










            • @RobertJacobson You're absolutely right. My algorithm is pretty weak. I'm not entirely clear on what kind of peaks the poster is looking for.
              – MassDefect
              yesterday













            up vote
            4
            down vote










            up vote
            4
            down vote









            You could try something like:



            pks = {};

            If[data1[[1]] > data1[[2]],
            AppendTo[pks, {1, data1[[1]]}]];

            For[i = 2, i <= Length[data1] - 1, i++,
            If[data1[[i - 1]] < data1[[i]] > data1[[i + 1]],
            AppendTo[pks, {i, data1[[i]]}]]];

            If[data1[[-1]] > data1[[-2]],
            AppendTo[pks, {Length[data1], data1[[-1]]}]];


            It's a simple and rather inelegant way of finding peaks. Basically, if a point is higher than the two points on either side, it's considered to be a peak. In the case of the first and last point, it's a peak if it's higher than the closest point. I'm not sure if that's what you mean by finding all the peaks.



            Is there a reason you can't use FindPeaks? It's almost always the best way. I didn't use it in my answer because you asked for an answer without, but I can get the exact same output with the following code:



            pks2 = FindPeaks[data1, 0, 0, -Infinity];


            If you want to find the local maximum over some range of values, you could do something like:



            Max[data1[[100;;200]]]


            It's position in the list can be found with:



            Position[data1, Max[data1[[100;;200]]]]


            Since you have a list, this will return the absolute local maximum. Calling Max on the entire list would of course return the global max.






            share|improve this answer








            New contributor




            MassDefect is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.









            You could try something like:



            pks = {};

            If[data1[[1]] > data1[[2]],
            AppendTo[pks, {1, data1[[1]]}]];

            For[i = 2, i <= Length[data1] - 1, i++,
            If[data1[[i - 1]] < data1[[i]] > data1[[i + 1]],
            AppendTo[pks, {i, data1[[i]]}]]];

            If[data1[[-1]] > data1[[-2]],
            AppendTo[pks, {Length[data1], data1[[-1]]}]];


            It's a simple and rather inelegant way of finding peaks. Basically, if a point is higher than the two points on either side, it's considered to be a peak. In the case of the first and last point, it's a peak if it's higher than the closest point. I'm not sure if that's what you mean by finding all the peaks.



            Is there a reason you can't use FindPeaks? It's almost always the best way. I didn't use it in my answer because you asked for an answer without, but I can get the exact same output with the following code:



            pks2 = FindPeaks[data1, 0, 0, -Infinity];


            If you want to find the local maximum over some range of values, you could do something like:



            Max[data1[[100;;200]]]


            It's position in the list can be found with:



            Position[data1, Max[data1[[100;;200]]]]


            Since you have a list, this will return the absolute local maximum. Calling Max on the entire list would of course return the global max.







            share|improve this answer








            New contributor




            MassDefect is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.









            share|improve this answer



            share|improve this answer






            New contributor




            MassDefect is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.









            answered 2 days ago









            MassDefect

            1111




            1111




            New contributor




            MassDefect is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.





            New contributor





            MassDefect is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.






            MassDefect is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.












            • If your data has noise, defining a peak as a point that is larger than its neighbors might not be useful. In the presence of noise, you could a) smooth the data, b) have a min threshold between the peak point and the closest neighbor, and/or c) require a peak point be a peak in some window in which points on the left increase while points on the right decrease (with some heuristic for constant sequences). Note that (c) is different from MassDefect's maximum over some range of values method. Other methods exist.
              – Robert Jacobson
              2 days ago










            • @RobertJacobson You're absolutely right. My algorithm is pretty weak. I'm not entirely clear on what kind of peaks the poster is looking for.
              – MassDefect
              yesterday


















            • If your data has noise, defining a peak as a point that is larger than its neighbors might not be useful. In the presence of noise, you could a) smooth the data, b) have a min threshold between the peak point and the closest neighbor, and/or c) require a peak point be a peak in some window in which points on the left increase while points on the right decrease (with some heuristic for constant sequences). Note that (c) is different from MassDefect's maximum over some range of values method. Other methods exist.
              – Robert Jacobson
              2 days ago










            • @RobertJacobson You're absolutely right. My algorithm is pretty weak. I'm not entirely clear on what kind of peaks the poster is looking for.
              – MassDefect
              yesterday
















            If your data has noise, defining a peak as a point that is larger than its neighbors might not be useful. In the presence of noise, you could a) smooth the data, b) have a min threshold between the peak point and the closest neighbor, and/or c) require a peak point be a peak in some window in which points on the left increase while points on the right decrease (with some heuristic for constant sequences). Note that (c) is different from MassDefect's maximum over some range of values method. Other methods exist.
            – Robert Jacobson
            2 days ago




            If your data has noise, defining a peak as a point that is larger than its neighbors might not be useful. In the presence of noise, you could a) smooth the data, b) have a min threshold between the peak point and the closest neighbor, and/or c) require a peak point be a peak in some window in which points on the left increase while points on the right decrease (with some heuristic for constant sequences). Note that (c) is different from MassDefect's maximum over some range of values method. Other methods exist.
            – Robert Jacobson
            2 days ago












            @RobertJacobson You're absolutely right. My algorithm is pretty weak. I'm not entirely clear on what kind of peaks the poster is looking for.
            – MassDefect
            yesterday




            @RobertJacobson You're absolutely right. My algorithm is pretty weak. I'm not entirely clear on what kind of peaks the poster is looking for.
            – MassDefect
            yesterday










            up vote
            4
            down vote













            The documentation describes a lot of how FindPeaks works, especially the section Scope > Parameters. It can all be done very efficiently with convolutions and vectorized operations.



            This is basic peak finding:



            findPeaks1[data_] := Module[{rightDiff, leftDiff, peaks},
            rightDiff = ListConvolve[{0, 1, -1}, data];
            leftDiff = ListConvolve[{-1, 1, 0}, data];
            Unitize[Sign[leftDiff] + Sign[rightDiff] - 2]
            ]


            The reason for the 1 at the end of the name will become clear later. The convolutions in this function perform almost the same job as Differences, in the end, we get a list that is 0 in those positions that correspond to values that are larger than both its neighbors.



            findPeaks1[data_, sigma_] := Module[{blurred},
            blurred = GaussianFilter[data, {3, sigma}];
            Unitize[findPeaks1[data] + findPeaks1[blurred]]
            ]


            In this step, we add the second argument, which corresponds to smoothing the data using a Gaussian filter with variance sigma. A zero in this list means that the corresponding element is a peak, using the definition in the previous function, in both the smoothed data and the original data.



            findPeaks1[data_, sigma_, s_] := Module[{sharpness},
            sharpness = ListConvolve[{1, -2, 1}, data];
            Unitize[findPeaks1[data, sigma] + 1 - Unitize[sharpness, s]]
            ]


            In this piece of code, we implemented the third argument, which says that the peaks that don't have a negative second derivative that is larger than s should be discarded. The convolution we use here is the definition for the negative discrete second derivative.



            findPeaks1[data_, sigma_, s_, t_] := Unitize[
            findPeaks1[data, sigma, s] + Unitize[UnitStep[Most@Rest@data - t] - 1]
            ]


            Finally, in this code block, we implement the fourth argument, which is a threshold. It says that the value of a peak must be larger than t.



            For efficiency reasons and because it made the implementation short and simple, these functions all pass lists of integers to each other. They have a 1 suffixed to their names to distinguish them from the interface which we shall now define:



            outpformat[data_, sel_] := Module[{pos, values},
            pos = Position[sel, 0] + 1;
            values = Extract[data, pos];
            Transpose[{Flatten[pos], values}]
            ]

            findPeaks[data_] := outpformat[data, findPeaks1[data]]
            findPeaks[data_, sigma_] := outpformat[data, findPeaks1[data, sigma]]
            findPeaks[data_, sigma_, s_] := outpformat[data, findPeaks1[data, sigma, s]]
            findPeaks[data_, sigma_, s_, t_] := outpformat[data, findPeaks1[data, sigma, s, t]]


            We now have a function findPeaks that does more or less the same thing as FindPeaks. I find that the result differs a bit for the Gaussian filter, they seem to do it a bit differently, but this still captures the idea.



            Of course, there are many other signal processing techniques that we could use to find peaks, but there is no universal answer to how the signal should be processed. If you find that this function is not enough, perhaps because the signal is noisy or something else, then you have to look at the signal to decide what filters and such might be appropriate to make the peaks you are looking for more distinguishable.






            share|improve this answer



























              up vote
              4
              down vote













              The documentation describes a lot of how FindPeaks works, especially the section Scope > Parameters. It can all be done very efficiently with convolutions and vectorized operations.



              This is basic peak finding:



              findPeaks1[data_] := Module[{rightDiff, leftDiff, peaks},
              rightDiff = ListConvolve[{0, 1, -1}, data];
              leftDiff = ListConvolve[{-1, 1, 0}, data];
              Unitize[Sign[leftDiff] + Sign[rightDiff] - 2]
              ]


              The reason for the 1 at the end of the name will become clear later. The convolutions in this function perform almost the same job as Differences, in the end, we get a list that is 0 in those positions that correspond to values that are larger than both its neighbors.



              findPeaks1[data_, sigma_] := Module[{blurred},
              blurred = GaussianFilter[data, {3, sigma}];
              Unitize[findPeaks1[data] + findPeaks1[blurred]]
              ]


              In this step, we add the second argument, which corresponds to smoothing the data using a Gaussian filter with variance sigma. A zero in this list means that the corresponding element is a peak, using the definition in the previous function, in both the smoothed data and the original data.



              findPeaks1[data_, sigma_, s_] := Module[{sharpness},
              sharpness = ListConvolve[{1, -2, 1}, data];
              Unitize[findPeaks1[data, sigma] + 1 - Unitize[sharpness, s]]
              ]


              In this piece of code, we implemented the third argument, which says that the peaks that don't have a negative second derivative that is larger than s should be discarded. The convolution we use here is the definition for the negative discrete second derivative.



              findPeaks1[data_, sigma_, s_, t_] := Unitize[
              findPeaks1[data, sigma, s] + Unitize[UnitStep[Most@Rest@data - t] - 1]
              ]


              Finally, in this code block, we implement the fourth argument, which is a threshold. It says that the value of a peak must be larger than t.



              For efficiency reasons and because it made the implementation short and simple, these functions all pass lists of integers to each other. They have a 1 suffixed to their names to distinguish them from the interface which we shall now define:



              outpformat[data_, sel_] := Module[{pos, values},
              pos = Position[sel, 0] + 1;
              values = Extract[data, pos];
              Transpose[{Flatten[pos], values}]
              ]

              findPeaks[data_] := outpformat[data, findPeaks1[data]]
              findPeaks[data_, sigma_] := outpformat[data, findPeaks1[data, sigma]]
              findPeaks[data_, sigma_, s_] := outpformat[data, findPeaks1[data, sigma, s]]
              findPeaks[data_, sigma_, s_, t_] := outpformat[data, findPeaks1[data, sigma, s, t]]


              We now have a function findPeaks that does more or less the same thing as FindPeaks. I find that the result differs a bit for the Gaussian filter, they seem to do it a bit differently, but this still captures the idea.



              Of course, there are many other signal processing techniques that we could use to find peaks, but there is no universal answer to how the signal should be processed. If you find that this function is not enough, perhaps because the signal is noisy or something else, then you have to look at the signal to decide what filters and such might be appropriate to make the peaks you are looking for more distinguishable.






              share|improve this answer

























                up vote
                4
                down vote










                up vote
                4
                down vote









                The documentation describes a lot of how FindPeaks works, especially the section Scope > Parameters. It can all be done very efficiently with convolutions and vectorized operations.



                This is basic peak finding:



                findPeaks1[data_] := Module[{rightDiff, leftDiff, peaks},
                rightDiff = ListConvolve[{0, 1, -1}, data];
                leftDiff = ListConvolve[{-1, 1, 0}, data];
                Unitize[Sign[leftDiff] + Sign[rightDiff] - 2]
                ]


                The reason for the 1 at the end of the name will become clear later. The convolutions in this function perform almost the same job as Differences, in the end, we get a list that is 0 in those positions that correspond to values that are larger than both its neighbors.



                findPeaks1[data_, sigma_] := Module[{blurred},
                blurred = GaussianFilter[data, {3, sigma}];
                Unitize[findPeaks1[data] + findPeaks1[blurred]]
                ]


                In this step, we add the second argument, which corresponds to smoothing the data using a Gaussian filter with variance sigma. A zero in this list means that the corresponding element is a peak, using the definition in the previous function, in both the smoothed data and the original data.



                findPeaks1[data_, sigma_, s_] := Module[{sharpness},
                sharpness = ListConvolve[{1, -2, 1}, data];
                Unitize[findPeaks1[data, sigma] + 1 - Unitize[sharpness, s]]
                ]


                In this piece of code, we implemented the third argument, which says that the peaks that don't have a negative second derivative that is larger than s should be discarded. The convolution we use here is the definition for the negative discrete second derivative.



                findPeaks1[data_, sigma_, s_, t_] := Unitize[
                findPeaks1[data, sigma, s] + Unitize[UnitStep[Most@Rest@data - t] - 1]
                ]


                Finally, in this code block, we implement the fourth argument, which is a threshold. It says that the value of a peak must be larger than t.



                For efficiency reasons and because it made the implementation short and simple, these functions all pass lists of integers to each other. They have a 1 suffixed to their names to distinguish them from the interface which we shall now define:



                outpformat[data_, sel_] := Module[{pos, values},
                pos = Position[sel, 0] + 1;
                values = Extract[data, pos];
                Transpose[{Flatten[pos], values}]
                ]

                findPeaks[data_] := outpformat[data, findPeaks1[data]]
                findPeaks[data_, sigma_] := outpformat[data, findPeaks1[data, sigma]]
                findPeaks[data_, sigma_, s_] := outpformat[data, findPeaks1[data, sigma, s]]
                findPeaks[data_, sigma_, s_, t_] := outpformat[data, findPeaks1[data, sigma, s, t]]


                We now have a function findPeaks that does more or less the same thing as FindPeaks. I find that the result differs a bit for the Gaussian filter, they seem to do it a bit differently, but this still captures the idea.



                Of course, there are many other signal processing techniques that we could use to find peaks, but there is no universal answer to how the signal should be processed. If you find that this function is not enough, perhaps because the signal is noisy or something else, then you have to look at the signal to decide what filters and such might be appropriate to make the peaks you are looking for more distinguishable.






                share|improve this answer














                The documentation describes a lot of how FindPeaks works, especially the section Scope > Parameters. It can all be done very efficiently with convolutions and vectorized operations.



                This is basic peak finding:



                findPeaks1[data_] := Module[{rightDiff, leftDiff, peaks},
                rightDiff = ListConvolve[{0, 1, -1}, data];
                leftDiff = ListConvolve[{-1, 1, 0}, data];
                Unitize[Sign[leftDiff] + Sign[rightDiff] - 2]
                ]


                The reason for the 1 at the end of the name will become clear later. The convolutions in this function perform almost the same job as Differences, in the end, we get a list that is 0 in those positions that correspond to values that are larger than both its neighbors.



                findPeaks1[data_, sigma_] := Module[{blurred},
                blurred = GaussianFilter[data, {3, sigma}];
                Unitize[findPeaks1[data] + findPeaks1[blurred]]
                ]


                In this step, we add the second argument, which corresponds to smoothing the data using a Gaussian filter with variance sigma. A zero in this list means that the corresponding element is a peak, using the definition in the previous function, in both the smoothed data and the original data.



                findPeaks1[data_, sigma_, s_] := Module[{sharpness},
                sharpness = ListConvolve[{1, -2, 1}, data];
                Unitize[findPeaks1[data, sigma] + 1 - Unitize[sharpness, s]]
                ]


                In this piece of code, we implemented the third argument, which says that the peaks that don't have a negative second derivative that is larger than s should be discarded. The convolution we use here is the definition for the negative discrete second derivative.



                findPeaks1[data_, sigma_, s_, t_] := Unitize[
                findPeaks1[data, sigma, s] + Unitize[UnitStep[Most@Rest@data - t] - 1]
                ]


                Finally, in this code block, we implement the fourth argument, which is a threshold. It says that the value of a peak must be larger than t.



                For efficiency reasons and because it made the implementation short and simple, these functions all pass lists of integers to each other. They have a 1 suffixed to their names to distinguish them from the interface which we shall now define:



                outpformat[data_, sel_] := Module[{pos, values},
                pos = Position[sel, 0] + 1;
                values = Extract[data, pos];
                Transpose[{Flatten[pos], values}]
                ]

                findPeaks[data_] := outpformat[data, findPeaks1[data]]
                findPeaks[data_, sigma_] := outpformat[data, findPeaks1[data, sigma]]
                findPeaks[data_, sigma_, s_] := outpformat[data, findPeaks1[data, sigma, s]]
                findPeaks[data_, sigma_, s_, t_] := outpformat[data, findPeaks1[data, sigma, s, t]]


                We now have a function findPeaks that does more or less the same thing as FindPeaks. I find that the result differs a bit for the Gaussian filter, they seem to do it a bit differently, but this still captures the idea.



                Of course, there are many other signal processing techniques that we could use to find peaks, but there is no universal answer to how the signal should be processed. If you find that this function is not enough, perhaps because the signal is noisy or something else, then you have to look at the signal to decide what filters and such might be appropriate to make the peaks you are looking for more distinguishable.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited 2 days ago

























                answered 2 days ago









                C. E.

                48.9k395197




                48.9k395197






























                     

                    draft saved


                    draft discarded



















































                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fmathematica.stackexchange.com%2fquestions%2f186630%2ffind-a-local-maximum-in-list-without-using-built-in-function-findpeaks%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    If I really need a card on my start hand, how many mulligans make sense? [duplicate]

                    Alcedinidae

                    Can an atomic nucleus contain both particles and antiparticles? [duplicate]