Пример #1
0
def end_pad_psg(psg, hyp, sample_rate, pad_value=0.0, check_lengths=False):
    """
    TODO

    Args:
        psg:
        hyp:
        sample_rate:
        pad_value:
        check_lengths:

    Returns:

    """
    n_seconds = hyp.total_duration - psg.shape[0] / sample_rate
    if n_seconds < 0:
        raise StripError("Hypnogram should be longer than PSG for "
                         "'end_pad_psg' to make sense. Got a negative time "
                         "difference of {} seconds.".format(n_seconds))
    n_inserts = int(n_seconds * sample_rate)
    padded_psg = np.empty(shape=[psg.shape[0] + n_inserts, psg.shape[1]],
                          dtype=psg.dtype)
    padded_psg[:len(psg)] = psg
    padded_psg[len(psg):] = pad_value
    if check_lengths and not assert_equal_length(padded_psg, hyp, sample_rate):
        raise _STRIP_ERR
    return padded_psg
Пример #2
0
def strip_hyp_to_match_psg_len(psg, hyp, sample_rate, check_lengths=False):
    """
    Strips a (longer) hypnogram to match the length of a (shorter) PSG
    See the SparseHypnogram.set_new_end_time method
    See drop_class function for argument description.
    """
    psg_len_sec = psg.shape[0] / sample_rate
    diff_sec = hyp.end_time - psg_len_sec
    if diff_sec < 0:
        raise StripError("PSG length is larger than HYP length, "
                         "should not strip HYP. Consider the "
                         "'strip_psg_to_match_hyp_len' or 'strip_to_match' "
                         "functions")
    elif diff_sec == 0:
        return hyp
    if diff_sec % hyp.period_length_sec:
        raise StripError("Time difference between PSG and HYP ({} sec) not"
                         " evenly divisible by the period length "
                         "({} sec)".format(diff_sec, hyp.period_length_sec))
    hyp.set_new_end_time(hyp.end_time - diff_sec)
    if check_lengths and not assert_equal_length(psg, hyp, sample_rate):
        raise _STRIP_ERR
    return hyp
Пример #3
0
def strip_psg_to_match_hyp_len(psg, hyp, sample_rate, check_lengths=False):
    """
    Trims the tail of a PSG to match the length of a hypnogram.
    See drop_class function for argument description.
    """
    psg_len_sec = psg.shape[0] / sample_rate
    diff_sec = psg_len_sec - hyp.total_duration
    if diff_sec < 0:
        raise StripError("HYP length is larger than PSG length, "
                         "should not strip PSG. Consider the "
                         "'strip_hyp_match_psg_len' or 'strip_to_match' "
                         "functions")
    elif diff_sec == 0:
        return psg
    idx_to_strip = int(sample_rate * diff_sec)
    if check_lengths and not assert_equal_length(psg, hyp, sample_rate):
        raise _STRIP_ERR
    return psg[:-idx_to_strip]
Пример #4
0
def drop_class(psg,
               hyp,
               class_int,
               sample_rate,
               strip_only=False,
               check_lengths=False,
               call_strip_to_match=True):
    """
    Drops a sleep stage / class with integer value 'class_int' entirely. That
    is, all 'class_int' stages in SparseHypnogram 'hyp' will be dropped and
    init times will be recomputed. The corresponding PSG signal will likewise
    be removed entirely.

    This function is used mostly to remove 'UNKNOWN', 'OTHERS', 'NOT SCORED'
    type sleep stage classes, often collectively assigned class integer 5.

    Note that due to the re-computing of the hypnogram init times, one should
    no longer look up sleep stages in the new, stripped hypnogram using real
    time stamps from the study (second '100' in the old and new hypnogram may
    no longer correspond to the same data).

    Also note that dropping a class this way will cause flanking PSG segments
    to transition sharply/non smoothly if the dropped class was not in the
    head or tail of the study. This is, however, in our experiance, not an
    issue as these stages - in our applications - mostly occur near the
    beginning or end of the study and rarely in general.

    Args:
        psg:           A ndarray, PSG data, of shape [N, C]
        hyp:           A SparseHypnogram
        class_int:     Integer value corresponding to the class that should be
                       dropped.
        sample_rate:   The sample rate (Hz) of the passed PSG
        strip_only:    If True, only drop segments for the class connected to
                       the start or end of the hypnogram. I.e. if the class
                       appears in the middle of the hypnogram flanked by other
                       classes, it will not be removed.
        check_lengths: Assert that the PSG and HYP have equal length after the
                       stripping function has been applied. This is usually
                       wanted, but the default parameter is set to False for
                       all strip functions, as they may be used inside other
                       strip functions. The high-level 'apply_strip_func'
                       function always sets check_lengths=True on the
                       'top-level' strip function.
        call_strip_to_match: Call call_strip_to_match() at the end of
                             this function (before length check)

    Returns:
        psg, hyp
    """
    # Get all stages of class 'class_int'
    mask = hyp.stages == class_int
    if strip_only:
        mask = convert_to_strip_mask(mask)

    # Get init and duration drop masks
    inits_to_drop = hyp.inits[mask]
    durs_to_drop = hyp.durations[mask]

    # Find all PSG indices that should be removed
    inds_to_remove = []
    for i, (start_sec, dur) in enumerate(zip(inits_to_drop, durs_to_drop)):
        end_sec = start_sec + dur
        # Convert to indices
        start_idx = int(start_sec * sample_rate)
        end_idx = int(end_sec * sample_rate)
        inds_to_remove.extend(range(start_idx, min(len(psg), end_idx)))

    # Drop PSG on inds
    psg = np.delete(psg, inds_to_remove, axis=0)

    # Drop the class from the hypnogram
    keep_mask = ~mask
    durations = hyp.durations[keep_mask]
    stages = hyp.stages[keep_mask]

    # Re-compute inits
    inits = np.array([0] + list(np.cumsum(durations)[:-1]))

    # Create new hypnogram (just to perform some value checks)
    hyp = SparseHypnogram(inits, durations, stages, hyp.period_length_sec)

    if call_strip_to_match:
        psg, hyp = strip_to_match(psg, hyp, sample_rate=sample_rate)
    if check_lengths and not assert_equal_length(psg, hyp, sample_rate):
        raise StripError("Unexpected difference between PSG length ({} "
                         "seconds) and HYP length ({} seconds). This error "
                         "occurred in 'drop_class' strip func on class {} "
                         "for SleepPair with sample rate:\n{}".format(
                             psg.shape[0] / sample_rate, hyp.total_duration,
                             class_int, sample_rate))
    return psg, hyp
Пример #5
0
removing a certain class and/or ensure that a pair of PSG and HYP files match
each other in length

OBS: It is always assumed that the PSG and HYP files start at the same real
time. That is, they are aligned with respect to their first entries. Any
discrepancy between PSG and HYP lengths is assumed to follow from either of the
two extending longer in time at the end of the study. The data that extends
beyond the other file will normally be discarded (see strip functions below)
"""

import numpy as np
from utime import defaults
from utime.hypnogram import SparseHypnogram
from utime.errors import NotLoadedError, StripError

_STRIP_ERR = StripError("Unexpected difference between PSG and HYP lengths.")


def _strip(hyp, mask, inits, durs, stages, pop_from_start):
    """
    Helper function for 'strip_class_leading' and 'strip_class_trailing'
    Removes elements from beginning of lists 'inits', 'durs', 'stages'
    according to 'mask' if pop_from_start=True, otherwise from the end of those
    lists.
    """
    for m in mask:
        if not m:
            break
        if pop_from_start:
            inits.pop(0), durs.pop(0), stages.pop(0)
        else: