# gaitsetpy.features

Features module for gait analysis and feature extraction.

This module provides both the new class-based feature extractors and legacy function-based API. All feature extractors inherit from BaseFeatureExtractor and are registered with the FeatureManager.

Maintainer: @aharshit123456

View Source

      1'''
      2Features module for gait analysis and feature extraction.
      3
      4This module provides both the new class-based feature extractors and legacy function-based API.
      5All feature extractors inherit from BaseFeatureExtractor and are registered with the FeatureManager.
      6
      7Maintainer: @aharshit123456
      8'''
      9
     10# Import the new class-based feature extractors
     11from .gait_features import GaitFeatureExtractor
     12from .physionet_features import LBPFeatureExtractor, FourierSeriesFeatureExtractor, PhysioNetFeatureExtractor
     13from .harup_features import HARUPFeatureExtractor
     14
     15# Import legacy functions for backward compatibility
     16from .physionet_features import extract_lbp_features, extract_fourier_features, extract_physionet_features
     17from .harup_features import extract_harup_features
     18from .utils import (
     19    calculate_mean,
     20    calculate_standard_deviation,
     21    calculate_variance,
     22    calculate_skewness,
     23    calculate_kurtosis,
     24    calculate_root_mean_square,
     25    calculate_range,
     26    calculate_median,
     27    calculate_mode,
     28    calculate_mean_absolute_value,
     29    calculate_median_absolute_deviation,
     30    calculate_peak_height,
     31    calculate_stride_times,
     32    calculate_step_time,
     33    calculate_cadence,
     34    calculate_freezing_index,
     35    calculate_dominant_frequency,
     36    calculate_peak_frequency,
     37    calculate_power_spectral_entropy,
     38    calculate_principal_harmonic_frequency,
     39    calculate_entropy,
     40    calculate_interquartile_range,
     41    calculate_correlation,
     42    calculate_auto_regression_coefficients
     43)
     44
     45from .gait_features import (
     46    get_mean_for_windows,
     47    get_standard_deviation_for_windows,
     48    get_variance_for_windows,
     49    get_skewness_for_windows,
     50    get_kurtosis_for_windows,
     51    get_root_mean_square_for_windows,
     52    get_range_for_windows,
     53    get_median_for_windows,
     54    get_mode_for_windows,
     55    get_mean_absolute_value_for_windows,
     56    get_median_absolute_deviation_for_windows,
     57    get_peak_height_for_windows,
     58    get_stride_times_for_windows,
     59    get_step_times_for_windows,
     60    get_cadence_for_windows,
     61    get_freezing_index_for_windows,
     62    get_dominant_frequency_for_windows,
     63    get_peak_frequency_for_windows,
     64    get_power_spectral_entropy_for_windows,
     65    get_principal_harmonic_frequency_for_windows,
     66    get_entropy_for_windows,
     67    get_interquartile_range_for_windows,
     68    get_correlation_for_windows,
     69    get_auto_regression_coefficients_for_windows,
     70    extract_gait_features
     71)
     72
     73# Import managers
     74from ..core.managers import FeatureManager
     75
     76# Register all feature extractors with the manager
     77def _register_extractors():
     78    """Register all available feature extractors with the FeatureManager."""
     79    manager = FeatureManager()
     80    manager.register_extractor("gait_features", GaitFeatureExtractor)
     81    manager.register_extractor("lbp_features", LBPFeatureExtractor)
     82    manager.register_extractor("fourier_features", FourierSeriesFeatureExtractor)
     83    manager.register_extractor("physionet_features", PhysioNetFeatureExtractor)
     84    manager.register_extractor("harup_features", HARUPFeatureExtractor)
     85
     86# Auto-register extractors when module is imported
     87_register_extractors()
     88
     89# Convenient access to the feature manager
     90def get_feature_manager():
     91    """Get the singleton FeatureManager instance."""
     92    return FeatureManager()
     93
     94# Helper function to get available extractors
     95def get_available_extractors():
     96    """Get list of available feature extractor names."""
     97    return FeatureManager().get_available_components()
     98
     99# Helper function to extract features using manager
    100def extract_features(extractor_name: str, windows, fs: int, **kwargs):
    101    """
    102    Extract features using the FeatureManager.
    103
    104    Args:
    105        extractor_name: Name of the feature extractor
    106        windows: List of sliding window dictionaries
    107        fs: Sampling frequency
    108        **kwargs: Additional arguments for feature extraction
    109
    110    Returns:
    111        List of feature dictionaries
    112    """
    113    return FeatureManager().extract_features(extractor_name, windows, fs, **kwargs)
    114
    115__all__ = [
    116    # New class-based extractors
    117    'GaitFeatureExtractor',
    118    'LBPFeatureExtractor',
    119    'FourierSeriesFeatureExtractor',
    120    'PhysioNetFeatureExtractor',
    121    'HARUPFeatureExtractor',
    122    # Legacy utility functions
    123    'calculate_mean',
    124    'calculate_standard_deviation',
    125    'calculate_variance',
    126    'calculate_skewness',
    127    'calculate_kurtosis',
    128    'calculate_root_mean_square',
    129    'calculate_range',
    130    'calculate_median',
    131    'calculate_mode',
    132    'calculate_mean_absolute_value',
    133    'calculate_median_absolute_deviation',
    134    'calculate_peak_height',
    135    'calculate_stride_times',
    136    'calculate_step_time',
    137    'calculate_cadence',
    138    'calculate_freezing_index',
    139    'calculate_dominant_frequency',
    140    'calculate_peak_frequency',
    141    'calculate_power_spectral_entropy',
    142    'calculate_principal_harmonic_frequency',
    143    'calculate_entropy',
    144    'calculate_interquartile_range',
    145    'calculate_correlation',
    146    'calculate_auto_regression_coefficients',
    147    # Legacy window functions
    148    'get_mean_for_windows',
    149    'get_standard_deviation_for_windows',
    150    'get_variance_for_windows',
    151    'get_skewness_for_windows',
    152    'get_kurtosis_for_windows',
    153    'get_root_mean_square_for_windows',
    154    'get_range_for_windows',
    155    'get_median_for_windows',
    156    'get_mode_for_windows',
    157    'get_mean_absolute_value_for_windows',
    158    'get_median_absolute_deviation_for_windows',
    159    'get_peak_height_for_windows',
    160    'get_stride_times_for_windows',
    161    'get_step_times_for_windows',
    162    'get_cadence_for_windows',
    163    'get_freezing_index_for_windows',
    164    'get_dominant_frequency_for_windows',
    165    'get_peak_frequency_for_windows',
    166    'get_power_spectral_entropy_for_windows',
    167    'get_principal_harmonic_frequency_for_windows',
    168    'get_entropy_for_windows',
    169    'get_interquartile_range_for_windows',
    170    'get_correlation_for_windows',
    171    'get_auto_regression_coefficients_for_windows',
    172    'extract_gait_features',
    173    # PhysioNet legacy functions
    174    'extract_lbp_features',
    175    'extract_fourier_features',
    176    'extract_physionet_features',
    177    # HAR-UP legacy functions
    178    'extract_harup_features',
    179    # Manager functions
    180    'get_feature_manager',
    181    'get_available_extractors',
    182    'extract_features'
    183]

