bioevents package

Submodules

bioevents.colors module

class bioevents.colors.Colors[source]

Bases: object

BLUE1: str = '#6dc5d5'

bioevents.event_handling module

class bioevents.event_handling.Event(on, off=None, tolerances=None)[source]

Bases: object

abuts(other)[source]

Determines whether the onset of one event is the offset of the other

default_duration = 1
property duration
classmethod from_duration(on, duration, tolerances=None)[source]
property off
property on
overlaps(other)[source]

Determines whether this event “overlaps” another, based on various criteria.

Notes

It’s important to note that this overlap is not necessarily a “symmetrical” operation. Because the duration and tolerances of THIS event are used, self.overlaps(other) may differ from other.overlaps(self). This is by design, given that a “reference” or “truth” event should govern the acceptance criteria of “predicted” event annotations, and not the other way around.

property tolerances
class bioevents.event_handling.EventClassSeries(event_stack: Dict[IntEnum, EventSeries], missing_enum: IntEnum | None = None)[source]

Bases: EventStack

A special type of EventStack wherein every timestamp is expected to have one and only one class

Notes

Useful for performing class confusion or agreement analytic, such as with hypnogram annotations

as_array(round_method_on=<ufunc 'ceil'>, round_method_off=<ufunc 'floor'>)[source]

Export all events as a list of classes

epoch_confusion_matrix(other, normalize=None)[source]

Generates a confusion matrix with ‘other’, epoch-by-epoch via boolean logic

Parameters:
Returns:

mat

Return type:

pd.DataFrame

event_confusion_matrix(other, assimilate_events=True)[source]

Generate a class-wise confusion matrix with ‘other’

Parameters:
  • other (EventClassSeries)

  • assimilate_events (Optional(bool), default=True) – If true, any overlapping Events from either series will be counted as one.

Returns:

mat

Return type:

pd.DataFrame

classmethod from_array(class_array, sampling_rate: float = 1.0)[source]

Initialize from a 1-D array of classes

class bioevents.event_handling.EventSeries(events, duration=None, sampling_rate: float = 1.0)[source]

Bases: list

append(item)[source]

Overrides built-in default to make sure the Event is concurrent with this series

as_bools(round_method_on=<ufunc 'ceil'>, round_method_off=<ufunc 'floor'>)[source]

Converts the Events to a boolean array, using parameterizable rounding methods

as_dataframe()[source]

Export events to a DataFrame

compute_agreement(other, normalize=False)[source]

Computes an agreement matrix with the given EventSeries

Parameters:
  • other (EventSeries)

  • normalize (Optional(bool), default=False) – Normalize results rows by the number of events in the respective “reference” series

Returns:

mat – 2 x 2 agreement matrix

Return type:

pd.DataFrame

debounce(persistence_period: float, min_duration: float) EventSeries[source]

Debounce the given signal to remove short events and merge close ones

Parameters:
  • persistence_period (float) – merge any two events separated by less than this number (of samples)

  • min_duration (float) – after merging close events, remove all events under this duration (in samples)

Returns:

debounced

Return type:

EventSeries

References

Description of debouncing applied in engineering context: https://my.eng.utah.edu/~cs5780/debouncing.pdf

property duration
epoch_confusion_matrix(other, normalize=None)[source]

Generates a confusion matrix with the given EventSeries epoch-by-epoch via boolean logic

Parameters:
  • other (EventSeries)

  • normalize (Optional(bool), default=False)

Returns:

mat

Return type:

pd.DataFrame

event_confusion_matrix(other, normalize=None, assimilate_events=True)[source]

Generates a confusion matrix with the given EventSeries via Event-wise logic

Parameters:
  • other (EventSeries)

  • normalize (Optional(bool), default=False)

  • assimilate_events (Optional(bool), default=True) – If true, any overlapping Events from either series will be counted as one.

Returns:

mat

Return type:

pd.DataFrame

extend(item)[source]

Overrides built-in default to make sure the Events are concurrent with this series

classmethod from_bools(bools, sampling_rate: float = 1.0)[source]

Converts the given boolean array into a series of discrete Events

is_compatible(item)[source]

See if self vs. other functions can be used with this item

plot(bottom=0, top=1, color=None, as_seconds=True)[source]

Simple plot with events filled between the given y values

resample(sampling_rate)[source]
property sampling_rate
set_tolerances(vals)[source]

Iteratively set the tolerances for all Events to the given vals

spans_event(item)[source]

See if the given event falls within the time bounds of this series

trim(start: int | None = None, end: int | None = None)[source]
class bioevents.event_handling.EventStack(event_stack: Dict[IntEnum, EventSeries])[source]

Bases: dict

KEY_ASSIGNED_ENUM = 'AssignedEnum'
KEY_DURATION = 'duration'
KEY_EVENT_STACK = 'event_stack'
KEY_META = 'meta'
KEY_SAMPLING_RATE = 'sampling_rate'
property classes
property duration
classmethod from_dict(data: dict, intenum: IntEnum) EventStack[source]
classmethod from_string_keys(event_stack: Dict[str, EventSeries])[source]
plot()[source]

Stacks individual series onto the same plot

classmethod read_json(filepath, intenum: IntEnum)[source]

Construct from the given filepath

resample(sampling_rate)[source]

Make a new EventStack with resampled EventSeries values

property sampling_rate
set_tolerances(vals)[source]

Iteratively set the tolerances of all Events within all EventSeries

to_dict() dict[source]
trim(start: int | None = None, end: int | None = None)[source]

Make a new, trimmed version of this EventStack

write_json(filepath)[source]

Serializes and exports event data to a JSON file

Notes

We’re not exporting any OverlapTolerances here… seems like the kind of thing you’d only want to do explicitly during analysis anyway

class bioevents.event_handling.MissingDataLabels(value)[source]

Bases: IntEnum

An enumeration.

ARTIFACT = -2
DISAGREE = -3
MISSING = 0
UNSCORABLE = -1
class bioevents.event_handling.OverlapTolerances(diff_on=inf, diff_off=inf, ratio_on=inf, ratio_off=inf)[source]

Bases: object

class bioevents.event_handling.SeizureEvent(on, off=None, tolerances=None)[source]

Bases: Event

default_duration = 120
bioevents.event_handling.confusion_matrix(true, pred, normalize=None)[source]

Notes

This is a paraphrased implementation of scikit-learn’s. We can use that method, but this allows our footprint and dependencies to remain minimal.

bioevents.hypnogram module

class bioevents.hypnogram.Hypnogram(event_stack)[source]

Bases: EventClassSeries

generate_report() HypnogramReport[source]
latency_to_first_persistent_sleep() float | None[source]

Latency to first epoch of persistent (10 minutes) sleep.

Returns:

latency – If no persistent sleep occurs, returns None. If persistent sleep occurs, returns latency (in samples).

Return type:

Optional(float)

Notes

This metric is traditionally reported in minutes. Please take note of the sample_rate to convert if needed.

latency_to_stage(stage: SleepStages) float | None[source]

Time from lights off to first epoch of the given stage

Parameters:

stage (SleepStages)

Returns:

latency – If stage does not occur, returns None. Otherwise, returns latency (in samples).

Return type:

Optional[float]

Notes

This metric is traditionally reported in minutes. Please take note of the sample_rate to convert if needed.

plot(unit='seconds', ax=None, color=None, alpha=1, label=None, **plt_kwargs)[source]

Stacks individual series onto the same plot

Parameters:
  • unit (str, Optional[default="seconds"]) – Must be one of the keyword args to datetime.timedelta This will affect the x-axis label’s units.

  • ax (plt.Axes, Optional)

  • color (plt.colors[Optional]) – Color to use for the hypnogram and any non-sleep labels

  • alpha (float, Optional[default=1]) – Alpha to use for hypnogram. A gradient thereof will be used for any non-sleep labels.

  • label (str, Optional) – Label to use for this hypnogram. Useful when plotting multiple hypnograms on the given axis “ax”

  • plt_kwargs (kwargs) – Any additional keyword args meant for plt.plot AND plt.fill_betweenx

Notes

This function is better aligned with the format of a hypnogram plot that is expected in sleep science.

classmethod read_json(filepath, intenum=<enum 'Labels'>)[source]

Construct from the given filepath

sleep_efficiency_percent(bedtime_series: EventSeries | None = None) float[source]
sleep_onset_latency(n1_tolerance: float = 0) float | None[source]

Find sleep onset latency from a hypnogram (in samples). Assume the start of the hypnogram is the start of bedtime (light off).

Parameters:

n1_tolerance (float, default 0) – If 0 (default), return latency to sleep onset as the first non-Wake stage (used for overnight sleep) If positive, units are samples, and more rules are followed (see Notes)

Returns:

sleep_onset_latency – Time (in samples) from beginning of this hypnogram until the sleep onset. See Notes for more information on the definition of sleep onset.

Return type:

Optional(float)

Notes

This metric is traditionally reported in minutes. Please take note of the sample_rate to convert if needed. There are multiple ways to report SOL:

  1. The start of first scored epoch of any stage of sleep.

    Typically used in overnight PSG.

  2. The start of the first epoch of “unequivocal sleep”.

    Typically used in MWT for assessment of daytime sleepiness. “Unequivocal sleep” is “defined as 3 consecutive epochs of stage 1 sleep, or 1 epoch of any other stage of sleep” (see Reference #1). Epochs, in this case, are 30 seconds long. Thus, the first epoch of any of the following would be considered unequivocal sleep onset:

    • [N1, N1, N1, …] three epochs of N1

    • [N1, N1, S, …] two epochs of N1, followed immediately by any other stage of sleep

    • [N1, S, …] one epoch of N1, followed immediately by any other stage of sleep

    • [S, …] one epoch of any other stage of sleep

    For algorithmic purposes, this reduces to the observation that unequivocal sleep occurs at the onset of ANY sleep other than short, N1-only sleep episodes lasting less than 90 seconds.

References

  1. Clinical study using SOL as a primary endpoint: https://clinicaltrials.gov/ct2/show/NCT03522506

time_in_stage(stage: SleepStages) float[source]

Total time spent in the given stage, in samples.

total_recording_time(bedtime_series: EventSeries | None = None) float[source]

Find total recording time, i.e., total time in bed, from a hypnogram (in samples)

Parameters:

bedtime_series (Optional[event_handling.EventSeries]) – An event series derived from clinical lights off/on annotations. In the clinical setting, this is the period of time when subjects are expected to attempt sleep. If given, only compute recording time that occurs during bedtime.

Notes

This metric is traditionally reported in minutes. Please take note of the sample_rate to convert if needed.

total_sleep_time(bedtime_series: EventSeries | None = None) float[source]

Find total sleep time (in samples) from a hypnogram

Parameters:

bedtime_series (Optional[event_handling.EventSeries]) – An event series derived from clinical lights off/on annotations. In the clinical setting, this is the period of time when subjects are expected to attempt sleep. If given, only compute sleep time that occurs during bedtime.

Notes

This metric is traditionally reported in minutes. Please take note of the sample_rate to convert if needed.

wake_after_sleep_onset() float | None[source]

Compute WASO (wake after sleep onset) from a hypnogram. Useful for overnight sleep

Returns:

WASO – If SOL is None, returns None. If SOL is not None, returns wake after sleep onset (in samples).

Return type:

Optional(float)

Notes

This metric is traditionally reported in minutes. Please take note of the sample_rate to convert if needed.

class bioevents.hypnogram.HypnogramReport(*, duration_minutes: float, latency_minutes_rem: float | None = None, latency_minutes_n1: float | None = None, latency_minutes_n2: float | None = None, latency_minutes_n3: float | None = None, latency_minutes_persistent_sleep: float | None = None, latency_minutes_rem_with_persistence: float | None = None, latency_minutes_sleep_onset: float | None = None, latency_minutes_sleep_onset_unequivocal: float | None = None, total_minutes_rem: float, total_minutes_n1: float, total_minutes_n2: float, total_minutes_n3: float, total_minutes_nrem: float, total_minutes_sleep: float, efficiency_rem: float, efficiency_rem_first_two_hours: float, efficiency_sleep: float, event_count_rem: int, event_count_wake: int, event_count_wake_persistent: int, wake_after_sleep_onset_minutes: float | None = None)[source]

Bases: BaseModel, ABC

Used for the EDS reports

classmethod check_awakenings(values)[source]
classmethod check_efficiencies(values)[source]
classmethod check_no_rem(values)[source]
classmethod check_total_times_add_up(values)[source]
duration_minutes: float
efficiency_rem: float
efficiency_rem_first_two_hours: float
efficiency_sleep: float
event_count_rem: int
event_count_wake: int
event_count_wake_persistent: int
latency_minutes_n1: float | None
latency_minutes_n2: float | None
latency_minutes_n3: float | None
latency_minutes_persistent_sleep: float | None
latency_minutes_rem: float | None
latency_minutes_rem_with_persistence: float | None
latency_minutes_sleep_onset: float | None
latency_minutes_sleep_onset_unequivocal: float | None
model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[dict[str, FieldInfo]] = {'duration_minutes': FieldInfo(annotation=float, required=True, metadata=[Ge(ge=0)]), 'efficiency_rem': FieldInfo(annotation=float, required=True, metadata=[None, Interval(gt=None, ge=0, lt=None, le=1), None, None]), 'efficiency_rem_first_two_hours': FieldInfo(annotation=float, required=True, metadata=[None, Interval(gt=None, ge=0, lt=None, le=1), None, None]), 'efficiency_sleep': FieldInfo(annotation=float, required=True, metadata=[None, Interval(gt=None, ge=0, lt=None, le=1), None, None]), 'event_count_rem': FieldInfo(annotation=int, required=True, metadata=[Ge(ge=0)]), 'event_count_wake': FieldInfo(annotation=int, required=True, metadata=[Ge(ge=0)]), 'event_count_wake_persistent': FieldInfo(annotation=int, required=True, metadata=[Ge(ge=0)]), 'latency_minutes_n1': FieldInfo(annotation=Union[Annotated[float, Ge(ge=0)], NoneType], required=False, default=None), 'latency_minutes_n2': FieldInfo(annotation=Union[Annotated[float, Ge(ge=0)], NoneType], required=False, default=None), 'latency_minutes_n3': FieldInfo(annotation=Union[Annotated[float, Ge(ge=0)], NoneType], required=False, default=None), 'latency_minutes_persistent_sleep': FieldInfo(annotation=Union[Annotated[float, Ge(ge=0)], NoneType], required=False, default=None), 'latency_minutes_rem': FieldInfo(annotation=Union[Annotated[float, Ge(ge=0)], NoneType], required=False, default=None), 'latency_minutes_rem_with_persistence': FieldInfo(annotation=Union[Annotated[float, Ge(ge=0)], NoneType], required=False, default=None), 'latency_minutes_sleep_onset': FieldInfo(annotation=Union[Annotated[float, Ge(ge=0)], NoneType], required=False, default=None), 'latency_minutes_sleep_onset_unequivocal': FieldInfo(annotation=Union[Annotated[float, Ge(ge=0)], NoneType], required=False, default=None), 'total_minutes_n1': FieldInfo(annotation=float, required=True, metadata=[Ge(ge=0)]), 'total_minutes_n2': FieldInfo(annotation=float, required=True, metadata=[Ge(ge=0)]), 'total_minutes_n3': FieldInfo(annotation=float, required=True, metadata=[Ge(ge=0)]), 'total_minutes_nrem': FieldInfo(annotation=float, required=True, metadata=[Ge(ge=0)]), 'total_minutes_rem': FieldInfo(annotation=float, required=True, metadata=[Ge(ge=0)]), 'total_minutes_sleep': FieldInfo(annotation=float, required=True, metadata=[Ge(ge=0)]), 'wake_after_sleep_onset_minutes': FieldInfo(annotation=Union[Annotated[float, Ge(ge=0)], NoneType], required=False, default=None)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].

This replaces Model.__fields__ from Pydantic V1.

total_minutes_n1: float
total_minutes_n2: float
total_minutes_n3: float
total_minutes_nrem: float
total_minutes_rem: float
total_minutes_sleep: float
wake_after_sleep_onset_minutes: float | None
class bioevents.hypnogram.Labels(value)[source]

Bases: IntEnum

An enumeration.

ARTIFACT = -2
DISAGREE = -3
MISSING = 0
N1 = 3
N2 = 2
N3 = 1
REM = 4
UNSCORABLE = -1
W = 5
class bioevents.hypnogram.SleepStages(value)[source]

Bases: IntEnum

An enumeration.

N1 = 3
N2 = 2
N3 = 1
REM = 4
W = 5

Module contents

Top-level package for BioEvents.