CadFromIcDetector#
- class mobgap.cadence.CadFromIcDetector(
- ic_detector: BaseIcDetector = cf(IcdShinImproved(axis='norm')),
- *,
- step_time_smoothing: BaseFilter = cf(HampelFilter(half_window_size=2, n_sigmas=3.0)),
- max_interpolation_gap_s: int = 3,
- silence_ic_warning: bool = False,
Calculate cadence per second by detecting initial contacts using a provided IC detector.
Warning
This method ignores the passed initial contacts and recalculates them from the data using the passed IC detector. If you want to use the ICs passed to the
calculatemethod, you should use theCadFromIcclass.This method will first calculate the initial contacts using the passed IC detector and then calculate the cadence per second from them.
This uses a robust outlier removal approach to deal with missing initial contacts. The output cadence is reported as the average for each 1 second bin within the data. Note that an incomplete second at the end is included, to make sure that the entire data range is covered.
Regions (i.e. second bouts) with no initial contacts are interpolated linearly based on the surrounding values, if the gap is smaller than the specified maximum interpolation gap. Regions without initial contacts that are larger than the specified maximum interpolation gap or at the very start or end of the recording are filled with NaNs.
For more details see the Notes section.
- Parameters:
- ic_detector
The IC detector used to detect the initial contacts.
- step_time_smoothing
The filter used to smooth the step time. This is used to remove outliers in the step time/cadence (e.g. when initial contacts are not detected). The filter is applied twice, once to the raw step time and a second time on the interpolated step time values per second. We recommend to use a Hampel filter for this.
- max_interpolation_gap_s
The maximum gap in seconds that is interpolated. If the gap is larger than this value, the second is filled with NaNs. We don’t fill “gaps” at the start and end of the recording, as we assume that gait sequences are cut anyway to start and end with a valid initial contact.
- silence_ic_warning
By default the method warns you, that it ignores the passed initial contacts and recalculates them from the data using the passed IC detector. We do that, as it is likely that users forget about this and might be surprised by the results. If you are aware of this and want to silence this warning, you can pass
silence_ic_warning=True.
- Other Parameters:
- data
The raw IMU data in the body passed to the
calculatemethod.- initial_contacts
The initial contacts passed to the
calculatemethod.- sampling_rate_hz
The sampling rate of the IMU data in Hz passed to the
calculatemethod.
- Attributes:
- cadence_per_sec_
The main output of the cadence calculation. It provides a DataFrame with the column
cadence_spmthat contains the cadence values with one value per full second of the provided data. The unit is1/min. The index of this dataframe is namedsec_center_samplesand contains the sample number of the center of the each second.- ic_detector_
The IC detector used to detect the initial contacts with the results attached. This is only available after the
detectmethod was called.- internal_ic_list_
The initial contacts detected by the
ic_detector_. This is equivalent toic_detector_.initial_contacts_.
Notes
The full process of calculating the cadence per second from initial contacts is as follows:
We calculate the step time from the initial contacts. This results in one less step time than initial contacts. We replicate the last step time to get the same number of step times as initial contacts, to also provide a cadence value for the last initial contact for the last part of a gait sequence.
We smooth the step time to remove outliers (using a Hampel filter by default).
We calculate the step time per second by averaging the step time over the second. This is a little tricky, as some seconds might not contain any initial contacts. For all seconds that contain at least one initial contact, we calculate the average step time over the second. The step time for seconds without initial contacts is interpolated linearly based on the surrounding values. If the gap is larger than the specified maximum interpolation gap, the second is filled with NaNs. We don’t fill “gaps” at the start and end of the recording, as we assume that gait sequences are cut anyway to start and end with a valid initial contact.
We smooth the step time per second again to remove outliers on a per second level.
We calculate the cadence per second by taking the inverse of the step time per second.
In case there are less initial contacts than the window size of the smoothing filter, we return NaNs for all seconds.
This approach deviates from the original Matlab implementations in a couple of ways:
The original Matlab implementation calculates the cadence first (before any smoothing) or interpolation, while we perform all calculations on the step time and only calculate the cadence at the very end. The reasoning behind that is that because cadence is calculated by taking an inverse, it is much harder to reason about outliers in the cadence values. Because of this “projection” it is also much harder to reason about what the effect of linear interpolation on the cadence values is.
The original Matlab implementation has no concept of “maximum interpolation gap”. Values are interpolated by taking the average of the surrounding values, independent of the length of the gap. We decided to introduce a maximum interpolation gap to not mask “issues” in the ic-detection.
When values are interpolated, the original Matlab implementation uses the average of the surrounding values. We decided to use linear interpolation instead, as this is more robust to outliers. These approaches are identical, if we only need to interpolate a single value, but for larger gaps, linear interpolation will start favoring the “closer” neighboring values, which we think is more appropriate.
The original Matlab implementation extrapolates missing values at the start and end of the recording, by simply repeating the first/last value. This was done, to make sure that subsequent calculations don’t need to deal with NaNs, but can easily mask issues in the ic-detection.
Methods
calculate(data, *, initial_contacts, ...)Calculate cadence from the passed data and initial contacts..
clone()Create a new instance of the class with all parameters copied over.
get_params([deep])Get parameters for this algorithm.
set_params(**params)Set the parameters of this Algorithm.
- __init__(
- ic_detector: BaseIcDetector = cf(IcdShinImproved(axis='norm')),
- *,
- step_time_smoothing: BaseFilter = cf(HampelFilter(half_window_size=2, n_sigmas=3.0)),
- max_interpolation_gap_s: int = 3,
- silence_ic_warning: bool = False,
- calculate(
- data: DataFrame,
- *,
- initial_contacts: DataFrame,
- sampling_rate_hz: float,
- **_: Unpack[dict[str, Any]],
Calculate cadence from the passed data and initial contacts..
- Parameters:
- data
The raw IMU data in the body frame. We usually assume that this is one gait sequence (i.e. that there are no non-walking periods in the data).
- initial_contacts
The initial contacts of the gait sequence. This should be passed as a DataFrame with the colum
icthat contains the sample number of the initial contacts. We usually assume that the first IC marks the start of the passed gait sequence and the last IC marks the end.- sampling_rate_hz
The sampling rate of the IMU data in Hz.
- Returns:
- self
The instance of the class with the
cadence_per_sec_attribute set to the calculated cadence.
- clone() Self[source]#
Create a new instance of the class with all parameters copied over.
This will create a new instance of the class itself and all nested objects
- get_params(deep: bool = True) dict[str, Any][source]#
Get parameters for this algorithm.
- Parameters:
- deep
Only relevant if object contains nested algorithm objects. If this is the case and deep is True, the params of these nested objects are included in the output using a prefix like
nested_object_name__(Note the two “_” at the end)
- Returns:
- params
Parameter names mapped to their values.