class GaitFeatureExtractor(gaitsetpy.core.base_classes.BaseFeatureExtractor): View Source

     49class GaitFeatureExtractor(BaseFeatureExtractor):
     50    """
     51    Comprehensive gait feature extractor class.
     52
     53    This class extracts various time-domain, frequency-domain, and statistical features
     54    from gait data sliding windows.
     55    """
     56
     57    def __init__(self, verbose: bool = True):
     58        super().__init__(
     59            name="gait_features",
     60            description="Comprehensive gait feature extractor for time-domain, frequency-domain, and statistical features"
     61        )
     62        self.verbose = verbose
     63        self.config = {
     64            'time_domain': True,
     65            'frequency_domain': True,
     66            'statistical': True,
     67            'ar_order': 3  # Order for auto-regression coefficients
     68        }
     69
     70        if self.verbose:
     71            print("🚀 GaitFeatureExtractor initialized successfully!")
     72            print(f"📊 Default configuration: {self.config}")
     73
     74    def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
     75        """
     76        Extract gait features from sliding windows.
     77
     78        Args:
     79            windows: List of sliding window dictionaries
     80            fs: Sampling frequency
     81            **kwargs: Additional arguments including time_domain, frequency_domain, statistical flags
     82
     83        Returns:
     84            List of feature dictionaries for each sensor
     85        """
     86        # Update config with any passed arguments
     87        time_domain = kwargs.get('time_domain', self.config['time_domain'])
     88        frequency_domain = kwargs.get('frequency_domain', self.config['frequency_domain'])
     89        statistical = kwargs.get('statistical', self.config['statistical'])
     90        ar_order = kwargs.get('ar_order', self.config['ar_order'])
     91
     92        if self.verbose:
     93            print("\n" + "="*60)
     94            print("🔍 STARTING GAIT FEATURE EXTRACTION")
     95            print("="*60)
     96            print(f"📈 Total sensors/windows to process: {len(windows)}")
     97            print(f"🔊 Sampling frequency: {fs} Hz")
     98            print(f"⏱️  Time domain features: {'✅' if time_domain else '❌'}")
     99            print(f"🌊 Frequency domain features: {'✅' if frequency_domain else '❌'}")
    100            print(f"📊 Statistical features: {'✅' if statistical else '❌'}")
    101            print(f"🔄 Auto-regression order: {ar_order}")
    102            print("-"*60)
    103
    104        features = []
    105
    106        # Main progress bar for processing all windows
    107        main_pbar = tqdm(
    108            windows,
    109            desc="🔍 Processing Sensors",
    110            unit="sensor",
    111            disable=not self.verbose
    112        )
    113
    114        for i, window_dict in enumerate(main_pbar):
    115            sensor_name = window_dict['name']
    116            window_data = window_dict['data']
    117
    118            if self.verbose:
    119                main_pbar.set_postfix({
    120                    'Current': sensor_name,
    121                    'Windows': len(window_data) if isinstance(window_data, list) else 1
    122                })
    123
    124            # Skip annotation windows
    125            if sensor_name == 'annotations':
    126                if self.verbose:
    127                    logger.info(f"📝 Processing annotation data for {sensor_name}")
    128
    129                features.append({
    130                    'name': sensor_name,
    131                    'features': {},
    132                    'annotations': [self._extract_annotation_labels(window) for window in window_data]
    133                })
    134                continue
    135
    136            if self.verbose:
    137                logger.info(f"🎯 Processing sensor: {sensor_name}")
    138                logger.info(f"📦 Number of windows: {len(window_data)}")
    139
    140            sensor_features = {'name': sensor_name, 'features': {}}
    141
    142            # Time domain features
    143            if time_domain:
    144                if self.verbose:
    145                    print(f"  ⏱️  Extracting time domain features for {sensor_name}...")
    146
    147                time_features = self._extract_time_domain_features(window_data)
    148                sensor_features['features'].update(time_features)
    149
    150                if self.verbose:
    151                    feature_count = sum(len(v) if isinstance(v, list) else 1 for v in time_features.values())
    152                    print(f"  ✅ Time domain: {len(time_features)} feature types, {feature_count} total features")
    153
    154            # Frequency domain features
    155            if frequency_domain:
    156                if self.verbose:
    157                    print(f"  🌊 Extracting frequency domain features for {sensor_name}...")
    158
    159                freq_features = self._extract_frequency_domain_features(window_data, fs)
    160                sensor_features['features'].update(freq_features)
    161
    162                if self.verbose:
    163                    feature_count = sum(len(v) if isinstance(v, list) else 1 for v in freq_features.values())
    164                    print(f"  ✅ Frequency domain: {len(freq_features)} feature types, {feature_count} total features")
    165
    166            # Statistical features
    167            if statistical:
    168                if self.verbose:
    169                    print(f"  📊 Extracting statistical features for {sensor_name}...")
    170
    171                stat_features = self._extract_statistical_features(window_data)
    172                sensor_features['features'].update(stat_features)
    173
    174                if self.verbose:
    175                    feature_count = sum(len(v) if isinstance(v, list) else 1 for v in stat_features.values())
    176                    print(f"  ✅ Statistical: {len(stat_features)} feature types, {feature_count} total features")
    177
    178            # Auto-regression coefficients
    179            if self.verbose:
    180                print(f"  🔄 Extracting auto-regression coefficients for {sensor_name}...")
    181
    182            ar_features = self._extract_ar_coefficients(window_data, ar_order)
    183            sensor_features['features'].update(ar_features)
    184
    185            if self.verbose:
    186                feature_count = sum(len(v) if isinstance(v, list) else 1 for v in ar_features.values())
    187                print(f"  ✅ Auto-regression: {len(ar_features)} feature types, {feature_count} total features")
    188
    189            # Calculate total features for this sensor
    190            total_features = sum(
    191                len(v) if isinstance(v, list) else 1
    192                for v in sensor_features['features'].values()
    193            )
    194
    195            if self.verbose:
    196                print(f"  🎯 Total features extracted for {sensor_name}: {total_features}")
    197                print(f"  📋 Feature types: {list(sensor_features['features'].keys())}")
    198                print("-"*40)
    199
    200            features.append(sensor_features)
    201
    202        if self.verbose:
    203            print("\n" + "="*60)
    204            print("🎉 FEATURE EXTRACTION COMPLETED!")
    205            print("="*60)
    206            print(f"📊 Total sensors processed: {len(features)}")
    207
    208            # Calculate overall statistics
    209            total_feature_count = 0
    210            for feature_dict in features:
    211                if 'features' in feature_dict:
    212                    total_feature_count += sum(
    213                        len(v) if isinstance(v, list) else 1
    214                        for v in feature_dict['features'].values()
    215                    )
    216
    217            print(f"🔢 Total features extracted: {total_feature_count}")
    218            print(f"📈 Average features per sensor: {total_feature_count / len(features):.1f}")
    219            print("="*60)
    220
    221        return features
    222
    223    def _extract_time_domain_features(self, windows: List) -> Dict[str, List]:
    224        """Extract time domain features from windows."""
    225        if self.verbose:
    226            print("    🔍 Computing time domain features...")
    227
    228        time_features = {}
    229
    230        # Define time domain feature functions
    231        time_domain_funcs = {
    232            'mean': calculate_mean,
    233            'std': calculate_standard_deviation,
    234            'variance': calculate_variance,
    235            'rms': calculate_root_mean_square,
    236            'range': calculate_range,
    237            'median': calculate_median,
    238            'mode': calculate_mode,
    239            'mean_absolute_value': calculate_mean_absolute_value,
    240            'median_absolute_deviation': calculate_median_absolute_deviation,
    241            'peak_height': calculate_peak_height,
    242            'zero_crossing_rate': calculate_zero_crossing_rate,
    243            'energy': calculate_energy,
    244        }
    245
    246        # Progress bar for time domain features
    247        feature_pbar = tqdm(
    248            time_domain_funcs.items(),
    249            desc="    ⏱️  Time features",
    250            unit="feature",
    251            leave=False,
    252            disable=not self.verbose
    253        )
    254
    255        for feature_name, func in feature_pbar:
    256            if self.verbose:
    257                feature_pbar.set_postfix({'Computing': feature_name})
    258
    259            time_features[feature_name] = [
    260                func(self._ensure_numpy_array(window)) for window in windows
    261            ]
    262
    263        return time_features
    264
    265    def _ensure_numpy_array(self, signal):
    266        """Convert pandas Series to numpy array if needed."""
    267        if hasattr(signal, 'values'):
    268            return signal.values
    269        return signal
    270
    271    def _extract_frequency_domain_features(self, windows: List, fs: int) -> Dict[str, List]:
    272        """Extract frequency domain features from windows."""
    273        if self.verbose:
    274            print("    🔍 Computing frequency domain features...")
    275
    276        freq_features = {}
    277
    278        # Define frequency domain feature functions
    279        freq_domain_funcs = {
    280            'dominant_frequency': lambda w: calculate_dominant_frequency(w, fs),
    281            'peak_frequency': lambda w: calculate_peak_frequency(w, fs),
    282            'power_spectral_entropy': lambda w: calculate_power_spectral_entropy(w, fs),
    283            'principal_harmonic_frequency': lambda w: calculate_principal_harmonic_frequency(w, fs),
    284            'stride_times': lambda w: calculate_stride_times(w, fs),
    285            'step_time': lambda w: calculate_step_time(w, fs),
    286            'cadence': lambda w: calculate_cadence(w, fs),
    287            'freezing_index': lambda w: calculate_freezing_index(w, fs),
    288        }
    289
    290        # Progress bar for frequency domain features
    291        feature_pbar = tqdm(
    292            freq_domain_funcs.items(),
    293            desc="    🌊 Freq features",
    294            unit="feature",
    295            leave=False,
    296            disable=not self.verbose
    297        )
    298
    299        for feature_name, func in feature_pbar:
    300            if self.verbose:
    301                feature_pbar.set_postfix({'Computing': feature_name})
    302
    303            freq_features[feature_name] = [
    304                func(self._ensure_numpy_array(window)) for window in windows
    305            ]
    306
    307        return freq_features
    308
    309    def _extract_statistical_features(self, windows: List) -> Dict[str, List]:
    310        """Extract statistical features from windows."""
    311        if self.verbose:
    312            print("    🔍 Computing statistical features...")
    313
    314        stat_features = {}
    315
    316        # Define statistical feature functions
    317        stat_funcs = {
    318            'skewness': calculate_skewness,
    319            'kurtosis': calculate_kurtosis,
    320            'entropy': calculate_entropy,
    321            'interquartile_range': calculate_interquartile_range,
    322        }
    323
    324        # Progress bar for statistical features
    325        feature_pbar = tqdm(
    326            stat_funcs.items(),
    327            desc="    📊 Stat features",
    328            unit="feature",
    329            leave=False,
    330            disable=not self.verbose
    331        )
    332
    333        for feature_name, func in feature_pbar:
    334            if self.verbose:
    335                feature_pbar.set_postfix({'Computing': feature_name})
    336
    337            stat_features[feature_name] = [
    338                func(self._ensure_numpy_array(window)) for window in windows
    339            ]
    340
    341        # Handle correlation separately (needs two signals)
    342        if self.verbose:
    343            print("      🔗 Computing correlation features...")
    344
    345        stat_features['correlation'] = [
    346            calculate_correlation(
    347                self._ensure_numpy_array(window)[:-1],
    348                self._ensure_numpy_array(window)[1:]
    349            ) if len(window) > 1 else 0
    350            for window in windows
    351        ]
    352
    353        return stat_features
    354
    355    def _extract_ar_coefficients(self, windows: List, order: int) -> Dict[str, List]:
    356        """Extract auto-regression coefficients from windows."""
    357        if self.verbose:
    358            print(f"    🔍 Computing auto-regression coefficients (order={order})...")
    359
    360        # Progress bar for AR coefficients
    361        ar_pbar = tqdm(
    362            windows,
    363            desc="    🔄 AR coeffs",
    364            unit="window",
    365            leave=False,
    366            disable=not self.verbose
    367        )
    368
    369        ar_coeffs = []
    370        for window in ar_pbar:
    371            coeffs = calculate_auto_regression_coefficients(
    372                self._ensure_numpy_array(window), order
    373            )
    374            ar_coeffs.append(coeffs)
    375
    376        return {'ar_coefficients': ar_coeffs}
    377
    378    def _extract_annotation_labels(self, window) -> int:
    379        """Extract the most common annotation label from a window."""
    380        if hasattr(window, 'mode'):
    381            return window.mode().iloc[0] if len(window.mode()) > 0 else 0
    382        else:
    383            # For numpy arrays or other types
    384            unique, counts = np.unique(window, return_counts=True)
    385            return unique[np.argmax(counts)]
    386
    387    def get_feature_names(self) -> List[str]:
    388        """
    389        Get names of all features that can be extracted.
    390
    391        Returns:
    392            List of feature names
    393        """
    394        time_domain_features = [
    395            'mean', 'std', 'variance', 'rms', 'range', 'median', 'mode',
    396            'mean_absolute_value', 'median_absolute_deviation', 'peak_height',
    397            'zero_crossing_rate', 'energy'
    398        ]
    399
    400        frequency_domain_features = [
    401            'dominant_frequency', 'peak_frequency', 'power_spectral_entropy',
    402            'principal_harmonic_frequency', 'stride_times', 'step_time',
    403            'cadence', 'freezing_index'
    404        ]
    405
    406        statistical_features = [
    407            'skewness', 'kurtosis', 'entropy', 'interquartile_range', 'correlation'
    408        ]
    409
    410        other_features = ['ar_coefficients']
    411
    412        return time_domain_features + frequency_domain_features + statistical_features + other_features
    413
    414    def print_extraction_summary(self, features: List[Dict]) -> None:
    415        """
    416        Print a detailed summary of extracted features.
    417
    418        Args:
    419            features: List of feature dictionaries returned by extract_features
    420        """
    421        print("\n" + "="*80)
    422        print("📊 FEATURE EXTRACTION SUMMARY")
    423        print("="*80)
    424
    425        for i, feature_dict in enumerate(features):
    426            sensor_name = feature_dict['name']
    427            print(f"\n🎯 Sensor {i+1}: {sensor_name}")
    428            print("-" * 40)
    429
    430            if 'features' in feature_dict and feature_dict['features']:
    431                for feature_type, feature_values in feature_dict['features'].items():
    432                    if isinstance(feature_values, list):
    433                        print(f"  📈 {feature_type}: {len(feature_values)} values")
    434                        if feature_values:
    435                            sample_value = feature_values[0]
    436                            if isinstance(sample_value, (list, np.ndarray)):
    437                                print(f"    └── Shape per window: {np.array(sample_value).shape}")
    438                            else:
    439                                print(f"    └── Sample value: {sample_value:.4f}")
    440                    else:
    441                        print(f"  📈 {feature_type}: {feature_values}")
    442
    443            if 'annotations' in feature_dict:
    444                print(f"  📝 Annotations: {len(feature_dict['annotations'])} windows")
    445
    446        print("\n" + "="*80)

Comprehensive gait feature extractor class.

This class extracts various time-domain, frequency-domain, and statistical features from gait data sliding windows.

GaitFeatureExtractor(verbose: bool = True) View Source

    57    def __init__(self, verbose: bool = True):
    58        super().__init__(
    59            name="gait_features",
    60            description="Comprehensive gait feature extractor for time-domain, frequency-domain, and statistical features"
    61        )
    62        self.verbose = verbose
    63        self.config = {
    64            'time_domain': True,
    65            'frequency_domain': True,
    66            'statistical': True,
    67            'ar_order': 3  # Order for auto-regression coefficients
    68        }
    69
    70        if self.verbose:
    71            print("🚀 GaitFeatureExtractor initialized successfully!")
    72            print(f"📊 Default configuration: {self.config}")

Initialize the feature extractor.

Args: name: Name of the feature extractor description: Description of the feature extractor

