Skip to content

multiplication

Multiplication

Bases: NToOneTransformer

N-to-1 transformation allowing to get 1 variable by applying a multiplication of N other variables.

Parameters:

Name Type Description Default
powers List[float]

Powers applied to each variable concerned by the multiplicative transformation.

required
ignore_input_indexes Optional[List[int]]

Indexes of the data in series to ignore when computing the repartition.

None
Source code in eki_mmo_equations/n_to_one_transformations/multiplication.py
class Multiplication(NToOneTransformer):
    """N-to-1 transformation allowing to get 1 variable by applying a multiplication of N other variables.

    ```math
        serie_1^{(\\alpha_1)} \\times serie_2^{(\\alpha_2)} \\times ... \\times serie_N^{(\\alpha_N)}
    ```

    Args:
        powers (List[float]): Powers applied to each variable concerned by the multiplicative transformation.
        ignore_input_indexes (Optional[List[int]], optional): Indexes of the data in series to ignore when
            computing the repartition.
    """

    def __init__(self, powers: List[float], ignore_input_indexes: Optional[List[int]] = None):
        self.powers: List[float] = powers
        self.ignore_input_indexes: List[int] = ignore_input_indexes if ignore_input_indexes else []

    @property
    def parameters(self) -> Dict[str, float]:
        return self.__dict__

    # ------- METHODS -------

    def transform(self, series: List[np.ndarray], copy=False) -> np.ndarray:
        series = super().transform(series, copy)

        return self._transformer(series, self.powers)

    def repartition(self, series: List[np.ndarray]) -> List[np.ndarray]:
        "Returns an estimated repartition of the output series from the initial input series."
        repartition_series = [serie for i, serie in enumerate(series) if i not in self.ignore_input_indexes]
        repartition_powers = [power for i, power in enumerate(self.powers) if i not in self.ignore_input_indexes]
        powered_series = [serie**power for serie, power in zip(repartition_series, repartition_powers)]
        with np.errstate(divide="ignore", invalid="ignore"):
            weighted_series = powered_series / reduce(lambda a, b: a * b, powered_series)
        proportionned_weighted_series = [
            np.nan_to_num(weighted_serie / np.sum(weighted_series, axis=0)) for weighted_serie in weighted_series
        ]
        for index in self.ignore_input_indexes:
            proportionned_weighted_series.insert(index, np.zeros(len(series[index])))

        return proportionned_weighted_series

    # ------- TRANSFORMERS -------

    @staticmethod
    def _transformer(series: List[np.ndarray], powers: List[float]) -> np.ndarray:
        "Returns the multiplication of the different input variables."
        transformed_series = [serie**power for serie, power in zip(series, powers)]
        return reduce(lambda a, b: a * b, transformed_series)

    # ------- CHECKERS -------

    def check_params(self, series: List[np.ndarray]):
        """Check if parameters respect their application scope."""
        if not len(self.powers) == len(series):
            raise ParameterScopeException(
                "Number of series don't match number of powers"
                + f"Number of powers:{len(self.powers)} . Number of series:{len(series)}"
            )
        if len(self.ignore_input_indexes) != len(set(self.ignore_input_indexes)):
            raise ParameterScopeException(
                f"Incorrect {self.__class__.__name__} parameters. Values ignore_input_indexes"
                f" should be unique. Current values:{self.ignore_input_indexes}"
            )
        if not all(index < len(series) for index in self.ignore_input_indexes):
            raise ParameterScopeException(
                f"Incorrect {self.__class__.__name__} parameters. Each value of ignore_input_indexes"
                f" should be lower than the number of series as input - lower than {len(series)}."
            )
        if len(self.ignore_input_indexes) >= len(series):
            raise ParameterScopeException(
                f"Incorrect {self.__class__.__name__}. Length of ignore_input_indexes"
                f" should be inferior to length of input series - lower than {len(series)}."
            )

check_params(series)

Check if parameters respect their application scope.

Source code in eki_mmo_equations/n_to_one_transformations/multiplication.py
def check_params(self, series: List[np.ndarray]):
    """Check if parameters respect their application scope."""
    if not len(self.powers) == len(series):
        raise ParameterScopeException(
            "Number of series don't match number of powers"
            + f"Number of powers:{len(self.powers)} . Number of series:{len(series)}"
        )
    if len(self.ignore_input_indexes) != len(set(self.ignore_input_indexes)):
        raise ParameterScopeException(
            f"Incorrect {self.__class__.__name__} parameters. Values ignore_input_indexes"
            f" should be unique. Current values:{self.ignore_input_indexes}"
        )
    if not all(index < len(series) for index in self.ignore_input_indexes):
        raise ParameterScopeException(
            f"Incorrect {self.__class__.__name__} parameters. Each value of ignore_input_indexes"
            f" should be lower than the number of series as input - lower than {len(series)}."
        )
    if len(self.ignore_input_indexes) >= len(series):
        raise ParameterScopeException(
            f"Incorrect {self.__class__.__name__}. Length of ignore_input_indexes"
            f" should be inferior to length of input series - lower than {len(series)}."
        )

repartition(series)

Returns an estimated repartition of the output series from the initial input series.

Source code in eki_mmo_equations/n_to_one_transformations/multiplication.py
def repartition(self, series: List[np.ndarray]) -> List[np.ndarray]:
    "Returns an estimated repartition of the output series from the initial input series."
    repartition_series = [serie for i, serie in enumerate(series) if i not in self.ignore_input_indexes]
    repartition_powers = [power for i, power in enumerate(self.powers) if i not in self.ignore_input_indexes]
    powered_series = [serie**power for serie, power in zip(repartition_series, repartition_powers)]
    with np.errstate(divide="ignore", invalid="ignore"):
        weighted_series = powered_series / reduce(lambda a, b: a * b, powered_series)
    proportionned_weighted_series = [
        np.nan_to_num(weighted_serie / np.sum(weighted_series, axis=0)) for weighted_serie in weighted_series
    ]
    for index in self.ignore_input_indexes:
        proportionned_weighted_series.insert(index, np.zeros(len(series[index])))

    return proportionned_weighted_series