meta00 / vital_sqi

A package for physiological signal quality control
MIT License
32 stars 11 forks source link

Rule functionality - user defined function #41

Open bahp opened 3 years ago

bahp commented 3 years ago

Hi guys,

I have notice that rule behaviour is not implemented yet so this is a question/advice.

I still need to better understand what is the idea behind the rule module and/or class. I had a look at the current .json example (see below) and with a bit of interpretation my understanding is that once an SQI is computed (sqi), you want to use pre-defined steps to filter out the original signal (s). At the moment you are defining those steps in a .json file (PS1), with entries defining the operand (op), the value and the label. If we are considering that this is just for programmers using the package, you could allow users to just define their own rules and if necessary put some constraints (PS2). This function can be passed as a parameter and called dynamically.

Otherwise...

Hope it helps.

Best

PS1: Example .json

"test_sqi": { "name": "test_sqi", "def": [ {"op": ">", "value": "10", "label": "reject"}, {"op": ">=", "value": "3", "label": "accept"}, {"op": "<", "value": "3", "label": "reject"}], "desc": "", "ref": "" }

PS2: Example function

def rule_xx(sqi):
    """Rule

        This rule....

        [1] XX et all

    Parameters
    ----------
    sqi: pd.Series or np.array
        The series with quality indexes

    Returns
    -------
    up to you
    """
    # I am assuming you want a dataframe where the 
    # first column is the sqi index and the second 
        # column is the label. But could be anything else 
        # you want.

    #{"op": ">", "value": "10", "label": "reject"},
        #{"op": ">=", "value": "3", "label": "accept"},
        #{"op": "<", "value": "3", "label": "reject"}],

    # Create dataframe
    r = pd.DataFrame()
    r['sqi'] = pd.Series(sqi)
    r['lbl'] = 'reject'

    # Add labels
    r.loc[r.sqi.between(3, 10), 'lbl'] = 'accept'

    # Return
    return r

# If you really need a class rule for some reason you can use
# something similar to the example show below, but it might
# be a bit of an overkill as functions are just so easy to call.

class Rule:
    """Rule"""

    def __init__(self, name, f=None):
        """Constructor"""
       # Check name is string
       # Check if f is callable
       # Set attributes
        self.name = name
        self.f = f

   def apply(sqi, **kwargs):
        """Apply rule"""
        return self.f(sqi)

 class RuleXXX(Rule):
      def __init__(self):
          super().__init__('xxx', rule_xxx)

# ----------------------
# Main
# ----------------------
# Opt I:
rule = Rule('xxx', rule_xxx)

# Opt II:
rule = RuleXXX()