verbose
config
def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]: View Source

     74    def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
     75        """
     76        Extract gait features from sliding windows.
     77
     78        Args:
     79            windows: List of sliding window dictionaries
     80            fs: Sampling frequency
     81            **kwargs: Additional arguments including time_domain, frequency_domain, statistical flags
     82
     83        Returns:
     84            List of feature dictionaries for each sensor
     85        """
     86        # Update config with any passed arguments
     87        time_domain = kwargs.get('time_domain', self.config['time_domain'])
     88        frequency_domain = kwargs.get('frequency_domain', self.config['frequency_domain'])
     89        statistical = kwargs.get('statistical', self.config['statistical'])
     90        ar_order = kwargs.get('ar_order', self.config['ar_order'])
     91
     92        if self.verbose:
     93            print("\n" + "="*60)
     94            print("🔍 STARTING GAIT FEATURE EXTRACTION")
     95            print("="*60)
     96            print(f"📈 Total sensors/windows to process: {len(windows)}")
     97            print(f"🔊 Sampling frequency: {fs} Hz")
     98            print(f"⏱️  Time domain features: {'✅' if time_domain else '❌'}")
     99            print(f"🌊 Frequency domain features: {'✅' if frequency_domain else '❌'}")
    100            print(f"📊 Statistical features: {'✅' if statistical else '❌'}")
    101            print(f"🔄 Auto-regression order: {ar_order}")
    102            print("-"*60)
    103
    104        features = []
    105
    106        # Main progress bar for processing all windows
    107        main_pbar = tqdm(
    108            windows,
    109            desc="🔍 Processing Sensors",
    110            unit="sensor",
    111            disable=not self.verbose
    112        )
    113
    114        for i, window_dict in enumerate(main_pbar):
    115            sensor_name = window_dict['name']
    116            window_data = window_dict['data']
    117
    118            if self.verbose:
    119                main_pbar.set_postfix({
    120                    'Current': sensor_name,
    121                    'Windows': len(window_data) if isinstance(window_data, list) else 1
    122                })
    123
    124            # Skip annotation windows
    125            if sensor_name == 'annotations':
    126                if self.verbose:
    127                    logger.info(f"📝 Processing annotation data for {sensor_name}")
    128
    129                features.append({
    130                    'name': sensor_name,
    131                    'features': {},
    132                    'annotations': [self._extract_annotation_labels(window) for window in window_data]
    133                })
    134                continue
    135
    136            if self.verbose:
    137                logger.info(f"🎯 Processing sensor: {sensor_name}")
    138                logger.info(f"📦 Number of windows: {len(window_data)}")
    139
    140            sensor_features = {'name': sensor_name, 'features': {}}
    141
    142            # Time domain features
    143            if time_domain:
    144                if self.verbose:
    145                    print(f"  ⏱️  Extracting time domain features for {sensor_name}...")
    146
    147                time_features = self._extract_time_domain_features(window_data)
    148                sensor_features['features'].update(time_features)
    149
    150                if self.verbose:
    151                    feature_count = sum(len(v) if isinstance(v, list) else 1 for v in time_features.values())
    152                    print(f"  ✅ Time domain: {len(time_features)} feature types, {feature_count} total features")
    153
    154            # Frequency domain features
    155            if frequency_domain:
    156                if self.verbose:
    157                    print(f"  🌊 Extracting frequency domain features for {sensor_name}...")
    158
    159                freq_features = self._extract_frequency_domain_features(window_data, fs)
    160                sensor_features['features'].update(freq_features)
    161
    162                if self.verbose:
    163                    feature_count = sum(len(v) if isinstance(v, list) else 1 for v in freq_features.values())
    164                    print(f"  ✅ Frequency domain: {len(freq_features)} feature types, {feature_count} total features")
    165
    166            # Statistical features
    167            if statistical:
    168                if self.verbose:
    169                    print(f"  📊 Extracting statistical features for {sensor_name}...")
    170
    171                stat_features = self._extract_statistical_features(window_data)
    172                sensor_features['features'].update(stat_features)
    173
    174                if self.verbose:
    175                    feature_count = sum(len(v) if isinstance(v, list) else 1 for v in stat_features.values())
    176                    print(f"  ✅ Statistical: {len(stat_features)} feature types, {feature_count} total features")
    177
    178            # Auto-regression coefficients
    179            if self.verbose:
    180                print(f"  🔄 Extracting auto-regression coefficients for {sensor_name}...")
    181
    182            ar_features = self._extract_ar_coefficients(window_data, ar_order)
    183            sensor_features['features'].update(ar_features)
    184
    185            if self.verbose:
    186                feature_count = sum(len(v) if isinstance(v, list) else 1 for v in ar_features.values())
    187                print(f"  ✅ Auto-regression: {len(ar_features)} feature types, {feature_count} total features")
    188
    189            # Calculate total features for this sensor
    190            total_features = sum(
    191                len(v) if isinstance(v, list) else 1
    192                for v in sensor_features['features'].values()
    193            )
    194
    195            if self.verbose:
    196                print(f"  🎯 Total features extracted for {sensor_name}: {total_features}")
    197                print(f"  📋 Feature types: {list(sensor_features['features'].keys())}")
    198                print("-"*40)
    199
    200            features.append(sensor_features)
    201
    202        if self.verbose:
    203            print("\n" + "="*60)
    204            print("🎉 FEATURE EXTRACTION COMPLETED!")
    205            print("="*60)
    206            print(f"📊 Total sensors processed: {len(features)}")
    207
    208            # Calculate overall statistics
    209            total_feature_count = 0
    210            for feature_dict in features:
    211                if 'features' in feature_dict:
    212                    total_feature_count += sum(
    213                        len(v) if isinstance(v, list) else 1
    214                        for v in feature_dict['features'].values()
    215                    )
    216
    217            print(f"🔢 Total features extracted: {total_feature_count}")
    218            print(f"📈 Average features per sensor: {total_feature_count / len(features):.1f}")
    219            print("="*60)
    220
    221        return features

Extract gait features from sliding windows.

Args: windows: List of sliding window dictionaries fs: Sampling frequency **kwargs: Additional arguments including time_domain, frequency_domain, statistical flags

Returns: List of feature dictionaries for each sensor

def get_feature_names(self) -> List[str]: View Source

    387    def get_feature_names(self) -> List[str]:
    388        """
    389        Get names of all features that can be extracted.
    390
    391        Returns:
    392            List of feature names
    393        """
    394        time_domain_features = [
    395            'mean', 'std', 'variance', 'rms', 'range', 'median', 'mode',
    396            'mean_absolute_value', 'median_absolute_deviation', 'peak_height',
    397            'zero_crossing_rate', 'energy'
    398        ]
    399
    400        frequency_domain_features = [
    401            'dominant_frequency', 'peak_frequency', 'power_spectral_entropy',
    402            'principal_harmonic_frequency', 'stride_times', 'step_time',
    403            'cadence', 'freezing_index'
    404        ]
    405
    406        statistical_features = [
    407            'skewness', 'kurtosis', 'entropy', 'interquartile_range', 'correlation'
    408        ]
    409
    410        other_features = ['ar_coefficients']
    411
    412        return time_domain_features + frequency_domain_features + statistical_features + other_features

Get names of all features that can be extracted.

Returns: List of feature names

def print_extraction_summary(self, features: List[Dict]) -> None: View Source

    414    def print_extraction_summary(self, features: List[Dict]) -> None:
    415        """
    416        Print a detailed summary of extracted features.
    417
    418        Args:
    419            features: List of feature dictionaries returned by extract_features
    420        """
    421        print("\n" + "="*80)
    422        print("📊 FEATURE EXTRACTION SUMMARY")
    423        print("="*80)
    424
    425        for i, feature_dict in enumerate(features):
    426            sensor_name = feature_dict['name']
    427            print(f"\n🎯 Sensor {i+1}: {sensor_name}")
    428            print("-" * 40)
    429
    430            if 'features' in feature_dict and feature_dict['features']:
    431                for feature_type, feature_values in feature_dict['features'].items():
    432                    if isinstance(feature_values, list):
    433                        print(f"  📈 {feature_type}: {len(feature_values)} values")
    434                        if feature_values:
    435                            sample_value = feature_values[0]
    436                            if isinstance(sample_value, (list, np.ndarray)):
    437                                print(f"    └── Shape per window: {np.array(sample_value).shape}")
    438                            else:
    439                                print(f"    └── Sample value: {sample_value:.4f}")
    440                    else:
    441                        print(f"  📈 {feature_type}: {feature_values}")
    442
    443            if 'annotations' in feature_dict:
    444                print(f"  📝 Annotations: {len(feature_dict['annotations'])} windows")
    445
    446        print("\n" + "="*80)

Print a detailed summary of extracted features.

Args: features: List of feature dictionaries returned by extract_features

##### Inherited Members

gaitsetpy.core.base_classes.BaseFeatureExtractor
  name
  description
  configure
  get_info

class LBPFeatureExtractor(gaitsetpy.core.base_classes.BaseFeatureExtractor): View Source

     26class LBPFeatureExtractor(BaseFeatureExtractor):
     27    """
     28    Local Binary Pattern (LBP) feature extractor for VGRF data.
     29
     30    This extractor converts time-series data into LBP codes and extracts
     31    histogram features from the LBP representation.
     32    """
     33
     34    def __init__(self, verbose: bool = True):
     35        super().__init__(
     36            name="lbp_features",
     37            description="Local Binary Pattern feature extractor for VGRF time-series data"
     38        )
     39        self.verbose = verbose
     40        self.config = {
     41            'radius': 2,  # LBP radius (number of neighbors)
     42            'n_bins': 256,  # Number of histogram bins
     43            'normalize': True  # Normalize histogram
     44        }
     45
     46        if self.verbose:
     47            print("🔍 LBP Feature Extractor initialized!")
     48
     49    def lbp_1d(self, data: np.ndarray, radius: int = 2) -> str:
     50        """
     51        Compute 1D Local Binary Pattern for time-series data.
     52
     53        Args:
     54            data: Input time-series data
     55            radius: Radius for LBP computation
     56
     57        Returns:
     58            LBP code as binary string
     59        """
     60        n = len(data)
     61        lbp_code = ''
     62
     63        for i in range(n):
     64            pattern = ''
     65            for j in range(i - radius, i + radius + 1):
     66                if j < 0 or j >= n:
     67                    pattern += '0'
     68                elif data[j] >= data[i]:
     69                    pattern += '1'
     70                else:
     71                    pattern += '0'
     72            lbp_code += pattern
     73
     74        return lbp_code
     75
     76    def lbp_to_histogram(self, lbp_code: str, n_bins: int = 256, normalize: bool = True) -> np.ndarray:
     77        """
     78        Convert LBP code to histogram features.
     79
     80        Args:
     81            lbp_code: Binary LBP code string
     82            n_bins: Number of histogram bins
     83            normalize: Whether to normalize histogram
     84
     85        Returns:
     86            Histogram features as numpy array
     87        """
     88        # Convert LBP code to integer values
     89        if len(lbp_code) == 0:
     90            return np.zeros(n_bins)
     91
     92        # Process LBP code in chunks of 8 bits (or smaller)
     93        chunk_size = 8
     94        lbp_values = []
     95
     96        for i in range(0, len(lbp_code), chunk_size):
     97            chunk = lbp_code[i:i + chunk_size]
     98            if len(chunk) > 0:
     99                # Convert binary string to integer
    100                try:
    101                    value = int(chunk, 2)
    102                    lbp_values.append(value % n_bins)  # Ensure within bin range
    103                except ValueError:
    104                    continue
    105
    106        if len(lbp_values) == 0:
    107            return np.zeros(n_bins)
    108
    109        # Create histogram
    110        hist, _ = np.histogram(lbp_values, bins=n_bins, range=(0, n_bins))
    111
    112        if normalize and np.sum(hist) > 0:
    113            hist = hist / np.sum(hist)
    114
    115        return hist
    116
    117    def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
    118        """
    119        Extract LBP features from sliding windows.
    120
    121        Args:
    122            windows: List of sliding window dictionaries
    123            fs: Sampling frequency (unused for LBP)
    124            **kwargs: Additional arguments
    125
    126        Returns:
    127            List of feature dictionaries
    128        """
    129        # Update config with any passed arguments
    130        radius = kwargs.get('radius', self.config['radius'])
    131        n_bins = kwargs.get('n_bins', self.config['n_bins'])
    132        normalize = kwargs.get('normalize', self.config['normalize'])
    133
    134        if self.verbose:
    135            print(f"\n🔍 LBP Feature Extraction")
    136            print(f"📊 Radius: {radius}, Bins: {n_bins}, Normalize: {normalize}")
    137
    138        features = []
    139
    140        for window_dict in tqdm(windows, desc="Processing LBP features", disable=not self.verbose):
    141            sensor_name = window_dict['name']
    142            window_data = window_dict['data']
    143
    144            # Skip annotation windows
    145            if sensor_name == 'annotations':
    146                continue
    147
    148            sensor_features = {'name': sensor_name, 'features': {}}
    149
    150            # Extract LBP features for each window
    151            lbp_histograms = []
    152            lbp_means = []
    153            lbp_stds = []
    154
    155            for window in window_data:
    156                # Ensure window is numpy array
    157                if hasattr(window, 'values'):
    158                    window = window.values
    159
    160                # Compute LBP
    161                lbp_code = self.lbp_1d(window, radius)
    162
    163                # Convert to histogram
    164                hist = self.lbp_to_histogram(lbp_code, n_bins, normalize)
    165                lbp_histograms.append(hist)
    166
    167                # Extract summary statistics
    168                lbp_means.append(np.mean(hist))
    169                lbp_stds.append(np.std(hist))
    170
    171            # Store features
    172            sensor_features['features'] = {
    173                'lbp_histograms': lbp_histograms,
    174                'lbp_mean': lbp_means,
    175                'lbp_std': lbp_stds,
    176                'lbp_energy': [np.sum(hist**2) for hist in lbp_histograms],
    177                'lbp_entropy': [self._calculate_entropy(hist) for hist in lbp_histograms]
    178            }
    179
    180            features.append(sensor_features)
    181
    182        return features
    183
    184    def _calculate_entropy(self, hist: np.ndarray) -> float:
    185        """Calculate entropy of histogram."""
    186        # Avoid log(0) by adding small value
    187        hist = hist + 1e-10
    188        return -np.sum(hist * np.log2(hist))
    189
    190    def get_feature_names(self) -> List[str]:
    191        """Get names of LBP features."""
    192        return [
    193            'lbp_histograms', 'lbp_mean', 'lbp_std',
    194            'lbp_energy', 'lbp_entropy'
    195        ]

Local Binary Pattern (LBP) feature extractor for VGRF data.

This extractor converts time-series data into LBP codes and extracts histogram features from the LBP representation.

LBPFeatureExtractor(verbose: bool = True) View Source

    34    def __init__(self, verbose: bool = True):
    35        super().__init__(
    36            name="lbp_features",
    37            description="Local Binary Pattern feature extractor for VGRF time-series data"
    38        )
    39        self.verbose = verbose
    40        self.config = {
    41            'radius': 2,  # LBP radius (number of neighbors)
    42            'n_bins': 256,  # Number of histogram bins
    43            'normalize': True  # Normalize histogram
    44        }
    45
    46        if self.verbose:
    47            print("🔍 LBP Feature Extractor initialized!")

Initialize the feature extractor.

Args: name: Name of the feature extractor description: Description of the feature extractor

verbose
config
def lbp_1d(self, data: numpy.ndarray, radius: int = 2) -> str: View Source

    49    def lbp_1d(self, data: np.ndarray, radius: int = 2) -> str:
    50        """
    51        Compute 1D Local Binary Pattern for time-series data.
    52
    53        Args:
    54            data: Input time-series data
    55            radius: Radius for LBP computation
    56
    57        Returns:
    58            LBP code as binary string
    59        """
    60        n = len(data)
    61        lbp_code = ''
    62
    63        for i in range(n):
    64            pattern = ''
    65            for j in range(i - radius, i + radius + 1):
    66                if j < 0 or j >= n:
    67                    pattern += '0'
    68                elif data[j] >= data[i]:
    69                    pattern += '1'
    70                else:
    71                    pattern += '0'
    72            lbp_code += pattern
    73
    74        return lbp_code

Compute 1D Local Binary Pattern for time-series data.

Args: data: Input time-series data radius: Radius for LBP computation

Returns: LBP code as binary string

def lbp_to_histogram( self, lbp_code: str, n_bins: int = 256, normalize: bool = True) -> numpy.ndarray: View Source

     76    def lbp_to_histogram(self, lbp_code: str, n_bins: int = 256, normalize: bool = True) -> np.ndarray:
     77        """
     78        Convert LBP code to histogram features.
     79
     80        Args:
     81            lbp_code: Binary LBP code string
     82            n_bins: Number of histogram bins
     83            normalize: Whether to normalize histogram
     84
     85        Returns:
     86            Histogram features as numpy array
     87        """
     88        # Convert LBP code to integer values
     89        if len(lbp_code) == 0:
     90            return np.zeros(n_bins)
     91
     92        # Process LBP code in chunks of 8 bits (or smaller)
     93        chunk_size = 8
     94        lbp_values = []
     95
     96        for i in range(0, len(lbp_code), chunk_size):
     97            chunk = lbp_code[i:i + chunk_size]
     98            if len(chunk) > 0:
     99                # Convert binary string to integer
    100                try:
    101                    value = int(chunk, 2)
    102                    lbp_values.append(value % n_bins)  # Ensure within bin range
    103                except ValueError:
    104                    continue
    105
    106        if len(lbp_values) == 0:
    107            return np.zeros(n_bins)
    108
    109        # Create histogram
    110        hist, _ = np.histogram(lbp_values, bins=n_bins, range=(0, n_bins))
    111
    112        if normalize and np.sum(hist) > 0:
    113            hist = hist / np.sum(hist)
    114
    115        return hist

Convert LBP code to histogram features.

Args: lbp_code: Binary LBP code string n_bins: Number of histogram bins normalize: Whether to normalize histogram

Returns: Histogram features as numpy array

def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]: View Source

    117    def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
    118        """
    119        Extract LBP features from sliding windows.
    120
    121        Args:
    122            windows: List of sliding window dictionaries
    123            fs: Sampling frequency (unused for LBP)
    124            **kwargs: Additional arguments
    125
    126        Returns:
    127            List of feature dictionaries
    128        """
    129        # Update config with any passed arguments
    130        radius = kwargs.get('radius', self.config['radius'])
    131        n_bins = kwargs.get('n_bins', self.config['n_bins'])
    132        normalize = kwargs.get('normalize', self.config['normalize'])
    133
    134        if self.verbose:
    135            print(f"\n🔍 LBP Feature Extraction")
    136            print(f"📊 Radius: {radius}, Bins: {n_bins}, Normalize: {normalize}")
    137
    138        features = []
    139
    140        for window_dict in tqdm(windows, desc="Processing LBP features", disable=not self.verbose):
    141            sensor_name = window_dict['name']
    142            window_data = window_dict['data']
    143
    144            # Skip annotation windows
    145            if sensor_name == 'annotations':
    146                continue
    147
    148            sensor_features = {'name': sensor_name, 'features': {}}
    149
    150            # Extract LBP features for each window
    151            lbp_histograms = []
    152            lbp_means = []
    153            lbp_stds = []
    154
    155            for window in window_data:
    156                # Ensure window is numpy array
    157                if hasattr(window, 'values'):
    158                    window = window.values
    159
    160                # Compute LBP
    161                lbp_code = self.lbp_1d(window, radius)
    162
    163                # Convert to histogram
    164                hist = self.lbp_to_histogram(lbp_code, n_bins, normalize)
    165                lbp_histograms.append(hist)
    166
    167                # Extract summary statistics
    168                lbp_means.append(np.mean(hist))
    169                lbp_stds.append(np.std(hist))
    170
    171            # Store features
    172            sensor_features['features'] = {
    173                'lbp_histograms': lbp_histograms,
    174                'lbp_mean': lbp_means,
    175                'lbp_std': lbp_stds,
    176                'lbp_energy': [np.sum(hist**2) for hist in lbp_histograms],
    177                'lbp_entropy': [self._calculate_entropy(hist) for hist in lbp_histograms]
    178            }
    179
    180            features.append(sensor_features)
    181
    182        return features

Extract LBP features from sliding windows.

Args: windows: List of sliding window dictionaries fs: Sampling frequency (unused for LBP) **kwargs: Additional arguments

Returns: List of feature dictionaries

def get_feature_names(self) -> List[str]: View Source

    190    def get_feature_names(self) -> List[str]:
    191        """Get names of LBP features."""
    192        return [
    193            'lbp_histograms', 'lbp_mean', 'lbp_std',
    194            'lbp_energy', 'lbp_entropy'
    195        ]

Get names of LBP features.

##### Inherited Members

gaitsetpy.core.base_classes.BaseFeatureExtractor
  name
  description
  configure
  get_info

class FourierSeriesFeatureExtractor(gaitsetpy.core.base_classes.BaseFeatureExtractor): View Source

    198class FourierSeriesFeatureExtractor(BaseFeatureExtractor):
    199    """
    200    Fourier Series feature extractor for VGRF data.
    201
    202    This extractor fits Fourier series to time-series data and extracts
    203    coefficients and reconstruction features.
    204    """
    205
    206    def __init__(self, verbose: bool = True):
    207        super().__init__(
    208            name="fourier_features",
    209            description="Fourier series feature extractor for VGRF time-series data"
    210        )
    211        self.verbose = verbose
    212        self.config = {
    213            'n_terms': 10,  # Number of Fourier terms
    214            'period': 3.0,  # Period for Fourier series
    215            'extract_coefficients': True,
    216            'extract_reconstruction_error': True
    217        }
    218
    219        if self.verbose:
    220            print("🌊 Fourier Series Feature Extractor initialized!")
    221
    222    def fit_fourier_series(self, signal: np.ndarray, time_points: np.ndarray,
    223                          period: float = 3.0, n_terms: int = 10) -> Dict[str, Any]:
    224        """
    225        Fit Fourier series to signal.
    226
    227        Args:
    228            signal: Input signal
    229            time_points: Time points
    230            period: Period of the Fourier series
    231            n_terms: Number of Fourier terms
    232
    233        Returns:
    234            Dictionary containing Fourier series parameters
    235        """
    236        try:
    237            # Calculate Fourier coefficients
    238            L = period
    239
    240            # Calculate a0 (DC component)
    241            a0 = 2/L * simpson(signal, time_points)
    242
    243            # Calculate an and bn coefficients
    244            an = []
    245            bn = []
    246
    247            for n in range(1, n_terms + 1):
    248                # Calculate an coefficient
    249                an_val = 2.0/L * simpson(signal * np.cos(2.*np.pi*n*time_points/L), time_points)
    250                an.append(an_val)
    251
    252                # Calculate bn coefficient
    253                bn_val = 2.0/L * simpson(signal * np.sin(2.*np.pi*n*time_points/L), time_points)
    254                bn.append(bn_val)
    255
    256            # Reconstruct signal
    257            reconstructed = np.full_like(time_points, a0/2)
    258            for n in range(n_terms):
    259                reconstructed += an[n] * np.cos(2.*np.pi*(n+1)*time_points/L)
    260                reconstructed += bn[n] * np.sin(2.*np.pi*(n+1)*time_points/L)
    261
    262            # Calculate reconstruction error
    263            reconstruction_error = np.mean((signal - reconstructed)**2)
    264
    265            return {
    266                'a0': a0,
    267                'an': an,
    268                'bn': bn,
    269                'reconstructed': reconstructed,
    270                'reconstruction_error': reconstruction_error,
    271                'fourier_energy': a0**2 + 2*np.sum(np.array(an)**2 + np.array(bn)**2)
    272            }
    273
    274        except Exception as e:
    275            if self.verbose:
    276                print(f"Error in Fourier series fitting: {e}")
    277            return {
    278                'a0': 0,
    279                'an': [0] * n_terms,
    280                'bn': [0] * n_terms,
    281                'reconstructed': np.zeros_like(time_points),
    282                'reconstruction_error': float('inf'),
    283                'fourier_energy': 0
    284            }
    285
    286    def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
    287        """
    288        Extract Fourier series features from sliding windows.
    289
    290        Args:
    291            windows: List of sliding window dictionaries
    292            fs: Sampling frequency
    293            **kwargs: Additional arguments
    294
    295        Returns:
    296            List of feature dictionaries
    297        """
    298        # Update config with any passed arguments
    299        n_terms = kwargs.get('n_terms', self.config['n_terms'])
    300        period = kwargs.get('period', self.config['period'])
    301
    302        if self.verbose:
    303            print(f"\n🌊 Fourier Series Feature Extraction")
    304            print(f"📊 Terms: {n_terms}, Period: {period}")
    305
    306        features = []
    307
    308        for window_dict in tqdm(windows, desc="Processing Fourier features", disable=not self.verbose):
    309            sensor_name = window_dict['name']
    310            window_data = window_dict['data']
    311
    312            # Skip annotation windows
    313            if sensor_name == 'annotations':
    314                continue
    315
    316            sensor_features = {'name': sensor_name, 'features': {}}
    317
    318            # Extract Fourier features for each window
    319            a0_values = []
    320            an_values = []
    321            bn_values = []
    322            reconstruction_errors = []
    323            fourier_energies = []
    324
    325            for window in window_data:
    326                # Ensure window is numpy array
    327                if hasattr(window, 'values'):
    328                    window = window.values
    329
    330                # Create time points
    331                time_points = np.linspace(0, period, len(window))
    332
    333                # Fit Fourier series
    334                fourier_result = self.fit_fourier_series(window, time_points, period, n_terms)
    335
    336                # Store results
    337                a0_values.append(fourier_result['a0'])
    338                an_values.append(fourier_result['an'])
    339                bn_values.append(fourier_result['bn'])
    340                reconstruction_errors.append(fourier_result['reconstruction_error'])
    341                fourier_energies.append(fourier_result['fourier_energy'])
    342
    343            # Store features
    344            sensor_features['features'] = {
    345                'fourier_a0': a0_values,
    346                'fourier_an': an_values,
    347                'fourier_bn': bn_values,
    348                'fourier_reconstruction_error': reconstruction_errors,
    349                'fourier_energy': fourier_energies,
    350                'fourier_an_mean': [np.mean(an) for an in an_values],
    351                'fourier_bn_mean': [np.mean(bn) for bn in bn_values],
    352                'fourier_an_std': [np.std(an) for an in an_values],
    353                'fourier_bn_std': [np.std(bn) for bn in bn_values]
    354            }
    355
    356            features.append(sensor_features)
    357
    358        return features
    359
    360    def get_feature_names(self) -> List[str]:
    361        """Get names of Fourier series features."""
    362        return [
    363            'fourier_a0', 'fourier_an', 'fourier_bn',
    364            'fourier_reconstruction_error', 'fourier_energy',
    365            'fourier_an_mean', 'fourier_bn_mean',
    366            'fourier_an_std', 'fourier_bn_std'
    367        ]

Fourier Series feature extractor for VGRF data.

This extractor fits Fourier series to time-series data and extracts coefficients and reconstruction features.

FourierSeriesFeatureExtractor(verbose: bool = True) View Source

    206    def __init__(self, verbose: bool = True):
    207        super().__init__(
    208            name="fourier_features",
    209            description="Fourier series feature extractor for VGRF time-series data"
    210        )
    211        self.verbose = verbose
    212        self.config = {
    213            'n_terms': 10,  # Number of Fourier terms
    214            'period': 3.0,  # Period for Fourier series
    215            'extract_coefficients': True,
    216            'extract_reconstruction_error': True
    217        }
    218
    219        if self.verbose:
    220            print("🌊 Fourier Series Feature Extractor initialized!")

Initialize the feature extractor.

Args: name: Name of the feature extractor description: Description of the feature extractor

verbose
config
def fit_fourier_series( self, signal: numpy.ndarray, time_points: numpy.ndarray, period: float = 3.0, n_terms: int = 10) -> Dict[str, Any]: View Source

    222    def fit_fourier_series(self, signal: np.ndarray, time_points: np.ndarray,
    223                          period: float = 3.0, n_terms: int = 10) -> Dict[str, Any]:
    224        """
    225        Fit Fourier series to signal.
    226
    227        Args:
    228            signal: Input signal
    229            time_points: Time points
    230            period: Period of the Fourier series
    231            n_terms: Number of Fourier terms
    232
    233        Returns:
    234            Dictionary containing Fourier series parameters
    235        """
    236        try:
    237            # Calculate Fourier coefficients
    238            L = period
    239
    240            # Calculate a0 (DC component)
    241            a0 = 2/L * simpson(signal, time_points)
    242
    243            # Calculate an and bn coefficients
    244            an = []
    245            bn = []
    246
    247            for n in range(1, n_terms + 1):
    248                # Calculate an coefficient
    249                an_val = 2.0/L * simpson(signal * np.cos(2.*np.pi*n*time_points/L), time_points)
    250                an.append(an_val)
    251
    252                # Calculate bn coefficient
    253                bn_val = 2.0/L * simpson(signal * np.sin(2.*np.pi*n*time_points/L), time_points)
    254                bn.append(bn_val)
    255
    256            # Reconstruct signal
    257            reconstructed = np.full_like(time_points, a0/2)
    258            for n in range(n_terms):
    259                reconstructed += an[n] * np.cos(2.*np.pi*(n+1)*time_points/L)
    260                reconstructed += bn[n] * np.sin(2.*np.pi*(n+1)*time_points/L)
    261
    262            # Calculate reconstruction error
    263            reconstruction_error = np.mean((signal - reconstructed)**2)
    264
    265            return {
    266                'a0': a0,
    267                'an': an,
    268                'bn': bn,
    269                'reconstructed': reconstructed,
    270                'reconstruction_error': reconstruction_error,
    271                'fourier_energy': a0**2 + 2*np.sum(np.array(an)**2 + np.array(bn)**2)
    272            }
    273
    274        except Exception as e:
    275            if self.verbose:
    276                print(f"Error in Fourier series fitting: {e}")
    277            return {
    278                'a0': 0,
    279                'an': [0] * n_terms,
    280                'bn': [0] * n_terms,
    281                'reconstructed': np.zeros_like(time_points),
    282                'reconstruction_error': float('inf'),
    283                'fourier_energy': 0
    284            }

Fit Fourier series to signal.

Args: signal: Input signal time_points: Time points period: Period of the Fourier series n_terms: Number of Fourier terms

Returns: Dictionary containing Fourier series parameters

def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]: View Source

    286    def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
    287        """
    288        Extract Fourier series features from sliding windows.
    289
    290        Args:
    291            windows: List of sliding window dictionaries
    292            fs: Sampling frequency
    293            **kwargs: Additional arguments
    294
    295        Returns:
    296            List of feature dictionaries
    297        """
    298        # Update config with any passed arguments
    299        n_terms = kwargs.get('n_terms', self.config['n_terms'])
    300        period = kwargs.get('period', self.config['period'])
    301
    302        if self.verbose:
    303            print(f"\n🌊 Fourier Series Feature Extraction")
    304            print(f"📊 Terms: {n_terms}, Period: {period}")
    305
    306        features = []
    307
    308        for window_dict in tqdm(windows, desc="Processing Fourier features", disable=not self.verbose):
    309            sensor_name = window_dict['name']
    310            window_data = window_dict['data']
    311
    312            # Skip annotation windows
    313            if sensor_name == 'annotations':
    314                continue
    315
    316            sensor_features = {'name': sensor_name, 'features': {}}
    317
    318            # Extract Fourier features for each window
    319            a0_values = []
    320            an_values = []
    321            bn_values = []
    322            reconstruction_errors = []
    323            fourier_energies = []
    324
    325            for window in window_data:
    326                # Ensure window is numpy array
    327                if hasattr(window, 'values'):
    328                    window = window.values
    329
    330                # Create time points
    331                time_points = np.linspace(0, period, len(window))
    332
    333                # Fit Fourier series
    334                fourier_result = self.fit_fourier_series(window, time_points, period, n_terms)
    335
    336                # Store results
    337                a0_values.append(fourier_result['a0'])
    338                an_values.append(fourier_result['an'])
    339                bn_values.append(fourier_result['bn'])
    340                reconstruction_errors.append(fourier_result['reconstruction_error'])
    341                fourier_energies.append(fourier_result['fourier_energy'])
    342
    343            # Store features
    344            sensor_features['features'] = {
    345                'fourier_a0': a0_values,
    346                'fourier_an': an_values,
    347                'fourier_bn': bn_values,
    348                'fourier_reconstruction_error': reconstruction_errors,
    349                'fourier_energy': fourier_energies,
    350                'fourier_an_mean': [np.mean(an) for an in an_values],
    351                'fourier_bn_mean': [np.mean(bn) for bn in bn_values],
    352                'fourier_an_std': [np.std(an) for an in an_values],
    353                'fourier_bn_std': [np.std(bn) for bn in bn_values]
    354            }
    355
    356            features.append(sensor_features)
    357
    358        return features

Extract Fourier series features from sliding windows.

Args: windows: List of sliding window dictionaries fs: Sampling frequency **kwargs: Additional arguments

Returns: List of feature dictionaries

def get_feature_names(self) -> List[str]: View Source

    360    def get_feature_names(self) -> List[str]:
    361        """Get names of Fourier series features."""
    362        return [
    363            'fourier_a0', 'fourier_an', 'fourier_bn',
    364            'fourier_reconstruction_error', 'fourier_energy',
    365            'fourier_an_mean', 'fourier_bn_mean',
    366            'fourier_an_std', 'fourier_bn_std'
    367        ]

Get names of Fourier series features.

##### Inherited Members

gaitsetpy.core.base_classes.BaseFeatureExtractor
  name
  description
  configure
  get_info

