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
Screenshot of sample signal attached.
October 18, 2018 at 09:59 am - Permalink
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:
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.
October 18, 2018 at 12:54 pm - Permalink
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
October 18, 2018 at 02:26 pm - Permalink
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.
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
October 19, 2018 at 01:12 pm - Permalink
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
October 19, 2018 at 01:27 pm - Permalink
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:
- 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:
(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
November 6, 2018 at 12:58 pm - Permalink
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))
November 6, 2018 at 01:45 pm - Permalink
In reply to I had to attend to other… by pmazzoni
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.
November 7, 2018 at 02:08 am - Permalink
Thanks for the clarification. I removed -inf, inf and returned the line to the version that AG originally wrote:
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().
November 7, 2018 at 10:16 am - Permalink
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:
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.
November 7, 2018 at 11:30 am - Permalink
It works now. Thank you!
November 8, 2018 at 02:25 pm - Permalink