Source code for pysatl_tsp.implementations.processor.pwma_handler

import math

from pysatl_tsp.core.processor.inductive.weighted_moving_average_handler import WeightedMovingAverageHandler


[docs] class PWMAHandler(WeightedMovingAverageHandler): """Pascal Weighted Moving Average (PWMA) handler. Calculates a weighted moving average using coefficients from Pascal's triangle as weights. Pascal's triangle provides a natural weighting scheme where the central values receive more weight than the extremes, creating a balanced but still centered weighting distribution. This implementation matches the functionality of pandas_ta.pwma. Inherits parameters from WeightedMovingAverageHandler: :param length: The period for the calculation, defaults to 10 :param asc: Whether weights should be applied in ascending order, defaults to False :param source: Input data source, defaults to None Example: .. code-block:: python # Create a data source with numeric values data_source = SimpleDataProvider([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]) # Create a PWMA handler with length of 4 pwma_handler = PWMAHandler(length=4) pwma_handler.set_source(data_source) # Process the data for value in pwma_handler: print(value) # First 3 values will be None (not enough data points) # For length=4, Pascal weights would be [1/8, 3/8, 3/8, 1/8] or [1/8, 3/8, 3/8, 1/8] when asc=False # The calculation gives more weight to the central values """
[docs] def _calculate_weights(self, length: int, asc: bool) -> list[float]: """Calculate Pascal's triangle weights for PWMA. Generates weights based on a row of Pascal's triangle and normalizes them to sum to 1.0. The row is determined by (length-1) to match the pandas_ta implementation. The weights order can be reversed based on the asc parameter. :param length: The number of weights to generate :param asc: Whether weights should be in ascending order :return: A list of normalized weights summing to 1.0 """ # Generate the row of Pascal's triangle # Using (length-1) to match pandas_ta implementation triangle = [self._combination(length - 1, i) for i in range(length)] # Normalize the weights to sum to 1.0 total = sum(triangle) weights = [w / total for w in triangle] if not asc: weights = weights[::-1] return weights
[docs] def _combination(self, n: int, r: int) -> float: """Calculate the binomial coefficient (n choose r). Computes the binomial coefficient, also known as "n choose r", which represents the number of ways to choose r items from a set of n items without regard to order. Uses math.comb when available (Python 3.8+) for efficiency, otherwise falls back to calculation using factorials. :param n: The total number of items :param r: The number of items to choose :return: The binomial coefficient """ return math.comb(n, r)
[docs] def _factorial(self, n: int) -> int: """Calculate the factorial of n. Computes n! recursively. This method is used as a fallback when math.comb is not available for binomial coefficient calculation. :param n: The number to calculate factorial for :return: The factorial of n """ if n <= 1: return 1 return n * self._factorial(n - 1)