├── .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 | }
--------------------------------------------------------------------------------