What is the best way to efficiently compute the teager energy kurtosis using list comprehension?

Viewed 95

I would like to calculate the Teager Energy Kurtosis in a function in Python 3.8. I think this should also work with list comprehension.

I tried it with the following code, but I get an error message that the numpy object is not iterable. The variable data contains a list with measured values from an accelerometer.

def EO(data):
       numerator = pow(len(data),2)*sum((pow(((pow(data[i+1],2) - pow(data[i],2))-(sum(pow(data[i+1],2) - pow(data[i],2))/len(data))),4)) for i in range(len(data)-1))
       denominator = pow(sum(pow(((pow(data[i+1],2) - pow(data[i],2))-(sum(pow(data[i+1],2) - pow(data[i],2))/len(data))),2) for i in range(len(data)-1)),2)
       energy_operator = numerator/denominator
       return energy_operator

What is the general approach for implementing such formulas where you have to iterate multiple times, also of course with regard to efficiency. The dataset from which the values are to be calculated contains 133329 entries.

I guess the main problem is that the sum of the denominator contains another sum which has to be formed first. How to do that ?. Without list comprehension I would iterate through the whole dataset twice with a for loop to first get the average value and with that calculate the rest in the second iteration. The readability of this is then of course gone.

Any suggestions are welcome !

Cheers, Gerrit

EDIT: This is the working code without using list comprehension:

def EO_5(data):
       summe = 0
       num_sum = 0
       den_sum = 0
       for i in range(1,len(data)-1):
          summe += pow(data[i],2)-((data[i-1])*(data[i+1]))
       ave = summe/len(data)
       for i in range(1,len(data)-1):
          num_sum += pow((pow(data[i],2)-((data[i-1])*(data[i+1])))-ave,4)
          den_sum += pow((pow(data[i],2)-((data[i-1])*(data[i+1])))-ave,2)
       numerator = (len(data)-1)*num_sum
       denominator = pow(den_sum,2)
       return numerator/denominator
2 Answers

Here it is.

def EO_5(data):
  ave = (sum([i**2 for i in data[1:-1]])-sum([i*j for i,j in zip(data[:-2],data[2:])]))/len(data)
  num = (sum([(j**2-i*k-ave)**4 for i,j,k in zip(data[:-2],data[1:-1],data[2:])]))*(len(data)-1)
  den = (sum([(j**2-i*k-ave)**2 for i,j,k in zip(data[:-2],data[1:-1],data[2:])]))**2
  return num/den
sum(pow(data[i+1],2) - pow(data[i],2))

I think that's the (a) problem. The argument to sum is basically just a number, when it should be something list-like (iterable).

The other problem is that this is badly over-golfed. Lines that run on that long are frowned on, etc.

The other other problem is that the math expressed in the two code-blocks you've shared don't seem to match. The first, which isn't working, seems to more closely follow what's in the image you linked, but IDK if that means it's "correct". Do you have a better reference for "Teager Energy Kurtosis".

I haven't tested this in any way, but it's pretty much how I'd simplify the code you said is working.

def EO_5(data):
    n = len(data) - 1)
    deltas = tuple(
        pow(x, 2) - (before * after)
        for (before, x, after)
        in zip(data[:-2], data[1:-1], data[2:])
    )
    ave = sum(deltas) / len(data)
    num_sum = sum(pow(d - ave, 4) for d in deltas)
    den_sum = sum(pow(d - ave, 2) for d in deltas)
    numerator = n * num_sum
    denominator = pow(den_sum, 2)
    return numerator / denominator

If you're having problems with performance, you may be able to get numpy to leverage vector operations to make this even more streamlined, but I have limited experience with that.

Related