Source code for driada.intense.validation

"""
Input validation for INTENSE analysis.

Provides validation functions for time series bunches, metrics, and common
parameters used throughout the INTENSE pipeline.
"""

import numpy as np

from ..information.info_base import TimeSeries, MultiTimeSeries


[docs] def validate_time_series_bunches(ts_bunch1, ts_bunch2) -> None: """ Validate time series bunches for INTENSE computations. Parameters ---------- ts_bunch1 : list of TimeSeries or MultiTimeSeries First set of time series objects (e.g., neural activity). ts_bunch2 : list of TimeSeries or MultiTimeSeries Second set of time series objects (e.g., behavioral features). Raises ------ ValueError If bunches are empty, contain wrong types, or have mismatched lengths. """ if len(ts_bunch1) == 0: raise ValueError("ts_bunch1 cannot be empty") if len(ts_bunch2) == 0: raise ValueError("ts_bunch2 cannot be empty") # Check lengths match lengths1 = [ len(ts.data) if isinstance(ts, TimeSeries) else ts.data.shape[1] for ts in ts_bunch1 ] lengths2 = [ len(ts.data) if isinstance(ts, TimeSeries) else ts.data.shape[1] for ts in ts_bunch2 ] if len(set(lengths1)) > 1: raise ValueError(f"All time series in ts_bunch1 must have same length, got {set(lengths1)}") if len(set(lengths2)) > 1: raise ValueError(f"All time series in ts_bunch2 must have same length, got {set(lengths2)}") if lengths1[0] != lengths2[0]: raise ValueError(f"Time series lengths don't match: {lengths1[0]} vs {lengths2[0]}")
[docs] def validate_metric(metric, allow_scipy=True) -> str: """ Validate metric name and check if it's supported. Parameters ---------- metric : str Metric name to validate. Supported metrics: - 'mi': Mutual information (supports multivariate data) - 'av': Activity ratio (requires one binary and one continuous variable) - 'fast_pearsonr': Fast Pearson correlation implementation - 'spearmanr', 'pearsonr', 'kendalltau': scipy.stats correlation functions - Any other callable from scipy.stats (if allow_scipy=True) allow_scipy : bool, default=True Whether to allow scipy.stats correlation functions. Returns ------- metric_type : str Type of metric: - 'mi': Mutual information metric - 'special': Special metrics ('av', 'fast_pearsonr') - 'scipy': scipy.stats functions Raises ------ ValueError If metric is not supported or not a callable function in scipy.stats. Notes ----- The function validates that scipy.stats attributes are callable to prevent accepting non-function attributes like constants or data arrays. """ # Built-in metrics if metric == "mi": return "mi" # Special metrics if metric in ["av", "fast_pearsonr"]: return "special" # Full scipy names scipy_correlation_metrics = ["spearmanr", "pearsonr", "kendalltau"] if metric in scipy_correlation_metrics: return "scipy" # Check if it's a scipy function if allow_scipy: try: import scipy.stats attr = getattr(scipy.stats, metric, None) if attr is not None and callable(attr): return "scipy" except ImportError: pass # If we get here, metric is not supported raise ValueError( f"Unsupported metric: {metric}. Supported metrics include: " f"'mi', 'av', 'fast_pearsonr', 'spearmanr', 'pearsonr', 'kendalltau', " f"and other scipy.stats functions." )
[docs] def validate_common_parameters(shift_window=None, ds=None, nsh=None, noise_const=None) -> None: """ Validate common INTENSE parameters. Parameters ---------- shift_window : int, optional Maximum shift window in frames. Must be non-negative. ds : int, optional Downsampling factor. Must be positive integer. nsh : int, optional Number of shuffles for significance testing. Must be positive integer. noise_const : float, optional Noise constant for numerical stability. Must be non-negative. Raises ------ TypeError If parameters have incorrect types (non-integer for int params; non-numeric for noise_const). ValueError If parameters have invalid values (negative shift_window or noise_const; non-positive ds or nsh). Notes ----- This function validates parameter types using isinstance checks for numpy compatibility (accepts both Python int and numpy integer types). """ if shift_window is not None: if not isinstance(shift_window, (int, np.integer)): raise TypeError(f"shift_window must be integer, got {type(shift_window).__name__}") if shift_window < 0: raise ValueError(f"shift_window must be non-negative, got {shift_window}") if ds is not None: if not isinstance(ds, (int, np.integer)): raise TypeError(f"ds must be integer, got {type(ds).__name__}") if ds <= 0: raise ValueError(f"ds must be positive, got {ds}") if nsh is not None: if not isinstance(nsh, (int, np.integer)): raise TypeError(f"nsh must be integer, got {type(nsh).__name__}") if nsh <= 0: raise ValueError(f"nsh must be positive, got {nsh}") if noise_const is not None: if not isinstance(noise_const, (int, float, np.number)): raise TypeError(f"noise_const must be numeric, got {type(noise_const).__name__}") if noise_const < 0: raise ValueError(f"noise_const must be non-negative, got {noise_const}")