├── README.md ├── msentropy.py └── test_msentropy_package.py /README.md: -------------------------------------------------------------------------------- 1 | # py-msentropy 2 | Multiscale Entropy with Python 3 | 4 | This package "msentropy.py" is a package to calculate the multi-scale entropy, the refined composite multi-scale entropy, the cross-sample entropy and the complexity index 5 | 6 | Required numpy (http://www.numpy.org) and pyeeg (http://pyeeg.sourceforge.net) 7 | 8 | It was developed with python 3.5 9 | -------------------------------------------------------------------------------- /msentropy.py: -------------------------------------------------------------------------------- 1 | # -*-coding:Latin-1 -* 2 | __author__ = "Antoine JAMIN" 3 | 4 | 5 | ''' 6 | This python package was developed during my internship at LARIS (http://laris.univ-angers.fr/fr/index.html) 7 | This python package implements some function to calculate multi-scale entropy, refined composite multi-scale entropy 8 | and cross-sample entropy. 9 | To develop this package we use these references : 10 | [S M. Pincus, 1991] -- Approximate entropy as mesure of system complexity. 11 | [J. Richman, 2000] -- Physiological time-series analysis using approximate entropy and sample entropy. 12 | [A. Humeau, 2015] -- The Multiscale Entropy Algorithm and Its Variants : A Review. 13 | [M. Costa,2002] -- Multiscale Entropy Analysis of Complex Physiologic Time Series. 14 | [S. Wu, 2014] -- Analysis of complex time series using refined composite multiscale entropy 15 | [Y. Chang, 2014] -- Application of a Modified Entropy Computational Method in Assessing the Complexity of Pulse 16 | Wave Velocity Signals in Healthy and Diabetic Subjects. 17 | [D. Kong, 2011] -- Use of modified sample entropy measurement to classify ventricular tachycardia and fibrillation. 18 | [T. Zhang, 2007] -- Cross-sample entropy statistic as a measure of complexity and regularity of renal sympathetic 19 | nerve activity in the rat. 20 | [W. Shi, 2013] -- Cross-sample entropy statistic as a measure of synchronism and cross-correlation of stock markets. 21 | [C. C. Chiu, 2011] -- Assessment of Diabetics with Various Degrees of Autonomic Neuropathy Based on 22 | Cross-Approximate Entropy 23 | This package use the pyeeg package you need to import it (Copyleft 2010 Forrest Sheng Bao http://fsbao.net) : 24 | http://pyeeg.sourceforge.net 25 | This package use the numpy package you need to import it (http://numpy.scipy.org) 26 | ''' 27 | 28 | import numpy as np 29 | import pyeeg 30 | 31 | 32 | # Variables globales 33 | nb_scales = 20 34 | length_sample = 1000 35 | 36 | 37 | 38 | 39 | ## Coarse graining procedure 40 | # tau : scale factor 41 | # signal : original signal 42 | # return the coarse_graining signal 43 | def coarse_graining(tau, signal): 44 | # signal lenght 45 | N = len(signal) 46 | # Coarse_graining signal initialisation 47 | y = np.zeros(int(len(signal) / tau)) 48 | for j in range(0, int(N / tau)): 49 | y[j] = sum(signal[i] / tau for i in range(int((j - 1) * tau), int(j * tau))) 50 | return y 51 | 52 | 53 | ## Multi-scale entropy 54 | # m : length of the patterns that compared to each other 55 | # r : tolerance 56 | # signal : original signal 57 | # return the Multi-scale entropy of the original signal (array of nbscales length) 58 | def mse(m, r, signal, nbscales=None): 59 | # Output initialisation 60 | if nbscales == None: 61 | nbscales = int((len(signal) * nb_scales) / length_sample) 62 | y = np.zeros(nbscales + 1) 63 | y[0] = float('nan') 64 | for i in range(1, nbscales + 1): 65 | y[i] = pyeeg.samp_entropy(coarse_graining(i, signal), m, r) 66 | return y 67 | 68 | 69 | ## calculation of the matching number 70 | # it use in the refined composite multi-scale entropy calculation 71 | def match(signal, m, r): 72 | N = len(signal) 73 | 74 | Em = pyeeg.embed_seq(signal, 1, m) 75 | Emp = pyeeg.embed_seq(signal, 1, m + 1) 76 | 77 | Cm, Cmp = np.zeros(N - m - 1) + 1e-100, np.zeros(N - m - 1) + 1e-100 78 | # in case there is 0 after counting. Log(0) is undefined. 79 | 80 | for i in range(0, N - m): 81 | for j in range(i + 1, N - m): # no self-match 82 | # if max(abs(Em[i]-Em[j])) <= R: # v 0.01_b_r1 83 | if pyeeg.in_range(Em[i], Em[j], r): 84 | Cm[i] += 1 85 | # if max(abs(Emp[i] - Emp[j])) <= R: # v 0.01_b_r1 86 | if abs(Emp[i][-1] - Emp[j][-1]) <= r: # check last one 87 | Cmp[i] += 1 88 | 89 | return sum(Cm), sum(Cmp) 90 | 91 | 92 | ## Refined Composite Multscale Entropy 93 | # signal : original signal 94 | # m : length of the patterns that compared to each other 95 | # r : tolerance 96 | # nbscales : 97 | # return the RCMSE of the original signal (array of nbscales length) 98 | def rcmse(signal, m, r, nbscales): 99 | Nm = 0 100 | Nmp = 0 101 | y = np.zeros(nbscales + 1) 102 | y[0] = float('nan') 103 | for i in range(1, nbscales + 1): 104 | for j in range(0, i): 105 | (Cm, Cmp) = match(coarse_graining(i, signal[i:]), m, r) 106 | Nm += Cm 107 | Nmp += Cmp 108 | y[i] = -np.log(Nmp / Nm) 109 | return y 110 | 111 | 112 | ## Caclulate the complexity index of the MSE (or RCMSE) of the original signal 113 | # sig : RCMSE or MSE of the original signal 114 | # inf : lower bound for the calcul 115 | # sup : upper bound for the calcul 116 | # return the complexity index value 117 | def complexity_index(sig, low, upp): 118 | ci = sum(sig[low:upp]) 119 | return ci 120 | 121 | 122 | ## Calculate the cross-sample entropy of 2 signals 123 | # u : signal 1 124 | # v : signal 2 125 | # m : length of the patterns that compared to each other 126 | # r : tolerance 127 | # return the cross-sample entropy value 128 | def cross_SampEn(u, v, m, r): 129 | B = 0.0 130 | A = 0.0 131 | if (len(u) != len(v)): 132 | raise Exception("Error : lenght of u different than lenght of v") 133 | N = len(u) 134 | for i in range(0, N - m): 135 | for j in range(0, N - m): 136 | B += cross_match(u[i:i + m], v[j:j + m], m, r) / (N - m) 137 | A += cross_match(u[i:i + m + 1], v[j:j + m + 1], m + 1, r) / (N - m) 138 | B /= N - m 139 | A /= N - m 140 | cse = -np.log(A / B) 141 | return cse 142 | 143 | 144 | ## calculation of the matching number 145 | # it use in the cross-sample entropy calculation 146 | def cross_match(signal1, signal2, m, r): 147 | # return 0 if not match and 1 if match 148 | d = [] 149 | for k in range(0, m): 150 | d.append(np.abs(signal1[k] - signal2[k])) 151 | if max(d) <= r: 152 | return 1 153 | else: 154 | return 0 -------------------------------------------------------------------------------- /test_msentropy_package.py: -------------------------------------------------------------------------------- 1 | # -*-coding:Latin-1 -* 2 | 3 | __author__ = "Antoine JAMIN" 4 | 5 | ''' 6 | To test the package we generate nb_signal white noise of N samples and we calculate the mse and the rcmse of each signal. 7 | After we print the mean of the nb_signal mse and the nb_signal rcmse. 8 | And we print also the complexity index for mse and rcmse and the cross-sample entropy value betweene white_noise[0] and 9 | white_noise[1] 10 | ''' 11 | 12 | import msentropy as msen 13 | import numpy as np 14 | import matplotlib.pyplot as plt 15 | 16 | # Variable definition 17 | nb_signal = 10 18 | N = 1000 19 | nbscales = 12 20 | m = 1 21 | 22 | # nb_signal white noise of N samples generation 23 | white_noise = [] 24 | for i in range(0, nb_signal): 25 | white_noise.append(np.random.normal(0, 1, size=N)) 26 | 27 | # MSE calculation for each white noise signal 28 | MSE = np.zeros(nbscales + 1) 29 | for j in range(0, nb_signal): 30 | signal = white_noise[j] 31 | MSE_temp = msen.mse(m, 0.15 * np.std(signal), signal, nbscales) 32 | for k in range(0, len(MSE_temp)): 33 | MSE[k] += MSE_temp[k] 34 | 35 | # mean of the nb_signal MSE 36 | MSE /= nb_signal 37 | 38 | # RCMSE calculation for each white noise signal 39 | RCMSE = np.zeros(nbscales + 1) 40 | for k in range(0, nb_signal): 41 | signal = white_noise[j] 42 | RCMSE_temp = msen.rcmse(signal, m, 0.15 * np.std(signal), nbscales) 43 | for k in range(0, len(RCMSE_temp)): 44 | RCMSE[k] += RCMSE_temp[k] 45 | 46 | # mean of the nb_signal RCMSE 47 | RCMSE /= nb_signal 48 | 49 | # Print the results 50 | fig = plt.figure() 51 | fig.add_subplot(211).plot(MSE, "b-o") 52 | plt.title("MSE") 53 | 54 | fig.add_subplot(212).plot(RCMSE, "r-o") 55 | plt.title("RCMSE") 56 | 57 | print("Complexity index of MSE = " + str(msen.complexity_index(MSE, 1, nbscales))) 58 | print("Complexity index of RCMSE = " + str(msen.complexity_index(RCMSE, 1, nbscales))) 59 | 60 | r = (0.15 * (np.std(white_noise[0] + np.std(white_noise[1])))) / 2 61 | print(("Cross-sample entropy of white noise 0 and 1 : " + str(msen.cross_SampEn(white_noise[0], white_noise[1], m, r)))) 62 | 63 | plt.show() 64 | --------------------------------------------------------------------------------