Source code for piel.analysis.signals.time.core.split

# separate_pulse_thresholds.py
import numpy as np
from typing import List, Optional, Dict
from piel.types import (
    TimeSignalData,
    MultiTimeSignalData,
)  # Adjust the import path as needed
from .threshold import (
    extract_pulses_from_signal,
    is_pulse_above_threshold,
)  # Ensure this import path is correct
from .compose import compose_pulses_into_signal


[docs] def separate_per_pulse_threshold( signal_data: TimeSignalData, first_signal_threshold: float, second_signal_threshold: float, trigger_delay_s: float, trigger_window_s: float = 25e-9, first_pre_pulse_time_s: float = 1e-9, first_post_pulse_time_s: float = 1e-9, second_pre_pulse_time_s: float = 1e-9, second_post_pulse_time_s: float = 1e-9, noise_std_multiplier: float = 3.0, data_time_signal_kwargs: Optional[Dict] = None, ) -> List[MultiTimeSignalData]: """ Separates pulses in a signal into two categories based on two threshold values. Parameters: signal_data (TimeSignalData): The input signal data containing multiple pulses. first_signal_threshold (float): The higher threshold to categorize pulses. second_signal_threshold (float): The lower threshold to categorize pulses. trigger_delay_s (float): Minimum time (in seconds) between pulses to prevent overlap. first_pre_pulse_time_s (float, optional): Time (in seconds) to include before each detected pulse. Defaults to 0.01. first_post_pulse_time_s (float, optional): Time (in seconds) to include after each detected pulse. Defaults to 0.01.\ second_pre_pulse_time_s (float, optional): Time (in seconds) to include before each detected pulse. Defaults to 0.01. second_post_pulse_time_s (float, optional): Time (in seconds) to include after each detected pulse. Defaults to 0.01. noise_std_multiplier (float, optional): Multiplier for noise standard deviation to set detection threshold. Defaults to 3.0. data_time_signal_kwargs (dict, optional): Additional keyword arguments for DataTimeSignalData. Returns: List[MultiTimeSignalData]: A list containing a single `MultiTimeSignalData` instance: - `high_threshold_pulses`: List of `DataTimeSignalData` for pulses above `first_signal_threshold`. - `low_threshold_pulses`: List of `DataTimeSignalData` for pulses above `second_signal_threshold` but below `first_signal_threshold`. """ if data_time_signal_kwargs is None: data_time_signal_kwargs = {} # Ensure thresholds are valid if second_signal_threshold >= first_signal_threshold: raise ValueError( "second_signal_threshold must be less than first_signal_threshold." ) # Extract high-threshold pulses high_threshold_pulses = extract_pulses_from_signal( full_data=signal_data, pre_pulse_time_s=first_pre_pulse_time_s, post_pulse_time_s=first_post_pulse_time_s, noise_std_multiplier=noise_std_multiplier, min_pulse_height=first_signal_threshold, data_time_signal_kwargs=data_time_signal_kwargs, ) # Extract low-threshold pulses low_threshold_pulses = extract_pulses_from_signal( full_data=signal_data, pre_pulse_time_s=second_pre_pulse_time_s, post_pulse_time_s=second_post_pulse_time_s, noise_std_multiplier=noise_std_multiplier, min_pulse_height=second_signal_threshold, data_time_signal_kwargs=data_time_signal_kwargs, ) # Filter low-threshold pulses by excluding those that exceed the first_signal_threshold filtered_low_threshold_pulses = [ pulse for pulse in low_threshold_pulses if not is_pulse_above_threshold(pulse, first_signal_threshold) ] # Function to find the peak time of a pulse def get_pulse_peak_time(pulse: TimeSignalData) -> float: if not pulse.data or not pulse.time_s: return float("inf") # Assign a large value if pulse data is empty max_idx = np.argmax(pulse.data) return pulse.time_s[max_idx] # Get peak times for all high threshold pulses high_pulse_peak_times = [ get_pulse_peak_time(pulse) for pulse in high_threshold_pulses ] # Filter low_threshold_pulses to ensure they are at least `trigger_delay_s` away from any high_threshold_pulse filtered_low_threshold_pulses = [] for low_pulse in low_threshold_pulses: low_pulse_peak_time = get_pulse_peak_time(low_pulse) # print("low") # print(low_pulse_peak_time) # Check distance from all high pulse peaks for high_peak in high_pulse_peak_times: # print("high") # print(high_peak) # print("diff") # print(abs(high_peak - low_pulse_peak_time)) if ( (abs(low_pulse_peak_time - high_peak) > trigger_delay_s) and (high_peak < low_pulse_peak_time) and (high_peak > low_pulse_peak_time - trigger_window_s) ): filtered_low_threshold_pulses.append(low_pulse) # print(True) # Additionally, ensure that low_threshold_pulses do not exceed the first_signal_threshold # This is to categorize pulses exclusively final_low_threshold_pulses = [ pulse for pulse in filtered_low_threshold_pulses if not is_pulse_above_threshold(pulse, first_signal_threshold) ] return [high_threshold_pulses, final_low_threshold_pulses]
[docs] def split_compose_per_pulse_threshold( signal_data: TimeSignalData, first_signal_threshold: float, second_signal_threshold: float, trigger_delay_s: float, first_pre_pulse_time_s: float = 1e-9, first_post_pulse_time_s: float = 1e-9, second_pre_pulse_time_s: float = 1e-9, second_post_pulse_time_s: float = 1e-9, noise_std_multiplier: float = 3.0, start_time_s: Optional[float] = None, end_time_s: Optional[float] = None, data_time_signal_kwargs: Optional[Dict] = None, ) -> MultiTimeSignalData: """ Separates pulses in a signal into two categories based on two threshold values. Parameters: signal_data (TimeSignalData): The input signal data containing multiple pulses. first_signal_threshold (float): The higher threshold to categorize pulses. second_signal_threshold (float): The lower threshold to categorize pulses. trigger_delay_s (float): Minimum time (in seconds) between pulses to prevent overlap. first_pre_pulse_time_s (float, optional): Time (in seconds) to include before each detected first pulse. Defaults to 0.01. first_post_pulse_time_s (float, optional): Time (in seconds) to include after each detected first pulse. Defaults to 0.01. second_pre_pulse_time_s (float, optional): Time (in seconds) to include before each detected second pulse. Defaults to 0.01. second_post_pulse_time_s (float, optional): Time (in seconds) to include after each detected second pulse. Defaults to 0.01. noise_std_multiplier (float, optional): Multiplier for noise standard deviation to set detection threshold. Defaults to 3.0. data_time_signal_kwargs (dict, optional): Additional keyword arguments for DataTimeSignalData. start_time_s (float, optional): Start time of the composed signal. If not provided, uses the first pulse's start time. end_time_s (float, optional): End time of the composed signal. If not provided, uses the last pulse's end time. Returns: List[TimeSignalData]: The composed full signals as [low_threshold_pulse_signal, high_threshold_pulse_signal] """ high_threshold_pulse_list, low_threshold_pulse_list = separate_per_pulse_threshold( signal_data=signal_data, first_signal_threshold=first_signal_threshold, second_signal_threshold=second_signal_threshold, trigger_delay_s=trigger_delay_s, first_pre_pulse_time_s=first_pre_pulse_time_s, first_post_pulse_time_s=first_post_pulse_time_s, second_post_pulse_time_s=second_post_pulse_time_s, second_pre_pulse_time_s=second_pre_pulse_time_s, noise_std_multiplier=noise_std_multiplier, data_time_signal_kwargs=data_time_signal_kwargs, ) low_threshold_pulse_signal = compose_pulses_into_signal( low_threshold_pulse_list, start_time_s=start_time_s, end_time_s=end_time_s, ) high_threshold_pulse_signal = compose_pulses_into_signal( high_threshold_pulse_list, start_time_s=start_time_s, end_time_s=end_time_s, ) return [low_threshold_pulse_signal, high_threshold_pulse_signal]