Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Add membership function options for fuzzy entropy #1051

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from

Conversation

DongCheninmuenster
Copy link

@DongCheninmuenster DongCheninmuenster commented Nov 18, 2024

Description

I noticed a TODO about membership functions in utils_entropy.py when I tried to use this package for fuzzy entropy analysis. Therefore I added following 8 kinds of membership functions:

    "exp"    : exponential,
    "gauss"  : gaussian,
    "cgauss" : constgaussian,
    "bell"   : bell,
    "z"      : z,
    "trapez" : trapezoidal,
    "tri"    : triangular,
    "sig"    : sigmoid,

See:

Azami, H., Li, P., Arnold, S. E., Escudero, J., & Humeau-Heurtier, A. (2019b). Fuzzy Entropy Metrics for the analysis of Biomedical Signals: Assessment and comparison. IEEE Access, 7, 104833–104847. https://doi.org/10.1109/access.2019.2930625

Zheng, J., Jiang, Z., & Pan, H. (2018b). Sigmoid-based refined composite multiscale fuzzy entropy and t-SNE based fault diagnosis approach for rolling bearing. Measurement, 129, 332–342. https://doi.org/10.1016/j.measurement.2018.07.045

Changes

  • Introduced a new func_name parameter for selecting the membership function.

  • Modified the tolerance parameter to accept both scalar and two-element vector formats as the parameters of membership functions. Detailed definition can be found in the referenced papers above.

    • If it's a scalar, it refers to a threshold.
    • If it's a two-element vector, it refers to threshold and power respectively.
  • To optimize memory usage, fuzzy entropy is calculated using block matrices. (block size is 10 in default)

Testing

Compared the result with Azami's MATLAB version: FuzzyEntropy_Matlab

Example

print(nk.entropy_fuzzy(series, func_name="exp", tolerance=[0.0018,4]))
print(nk.entropy_fuzzy(series, func_name="gauss", tolerance=0.1253))
print(nk.entropy_fuzzy(series, func_name="cgauss", tolerance=0.0903))
print(nk.entropy_fuzzy(series, func_name="bell", tolerance=[0.1414,2]))
print(nk.entropy_fuzzy(series, func_name="z", tolerance=0.1309))
print(nk.entropy_fuzzy(series, func_name="trapez", tolerance=0.1286))
print(nk.entropy_fuzzy(series, func_name="tri", tolerance=0.3))

Checklist

  • I have read the CONTRIBUTING file.
  • My PR is targeted at the dev branch (and not towards the master branch).
  • I ran the CODE CHECKS on the files I added or modified and fixed the errors.
  • I have added the newly added features to News.rst (if applicable)

I have added membership functions to the fuzzy entropy algorithm by modifying the parameters in the entropy_sample function. Although I'm still a beginner in Python, the good news is that it works as expected. I compared my code and results with Azami's MATLAB version to verify this.

However, I believe there may be a more efficient approach, especially by improving the complexity_tolerance function. I'm continuing to work on implementing this optimization.

Additionally, I have modified the core algorithm of fuzzy entropy by using block matrix, which helps reduce memory usage.
Copy link
Author

@DongCheninmuenster DongCheninmuenster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some changes are just format changes modified by checks code

@DerAndereJohannes
Copy link
Contributor

Awesome work!

I am not too confident on my knowledge of Fuzzy Entropy, but FWIW, I double checked with the referenced MATLAB code on a vector that I generated to test both. To make it easier for reviewers, here is the code for anyone to quickly replicate:

Python:

import os
import neurokit2 as nk
import numpy as np

series = None

if not 'series.txt' in os.listdir(os.getcwd()):
    series = np.random.rand(1000)
    series.tofile('series.txt', sep=',')
else:
    series = np.fromfile('series.txt', sep=',')

print(nk.entropy_fuzzy(series, func_name="exp", tolerance=[0.0018,4]))
print(nk.entropy_fuzzy(series, func_name="gauss", tolerance=0.1253))
print(nk.entropy_fuzzy(series, func_name="cgauss", tolerance=0.0903))
print(nk.entropy_fuzzy(series, func_name="bell", tolerance=[0.1414,2]))
print(nk.entropy_fuzzy(series, func_name="z", tolerance=0.1309))
print(nk.entropy_fuzzy(series, func_name="trapez", tolerance=0.1286))
print(nk.entropy_fuzzy(series, func_name="tri", tolerance=0.3))

Matlab:

% Fuzzy tests - fuzzy_tests.m

data = readmatrix('series.txt');

% This function calculates fuzzy entropy (FuzEn) of a univariate
% signal ts, using different fuzzy membership functions (MFs)%       
%       Inputs:
%               ts     --- time-series  - a vector of size 1 x N (the number of sample points)
%               m      --- embedding dimension
%               mf     --- membership function, chosen from the follwing
%                          'Triangular', 'Trapezoidal', 'Z_shaped', 'Bell_shaped',
%                          'Gaussian', 'Constant_Gaussian', 'Exponential'
%               rn      --- threshold r and order n (scalar or vector based upon mf)
%                          scalar: threshold
%                          vector: [threshold r, order n]
%               local  --- local similarity (1) or global similarity (0)
%               tau    --- time delay

FuzEn_MFs(data, 2, 'Exponential', [0.0018*std(data) 4], 1,1)
FuzEn_MFs(data, 2, 'Gaussian', 0.1253*std(data), 1,1)
FuzEn_MFs(data, 2, 'Constant_Gaussian', 0.0903*std(data), 1,1)
FuzEn_MFs(data, 2, 'Bell_shaped', [0.1414*std(data) 2], 1,1)
FuzEn_MFs(data, 2, 'Z_shaped', 0.1309*std(data), 1,1)
FuzEn_MFs(data, 2, 'Trapezoidal', 0.1286*std(data), 1,1)
FuzEn_MFs(data, 2, 'Triangular', 0.3*std(data), 1,1)

Regarding the code, there are not many changes I would propose, however one thing I can think of is that I think the membership functions should be made private (with an underscore before the function name e.g., exponential becomes _exponential). Another is that make sure in the documentation of parameters in functions that you follow python typing styles. E.g., in entropy_fuzzy you mark tolerance as scalar and two-element vector where in typing typing terms this would be something like Union[float, list, np.ndarray]. The typing for string in python is also str which you should use for func_name

The one main comment I can give is that your code needs unit tests to make sure that the code is all covered in case something happens in the future and to make sure that everything is always working properly.

@DominiqueMakowski
Copy link
Member

Thank you so much @DerAndereJohannes that's really helpful - I do agree with all your suggestions

@DongCheninmuenster
Copy link
Author

Thank you so much @DerAndereJohannes for your suggestions. I’m a complete beginner in Python and still learning it. To be honest, some of the context is beyond my current knowledge of Python, but I will do my best to work on it.

@DerAndereJohannes
Copy link
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants