Skip to content

Feature extraction

eitprocessing.features.breath_detection.BreathDetection dataclass

BreathDetection(
    *,
    minimum_duration: float = 2 / 3,
    averaging_window_duration: float = 15,
    averaging_window_function: Callable[[int], ArrayLike] | None = blackman,
    amplitude_cutoff_fraction: float | None = 0.25,
    invalid_data_removal_window_length: float = 0.5,
    invalid_data_removal_percentile: int = 5,
    invalid_data_removal_multiplier: int = 4
)

Algorithm for detecting breaths in data representing respiration.

This algorithm detects the position of breaths in data by detecting valleys (local minimum values) and peaks (local maximum values) in data. BreathDetection has a default minimum duration of breaths to be detected. The minimum duration should be short enough to include the shortest expected breath in the data. The minimum duration is implemented as the minimum time between peaks and between valleys.

Examples:

>>> bd = BreathDetection(minimum_duration=0.5)
>>> breaths = bd.find_breaths(
...     sequency=seq,
...     continuousdata_label="global_impedance_(raw)"
... )

>>> global_impedance = seq.continuous_data["global_impedance_(raw)"]
>>> breaths = bd.find_breaths(continuous_data=global_impedance)
PARAMETER DESCRIPTION
minimum_duration

minimum expected duration of breaths, defaults to 2/3 of a second

TYPE: float DEFAULT: 2 / 3

averaging_window_duration

duration of window used for averaging the data, defaults to 15 seconds

TYPE: float DEFAULT: 15

averaging_window_function

function used to create a window for averaging the data, defaults to np.blackman

TYPE: Callable[[int], ArrayLike] | None DEFAULT: blackman

amplitude_cutoff_fraction

fraction of the median amplitude below which breaths are removed, defaults to 0.25

TYPE: float | None DEFAULT: 0.25

invalid_data_removal_window_length

window around invalid data in which breaths are removed, defaults to 0.5

TYPE: float DEFAULT: 0.5

invalid_data_removal_percentile

the nth percentile of values used to remove outliers, defaults to 5

TYPE: int DEFAULT: 5

invalid_data_removal_multiplier

the multiplier used to remove outliers, defaults to 4

TYPE: int DEFAULT: 4

find_breaths

find_breaths(
    continuous_data: ContinuousData,
    result_label: str = "breaths",
    sequence: Sequence | None = None,
    store: bool | None = None,
) -> IntervalData

Find breaths based on peaks and valleys, removing edge cases and breaths during invalid data.

First, it naively finds any peaks that are a certain distance apart and higher than the moving average, and similarly valleys that are a certain distance apart and below the moving average.

Next, valleys at the start and end of the signal are removed to ensure the first and last valleys are actual valleys, and not just the start or end of the signal. Peaks before the first or after the last valley are removed, to ensure peaks always fall between two valleys.

At this point, it is possible multiple peaks exist between two valleys. Lower peaks are removed leaving only the highest peak between two valleys. Similarly, multiple valleys between two peaks are reduced to only the lowest valley.

As a last step, breaths with a low amplitude (the average between the inspiratory and expiratory amplitudes) are removed.

Breaths are constructed as a valley-peak-valley combination, representing the start of inspiration, the end of inspiration/start of expiration, and end of expiration.

PARAMETER DESCRIPTION
continuous_data

optional, a ContinuousData object that contains the data

TYPE: ContinuousData

result_label

label of the returned IntervalData object, defaults to 'breaths'.

TYPE: str DEFAULT: 'breaths'

sequence

optional, Sequence that contains the object to detect breaths in, and/or to store the result in

TYPE: Sequence | None DEFAULT: None

store

whether to store the result in the sequence, defaults to True if a Sequence if provided.

TYPE: bool | None DEFAULT: None

RETURNS DESCRIPTION
IntervalData

An IntervalData object containing Breath objects.

eitprocessing.features.pixel_breath.PixelBreath dataclass

PixelBreath(
    *,
    breath_detection: BreathDetection = _return_sentinel_breath_detection(),
    breath_detection_kwargs: InitVar[dict | None] = None,
    phase_correction_mode: (
        Literal["negative amplitude", "phase shift", "none"] | None
    ) = "negative amplitude"
)

Algorithm for detecting timing of pixel breaths in pixel impedance data.

This algorithm detects the position of start of inspiration, end of inspiration and end of expiration in pixel impedance data. It uses BreathDetection to find the global start and end of inspiration and expiration. These points are then used to find the start/end of pixel inspiration/expiration in pixel impedance data.

Since this algorithm uses the previous and next global breath to determine the start and end of a pixel breath, the first and last global breaths can not be used to determine pixel breaths. They are always set to None in the return list. Breaths that could not properly be detected are set to None as well.

