************************ Implement a new Function ************************ Purpose of this Chapter ======================= The aim of this chapter to explain how to implement a new function. This will be presented by implementing a simple example. Let's assume that the data of our series has to be resampled before the execution of a test. To handle it, we implement a function that resamples the data to a *1-second* resolution and interpolates the *NaN-values* and the gaps. For simplicity's sake, we assume that we always need a *1-second* resolution. Moreover, the user should have the opportunity to define the interpolation method *(e.g., linear)*. Define the Metadata of the Function =================================== .. note:: Each function has to inherit from the abstract class **BaseFunction** and has to implement the abstract method **apply**. Moreover, each function has to provide its metadata. The following metadata has to be provided by each function: *NAME* and *DESCRIPTION*. See also: :class:`autom8qc.functions.base.BaseFunction` .. warning:: Make sure that the class name ends with the suffix **Function**. Other modules will check for the suffix to identify that the class is a function. .. code-block:: python import numpy as np import pandas as pd from autom8qc.core import exceptions from autom8qc.core.parameters import Parameter from autom8qc.core.parameters import ParameterList from autom8qc.functions.base import BaseFunction class InterpolationFunction(BaseFunction): NAME = "Simple interpolation" DESCRIPTION = "Resamples the data to a 1-second resolution and fill the gaps with a interpolation" Define the Supported Parameters =============================== Each function has to provide the supported parameters. Therefore, you have to implement the static method **supported_parameters**. The method allows you to access them without creating an instance of the class. If your function doesn't need additional parameters, you don't have to implement it. For our example, we have the interpolation method as a parameter. .. code-block:: python @staticmethod def supported_parameters(): return ParameterList( Parameter( name="method", description="Interpolation technique to use (e.g., linear)", dtype=str, optional=False, default="linear" ) ) Implement the Constructor ========================= The constructor is a method that is called when an object is created. In our case, we have to pass the parameter **method** to the constructor and store the value in the related **Parameter** which we defined in the method *supported_parameters*. Note that you don’t have to check the type of the parameters since a Parameter checks the type when you set the value. If you want to implement additional checks, you have to implement them in the constructor and raise an exception if a constraint is not satisfied. Finally, you have to call the super method **check_metadata** that checks if the instance is valid. In our example, our constructor needs the parameter **method** *(default: linear)* that defines the interpolation technique. .. note:: We use the interpolation techniques that pandas provide. See also: **pd.DataFrame.interpolate** .. warning:: Make sure that you call the super constructor before assigning the parameters. .. code-block:: python :emphasize-lines: 2 def __init__(self, method="linear"): super().__init__() self.parameters["method"] = method self.check_metadata() Implement the Abstract Method ============================= Finally, we have to implement the abstract method **apply**. Our method takes a **pd.Series** as a parameter and returns the resampled data. For our example, we assume that we only support series. If a non-series will pass to the method, we will raise an error. .. code-block:: python def apply(self, data): if not isinstance(series, pd.Series): raise exceptions.InvalidType("Data must be an instance of pd.Series!") method = self.parameters["method"].value data = data.resample("1s") return data.interpolate(method)