Sample Entropy calculation?

I am interested in calculating the sample entropy of a mostly sinusoidal signal:

Richman, J. S., and J. R. Moorman. “Physiological Time-Series Analysis Using Approximate Entropy and Sample Entropy.” American Journal of Physiology. Heart and Circulatory Physiology 278, no. 6 (June 2000): H2039-2049. https://doi.org/10.1152/ajpheart.2000.278.6.H2039

The signal consists of the angular velocity of a person's wrist during repetitive pronation-supination movements. There are intermittent "breaks" in the movement, in which the velocity has a handful (2-5) small peaks between the major, regularly occurring peaks. I'd like to measure these as irregularities in the signal. Sample entropy seems like an appropriate quantity for this purpose. Does Igor have functions to calculate sample entropy or some related measure of signal regularity/complexity?

Pietro

Hello Pietro,

 

I found the reference that you provided to be almost unreadable.  If you can send me a reference with readable equations I'd be interested in looking at the an Igor implementation. 

At a simplified level, I found the following: http://daedalus.scl.sztaki.hu/phdws2004/abstract/phdws2004_abstract_4.p… which provides two "algorithms" that are very easy to formulate using MatrixOP.  Specifically, the second algorithm for entropy2 can be formulated for an input signal in inWave:

Function entropy2(inWave)
    Wave inWave
   
    MatrixOP/O/FREE quo=abs(subtractMean(inWave,0))
    MatrixOP/O/FREE ent=sum(quo*log(quo))
    return -ent[0]
End

I hope this helps,

 

A.G.

I am glad i am not alone in finding that article difficult. It is the original article in which sample entropy was introduced, as a modification of approximate entropy (Pincus). I found a later chapter by the same authors that seems more readable (attached). The Wikipedia entry for Sample Entropy (https://en.wikipedia.org/wiki/Sample_entropy) has an outline of a Python routine. If you are interested enough, perhaps you can make something from this info. I will look at the option you suggested.

 

Thank you!

 

Pietro

Hello Pietro,

I think the wiki text is more readable, particularly the Definition section.  In any event, below is a quick sample of code that I wrote for this definition.  I have not tested it but I think it should be clear how to use or modify it for other definitions of distance functions.  I also did not make any effort to get efficient performance.

I hope this helps,

A.G.

 

Function sampEn(inWave,em,rTol)
    Wave inWave
    Variable em // embedding dimension 
    Variable rTol   // tolerance

    Variable AA=sampEnM(inWave,em+1,rTol)
    Variable BB=sampEnM(inWave,em,rTol)
    return -log(AA/BB)
End


Function sampEnM(inWave,em,rTol)
    Wave inWave
    Variable em      
    Variable rTol    
   
    Variable rows=DimSize(inWave,0)
    // create all possible contiguous vectors of length em
    Variable numVectors=rows-em+1
    Variable count=0
    Variable i,j,row
    for(i=0;i<numVectors;i+=1)
        for(j=i+1;j<numVectors;j+=1)
            MatrixOP/O/FREE V1=sum(abs(subrange(inWave,i,0,em)-subrange(inWave,j,0,em)))
            if(V1[0]<rTol)
                count+=1
            endif
        endfor
    endfor
    return count
End

 

Wow, that is so nice of you, A.G.! Thanks! I'll try this out.

You must really enjoy your work. I really appreciate it.

Pietro

I had to attend to other work and I am only trying to implement this function for the first time today. Unfortunately, I am getting an error that I can't figure out. Here are the steps I took starting with A.G.'s code above:

- The code as written above gave me a compile-time error, "Expected operand", at the line:

            MatrixOP/O/FREE V1=sum(abs(subrange(inWave,i,0,em)-subrange(inWave,j,0,em)))

 

- I read that the sum function expects a range, and so I added the range -inf, inf:

     MatrixOP/O/FREE V1=sum(abs(subrange(inWave,i,0,em)-subrange(inWave,j,0,em) ), -inf , inf )

 

- I tested the function on the attached time series, velF_O1. I chose values m = 1, rTol = 200, based on the following comment in Richman and Moorman's 2004 chapter:

//"...How does one pick m and r? The usual suggestion is that m should be 1 or 2, noting 
//that there are more template matches and thus less bias for m = 1, but that 
//m = 2 (or greater) reveals more of the dynamics of the data. The usual suggestion
//is that r should be 0.2 times the SD of the data set on empirical grounds."

(Richman, Joshua S., Douglas E. Lake, and J. Randall Moorman. “Sample Entropy.” In Methods in Enzymology, 384:172–84. Numerical Computer Methods, Part E. Academic Press, 2004. https://doi.org/10.1016/S0076-6879(04)84011-4.):

 

- So I tried:

print sampEn(velF_O1, 1, 200)

 

- This gave me the runtime error: "Wrong number of parameters" at the line

MatrixOP/O/FREE V1=sum(abs(subrange(inWave,i,0,em)-subrange(inWave,j,0,em) ), -inf , inf )

 

I will appreciate any suggestions. I have very limited experience using MatrixOp functions and maybe I am missing something obvious to others.

 

Pietro

 

UPDATE:

I was able to use the Python code given in the Wikipedia page (see code below). I don't suppose that Igor has the functionality yet to call Python scripts and receive results from them? Or at least to run Unix commands in the Mac's Terminal app?

....[EDIT]: I see I can use Applescript to run shell commands. Still, it would be great if Igor 9 included a more direct interface with Python scripts...

------

import numpy as np
#import pylab as pl

def SampEn(U, m, r):

    def _maxdist(x_i, x_j):
        result = max([abs(ua - va) for ua, va in zip(x_i, x_j)])
        return result

    def _phi(m):
        x = [[U[j] for j in range(i, i + m - 1 + 1)] for i in range(N - m + 1)]
        C = [len([1 for j in range(len(x)) if i != j and _maxdist(x[i], x[j]) <= r]) for i in range(len(x))]
        return sum(C)

    N = len(U)

    return -np.log(_phi(m+1) / _phi(m))

 

In reply to by pmazzoni

pmazzoni wrote:

- This gave me the runtime error: "Wrong number of parameters" at the line

MatrixOP/O/FREE V1=sum(abs(subrange(inWave,i,0,em)-subrange(inWave,j,0,em) ), -inf , inf )

 

I believe the initial error message refers to the MatrixOP subRange command, which expects a wave and both start and end rows and columns. Is inWave 1D or 2D? sum() within MatrixOP doesn't require a range.

Thanks for the clarification. I removed -inf, inf and returned the line to the version that AG originally wrote:

            MatrixOP/O/FREE V1=sum(abs(subrange(inWave,i,0,em)-subrange(inWave,j,0,em) ))

My wave is 1D. Does it sound like subrange is missing an argument, or that it has en extra argument? I am embarrassed that I am trying to implement a function I do not fully understand myself.

Note that the compile error with this line highlights that last right parentheses and says "Expected operand". That's why I thought that the missing operand was for sum().

Hello Pietro,

Please don't confuse the sum() function in MatrixOP and the with the standard sum() function.  The one in MatrixOP operates on a whole matrix token.  The matrix token used in my code was created from the abs() function applied to two subrange() calls.  Here the syntax should be subRange(w,rs,re,cs,ce) where rs is the starting row, re is the end row, cs is the starting column and ce is the end column.  Clearly, one argument was missing in the original code.  If you have a single column then cs=ce=0 and the command should be something like:

 Variable em1=em-1
 MatrixOP/O/FREE V1=sum(abs(subrange(inWave,i,i+em1,0,0)-subrange(inWave,j,j+em1,0,0)))

I apologize for the confusion.