# gaitsetpy.features.physionet_features

PhysioNet VGRF Feature Extractor. Maintainer: @aharshit123456

This module contains feature extractors specific to the PhysioNet VGRF dataset, including Local Binary Pattern (LBP) and Fourier series analysis.

View Source

      1'''
      2PhysioNet VGRF Feature Extractor.
      3Maintainer: @aharshit123456
      4
      5This module contains feature extractors specific to the PhysioNet VGRF dataset,
      6including Local Binary Pattern (LBP) and Fourier series analysis.
      7'''
      8
      9from typing import List, Dict, Any
     10import numpy as np
     11import pandas as pd
     12from scipy.integrate import simpson
     13from scipy.signal import find_peaks
     14from scipy.optimize import curve_fit
     15from scipy import fftpack
     16import logging
     17from tqdm import tqdm
     18from ..core.base_classes import BaseFeatureExtractor
     19
     20# Set up logging
     21logging.basicConfig(level=logging.INFO)
     22logger = logging.getLogger(__name__)
     23
     24
     25class LBPFeatureExtractor(BaseFeatureExtractor):
     26    """
     27    Local Binary Pattern (LBP) feature extractor for VGRF data.
     28
     29    This extractor converts time-series data into LBP codes and extracts
     30    histogram features from the LBP representation.
     31    """
     32
     33    def __init__(self, verbose: bool = True):
     34        super().__init__(
     35            name="lbp_features",
     36            description="Local Binary Pattern feature extractor for VGRF time-series data"
     37        )
     38        self.verbose = verbose
     39        self.config = {
     40            'radius': 2,  # LBP radius (number of neighbors)
     41            'n_bins': 256,  # Number of histogram bins
     42            'normalize': True  # Normalize histogram
     43        }
     44
     45        if self.verbose:
     46            print("🔍 LBP Feature Extractor initialized!")
     47
     48    def lbp_1d(self, data: np.ndarray, radius: int = 2) -> str:
     49        """
     50        Compute 1D Local Binary Pattern for time-series data.
     51
     52        Args:
     53            data: Input time-series data
     54            radius: Radius for LBP computation
     55
     56        Returns:
     57            LBP code as binary string
     58        """
     59        n = len(data)
     60        lbp_code = ''
     61
     62        for i in range(n):
     63            pattern = ''
     64            for j in range(i - radius, i + radius + 1):
     65                if j < 0 or j >= n:
     66                    pattern += '0'
     67                elif data[j] >= data[i]:
     68                    pattern += '1'
     69                else:
     70                    pattern += '0'
     71            lbp_code += pattern
     72
     73        return lbp_code
     74
     75    def lbp_to_histogram(self, lbp_code: str, n_bins: int = 256, normalize: bool = True) -> np.ndarray:
     76        """
     77        Convert LBP code to histogram features.
     78
     79        Args:
     80            lbp_code: Binary LBP code string
     81            n_bins: Number of histogram bins
     82            normalize: Whether to normalize histogram
     83
     84        Returns:
     85            Histogram features as numpy array
     86        """
     87        # Convert LBP code to integer values
     88        if len(lbp_code) == 0:
     89            return np.zeros(n_bins)
     90
     91        # Process LBP code in chunks of 8 bits (or smaller)
     92        chunk_size = 8
     93        lbp_values = []
     94
     95        for i in range(0, len(lbp_code), chunk_size):
     96            chunk = lbp_code[i:i + chunk_size]
     97            if len(chunk) > 0:
     98                # Convert binary string to integer
     99                try:
    100                    value = int(chunk, 2)
    101                    lbp_values.append(value % n_bins)  # Ensure within bin range
    102                except ValueError:
    103                    continue
    104
    105        if len(lbp_values) == 0:
    106            return np.zeros(n_bins)
    107
    108        # Create histogram
    109        hist, _ = np.histogram(lbp_values, bins=n_bins, range=(0, n_bins))
    110
    111        if normalize and np.sum(hist) > 0:
    112            hist = hist / np.sum(hist)
    113
    114        return hist
    115
    116    def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
    117        """
    118        Extract LBP features from sliding windows.
    119
    120        Args:
    121            windows: List of sliding window dictionaries
    122            fs: Sampling frequency (unused for LBP)
    123            **kwargs: Additional arguments
    124
    125        Returns:
    126            List of feature dictionaries
    127        """
    128        # Update config with any passed arguments
    129        radius = kwargs.get('radius', self.config['radius'])
    130        n_bins = kwargs.get('n_bins', self.config['n_bins'])
    131        normalize = kwargs.get('normalize', self.config['normalize'])
    132
    133        if self.verbose:
    134            print(f"\n🔍 LBP Feature Extraction")
    135            print(f"📊 Radius: {radius}, Bins: {n_bins}, Normalize: {normalize}")
    136
    137        features = []
    138
    139        for window_dict in tqdm(windows, desc="Processing LBP features", disable=not self.verbose):
    140            sensor_name = window_dict['name']
    141            window_data = window_dict['data']
    142
    143            # Skip annotation windows
    144            if sensor_name == 'annotations':
    145                continue
    146
    147            sensor_features = {'name': sensor_name, 'features': {}}
    148
    149            # Extract LBP features for each window
    150            lbp_histograms = []
    151            lbp_means = []
    152            lbp_stds = []
    153
    154            for window in window_data:
    155                # Ensure window is numpy array
    156                if hasattr(window, 'values'):
    157                    window = window.values
    158
    159                # Compute LBP
    160                lbp_code = self.lbp_1d(window, radius)
    161
    162                # Convert to histogram
    163                hist = self.lbp_to_histogram(lbp_code, n_bins, normalize)
    164                lbp_histograms.append(hist)
    165
    166                # Extract summary statistics
    167                lbp_means.append(np.mean(hist))
    168                lbp_stds.append(np.std(hist))
    169
    170            # Store features
    171            sensor_features['features'] = {
    172                'lbp_histograms': lbp_histograms,
    173                'lbp_mean': lbp_means,
    174                'lbp_std': lbp_stds,
    175                'lbp_energy': [np.sum(hist**2) for hist in lbp_histograms],
    176                'lbp_entropy': [self._calculate_entropy(hist) for hist in lbp_histograms]
    177            }
    178
    179            features.append(sensor_features)
    180
    181        return features
    182
    183    def _calculate_entropy(self, hist: np.ndarray) -> float:
    184        """Calculate entropy of histogram."""
    185        # Avoid log(0) by adding small value
    186        hist = hist + 1e-10
    187        return -np.sum(hist * np.log2(hist))
    188
    189    def get_feature_names(self) -> List[str]:
    190        """Get names of LBP features."""
    191        return [
    192            'lbp_histograms', 'lbp_mean', 'lbp_std',
    193            'lbp_energy', 'lbp_entropy'
    194        ]
    195
    196
    197class FourierSeriesFeatureExtractor(BaseFeatureExtractor):
    198    """
    199    Fourier Series feature extractor for VGRF data.
    200
    201    This extractor fits Fourier series to time-series data and extracts
    202    coefficients and reconstruction features.
    203    """
    204
    205    def __init__(self, verbose: bool = True):
    206        super().__init__(
    207            name="fourier_features",
    208            description="Fourier series feature extractor for VGRF time-series data"
    209        )
    210        self.verbose = verbose
    211        self.config = {
    212            'n_terms': 10,  # Number of Fourier terms
    213            'period': 3.0,  # Period for Fourier series
    214            'extract_coefficients': True,
    215            'extract_reconstruction_error': True
    216        }
    217
    218        if self.verbose:
    219            print("🌊 Fourier Series Feature Extractor initialized!")
    220
    221    def fit_fourier_series(self, signal: np.ndarray, time_points: np.ndarray,
    222                          period: float = 3.0, n_terms: int = 10) -> Dict[str, Any]:
    223        """
    224        Fit Fourier series to signal.
    225
    226        Args:
    227            signal: Input signal
    228            time_points: Time points
    229            period: Period of the Fourier series
    230            n_terms: Number of Fourier terms
    231
    232        Returns:
    233            Dictionary containing Fourier series parameters
    234        """
    235        try:
    236            # Calculate Fourier coefficients
    237            L = period
    238
    239            # Calculate a0 (DC component)
    240            a0 = 2/L * simpson(signal, time_points)
    241
    242            # Calculate an and bn coefficients
    243            an = []
    244            bn = []
    245
    246            for n in range(1, n_terms + 1):
    247                # Calculate an coefficient
    248                an_val = 2.0/L * simpson(signal * np.cos(2.*np.pi*n*time_points/L), time_points)
    249                an.append(an_val)
    250
    251                # Calculate bn coefficient
    252                bn_val = 2.0/L * simpson(signal * np.sin(2.*np.pi*n*time_points/L), time_points)
    253                bn.append(bn_val)
    254
    255            # Reconstruct signal
    256            reconstructed = np.full_like(time_points, a0/2)
    257            for n in range(n_terms):
    258                reconstructed += an[n] * np.cos(2.*np.pi*(n+1)*time_points/L)
    259                reconstructed += bn[n] * np.sin(2.*np.pi*(n+1)*time_points/L)
    260
    261            # Calculate reconstruction error
    262            reconstruction_error = np.mean((signal - reconstructed)**2)
    263
    264            return {
    265                'a0': a0,
    266                'an': an,
    267                'bn': bn,
    268                'reconstructed': reconstructed,
    269                'reconstruction_error': reconstruction_error,
    270                'fourier_energy': a0**2 + 2*np.sum(np.array(an)**2 + np.array(bn)**2)
    271            }
    272
    273        except Exception as e:
    274            if self.verbose:
    275                print(f"Error in Fourier series fitting: {e}")
    276            return {
    277                'a0': 0,
    278                'an': [0] * n_terms,
    279                'bn': [0] * n_terms,
    280                'reconstructed': np.zeros_like(time_points),
    281                'reconstruction_error': float('inf'),
    282                'fourier_energy': 0
    283            }
    284
    285    def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
    286        """
    287        Extract Fourier series features from sliding windows.
    288
    289        Args:
    290            windows: List of sliding window dictionaries
    291            fs: Sampling frequency
    292            **kwargs: Additional arguments
    293
    294        Returns:
    295            List of feature dictionaries
    296        """
    297        # Update config with any passed arguments
    298        n_terms = kwargs.get('n_terms', self.config['n_terms'])
    299        period = kwargs.get('period', self.config['period'])
    300
    301        if self.verbose:
    302            print(f"\n🌊 Fourier Series Feature Extraction")
    303            print(f"📊 Terms: {n_terms}, Period: {period}")
    304
    305        features = []
    306
    307        for window_dict in tqdm(windows, desc="Processing Fourier features", disable=not self.verbose):
    308            sensor_name = window_dict['name']
    309            window_data = window_dict['data']
    310
    311            # Skip annotation windows
    312            if sensor_name == 'annotations':
    313                continue
    314
    315            sensor_features = {'name': sensor_name, 'features': {}}
    316
    317            # Extract Fourier features for each window
    318            a0_values = []
    319            an_values = []
    320            bn_values = []
    321            reconstruction_errors = []
    322            fourier_energies = []
    323
    324            for window in window_data:
    325                # Ensure window is numpy array
    326                if hasattr(window, 'values'):
    327                    window = window.values
    328
    329                # Create time points
    330                time_points = np.linspace(0, period, len(window))
    331
    332                # Fit Fourier series
    333                fourier_result = self.fit_fourier_series(window, time_points, period, n_terms)
    334
    335                # Store results
    336                a0_values.append(fourier_result['a0'])
    337                an_values.append(fourier_result['an'])
    338                bn_values.append(fourier_result['bn'])
    339                reconstruction_errors.append(fourier_result['reconstruction_error'])
    340                fourier_energies.append(fourier_result['fourier_energy'])
    341
    342            # Store features
    343            sensor_features['features'] = {
    344                'fourier_a0': a0_values,
    345                'fourier_an': an_values,
    346                'fourier_bn': bn_values,
    347                'fourier_reconstruction_error': reconstruction_errors,
    348                'fourier_energy': fourier_energies,
    349                'fourier_an_mean': [np.mean(an) for an in an_values],
    350                'fourier_bn_mean': [np.mean(bn) for bn in bn_values],
    351                'fourier_an_std': [np.std(an) for an in an_values],
    352                'fourier_bn_std': [np.std(bn) for bn in bn_values]
    353            }
    354
    355            features.append(sensor_features)
    356
    357        return features
    358
    359    def get_feature_names(self) -> List[str]:
    360        """Get names of Fourier series features."""
    361        return [
    362            'fourier_a0', 'fourier_an', 'fourier_bn',
    363            'fourier_reconstruction_error', 'fourier_energy',
    364            'fourier_an_mean', 'fourier_bn_mean',
    365            'fourier_an_std', 'fourier_bn_std'
    366        ]
    367
    368
    369class PhysioNetFeatureExtractor(BaseFeatureExtractor):
    370    """
    371    Combined feature extractor for PhysioNet VGRF data.
    372
    373    This extractor combines LBP and Fourier series features along with
    374    basic statistical features specific to VGRF data.
    375    """
    376
    377    def __init__(self, verbose: bool = True):
    378        super().__init__(
    379            name="physionet_features",
    380            description="Combined feature extractor for PhysioNet VGRF data including LBP and Fourier features"
    381        )
    382        self.verbose = verbose
    383        self.lbp_extractor = LBPFeatureExtractor(verbose=False)
    384        self.fourier_extractor = FourierSeriesFeatureExtractor(verbose=False)
    385
    386        if self.verbose:
    387            print("🚀 PhysioNet Feature Extractor initialized!")
    388
    389    def extract_features(self, windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
    390        """
    391        Extract combined features from sliding windows.
    392
    393        Args:
    394            windows: List of sliding window dictionaries
    395            fs: Sampling frequency
    396            **kwargs: Additional arguments
    397
    398        Returns:
    399            List of feature dictionaries
    400        """
    401        # Extract features from each extractor
    402        extract_lbp = kwargs.get('extract_lbp', True)
    403        extract_fourier = kwargs.get('extract_fourier', True)
    404        extract_statistical = kwargs.get('extract_statistical', True)
    405
    406        if self.verbose:
    407            print(f"\n🔍 PhysioNet Feature Extraction")
    408            print(f"📊 LBP: {extract_lbp}, Fourier: {extract_fourier}, Statistical: {extract_statistical}")
    409
    410        features = []
    411
    412        # Extract LBP features
    413        if extract_lbp:
    414            lbp_features = self.lbp_extractor.extract_features(windows, fs, **kwargs)
    415        else:
    416            lbp_features = []
    417
    418        # Extract Fourier features
    419        if extract_fourier:
    420            fourier_features = self.fourier_extractor.extract_features(windows, fs, **kwargs)
    421        else:
    422            fourier_features = []
    423
    424        # Extract statistical features
    425        if extract_statistical:
    426            statistical_features = self._extract_statistical_features(windows)
    427        else:
    428            statistical_features = []
    429
    430        # Combine features
    431        for i, window_dict in enumerate(windows):
    432            sensor_name = window_dict['name']
    433
    434            # Skip annotation windows
    435            if sensor_name == 'annotations':
    436                continue
    437
    438            combined_features = {'name': sensor_name, 'features': {}}
    439
    440            # Add LBP features
    441            if extract_lbp and i < len(lbp_features):
    442                combined_features['features'].update(lbp_features[i]['features'])
    443
    444            # Add Fourier features
    445            if extract_fourier and i < len(fourier_features):
    446                combined_features['features'].update(fourier_features[i]['features'])
    447
    448            # Add statistical features
    449            if extract_statistical and i < len(statistical_features):
    450                combined_features['features'].update(statistical_features[i]['features'])
    451
    452            features.append(combined_features)
    453
    454        return features
    455
    456    def _extract_statistical_features(self, windows: List[Dict]) -> List[Dict]:
    457        """Extract basic statistical features."""
    458        features = []
    459
    460        for window_dict in windows:
    461            sensor_name = window_dict['name']
    462            window_data = window_dict['data']
    463
    464            # Skip annotation windows
    465            if sensor_name == 'annotations':
    466                continue
    467
    468            sensor_features = {'name': sensor_name, 'features': {}}
    469
    470            # Extract statistical features for each window
    471            means = []
    472            stds = []
    473            maxs = []
    474            mins = []
    475            ranges = []
    476
    477            for window in window_data:
    478                # Ensure window is numpy array
    479                if hasattr(window, 'values'):
    480                    window = window.values
    481
    482                means.append(np.mean(window))
    483                stds.append(np.std(window))
    484                maxs.append(np.max(window))
    485                mins.append(np.min(window))
    486                ranges.append(np.max(window) - np.min(window))
    487
    488            # Store features
    489            sensor_features['features'] = {
    490                'vgrf_mean': means,
    491                'vgrf_std': stds,
    492                'vgrf_max': maxs,
    493                'vgrf_min': mins,
    494                'vgrf_range': ranges
    495            }
    496
    497            features.append(sensor_features)
    498
    499        return features
    500
    501    def get_feature_names(self) -> List[str]:
    502        """Get names of all features."""
    503        feature_names = []
    504        feature_names.extend(self.lbp_extractor.get_feature_names())
    505        feature_names.extend(self.fourier_extractor.get_feature_names())
    506        feature_names.extend(['vgrf_mean', 'vgrf_std', 'vgrf_max', 'vgrf_min', 'vgrf_range'])
    507        return feature_names
    508
    509
    510# Legacy functions for backward compatibility
    511def extract_lbp_features(windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
    512    """
    513    Legacy function to extract LBP features.
    514
    515    Args:
    516        windows: List of sliding window dictionaries
    517        fs: Sampling frequency
    518        **kwargs: Additional arguments
    519
    520    Returns:
    521        List of feature dictionaries
    522    """
    523    extractor = LBPFeatureExtractor(verbose=kwargs.get('verbose', True))
    524    return extractor.extract_features(windows, fs, **kwargs)
    525
    526
    527def extract_fourier_features(windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
    528    """
    529    Legacy function to extract Fourier series features.
    530
    531    Args:
    532        windows: List of sliding window dictionaries
    533        fs: Sampling frequency
    534        **kwargs: Additional arguments
    535
    536    Returns:
    537        List of feature dictionaries
    538    """
    539    extractor = FourierSeriesFeatureExtractor(verbose=kwargs.get('verbose', True))
    540    return extractor.extract_features(windows, fs, **kwargs)
    541
    542
    543def extract_physionet_features(windows: List[Dict], fs: int, **kwargs) -> List[Dict]:
    544    """
    545    Legacy function to extract combined PhysioNet features.
    546
    547    Args:
    548        windows: List of sliding window dictionaries
    549        fs: Sampling frequency
    550        **kwargs: Additional arguments
    551
    552    Returns:
    553        List of feature dictionaries
    554    """
    555    extractor = PhysioNetFeatureExtractor(verbose=kwargs.get('verbose', True))
    556    return extractor.extract_features(windows, fs, **kwargs)

logger = <Logger gaitsetpy.features.physionet_features (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

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