class PhysioNetFeatureExtractor(gaitsetpy.core.base_classes.BaseFeatureExtractor): View Source

    370class PhysioNetFeatureExtractor(BaseFeatureExtractor):
    371    """
    372    Combined feature extractor for PhysioNet VGRF data.
    373
    374    This extractor combines LBP and Fourier series features along with
    375    basic statistical features specific to VGRF data.
    376    """
    377
    378    def __init__(self, verbose: bool = True):
    379        super().__init__(
    380            name="physionet_features",
    381            description="Combined feature extractor for PhysioNet VGRF data including LBP and Fourier features"
    382        )
    383        self.verbose = verbose
    384        self.lbp_extractor = LBPFeatureExtractor(verbose=False)
    385        self.fourier_extractor = FourierSeriesFeatureExtractor(verbose=False)
    386
    387        if self.verbose:
    388            print("🚀 PhysioNet Feature Extractor initialized!")
    389
    390    def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
    391        """
    392        Extract combined features from sliding windows.
    393
    394        Args:
    395            windows: List of sliding window dictionaries
    396            fs: Sampling frequency
    397            **kwargs: Additional arguments
    398
    399        Returns:
    400            List of feature dictionaries
    401        """
    402        # Extract features from each extractor
    403        extract_lbp = kwargs.get('extract_lbp', True)
    404        extract_fourier = kwargs.get('extract_fourier', True)
    405        extract_statistical = kwargs.get('extract_statistical', True)
    406
    407        if self.verbose:
    408            print(f"\n🔍 PhysioNet Feature Extraction")
    409            print(f"📊 LBP: {extract_lbp}, Fourier: {extract_fourier}, Statistical: {extract_statistical}")
    410
    411        features = []
    412
    413        # Extract LBP features
    414        if extract_lbp:
    415            lbp_features = self.lbp_extractor.extract_features(windows, fs, **kwargs)
    416        else:
    417            lbp_features = []
    418
    419        # Extract Fourier features
    420        if extract_fourier:
    421            fourier_features = self.fourier_extractor.extract_features(windows, fs, **kwargs)
    422        else:
    423            fourier_features = []
    424
    425        # Extract statistical features
    426        if extract_statistical:
    427            statistical_features = self._extract_statistical_features(windows)
    428        else:
    429            statistical_features = []
    430
    431        # Combine features
    432        for i, window_dict in enumerate(windows):
    433            sensor_name = window_dict['name']
    434
    435            # Skip annotation windows
    436            if sensor_name == 'annotations':
    437                continue
    438
    439            combined_features = {'name': sensor_name, 'features': {}}
    440
    441            # Add LBP features
    442            if extract_lbp and i < len(lbp_features):
    443                combined_features['features'].update(lbp_features[i]['features'])
    444
    445            # Add Fourier features
    446            if extract_fourier and i < len(fourier_features):
    447                combined_features['features'].update(fourier_features[i]['features'])
    448
    449            # Add statistical features
    450            if extract_statistical and i < len(statistical_features):
    451                combined_features['features'].update(statistical_features[i]['features'])
    452
    453            features.append(combined_features)
    454
    455        return features
    456
    457    def _extract_statistical_features(self, windows: List[Dict]) -> List[Dict]:
    458        """Extract basic statistical features."""
    459        features = []
    460
    461        for window_dict in windows:
    462            sensor_name = window_dict['name']
    463            window_data = window_dict['data']
    464
    465            # Skip annotation windows
    466            if sensor_name == 'annotations':
    467                continue
    468
    469            sensor_features = {'name': sensor_name, 'features': {}}
    470
    471            # Extract statistical features for each window
    472            means = []
    473            stds = []
    474            maxs = []
    475            mins = []
    476            ranges = []
    477
    478            for window in window_data:
    479                # Ensure window is numpy array
    480                if hasattr(window, 'values'):
    481                    window = window.values
    482
    483                means.append(np.mean(window))
    484                stds.append(np.std(window))
    485                maxs.append(np.max(window))
    486                mins.append(np.min(window))
    487                ranges.append(np.max(window) - np.min(window))
    488
    489            # Store features
    490            sensor_features['features'] = {
    491                'vgrf_mean': means,
    492                'vgrf_std': stds,
    493                'vgrf_max': maxs,
    494                'vgrf_min': mins,
    495                'vgrf_range': ranges
    496            }
    497
    498            features.append(sensor_features)
    499
    500        return features
    501
    502    def get_feature_names(self) -> List[str]:
    503        """Get names of all features."""
    504        feature_names = []
    505        feature_names.extend(self.lbp_extractor.get_feature_names())
    506        feature_names.extend(self.fourier_extractor.get_feature_names())
    507        feature_names.extend(['vgrf_mean', 'vgrf_std', 'vgrf_max', 'vgrf_min', 'vgrf_range'])
    508        return feature_names

Combined feature extractor for PhysioNet VGRF data.

This extractor combines LBP and Fourier series features along with basic statistical features specific to VGRF data.

PhysioNetFeatureExtractor(verbose: bool = True) View Source

    378    def __init__(self, verbose: bool = True):
    379        super().__init__(
    380            name="physionet_features",
    381            description="Combined feature extractor for PhysioNet VGRF data including LBP and Fourier features"
    382        )
    383        self.verbose = verbose
    384        self.lbp_extractor = LBPFeatureExtractor(verbose=False)
    385        self.fourier_extractor = FourierSeriesFeatureExtractor(verbose=False)
    386
    387        if self.verbose:
    388            print("🚀 PhysioNet Feature Extractor initialized!")

Initialize the feature extractor.

Args: name: Name of the feature extractor description: Description of the feature extractor

verbose
lbp_extractor
fourier_extractor
def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]: View Source

    390    def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
    391        """
    392        Extract combined features from sliding windows.
    393
    394        Args:
    395            windows: List of sliding window dictionaries
    396            fs: Sampling frequency
    397            **kwargs: Additional arguments
    398
    399        Returns:
    400            List of feature dictionaries
    401        """
    402        # Extract features from each extractor
    403        extract_lbp = kwargs.get('extract_lbp', True)
    404        extract_fourier = kwargs.get('extract_fourier', True)
    405        extract_statistical = kwargs.get('extract_statistical', True)
    406
    407        if self.verbose:
    408            print(f"\n🔍 PhysioNet Feature Extraction")
    409            print(f"📊 LBP: {extract_lbp}, Fourier: {extract_fourier}, Statistical: {extract_statistical}")
    410
    411        features = []
    412
    413        # Extract LBP features
    414        if extract_lbp:
    415            lbp_features = self.lbp_extractor.extract_features(windows, fs, **kwargs)
    416        else:
    417            lbp_features = []
    418
    419        # Extract Fourier features
    420        if extract_fourier:
    421            fourier_features = self.fourier_extractor.extract_features(windows, fs, **kwargs)
    422        else:
    423            fourier_features = []
    424
    425        # Extract statistical features
    426        if extract_statistical:
    427            statistical_features = self._extract_statistical_features(windows)
    428        else:
    429            statistical_features = []
    430
    431        # Combine features
    432        for i, window_dict in enumerate(windows):
    433            sensor_name = window_dict['name']
    434
    435            # Skip annotation windows
    436            if sensor_name == 'annotations':
    437                continue
    438
    439            combined_features = {'name': sensor_name, 'features': {}}
    440
    441            # Add LBP features
    442            if extract_lbp and i < len(lbp_features):
    443                combined_features['features'].update(lbp_features[i]['features'])
    444
    445            # Add Fourier features
    446            if extract_fourier and i < len(fourier_features):
    447                combined_features['features'].update(fourier_features[i]['features'])
    448
    449            # Add statistical features
    450            if extract_statistical and i < len(statistical_features):
    451                combined_features['features'].update(statistical_features[i]['features'])
    452
    453            features.append(combined_features)
    454
    455        return features

Extract combined features from sliding windows.

Args: windows: List of sliding window dictionaries fs: Sampling frequency **kwargs: Additional arguments

Returns: List of feature dictionaries

def get_feature_names(self) -> List[str]: View Source

    502    def get_feature_names(self) -> List[str]:
    503        """Get names of all features."""
    504        feature_names = []
    505        feature_names.extend(self.lbp_extractor.get_feature_names())
    506        feature_names.extend(self.fourier_extractor.get_feature_names())
    507        feature_names.extend(['vgrf_mean', 'vgrf_std', 'vgrf_max', 'vgrf_min', 'vgrf_range'])
    508        return feature_names

Get names of all features.

##### Inherited Members

gaitsetpy.core.base_classes.BaseFeatureExtractor
  name
  description
  config
  configure
  get_info

class HARUPFeatureExtractor(gaitsetpy.core.base_classes.BaseFeatureExtractor): View Source

     21class HARUPFeatureExtractor(BaseFeatureExtractor):
     22    """
     23    HAR-UP feature extractor class.
     24
     25    This class implements the feature extraction methods used in the HAR-UP project.
     26    It extracts both time-domain and frequency-domain features from sensor data.
     27    """
     28
     29    def __init__(self, verbose: bool = False):
     30        """
     31        Initialize the HAR-UP feature extractor.
     32
     33        Args:
     34            verbose: Whether to print progress information
     35        """
     36        super().__init__(
     37            name="harup",
     38            description="HAR-UP Feature Extractor - Extracts features used in the HAR-UP project"
     39        )
     40        self.config = {
     41            'time_domain': True,
     42            'frequency_domain': True,
     43            'verbose': verbose
     44        }
     45
     46        # Define the features to extract
     47        self.time_domain_features = [
     48            'mean', 'std', 'rms', 'max_amp', 'min_amp', 'median',
     49            'zero_crossings', 'skewness', 'kurtosis', 'q1', 'q3', 'autocorr'
     50        ]
     51
     52        self.freq_domain_features = [
     53            'energy'
     54        ]
     55
     56    def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
     57        """
     58        Extract features from sliding windows.
     59
     60        Args:
     61            windows: List of sliding window dictionaries
     62            fs: Sampling frequency
     63            **kwargs: Additional arguments for feature extraction
     64
     65        Returns:
     66            List of feature dictionaries
     67        """
     68        # Update config with kwargs
     69        self.config.update(kwargs)
     70
     71        all_features = []
     72
     73        # Skip label and activity_id windows
     74        sensor_windows = [w for w in windows if w["name"] not in ["labels", "activity_id"]]
     75
     76        if self.config['verbose']:
     77            print(f"Extracting features from {len(sensor_windows)} sensor windows")
     78
     79        # Process each sensor window
     80        for window in sensor_windows:
     81            sensor_name = window["name"]
     82            sensor_data = window["data"]
     83
     84            if self.config['verbose']:
     85                print(f"Processing {sensor_name} with {len(sensor_data)} windows")
     86
     87            # For each window of this sensor
     88            for i, window_data in enumerate(sensor_data):
     89                features = {}
     90                features["sensor"] = sensor_name
     91
     92                # Time domain features
     93                if self.config['time_domain']:
     94                    self._extract_time_domain_features(window_data, features)
     95
     96                # Frequency domain features
     97                if self.config['frequency_domain']:
     98                    self._extract_freq_domain_features(window_data, features)
     99
    100                all_features.append(features)
    101
    102        return all_features
    103
    104    def _extract_time_domain_features(self, window_data: np.ndarray, features: Dict[str, Any]):
    105        """
    106        Extract time domain features from a window.
    107
    108        Args:
    109            window_data: Window data
    110            features: Dictionary to store the extracted features
    111        """
    112        # Basic statistical features
    113        features["mean"] = np.mean(window_data)
    114        features["std"] = np.std(window_data)
    115        features["rms"] = np.sqrt(np.mean(window_data**2))
    116        features["max_amp"] = np.max(np.abs(window_data))
    117        features["min_amp"] = np.min(np.abs(window_data))
    118        features["median"] = np.median(window_data)
    119
    120        # Zero crossings
    121        zero_crossings = np.where(np.diff(np.signbit(window_data)))[0]
    122        features["zero_crossings"] = len(zero_crossings)
    123
    124        # Higher-order statistics
    125        features["skewness"] = skew(window_data)
    126        features["kurtosis"] = kurtosis(window_data)
    127
    128        # Quartiles
    129        features["q1"] = np.percentile(window_data, 25)
    130        features["q3"] = np.percentile(window_data, 75)
    131
    132        # Autocorrelation
    133        autocorr = np.correlate(window_data, window_data, mode='full')
    134        features["autocorr"] = np.median(autocorr)
    135
    136    def _extract_freq_domain_features(self, window_data: np.ndarray, features: Dict[str, Any]):
    137        """
    138        Extract frequency domain features from a window.
    139
    140        Args:
    141            window_data: Window data
    142            features: Dictionary to store the extracted features
    143        """
    144        # FFT
    145        fft_values = abs(rfft(np.asarray(window_data)))
    146
    147        # Energy
    148        features["energy"] = np.sum(fft_values**2)
    149
    150    def get_feature_names(self) -> List[str]:
    151        """
    152        Get names of features extracted by this extractor.
    153
    154        Returns:
    155            List of feature names
    156        """
    157        feature_names = []
    158
    159        if self.config['time_domain']:
    160            feature_names.extend(self.time_domain_features)
    161
    162        if self.config['frequency_domain']:
    163            feature_names.extend(self.freq_domain_features)
    164
    165        return feature_names

HAR-UP feature extractor class.

This class implements the feature extraction methods used in the HAR-UP project. It extracts both time-domain and frequency-domain features from sensor data.

