├── .gitignore ├── LICENSE ├── README.md ├── cecp.R ├── complexity.R ├── ordinal_pattern.R ├── p_entropy.py └── permu_entropy.R /.gitignore: -------------------------------------------------------------------------------- 1 | # VS code 2 | .vscode 3 | 4 | # venv 5 | venv -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 srk-srinivasan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Permutation-Entropy 2 | Python and R Codes for Computing Permutation Entropy and Jensen-Shannon Complexity

3 | Permutation Entropy is computed using the method prescribed by Bandt and Pompe
4 | Bandt, C., & Pompe, B. (2002). Permutation entropy: a natural complexity measure for time series. Physical review letters, 88(17), 174102.

5 | Weighted Permutation Entropy is computed using the method prescribed by Fadlallah et al.
6 | Fadlallah, B., Chen, B., Keil, A., & Príncipe, J. (2013). Weighted-permutation entropy: A complexity measure for time series incorporating amplitude information. Physical Review E, 87(2), 022911. 7 | -------------------------------------------------------------------------------- /cecp.R: -------------------------------------------------------------------------------- 1 | # Function to compute Complexity-Entropy Causality Plane (CECP) 2 | # Input(2 arguments. Null arguments are not vaild) 3 | # x = Time series (type = numeric vector) 4 | # dim = Embedding dimension (type = numeric) 5 | # Output is a list. List contains ordinal pattern distribution, 6 | # normalized permutation entropy, and complexity 7 | 8 | cecp<-function(x,dim){ 9 | 10 | op<-ordinal_pattern(x,dim) 11 | npe<-permu_entropy(op) 12 | comp_JS<-complexity(op) 13 | 14 | result_list<-list(op=op,npe=npe,complexity=comp_JS) 15 | return(result_list) 16 | } -------------------------------------------------------------------------------- /complexity.R: -------------------------------------------------------------------------------- 1 | # Function to compute complexity of a given time series 2 | # Input (1 argument. Null argument not valid) 3 | # op = Ordinal pattern computed using the function ordinal_pattern 4 | # op (type=numeric vector) 5 | # Output is complexity (type=numeric) 6 | 7 | complexity<-function(op){ 8 | 9 | # Comp_JS = Q_o * JSdivergence * pe 10 | # Normalizing constant Q_o 11 | # JSdivergence = Jensen-Shannon divergence 12 | # pe = permutation entopry 13 | 14 | pe<-permu_entropy(op) 15 | 16 | constant1<- (0.5+((1 - 0.5)/length(op)))*log(0.5+((1 - 0.5)/length(op))) 17 | constant2<-((1 - 0.5)/length(op))*log((1 - 0.5)/length(op))*(length(op) - 1) 18 | constant3<- 0.5*log(length(op)) 19 | Q_o<- -1/(constant1+constant2+constant3) 20 | 21 | temp_op_prob<-op/sum(op) 22 | temp_op_prob2<-(0.5*temp_op_prob)+(0.5*(1/length(op))) 23 | JSdivergence<-(entropy::entropy(temp_op_prob2) - 0.5 * entropy::entropy(temp_op_prob) - 0.5 * log(length(op))) 24 | 25 | Comp_JS = Q_o * JSdivergence * pe 26 | return(Comp_JS) 27 | 28 | } 29 | -------------------------------------------------------------------------------- /ordinal_pattern.R: -------------------------------------------------------------------------------- 1 | # Function to compute the ordinal patterns for a given time series. 2 | # Input (2 arguments. Null arguments are not vaild) 3 | # x = Given time series (type=numeric vector) 4 | # dim = Embedding dimension (type=numeric) 5 | # Commonly used value of dim ranges from 3 to 7 6 | # Output is a numeric vector of size=(dim)! 7 | 8 | ordinal_pattern<-function(x,dim){ 9 | 10 | # Generate ordinal numbers to assign. For example if dim =3, then 11 | # ordinal number=0,1,2 12 | ordinal_numbers<-seq(0,(dim-1),by=1) 13 | 14 | # Compute all possible permutations of the ordinal numbers. 15 | # Maximum size of possible_pattern=dim! 16 | possible_pattern<-(combinat::permn(ordinal_numbers)) 17 | 18 | # Initialize result. Result is the output. 19 | result<-0 20 | result[1:length(possible_pattern)]<-0 21 | 22 | # Loop for computation of ordinal pattern 23 | for(i in 1:(length(x)-(dim-1))){ 24 | temp<-x[i:(i+(dim-1))] 25 | tempseq<-seq(0,dim-1,by=1) 26 | tempdata<-data.frame(temp,tempseq) 27 | tempdata<-tempdata[order(temp),] 28 | 29 | for(j in 1: length(possible_pattern)){ 30 | if (all(possible_pattern[[j]]==tempdata$tempseq)){ 31 | result[j]<-result[j]+1 32 | } 33 | 34 | } 35 | 36 | } 37 | 38 | return(result) 39 | 40 | } 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /p_entropy.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import math 3 | import itertools 4 | 5 | def s_entropy(p): 6 | """Shannon entropy (base 2) for a probability distribution p.""" 7 | p = np.asarray(p) 8 | p = p[p > 0] 9 | return -np.sum(p * np.log2(p)) 10 | 11 | def ordinal_patterns(ts, embdim, embdelay): 12 | """ 13 | Computes normalized frequency of all ordinal patterns (including zeros). 14 | Based on Bandt-Pompe method. 15 | """ 16 | ts = np.asarray(ts) 17 | m, t = embdim, embdelay 18 | n = len(ts) 19 | n_patterns = math.factorial(m) 20 | 21 | # All possible patterns in lexicographic order 22 | patterns = list(itertools.permutations(range(m))) 23 | pattern_index = {pat: idx for idx, pat in enumerate(patterns)} 24 | counts = np.zeros(n_patterns) 25 | 26 | for i in range(n - (m - 1) * t): 27 | window = ts[i:i + t * m:t] 28 | order = tuple(np.argsort(window)) 29 | counts[pattern_index[order]] += 1 30 | 31 | total = np.sum(counts) 32 | return counts / total if total > 0 else counts 33 | 34 | def permutation_entropy(ts, embdim, embdelay): 35 | """ 36 | Returns normalized permutation entropy (log base 2), using all possible patterns. 37 | Matches Bandt-Pompe + Rosso et al. (2007). 38 | """ 39 | p = ordinal_patterns(ts, embdim, embdelay) 40 | max_entropy = np.log2(len(p)) if len(p) > 0 else 0 41 | return s_entropy(p) / max_entropy if max_entropy > 0 else 0.0 42 | 43 | def complexity(ts, embdim, embdelay): 44 | """ 45 | Computes statistical complexity (Rosso et al.): 46 | - Q_J: Jensen-Shannon divergence between observed and uniform over ALL patterns 47 | - H_S: Normalized permutation entropy 48 | - C_JS = Q_J * H_S 49 | """ 50 | p = ordinal_patterns(ts, embdim, embdelay) 51 | pe = permutation_entropy(ts, embdim, embdelay) 52 | 53 | n = len(p) 54 | uniform = np.ones(n) / n 55 | avg = 0.5 * (p + uniform) 56 | 57 | js_div = s_entropy(avg) - 0.5 * s_entropy(p) - 0.5 * s_entropy(uniform) 58 | q0 = -0.5 * ((n + 1) / n * np.log2(n + 1) - 2 * np.log2(2 * n) + np.log2(n)) 59 | normalized_js = js_div / q0 if q0 > 0 else 0 60 | 61 | return normalized_js * pe 62 | -------------------------------------------------------------------------------- /permu_entropy.R: -------------------------------------------------------------------------------- 1 | # Function to compute permutation entropy of a given time series 2 | # Input (1 argument, Null argument not valid) 3 | # op = Ordinal pattern computed using the function ordinal_pattern 4 | # op (type=numeric vector) 5 | # Output is normalized permutation entropy (type=numeric) 6 | 7 | permu_entropy<-function(op){ 8 | # Compute maximum entropy. maximum entropy = log(dim!) 9 | # or maximum entropy = log(length(ordinal_pattern)) 10 | entropy_max<-log(length(op)) 11 | 12 | # Normalized permutation entropy 13 | npe<-entropy::entropy(op)/entropy_max 14 | return(npe) 15 | 16 | } --------------------------------------------------------------------------------