********************** Implement a new Mapper ********************** Purpose of this Chapter ======================= The aim of this chapter is to explain how to implement a new mapper. This will be presented by implementing a simple example. Let's assume that we want to implement a mapper that maps probabilities to validities. Our mapper maps probabilities lower than the passed *threshold* to **Erroneous** and values greater equals than the *threshold* to **Good**. To reduce overhead, we use the **autom8qc.core.validitiesStandardValidities** and don't define them by our own. Define the Metadata of the Mapper ================================= .. note:: Each mapper has to inherit from the abstract class **BaseMapper** and has to implement the abstract method **map**. Moreover, each mapper has to provide its metadata. The following metadata has to be provided by each mapper: *NAME* and *DESCRIPTION*. See also: :class:`autom8qc.mappers.base.BaseMapper` .. warning:: Make sure that the class name ends with the suffix **Mapper**. Other modules will check for the suffix to identify that the class is a mapper. .. 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.core.validities import StandardValidities from autom8qc.mappers.base import BaseMapper class SimpleMapper(BaseMapper): NAME = "Simple Mapper" DESCRIPTION = "Maps probabilities greater equals than 0.5 to Good, otherwise to Erroneous" Define the Supported Parameters =============================== Each mapper has to provide the supported parameters. Therefore, you have to implement the static method **supported_parameters**. The method allows you to access the supported parameters without creating an instance of the class. If your mapper doesn't need additional parameters you don't have to implement it. In our case, we have the additional parameter **threshold** with the default value *0.5*. .. code-block:: python @staticmethod def supported_parameters(): return ParameterList( Parameter( name="threshold", description="Threshold value", dtype=float, optional=False, default=0.5 ) ) 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 **threshold** 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 *(e.g., the threshold must be between 0 and 1)*, 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. .. warning:: Make sure that you call the super constructor before assigning the parameters. .. code-block:: python :emphasize-lines: 4 def __init__(self, threshold=0.5): if threshold <= 0 or threshold > 1: raise exceptions.InvalidValue("threshold must be between 0 and 1!") super().__init__() self.parameters["threshold"] = threshold self.validities = StandardValidities() self.check_metadata() Implement the Abstract Method ============================= Finally, we have to implement the abstract method **map**. The method takes a **pd.Series** as a parameter and returns the mapped values. For our example, we assume that we only support series. If a non-series will pass to the method, the method raises an error. .. code-block:: python def map(self, probabilities): if not isinstance(probabilities, pd.Series): raise exceptions.InvalidType("probabilities must be an instance of pd.Series!") threshold = self.parameters["threshold"].value mask = probabilities[probabilities >= threshold] flags = pd.Series(self.validities.ERRONEOUS, index=probabilities.index) flags[mask] = self.validities.GOOD return flags