HARUPFeatureExtractor(verbose: bool = False) View Source

    29    def __init__(self, verbose: bool = False):
    30        """
    31        Initialize the HAR-UP feature extractor.
    32
    33        Args:
    34            verbose: Whether to print progress information
    35        """
    36        super().__init__(
    37            name="harup",
    38            description="HAR-UP Feature Extractor - Extracts features used in the HAR-UP project"
    39        )
    40        self.config = {
    41            'time_domain': True,
    42            'frequency_domain': True,
    43            'verbose': verbose
    44        }
    45
    46        # Define the features to extract
    47        self.time_domain_features = [
    48            'mean', 'std', 'rms', 'max_amp', 'min_amp', 'median',
    49            'zero_crossings', 'skewness', 'kurtosis', 'q1', 'q3', 'autocorr'
    50        ]
    51
    52        self.freq_domain_features = [
    53            'energy'
    54        ]

Initialize the HAR-UP feature extractor.

Args: verbose: Whether to print progress information

config
time_domain_features
freq_domain_features
def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]: View Source

     56    def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
     57        """
     58        Extract features from sliding windows.
     59
     60        Args:
     61            windows: List of sliding window dictionaries
     62            fs: Sampling frequency
     63            **kwargs: Additional arguments for feature extraction
     64
     65        Returns:
     66            List of feature dictionaries
     67        """
     68        # Update config with kwargs
     69        self.config.update(kwargs)
     70
     71        all_features = []
     72
     73        # Skip label and activity_id windows
     74        sensor_windows = [w for w in windows if w["name"] not in ["labels", "activity_id"]]
     75
     76        if self.config['verbose']:
     77            print(f"Extracting features from {len(sensor_windows)} sensor windows")
     78
     79        # Process each sensor window
     80        for window in sensor_windows:
     81            sensor_name = window["name"]
     82            sensor_data = window["data"]
     83
     84            if self.config['verbose']:
     85                print(f"Processing {sensor_name} with {len(sensor_data)} windows")
     86
     87            # For each window of this sensor
     88            for i, window_data in enumerate(sensor_data):
     89                features = {}
     90                features["sensor"] = sensor_name
     91
     92                # Time domain features
     93                if self.config['time_domain']:
     94                    self._extract_time_domain_features(window_data, features)
     95
     96                # Frequency domain features
     97                if self.config['frequency_domain']:
     98                    self._extract_freq_domain_features(window_data, features)
     99
    100                all_features.append(features)
    101
    102        return all_features

Extract features from sliding windows.

Args: windows: List of sliding window dictionaries fs: Sampling frequency **kwargs: Additional arguments for feature extraction

Returns: List of feature dictionaries

def get_feature_names(self) -> List[str]: View Source

    150    def get_feature_names(self) -> List[str]:
    151        """
    152        Get names of features extracted by this extractor.
    153
    154        Returns:
    155            List of feature names
    156        """
    157        feature_names = []
    158
    159        if self.config['time_domain']:
    160            feature_names.extend(self.time_domain_features)
    161
    162        if self.config['frequency_domain']:
    163            feature_names.extend(self.freq_domain_features)
    164
    165        return feature_names

Get names of features extracted by this extractor.

Returns: List of feature names

##### Inherited Members

gaitsetpy.core.base_classes.BaseFeatureExtractor
  name
  description
  configure
  get_info

def calculate_mean(signal): View Source

    133def calculate_mean(signal):
    134    """Calculate the mean of the signal."""
    135    return np.mean(signal)

Calculate the mean of the signal.

def calculate_standard_deviation(signal): View Source

    64def calculate_standard_deviation(signal):
    65    """
    66    Calculate the standard deviation of a signal.
    67    Args:
    68        signal (np.array): Input signal.
    69    Returns:
    70        std_dev (float): Standard deviation.
    71    """
    72    return np.std(signal)

Calculate the standard deviation of a signal. Args: signal (np.array): Input signal. Returns: std_dev (float): Standard deviation.

def calculate_variance(signal): View Source

     96def calculate_variance(signal):
     97    """
     98    Calculate the variance of a signal.
     99    Args:
    100        signal (np.array): Input signal.
    101    Returns:
    102        variance (float): Variance.
    103    """
    104    return np.var(signal)

Calculate the variance of a signal. Args: signal (np.array): Input signal. Returns: variance (float): Variance.

def calculate_skewness(signal): View Source

    149def calculate_skewness(signal):
    150    """Calculate the skewness of the signal."""
    151    try:
    152        return skew(signal)
    153    except Exception as e:
    154        print(f"An error occurred in skewness: {e}")
    155        return 0

Calculate the skewness of the signal.

def calculate_kurtosis(signal): View Source

    106def calculate_kurtosis(signal):
    107    """
    108    Calculate the kurtosis of a signal.
    109    Args:
    110        signal (np.array): Input signal.
    111    Returns:
    112        kurtosis_value (float): Kurtosis.
    113    """
    114    try:
    115        return kurtosis(signal, fisher=False)
    116    except Exception as e:
    117        print(f"An error occurred in feature 'kurtosis': {e}")
    118        return 0

Calculate the kurtosis of a signal. Args: signal (np.array): Input signal. Returns: kurtosis_value (float): Kurtosis.

def calculate_root_mean_square(signal): View Source

    157def calculate_root_mean_square(signal):
    158    """Calculate the root mean square of the signal."""
    159    return np.sqrt(np.mean(np.square(signal)))

Calculate the root mean square of the signal.

def calculate_range(signal): View Source

    161def calculate_range(signal):
    162    """Calculate the range of the signal."""
    163    return np.max(signal) - np.min(signal)

Calculate the range of the signal.

def calculate_median(signal): View Source

    145def calculate_median(signal):
    146    """Calculate the median of the signal."""
    147    return np.median(signal)

Calculate the median of the signal.

def calculate_mode(signal): View Source

    194def calculate_mode(signal):
    195    """Calculate the mode of the signal."""
    196    values, counts = np.unique(signal, return_counts=True)
    197    return values[np.argmax(counts)]

Calculate the mode of the signal.

def calculate_mean_absolute_value(signal): View Source

    206def calculate_mean_absolute_value(signal):
    207    """Calculate the mean absolute value of the signal."""
    208    return np.mean(np.abs(signal))

Calculate the mean absolute value of the signal.

def calculate_median_absolute_deviation(signal): View Source

    210def calculate_median_absolute_deviation(signal):
    211    """Calculate the median absolute deviation of the signal."""
    212    return np.median(np.abs(signal - np.median(signal)))

Calculate the median absolute deviation of the signal.

def calculate_peak_height(signal): View Source

    180def calculate_peak_height(signal):
    181    """Calculate the peak height of the signal."""
    182    peaks, _ = find_peaks(signal)
    183    return np.max(signal[peaks]) if len(peaks) > 0 else 0

Calculate the peak height of the signal.

def calculate_stride_times(signal, fs): View Source

     9def calculate_stride_times(signal, fs):
    10    """
    11    Calculate stride times from a signal using peak detection.
    12    Args:
    13        signal (np.array): Input signal.
    14        fs (int): Sampling frequency.
    15    Returns:
    16        avg_stride_time (float): Average stride time.
    17    """
    18    peaks, _ = find_peaks(signal)
    19    stride_times = np.diff(peaks) / fs
    20    avg_stride_time = np.mean(stride_times) if len(stride_times) > 0 else 0
    21    return avg_stride_time

Calculate stride times from a signal using peak detection. Args: signal (np.array): Input signal. fs (int): Sampling frequency. Returns: avg_stride_time (float): Average stride time.

def calculate_step_time(signal, fs): View Source

    120def calculate_step_time(signal, fs):
    121    """
    122    Calculate step times from a signal using peak detection.
    123    Args:
    124        signal (np.array): Input signal.
    125        fs (int): Sampling frequency.
    126    Returns:
    127        step_times (np.array): Array of step times.
    128    """
    129    peaks, _ = find_peaks(signal)
    130    step_times = np.diff(peaks) / fs
    131    return step_times

Calculate step times from a signal using peak detection. Args: signal (np.array): Input signal. fs (int): Sampling frequency. Returns: step_times (np.array): Array of step times.

def calculate_cadence(signal, fs): View Source

    199def calculate_cadence(signal, fs):
    200    """Calculate the cadence (steps per minute) of the signal."""
    201    peaks, _ = find_peaks(signal)
    202    step_count = len(peaks)
    203    duration = len(signal) / fs
    204    return (step_count / duration) * 60

Calculate the cadence (steps per minute) of the signal.

def calculate_freezing_index(signal, fs): View Source

    50def calculate_freezing_index(signal, fs):
    51    """
    52    Calculate the freezing index of a signal.
    53    Args:
    54        signal (np.array): Input signal.
    55        fs (int): Sampling frequency.
    56    Returns:
    57        freezing_index (float): Freezing index.
    58    """
    59    power_3_8 = calculate_power(signal, fs, (3, 8))
    60    power_0_5_3 = calculate_power(signal, fs, (0.5, 3))
    61    freezing_index = power_3_8 / power_0_5_3 if power_0_5_3 != 0 else 0
    62    return freezing_index

Calculate the freezing index of a signal. Args: signal (np.array): Input signal. fs (int): Sampling frequency. Returns: freezing_index (float): Freezing index.

def calculate_dominant_frequency(signal, fs): View Source

    169def calculate_dominant_frequency(signal, fs):
    170    """Calculate the dominant frequency of the signal."""
    171    try:
    172        fft_values = np.abs(fft(signal))
    173        freqs = np.fft.fftfreq(len(signal), 1 / fs)
    174        dominant_freq = freqs[np.argmax(fft_values)]
    175        return dominant_freq
    176    except Exception as e:
    177        print(f"An error occurred: {e}")
    178        return 0

Calculate the dominant frequency of the signal.

def calculate_peak_frequency(signal, fs): View Source

    214def calculate_peak_frequency(signal, fs):
    215    """Calculate the peak frequency of the signal."""
    216    try:
    217        f, Pxx = welch(signal, fs=fs, nperseg=min(len(signal), 192))  # Ensure nperseg ≤ length
    218        return f[np.argmax(Pxx)]
    219    except Exception as e:
    220        print(f"An error occurred in feature 'peak_frequency': {e}")
    221        return 0

Calculate the peak frequency of the signal.

def calculate_power_spectral_entropy(signal, fs): View Source

    233def calculate_power_spectral_entropy(signal, fs):
    234    """Calculate the power spectral entropy of the signal."""
    235    try:
    236        f, Pxx = welch(signal, fs=fs, nperseg=min(len(signal), 192))  # Ensure nperseg ≤ length
    237        Pxx_norm = Pxx / np.sum(Pxx)
    238        return -np.sum(Pxx_norm * np.log2(Pxx_norm + np.finfo(float).eps))
    239    except Exception as e:
    240        print(f"An error occurred in feature 'power spectral entropy': {e}")
    241        return 0

Calculate the power spectral entropy of the signal.

def calculate_principal_harmonic_frequency(signal, fs): View Source

    243def calculate_principal_harmonic_frequency(signal, fs):
    244    """Calculate the principal harmonic frequency of the signal."""
    245    try:
    246        fft_values = np.abs(fft(signal))
    247        freqs = np.fft.fftfreq(len(signal), 1 / fs)
    248        return freqs[np.argmax(fft_values)]
    249    except Exception as e:
    250        print(f"An error occurred in feature 'principal_harmonic_frequency': {e}")
    251        return 0

Calculate the principal harmonic frequency of the signal.

def calculate_entropy(signal): View Source

    74def calculate_entropy(signal):
    75    """
    76    Calculate the entropy of a signal.
    77    Args:
    78        signal (np.array): Input signal.
    79    Returns:
    80        entropy_value (float): Entropy.
    81    """
    82    value, counts = np.unique(signal, return_counts=True)
    83    probabilities = counts / len(signal)
    84    return entropy(probabilities, base=2)

Calculate the entropy of a signal. Args: signal (np.array): Input signal. Returns: entropy_value (float): Entropy.

def calculate_interquartile_range(signal): View Source

    185def calculate_interquartile_range(signal):
    186    """Calculate the interquartile range of the signal."""
    187    try:
    188        q75, q25 = np.percentile(signal, [75, 25])
    189        return q75 - q25
    190    except Exception as e:
    191        print(f"An error occurred in feature 'interquartile_range': {e}")
    192        return 0

Calculate the interquartile range of the signal.

def calculate_correlation(signal1, signal2): View Source

    165def calculate_correlation(signal1, signal2):
    166    """Calculate the correlation between two signals."""
    167    return np.corrcoef(signal1, signal2)[0, 1]

Calculate the correlation between two signals.

def calculate_auto_regression_coefficients(signal, order=3): View Source

    253def calculate_auto_regression_coefficients(signal, order=3):
    254    """Calculate the auto-regression coefficients of the signal."""
    255    try:
    256        model = AutoReg(signal, lags=order)
    257        results = model.fit()
    258        return results.params
    259    except Exception as e:
    260        print(f"An error occurred in feature 'auto_regression_coefficients': {e}")
    261        return 0

Calculate the auto-regression coefficients of the signal.

def get_mean_for_windows(windows): View Source

    486def get_mean_for_windows(windows):
    487    return [calculate_mean(window) for window in windows]

def get_standard_deviation_for_windows(windows): View Source

    489def get_standard_deviation_for_windows(windows):
    490    return [calculate_standard_deviation(window) for window in windows]

def get_variance_for_windows(windows): View Source

    492def get_variance_for_windows(windows):
    493    return [calculate_variance(window) for window in windows]

def get_skewness_for_windows(windows): View Source

    495def get_skewness_for_windows(windows):
    496    return [calculate_skewness(window) for window in windows]

def get_kurtosis_for_windows(windows): View Source

    478def get_kurtosis_for_windows(windows):
    479    """Calculate kurtosis values for all windows in the input."""
    480    return [calculate_kurtosis(window) for window in windows]

Calculate kurtosis values for all windows in the input.

def get_root_mean_square_for_windows(windows): View Source

    498def get_root_mean_square_for_windows(windows):
    499    return [calculate_root_mean_square(window) for window in windows]

def get_range_for_windows(windows): View Source

    501def get_range_for_windows(windows):
    502    return [calculate_range(window) for window in windows]

def get_median_for_windows(windows): View Source

    504def get_median_for_windows(windows):
    505    return [calculate_median(window) for window in windows]

def get_mode_for_windows(windows): View Source

    507def get_mode_for_windows(windows):
    508    return [calculate_mode(window) for window in windows]

def get_mean_absolute_value_for_windows(windows): View Source

    510def get_mean_absolute_value_for_windows(windows):
    511    return [calculate_mean_absolute_value(window) for window in windows]

def get_median_absolute_deviation_for_windows(windows): View Source

    513def get_median_absolute_deviation_for_windows(windows):
    514    return [calculate_median_absolute_deviation(window) for window in windows]

def get_peak_height_for_windows(windows): View Source

    516def get_peak_height_for_windows(windows):
    517    return [calculate_peak_height(window) for window in windows]

def get_stride_times_for_windows(windows, fs): View Source

    450def get_stride_times_for_windows(windows, fs):
    451    """Calculate stride times for all windows in the input."""
    452    return [calculate_stride_times(window, fs) for window in windows]

Calculate stride times for all windows in the input.

def get_step_times_for_windows(windows, fs): View Source

    482def get_step_times_for_windows(windows, fs):
    483    """Calculate step times for all windows in the input."""
    484    return [calculate_step_time(window, fs) for window in windows]

Calculate step times for all windows in the input.

def get_cadence_for_windows(windows, fs): View Source

    519def get_cadence_for_windows(windows, fs):
    520    return [calculate_cadence(window, fs) for window in windows]

def get_freezing_index_for_windows(windows, fs): View Source

    522def get_freezing_index_for_windows(windows, fs):
    523    return [calculate_freezing_index(window, fs) for window in windows]

def get_dominant_frequency_for_windows(windows, fs): View Source

    525def get_dominant_frequency_for_windows(windows, fs):
    526    return [calculate_dominant_frequency(window, fs) for window in windows]

def get_peak_frequency_for_windows(windows, fs): View Source

    528def get_peak_frequency_for_windows(windows, fs):
    529    return [calculate_peak_frequency(window, fs) for window in windows]

def get_power_spectral_entropy_for_windows(windows, fs): View Source

    531def get_power_spectral_entropy_for_windows(windows, fs):
    532    return [calculate_power_spectral_entropy(window, fs) for window in windows]

def get_principal_harmonic_frequency_for_windows(windows, fs): View Source

    534def get_principal_harmonic_frequency_for_windows(windows, fs):
    535    return [calculate_principal_harmonic_frequency(window, fs) for window in windows]

def get_entropy_for_windows(windows): View Source

    537def get_entropy_for_windows(windows):
    538    return [calculate_entropy(window) for window in windows]

def get_interquartile_range_for_windows(windows): View Source

    540def get_interquartile_range_for_windows(windows):
    541    return [calculate_interquartile_range(window) for window in windows]

def get_correlation_for_windows(windows): View Source

    543def get_correlation_for_windows(windows):
    544    # For correlation, we need to handle it differently since it needs two signals
    545    # We'll calculate autocorrelation for each window
    546    return [calculate_correlation(window[:-1], window[1:]) if len(window) > 1 else 0 for window in windows]

def get_auto_regression_coefficients_for_windows(windows, order=3): View Source

    548def get_auto_regression_coefficients_for_windows(windows, order=3):
    549    return [calculate_auto_regression_coefficients(window, order) for window in windows]

def extract_gait_features( daphnet_windows, fs, time_domain=True, frequency_domain=True, statistical=True, verbose=True): View Source

    551def extract_gait_features(daphnet_windows, fs, time_domain=True, frequency_domain=True, statistical=True, verbose=True):
    552    """
    553    Legacy function for extracting gait features.
    554
    555    Args:
    556        daphnet_windows: List of sliding window dictionaries
    557        fs: Sampling frequency
    558        time_domain: Whether to extract time domain features
    559        frequency_domain: Whether to extract frequency domain features
    560        statistical: Whether to extract statistical features
    561        verbose: Whether to show verbose output and progress bars
    562
    563    Returns:
    564        List of feature dictionaries
    565    """
    566    extractor = GaitFeatureExtractor(verbose=verbose)
    567    return extractor.extract_features(
    568        daphnet_windows, fs,
    569        time_domain=time_domain,
    570        frequency_domain=frequency_domain,
    571        statistical=statistical
    572    )

Legacy function for extracting gait features.

Args: daphnet_windows: List of sliding window dictionaries fs: Sampling frequency time_domain: Whether to extract time domain features frequency_domain: Whether to extract frequency domain features statistical: Whether to extract statistical features verbose: Whether to show verbose output and progress bars

Returns: List of feature dictionaries

def extract_lbp_features(windows: List[Dict], fs: int, **kwargs) -> List[Dict]: View Source

    512def extract_lbp_features(windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
    513    """
    514    Legacy function to extract LBP features.
    515
    516    Args:
    517        windows: List of sliding window dictionaries
    518        fs: Sampling frequency
    519        **kwargs: Additional arguments
    520
    521    Returns:
    522        List of feature dictionaries
    523    """
    524    extractor = LBPFeatureExtractor(verbose=kwargs.get('verbose', True))
    525    return extractor.extract_features(windows, fs, **kwargs)

Legacy function to extract LBP features.

Args: windows: List of sliding window dictionaries fs: Sampling frequency **kwargs: Additional arguments

Returns: List of feature dictionaries

def extract_fourier_features(windows: List[Dict], fs: int, **kwargs) -> List[Dict]: View Source

    528def extract_fourier_features(windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
    529    """
    530    Legacy function to extract Fourier series features.
    531
    532    Args:
    533        windows: List of sliding window dictionaries
    534        fs: Sampling frequency
    535        **kwargs: Additional arguments
    536
    537    Returns:
    538        List of feature dictionaries
    539    """
    540    extractor = FourierSeriesFeatureExtractor(verbose=kwargs.get('verbose', True))
    541    return extractor.extract_features(windows, fs, **kwargs)

Legacy function to extract Fourier series features.

Args: windows: List of sliding window dictionaries fs: Sampling frequency **kwargs: Additional arguments

Returns: List of feature dictionaries

def extract_physionet_features(windows: List[Dict], fs: int, **kwargs) -> List[Dict]: View Source

    544def extract_physionet_features(windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
    545    """
    546    Legacy function to extract combined PhysioNet features.
    547
    548    Args:
    549        windows: List of sliding window dictionaries
    550        fs: Sampling frequency
    551        **kwargs: Additional arguments
    552
    553    Returns:
    554        List of feature dictionaries
    555    """
    556    extractor = PhysioNetFeatureExtractor(verbose=kwargs.get('verbose', True))
    557    return extractor.extract_features(windows, fs, **kwargs)

Legacy function to extract combined PhysioNet features.

Args: windows: List of sliding window dictionaries fs: Sampling frequency **kwargs: Additional arguments

Returns: List of feature dictionaries

def extract_harup_features( windows: List[Dict], fs: int = 100, time_domain: bool = True, freq_domain: bool = True, verbose: bool = False) -> List[Dict]: View Source

    169def extract_harup_features(windows: List[Dict], fs: int = 100,
    170                         time_domain: bool = True, freq_domain: bool = True,
    171                         verbose: bool = False) -> List[Dict]:
    172    """
    173    Legacy function for extracting HAR-UP features.
    174
    175    Args:
    176        windows: List of sliding window dictionaries
    177        fs: Sampling frequency (default: 100Hz)
    178        time_domain: Whether to extract time domain features
    179        freq_domain: Whether to extract frequency domain features
    180        verbose: Whether to print progress information
    181
    182    Returns:
    183        List of feature dictionaries
    184    """
    185    extractor = HARUPFeatureExtractor(verbose=verbose)
    186    return extractor.extract_features(
    187        windows,
    188        fs=fs,
    189        time_domain=time_domain,
    190        frequency_domain=freq_domain
    191    )

Legacy function for extracting HAR-UP features.

Args: windows: List of sliding window dictionaries fs: Sampling frequency (default: 100Hz) time_domain: Whether to extract time domain features freq_domain: Whether to extract frequency domain features verbose: Whether to print progress information

Returns: List of feature dictionaries

def get_feature_manager(): View Source

    91def get_feature_manager():
    92    """Get the singleton FeatureManager instance."""
    93    return FeatureManager()

Get the singleton FeatureManager instance.

def get_available_extractors(): View Source

    96def get_available_extractors():
    97    """Get list of available feature extractor names."""
    98    return FeatureManager().get_available_components()

Get list of available feature extractor names.

def extract_features(extractor_name: str, windows, fs: int, **kwargs): View Source

    101def extract_features(extractor_name: str, windows, fs: int, **kwargs):
    102    """
    103    Extract features using the FeatureManager.
    104
    105    Args:
    106        extractor_name: Name of the feature extractor
    107        windows: List of sliding window dictionaries
    108        fs: Sampling frequency
    109        **kwargs: Additional arguments for feature extraction
    110
    111    Returns:
    112        List of feature dictionaries
    113    """
    114    return FeatureManager().extract_features(extractor_name, windows, fs, **kwargs)

Extract features using the FeatureManager.

Args: extractor_name: Name of the feature extractor windows: List of sliding window dictionaries fs: Sampling frequency **kwargs: Additional arguments for feature extraction

Returns: List of feature dictionaries