Some pixel breaths may be phase shifted (inflation starts and ends later compared to others, e.g., due to pendelluft or late airway opening). Other pixel breaths may have a negative amplitude (impedance decreases during inspiration, e.g., due to pleural effusion or reconstruction artifacts). It is not always possible to determine whether a pixel is out of phase or has a negative amplitude. PixelBreath has three different phase correction modes. In 'negative amplitude' mode (default), pixels that have a decrease in amplitude between the start and end of globally defined inspiration, will have a negative amplitude and smaller phase shift. In 'phase shift' mode, all pixel breaths will have positive amplitudes, but can have large phase shifts. In 'none'/None mode, all pixels are assumed to be within rouglhy -90 to 90 degrees of phase. Note that the 'none' mode can lead to unexpected results, such as ultra-short (down to 2 frames) or very long breaths.

Example:

>>> pi = PixelBreath()
>>> eit_data = sequence.eit_data['raw']
>>> continuous_data = sequence.continuous_data['global_impedance_(raw)']
>>> pixel_breaths = pi.find_pixel_breaths(eit_data, continuous_data, sequence)

PARAMETER DESCRIPTION
breath_detection

BreathDetection object to use for detecting breaths.

TYPE: BreathDetection DEFAULT: _return_sentinel_breath_detection()

phase_correction_mode

How to resolve pixels that are out-of-phase. Defaults to "negative amplitude".

TYPE: Literal['negative amplitude', 'phase shift', 'none'] | None DEFAULT: 'negative amplitude'

find_pixel_breaths

find_pixel_breaths(
    eit_data: EITData,
    continuous_data: ContinuousData,
    sequence: Sequence | None = None,
    store: bool | None = None,
    result_label: str = "pixel_breaths",
) -> IntervalData

Find pixel breaths in the data.

This method finds the pixel start/end of inspiration/expiration based on the start/end of inspiration/expiration as detected in the continuous data.

For most pixels, the start of a breath (start inspiration) is the valley between the middles (start of expiration) of the globally defined breaths on either side. The end of a pixel breath is the start of the next pixel breath. The middle of the pixel breath is the peak between the start and end of the pixel breath.

If the pixel is out of phase or has negative amplitude, the definition of the breath depends on the phase correction mode. In 'negative amplitude' mode, the start of a breath is the peak between the middles of the globally defined breaths on either side, while the middle of the pixel breath is the valley of the start and end of the pixel breath. In 'phase shift' mode, first the phase shift between the pixel impedance and global impedance is determined as the highest crosscorrelation between the signals near a phase shift of 0. The start of breath is the valley between the phase shifted middles of the globally defined breaths on either side.

Pixel breaths are constructed as a valley-peak-valley combination, representing the start of inspiration, the end of inspiration/start of expiration, and end of expiration.

PARAMETER DESCRIPTION
eit_data

EITData to apply the algorithm to.

TYPE: EITData

continuous_data

ContinuousData to use for global breath detection.

TYPE: ContinuousData

result_label

label of the returned IntervalData object, defaults to 'pixel_breaths'.

TYPE: str DEFAULT: 'pixel_breaths'

sequence

optional, Sequence that contains the object to detect pixel breaths in, and/or to store the result

TYPE: Sequence | None DEFAULT: None

store

whether to store the result in the sequence, defaults to True if a Sequence if provided.

TYPE: bool | None DEFAULT: None

RETURNS DESCRIPTION
IntervalData

An IntervalData object containing Breath objects.

RAISES DESCRIPTION
RuntimeError

If store is set to true but no sequence is provided.

ValueError

If the provided sequence is not an instance of the Sequence dataclass.

eitprocessing.features.moving_average.MovingAverage dataclass

MovingAverage(
    window_size: int,
    window_function: Callable | None = None,
    padding_type: str = "edge",
)

Algorithm for calculating the moving average of the data.

This class provides a method for calculating of the moving average of a 1D signal by convolution with a window with a given size. If not window function is provided, all samples within that window contribute equally to the moving average. If a window function is provided, the samples are weighed according to the values in the window function.

Before convolution the data is padded. The padding type is 'edge' by default. See np.pad() for more information. Padding adds values at the start and end with the first/last value, to more accurately determine the average at the boundaries of the data.

PARAMETER DESCRIPTION
window_size

the number of data points in the averaging window. Should be odd; is increased by 1 if even.

TYPE: int

window_function

window function, e.g. np.blackman.

TYPE: Callable | None DEFAULT: None

padding_type

see np.pad().

TYPE: str DEFAULT: 'edge'

RETURNS DESCRIPTION

np.ndarray: moving average of data with the same shape as data.

apply

apply(data: ndarray) -> ndarray

Apply the moving average on the data.

PARAMETER DESCRIPTION
data

input data as 1D array

TYPE: NDArray