Source code for cursus.processing.temporal.sequence_padding_processor

"""
Sequence Padding Processor for Temporal Self-Attention Model

This module provides atomic sequence padding/truncation for temporal sequences.
Extracted from TSA preprocess_functions.py logic.
"""

import numpy as np
import pandas as pd
from typing import Dict, List, Optional, Union, Any
import logging

from ..processors import Processor

logger = logging.getLogger(__name__)


[docs] class SequencePaddingProcessor(Processor): """ Pads or truncates sequences to a target length. Extracted from TSA preprocess_functions.py: - seq_cat_mtx = np.pad(seq_cat_mtx, [(seq_len - 1 - len(seq_cat_vars_mtx), 0), (0, 0)]) Args: target_length: Desired sequence length padding_strategy: 'pre', 'post' truncation_strategy: 'pre', 'post' padding_value: Value to use for padding axis: Axis along which to pad/truncate """ def __init__( self, target_length: int = 51, padding_strategy: str = "pre", truncation_strategy: str = "post", padding_value: Union[int, float] = 0, axis: int = 0, ): super().__init__() if not isinstance(target_length, int) or target_length <= 0: raise ValueError( f"target_length must be a positive int, got {target_length!r}" ) if not isinstance(axis, int) or axis < 0: # This processor indexes shape[axis] for an N-D sequence; a negative # or non-int axis silently mis-pads. Restrict to non-negative ints. raise ValueError(f"axis must be a non-negative int, got {axis!r}") self.target_length = target_length self.padding_strategy = padding_strategy self.truncation_strategy = truncation_strategy self.padding_value = padding_value self.axis = axis self.is_fitted = False
[docs] def fit(self, data: Any) -> "SequencePaddingProcessor": """No fitting required for padding""" self.is_fitted = True logger.info( f"SequencePaddingProcessor fitted with target_length: {self.target_length}" ) return self
[docs] def process(self, input_data: Union[np.ndarray, List]) -> Union[np.ndarray, List]: """Apply sequence padding/truncation""" if not self.is_fitted: raise RuntimeError("Processor must be fitted before processing") if isinstance(input_data, np.ndarray): return self._process_numpy_array(input_data) elif isinstance(input_data, list): return self._process_list(input_data) else: raise ValueError(f"Unsupported input type: {type(input_data)}")
def _process_numpy_array(self, input_data: np.ndarray) -> np.ndarray: """Process numpy array input""" current_length = input_data.shape[self.axis] if current_length == self.target_length: return input_data elif current_length < self.target_length: # Padding required pad_width = [(0, 0)] * input_data.ndim pad_amount = self.target_length - current_length if self.padding_strategy == "pre": pad_width[self.axis] = (pad_amount, 0) else: # post pad_width[self.axis] = (0, pad_amount) return np.pad(input_data, pad_width, constant_values=self.padding_value) else: # Truncation required if self.truncation_strategy == "pre": # Keep last target_length elements slices = [slice(None)] * input_data.ndim slices[self.axis] = slice(-self.target_length, None) return input_data[tuple(slices)] else: # post # Keep first target_length elements slices = [slice(None)] * input_data.ndim slices[self.axis] = slice(self.target_length) return input_data[tuple(slices)] def _process_list(self, input_data: List) -> List: """Process list input""" current_length = len(input_data) if current_length == self.target_length: return input_data elif current_length < self.target_length: # Padding required pad_amount = self.target_length - current_length padding = [self.padding_value] * pad_amount if self.padding_strategy == "pre": return padding + input_data else: # post return input_data + padding else: # Truncation required if self.truncation_strategy == "pre": # Keep last target_length elements return input_data[-self.target_length :] else: # post # Keep first target_length elements return input_data[: self.target_length]
[docs] def get_config(self) -> Dict[str, Any]: """Return processor configuration""" return { "target_length": self.target_length, "padding_strategy": self.padding_strategy, "truncation_strategy": self.truncation_strategy, "padding_value": self.padding_value, "axis": self.axis, }
def __repr__(self) -> str: return ( f"SequencePaddingProcessor(target_length={self.target_length}, " f"padding_strategy='{self.padding_strategy}', " f"truncation_strategy='{self.truncation_strategy}')" )